/* ============================================================================
   linguaskill-general.css - Moteur de test Linguaskill General (erosophia.eu)
   S'aligne sur les variables de theme du site (--bg-article, --text-color,
   --border-color, --shadow-color) comme partials/paywall.php : le clair/sombre
   suit automatiquement, sans regles dark dediees.
   ========================================================================== */

.ls-container { max-width: 980px; margin: 0 auto; padding: 16px; color: var(--text-color); }

/* -- Cartes / notices ---------------------------------------------------- */
.ls-card {
    background: var(--bg-article, #fff);
    border: 1px solid var(--border-color, #ddd);
    border-radius: 12px;
    box-shadow: 0 3px 14px var(--shadow-color, rgba(0,0,0,.08));
    padding: 22px 24px;
    margin: 16px 0;
}
/* Cartes de PASSATION et de RÉSULTAT : pas de conteneur — contenu à plat,
   comme l'examen réel. Épuré : ni cadre, ni ombre, ni arrondi. */
.ls-card.ls-task,
.ls-card.ls-passation,
.ls-card.ls-speaking-prompt,
.ls-card.ls-speaking-recorder,
.ls-card.ls-writing-answer,
.ls-card.ls-review,
.ls-card.ls-result-head {
    background: none;
    border: none;
    box-shadow: none;
    border-radius: 0;
    padding: 8px 0;
}
.ls-notice { text-align: center; }
.ls-warn   { color: #b00020; }
.ls-hint   { font-size: .9em; opacity: .75; margin: 10px 0 4px; }
.ls-preview-note {
    background: rgba(255,193,7,.14);
    border: 1px dashed var(--border-color, #ffc107);
    border-radius: 8px; padding: 10px 14px; font-size: .95em;
}

/* -- Chrome (bandeau Instructions + statut) ------------------------------ */
.ls-chrome {
    position: sticky; top: 0; z-index: 20;
    /* Full-bleed : les marges négatives étendent le bandeau jusqu'aux bords
       de la fenêtre sans utiliser 100vw (qui provoque un scroll horizontal).
       Le margin-top négatif annule le padding-haut de .ls-training pour que
       le bandeau colle au hero, sans liseré blanc. */
    margin-top: -16px;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    margin-bottom: 16px;
    background: #1f2430;
    color: #fff;
    overflow: hidden;
}
.ls-chrome-top {
    display: flex !important; justify-content: space-between; align-items: center;
    /* Contenu aligné sur la colonne centrée (1200px), pas collé aux bords. */
    max-width: 1200px; margin: 0 auto;
    padding: 10px 20px; font-weight: 500;
    cursor: pointer; list-style: none;
    color: #fff;
}
.ls-chrome-top::-webkit-details-marker { display: none; }
.ls-chrome-top::marker { content: ""; }
.ls-chrome-label {
    display: inline-flex; align-items: center; gap: 8px;
    font-weight: 600; color: #fff;
}
/* Flèche d'ouverture/fermeture de l'accordéon : plus grande et bien visible,
   avec une rotation douce entre ▸ (fermé) et ▾ (ouvert). */
.ls-chrome-label::before {
    content: "\25B8"; font-size: 1.2em; line-height: 1;
    display: inline-block; transition: transform .25s ease;
}
.ls-chrome-instructions[open] .ls-chrome-label::before { transform: rotate(90deg); }
/* (Icône « volume » décorative retirée : elle ne réglait rien et induisait en
   erreur. Le volume se règle via le navigateur / le système.) */
.ls-chrome-right {
    display: flex; gap: 16px; align-items: center;
    font-weight: 500; color: rgba(255,255,255,.85);
}
.ls-instr-body {
    max-width: 1200px; margin: 0 auto;
    padding: 4px 20px 14px; line-height: 1.5;
    color: rgba(255,255,255,.92);
    max-height: 40vh; overflow-y: auto;
}

.ls-progress { font-variant-numeric: tabular-nums; color: rgba(255,255,255,.85); }
.ls-timer { font-variant-numeric: tabular-nums; color: rgba(255,255,255,.85); }
/* Timer volontairement toujours blanc, même en fin de temps (pas d'alerte rouge). */
.ls-timer-warn .ls-timer-value, .ls-timer.ls-timer-warn { color: rgba(255,255,255,.85); font-weight: 400; }

/* -- Intro --------------------------------------------------------------- */
.ls-intro { text-align: center; padding: clamp(28px, 6vw, 56px) 20px; }

/* Logo : présence affirmée en haut de l'écran d'intro. */
.ls-intro-logo { margin: 0 auto; line-height: 0; }
.ls-intro-logo .ls-logo {
    display: inline-block; width: clamp(150px, 42vw, 232px); height: auto;
}
.ls-intro-logo .ls-logo svg { display: block; width: 100%; height: auto; }
/* Défense : certains thèmes globaux appliquent fill/currentColor aux svg ; on
   réaffirme les couleurs du logo (déjà en style inline dans le SVG). */
.ls-intro-logo .ls-logo svg path { fill: #494D57; }
.ls-intro-logo .ls-logo svg polygon { fill: #F5C518; }

/* Titre de l'activité (Speaking / Writing / Reading / Listening) : lisible et
   net, sans écraser le logo au-dessus. */
.ls-intro-title {
    font-size: clamp(1.5em, 4.6vw, 1.85em);
    font-weight: 800; line-height: 1.15;
    margin: clamp(8px, 1.8vw, 14px) 0 clamp(4px, 1vw, 8px);
}

/* Sous-titre « Instructions and Information » — identique sur les 4 modules. */
.ls-intro-subtitle {
    margin: 0 0 clamp(18px, 3.5vw, 26px);
    text-align: center; color: #5b6770;
    font-size: clamp(1em, 2.4vw, 1.1em); font-weight: 600;
}

/* Bloc d'instructions : conteneur centré de largeur maîtrisée. Les lignes
   restent centrées, mais la largeur limitée évite les coupures de phrases en
   plein milieu (le souci sur mobile venait d'une largeur trop grande). */
.ls-intro-lines {
    max-width: 600px; margin: 0 auto;
    display: flex; flex-direction: column; gap: clamp(8px, 1.6vw, 12px);
}
.ls-intro-line {
    margin: 0; line-height: 1.55; color: #3a4750;
    text-align: center; text-wrap: balance;
}
@media (max-width: 640px) {
    .ls-intro-lines { max-width: 40ch; }
    .ls-intro-line { font-size: .98em; }
}
.ls-startform { margin-top: 28px; }

/* -- Écran d'accès : vérification volume / micro -------------------------- */
/* Affiché seulement sur Listening (volume) et Speaking (volume + micro).
   Couleurs : variables de thème du site (comme le reste du fichier) — le mode
   sombre suit automatiquement, aucune règle dark dédiée.
   Le panneau est aligné à gauche : c'est un formulaire de réglage, pas du texte
   d'instructions ; le centrage nuirait à la lecture des libellés. */
.ls-check {
    max-width: 620px;
    margin: clamp(26px, 5vw, 38px) auto 0;
    padding: clamp(18px, 3.4vw, 26px);
    background: var(--bg-article, #fff);
    border: 1px solid var(--border-color, #ddd);
    border-radius: 14px;
    box-shadow: 0 3px 14px var(--shadow-color, rgba(0,0,0,.08));
    text-align: left;
    color: var(--text-color, #222);
}

/* Curseur de volume et bloc micro : sans JS ils ne règlent rien. Ils restent
   masqués tant que l'engine n'a pas posé .ls-check-js sur le panneau (le
   <audio controls> natif, lui, reste utilisable pour le volume).
   Règle volontairement limitée à .ls-check : la .ls-js-only globale est visible
   par défaut et sert à d'autres rendus — ne pas changer son comportement. */
.ls-check:not(.ls-check-js) .ls-js-only { display: none; }

/* En-tête : filet de marque à gauche, comme les encarts de score. */
.ls-check-title {
    display: flex; align-items: center; gap: 10px;
    margin: 0 0 6px; font-size: 1.02em; font-weight: 800;
    letter-spacing: .01em;
}
.ls-check-title::before {
    content: ""; width: 4px; height: 1.05em; border-radius: 2px;
    background: var(--ls-band, #FFD152); flex: none;
}
.ls-check-sub {
    margin: 0 0 18px; font-size: .9em; line-height: 1.55; opacity: .75;
}

/* Rangées : séparateur discret, jamais de bordure sous la dernière. */
.ls-check-row + .ls-check-row {
    margin-top: 18px; padding-top: 18px;
    border-top: 1px solid var(--border-color, #e6eaee);
}
.ls-check-head {
    display: flex; align-items: center; gap: 9px; margin-bottom: 3px;
}
.ls-check-label { font-weight: 700; font-size: .96em; }

/* Pastille d'icône : cercle neutre qui passe au jaune de marque une fois
   l'étape validée (.is-done posé par l'engine). */
.ls-check-ico {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--surface-2, rgba(127,127,127,.12));
    transition: background .25s ease, transform .25s ease;
}
/* Défense : certains thèmes globaux forcent fill/currentColor sur les <svg>.
   On réaffirme le tracé au niveau du <svg> ET de ses formes (toutes formes
   confondues), sinon les icônes apparaissent en aplats noirs. */
.ls-check-ico svg,
.ls-check-vol-ico svg {
    width: 14px; height: 14px; display: block;
    fill: none !important; stroke: currentColor !important;
}
.ls-check-ico svg > *,
.ls-check-vol-ico svg > * {
    fill: none !important; stroke: currentColor !important;
}
.ls-check-row.is-done .ls-check-ico {
    background: var(--ls-band, #FFD152); color: #14310e; transform: scale(1.06);
}

.ls-check-hint { margin: 0 0 12px; font-size: .86em; line-height: 1.55; opacity: .7; }

/* Ligne de commandes : bouton + statut, jamais de saut de ligne disgracieux. */
.ls-check-ctrls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ls-check-ctrls .btn-u { cursor: pointer; }

/* Curseur de volume : rangée compacte avec valeur en chiffres tabulaires pour
   que la largeur ne tremble pas pendant le glissement. */
.ls-check-vol {
    display: flex; align-items: center; gap: 12px; margin-top: 14px;
}
.ls-check-vol-ico { flex: none; opacity: .55; display: inline-flex; }
.ls-check-slider {
    flex: 1 1 auto; min-width: 0;
    accent-color: var(--ls-band, #FFD152);
    height: 22px; cursor: pointer;
}
.ls-check-vol-val {
    font-variant-numeric: tabular-nums; font-size: .84em;
    opacity: .7; min-width: 4ch; text-align: right;
}

/* Lecteur natif de l'échantillon : remplacé par .ls-sample-play quand JS est
   actif (le widget natif jure avec le reste). Sans JS, il reste visible. */
.ls-check-audio { width: 100%; max-width: 380px; display: block; }
.ls-check-js .ls-check-audio { display: none; }

/* Bouton « Play sample » : icône + libellé, alignés proprement. */
.ls-sample-play {
    display: inline-flex; align-items: center; gap: 8px;
}
.ls-sample-ico { display: inline-flex; }
.ls-sample-ico svg {
    width: 13px; height: 13px; display: block;
    fill: none !important; stroke: currentColor !important;
}
.ls-sample-ico svg > * { fill: none !important; stroke: currentColor !important; }
/* L'icône « play » est pleine : c'est la seule forme qu'on remplit. */
.ls-sample-play:not(.is-playing) .ls-sample-ico svg > polygon {
    fill: currentColor !important;
}
.ls-sample-play.is-playing .ls-sample-ico svg > rect { fill: currentColor !important; }
.ls-mic-playback { width: 100%; max-width: 380px; display: block; margin-top: 12px; }

/* Statut : réserve sa hauteur pour éviter tout saut de mise en page quand le
   texte apparaît. */
.ls-check-status {
    font-size: .84em; opacity: .75; min-height: 1.4em; line-height: 1.4;
}
/* Vert/rouge : teintes éclaircies via color-mix pour rester lisibles sur fond
   sombre ; repli en couleur fixe pour les navigateurs sans color-mix. */
.ls-check-status.is-ok   { color: #1d7a41; opacity: 1; font-weight: 600; }
.ls-check-status.is-err  { color: #b00020; opacity: 1; font-weight: 600; }
.ls-check-status.is-busy { opacity: .75; }
@supports (color: color-mix(in srgb, red, white)) {
    .ls-check-status.is-ok  { color: color-mix(in srgb, #24a35a 65%, var(--text-color, #222)); }
    .ls-check-status.is-err { color: color-mix(in srgb, #e5484d 65%, var(--text-color, #222)); }
}

/* VU-mètre segmenté (12 diodes) : plus lisible qu'une barre continue et fidèle
   aux enregistreurs matériels. L'engine allume les segments via .is-lit ; les
   3 derniers passent au rouge (saturation). */
.ls-meter {
    display: flex; gap: 3px; margin-top: 12px; height: 12px;
}
.ls-meter-seg {
    flex: 1 1 0; border-radius: 2px;
    background: var(--surface-2, rgba(127,127,127,.16));
    transition: background .07s linear, opacity .07s linear;
}
.ls-meter-seg.is-lit      { background: var(--ls-band, #FFD152); }
.ls-meter-seg.is-lit.is-hot { background: #d32f2f; }
.ls-meter.is-idle .ls-meter-seg { opacity: .5; }

/* Bouton de test en cours : pulsation sobre, alignée sur .ls-rec-start. */
.ls-mic-test.is-testing {
    background: #d32f2f; border-color: #d32f2f; color: #fff;
}

/* Compte à rebours du test micro. */
.ls-mic-countdown { font-variant-numeric: tabular-nums; font-size: .84em; opacity: .6; }

/* Bilan : affiché une fois les étapes validées, juste au-dessus du Start. */
.ls-check-done {
    display: none; align-items: center; gap: 8px;
    margin: 16px 0 0; padding-top: 14px;
    border-top: 1px solid var(--border-color, #e6eaee);
    font-size: .88em; font-weight: 600; color: #1d7a41;
}
@supports (color: color-mix(in srgb, red, white)) {
    .ls-check-done { color: color-mix(in srgb, #24a35a 65%, var(--text-color, #222)); }
}
.ls-check.is-ready .ls-check-done { display: flex; animation: ls-check-in .35s ease both; }
@keyframes ls-check-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}

@media (max-width: 640px) {
    .ls-check { padding: 16px; }
    .ls-check-audio, .ls-mic-playback { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
    .ls-check-ico, .ls-meter-seg { transition: none; }
    .ls-check-row.is-done .ls-check-ico { transform: none; }
    .ls-check.is-ready .ls-check-done { animation: none; }
}
/* Le panneau de réglage n'a aucun sens sur une feuille imprimée. */
@media print { .ls-check { display: none; } }

/* -- Boutons (completent buttons.css sans le dupliquer) ------------------ */
.ls-btn {
    display: inline-block; padding: 10px 22px; border-radius: 8px;
    border: 1px solid var(--border-color, #bbb);
    background: var(--bg-article, #fff); color: var(--text-color, #222);
    font-size: 1em; cursor: pointer; text-decoration: none;
}
.ls-btn:hover { filter: brightness(.96); }
.ls-btn-primary { background: #1f7cff; border-color: #1f7cff; color: #fff; font-weight: 600; }
.ls-btn-primary:hover { filter: brightness(1.08); }

/* -- Passages & questions ------------------------------------------------ */
.ls-passage { line-height: 1.7; font-size: 1.02em; }
.ls-passage-title { margin: 0 0 4px; }
.ls-passage-subtitle { margin: 0 0 14px; opacity: .75; font-style: italic; }
.ls-prompt { line-height: 1.55; }
.ls-qnum {
    display: inline-block; min-width: 26px; text-align: center;
    background: var(--border-color, #eee); border-radius: 6px;
    font-weight: 700; font-size: .9em; padding: 1px 5px; margin-right: 4px;
}
.ls-question { border-top: 1px dashed var(--border-color, #e3e3e3); padding-top: 12px; margin-top: 14px; }
.ls-question:first-of-type { border-top: 0; }

/* -- Options radio : pastille circulaire portant la lettre ----------------
   Le radio natif est masqué VISUELLEMENT (pas display:none) : il reste dans le
   flux, focusable au clavier et lisible par les lecteurs d'écran. C'est la
   pastille .ls-opt-key qui matérialise l'état coché.
   Sans CSS, on retrouve le rendu natif : label + radio + lettre + texte. */
.ls-options { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 4px; }

.ls-option {
    position: relative;
    display: flex; gap: 12px; align-items: center;
    padding: 10px 14px;
    border: 1px solid var(--border-color, #ddd); border-radius: 10px;
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}

/* Radio natif : masqué à l'œil, conservé pour le clavier et l'accessibilité. */
.ls-option input[type="radio"] {
    position: absolute; width: 1px; height: 1px;
    margin: 0; padding: 0; border: 0;
    clip: rect(0 0 0 0); clip-path: inset(50%);
    overflow: hidden; white-space: nowrap;
}

/* La lettre devient la pastille : c'est elle qui indique la sélection.
   Sélecteur restreint aux options radio : la même classe sert aussi de simple
   légende dans les grilles de matching (.ls-grid-opt .ls-opt-key), qu'il ne
   faut pas transformer en cercle. */
.ls-option > .ls-opt-key {
    flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 50%;
    border: 1px solid var(--border-color, #ccc);
    background: transparent; color: #5b6770;
    font-weight: 700; font-size: .88em; line-height: 1;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.ls-option > .ls-opt-text { line-height: 1.5; }

/* Survol : discret. (Teinte de fond retirée — décision Boris 08/2026 :
   aucun fond sur les options ; la bordure seule marque le survol.) */
.ls-option:hover { border-color: #b9c6d4; }
.ls-option:hover > .ls-opt-key { border-color: #9fb2c6; }

/* Sélection : pastille pleine, contour de l'option souligné.
   (Teinte de fond retirée — décision Boris 08/2026 : la bordure bleue et
   la clé remplie suffisent comme indicateurs de sélection.) */
.ls-option:has(input:checked) {
    border-color: #1f7cff;
}
.ls-option:has(input:checked) > .ls-opt-key {
    background: #1f7cff; border-color: #1f7cff; color: #fff;
}

/* Focus clavier : anneau porté par l'option, puisque le radio est masqué. */
.ls-option:has(input:focus-visible) {
    outline: 2px solid #1f7cff; outline-offset: 2px;
}

/* Filet si :has() n'est pas supporté (navigateurs anciens) : sans lui, l'état
   coché serait INVISIBLE puisque le radio natif est masqué. On réaffiche alors
   le radio et on neutralise la pastille. */
@supports not selector(:has(*)) {
    .ls-option input[type="radio"] {
        position: static; width: auto; height: auto;
        clip: auto; clip-path: none; overflow: visible;
    }
    .ls-option > .ls-opt-key {
        width: auto; height: auto; border: 0; border-radius: 0;
        background: transparent; color: inherit;
    }
}

/* Options SANS texte (multiple-text-matching : seules les lettres A-D désignent
   un texte source). Une barre pleine largeur avec une pastille centrée n'aurait
   aucun sens : on passe en rangée compacte de pastilles cliquables. */
.ls-options:not(:has(.ls-opt-text)) {
    flex-direction: row; flex-wrap: wrap; gap: 10px;
}
/* L'option n'est plus qu'un conteneur transparent : sans cela, son propre
   border arrondi doublerait le cercle de la pastille (double anneau). */
.ls-options:not(:has(.ls-opt-text)) .ls-option {
    flex: none; padding: 0; border: 0; border-radius: 999px;
    background: transparent;
}
.ls-options:not(:has(.ls-opt-text)) .ls-option:hover,
.ls-options:not(:has(.ls-opt-text)) .ls-option:has(input:checked) {
    border-color: transparent; background: transparent;
}
.ls-options:not(:has(.ls-opt-text)) .ls-option > .ls-opt-key {
    width: 40px; height: 40px; font-size: .95em;
}

/* Impression : pas d'aplat de couleur, on marque la sélection par le contour. */
@media print {
    .ls-option { border-color: #999; background: transparent; }
    .ls-option:has(input:checked) > .ls-opt-key { background: transparent; color: #000; border-color: #000; border-width: 2px; }
}

/* -- Gaps ouverts (open-gap-fill) ---------------------------------------- */
.ls-gap-wrap { white-space: nowrap; }
.ls-gap-num {
    display: inline-block; font-size: .75em; font-weight: 700;
    background: var(--border-color, #eee); border-radius: 5px;
    padding: 0 5px; margin-right: 3px; vertical-align: middle;
}
.ls-gap-input {
    width: 110px; padding: 3px 8px; font: inherit;
    border: 0; border-bottom: 2px solid #1f7cff; border-radius: 4px 4px 0 0;
    background: rgba(31,124,255,.07); color: inherit;
}
.ls-gap-input:focus { outline: 2px solid #1f7cff; outline-offset: 1px; }
/* Blanc à compléter : le trait est le border-bottom (pas d'underscores dans le
   HTML, sinon deux traits se superposent). La largeur minimale tient lieu de
   « longueur du blanc ». */
.ls-gap-blank {
    display: inline-block; min-width: 84px;
    border-bottom: 2px solid var(--text-color, #444);
    vertical-align: baseline;
}

/* -- mc-gap-fill : gaps cliquables + panneau du gap actif ----------------
   Rendu calqué sur l'écran officiel : le trou est un rectangle PLEIN bleu pâle,
   précédé d'une pastille portant son numéro LOCAL (1..5). Le trou actif passe en
   blanc, bordé de bleu, avec sa pastille en bleu plein. Une fois rempli, le mot
   choisi s'affiche dans le rectangle. */
.ls-gapbtn {
    font: inherit; cursor: pointer;
    display: inline-flex; align-items: stretch; gap: 0;
    padding: 0; overflow: hidden;
    border: 1px solid transparent; border-radius: 4px;
    background: transparent; color: inherit;
    vertical-align: baseline; line-height: 1.4;
}

/* Pastille du numéro : sombre au repos, bleue quand le gap est actif. */
.ls-gapbtn-num {
    flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 22px; padding: 2px 6px;
    background: #2b3038; color: #fff;
    font-size: .8em; font-weight: 700; line-height: 1.4;
}

/* Le blanc lui-même : aplat bleu pâle, largeur FIXE. Surtout pas de flex-grow :
   le rectangle s'étirerait selon la place restante sur sa ligne, donnant des
   trous de largeurs différentes. Il ne s'élargit qu'au mot qu'il contient. */
.ls-gapbtn-text {
    flex: 0 0 auto;
    min-width: 96px; padding: 2px 10px;
    background: #e8eff9; color: inherit;
    text-align: center; white-space: nowrap;
}

.ls-gapbtn:hover .ls-gapbtn-text { background: #dce7f6; }

/* Gap actif : rectangle blanc cerné de bleu, pastille bleue. */
.ls-gapbtn.ls-gap-active { border-color: #1f7cff; }
.ls-gapbtn.ls-gap-active .ls-gapbtn-num  { background: #1f7cff; }
.ls-gapbtn.ls-gap-active .ls-gapbtn-text { background: #fff; }

/* Gap rempli : le mot choisi, en évidence. */
.ls-gapbtn.ls-gap-filled .ls-gapbtn-text { font-weight: 700; }

/* Focus clavier. */
.ls-gapbtn:focus-visible { outline: 2px solid #1f7cff; outline-offset: 2px; }

/* open-gap-fill : même pastille numérotée devant le champ de saisie. */
.ls-gap-wrap > .ls-gapbtn-num {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 22px; padding: 2px 6px; margin-right: 4px;
    background: #2b3038; color: #fff; border-radius: 4px;
    font-size: .8em; font-weight: 700; vertical-align: middle;
}
/* -- mc-gap-fill : bandeau de choix en bas d'écran ------------------------
   Calqué sur l'écran officiel : les quatre choix du gap actif occupent toute la
   largeur, en colonnes égales sur fond bleu plein, juste au-dessus de la barre
   de soumission. Un seul bandeau visible à la fois (celui du gap actif).
   Sans JS, tous les panneaux s'affichent en flux (voir <noscript>). */
.ls-gap-options {
    display: none;
    /* Le bandeau se cale AU-DESSUS de la barre de soumission fixe (passation).
       En entraînement il n'y a pas de barre : --ls-footer-h vaut 0 et le
       bandeau touche le bas de la fenêtre. Sans cette variable, un « bottom:
       60px » codé en dur laissait un vide sous les propositions. */
    position: fixed; left: 0; right: 0; z-index: 14;
    bottom: var(--ls-footer-h, 0px);
    margin: 0; padding: 0; border: 0;           /* reset <fieldset> */
    background: #0b63d6;
    box-shadow: 0 -2px 10px rgba(0,0,0,.10);
}
.ls-gap-options.active { display: block; }

/* La barre de soumission est désormais dans le flux (voir .ls-footerbar) :
   elle n'occupe plus le bas de la fenêtre, le bandeau de choix des gaps se
   cale donc directement au bord inférieur. La classe .ls-has-footerbar reste
   posée par les pages (sans effet), la variable vaut 0 partout. */
main.ls-has-footerbar { --ls-footer-h: 0px; }

/* La légende « Gap N » ne s'affiche PAS : la pastille bleue dans le texte indique
   déjà quel gap est actif (comme à l'examen). On la conserve toutefois dans le
   DOM, masquée visuellement, pour nommer le groupe de radios aux lecteurs d'écran. */
.ls-gap-options legend {
    position: absolute; width: 1px; height: 1px;
    margin: 0; padding: 0; border: 0;
    clip: rect(0 0 0 0); clip-path: inset(50%);
    overflow: hidden; white-space: nowrap;
}

/* Les quatre choix : colonnes égales, séparées par un filet clair. */
.ls-gap-options .ls-options {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    gap: 0; margin: 0;
}
.ls-gap-options .ls-option {
    display: flex; align-items: center; justify-content: center;
    min-height: 56px;                 /* hauteur de bande régulière */
    margin: 0; padding: 12px 8px;
    border: 0; border-left: 1px solid rgba(255,255,255,.25);
    border-radius: 0; background: transparent;
    color: #fff; text-align: center;
}
/* Les cellules de la grille s'étirent : le label occupe toute la hauteur de la
   rangée, ce qui rend son centrage vertical effectif. */
.ls-gap-options .ls-options { align-items: stretch; }
.ls-gap-options .ls-option:first-child { border-left: 0; }
.ls-gap-options .ls-option:hover { background: rgba(255,255,255,.10); }

/* La pastille de lettre n'a pas sa place ici : le mot suffit, comme à l'examen. */
.ls-gap-options .ls-option > .ls-opt-key { display: none; }
.ls-gap-options .ls-option > .ls-opt-text { font-size: 1.02em; }

/* Choix retenu : inversion, sobre. */
.ls-gap-options .ls-option:has(input:checked) {
    background: #fff; color: #0b63d6;
}
.ls-gap-options .ls-option:has(input:checked) > .ls-opt-text { font-weight: 700; }
.ls-gap-options .ls-option:has(input:focus-visible) {
    outline: 2px solid #fff; outline-offset: -4px;
}

/* Le texte du passage ne doit pas passer sous le bandeau. Le formulaire compense
   déjà la barre de soumission (84px) ; on ajoute ici la hauteur du bandeau (56px). */
/* Réserve sous le texte : hauteur du bandeau de choix (~76px) + celle de la
   barre de soumission si elle existe. Sinon le dernier gap se retrouve caché
   derrière le bandeau au moment où on le clique. */
.ls-mcgapfill { padding-bottom: calc(76px + var(--ls-footer-h, 0px)); }

/* En ENTRAÎNEMENT, le bandeau flotte au-dessus de tout ce qui suit la carte
   (boutons de niveau, retour au programme). On réserve la place en bas de page
   plutôt que de masquer ces liens : le candidat doit pouvoir sortir. */
main.ls-training:has(.ls-gap-options) { padding-bottom: 90px; }

/* Petit écran : deux colonnes plutôt que quatre illisibles. */
@media (max-width: 640px) {
    .ls-gap-options .ls-options { grid-auto-flow: row; grid-template-columns: 1fr 1fr; }
    .ls-gap-options .ls-option { min-height: 46px; padding: 10px 6px; border-top: 1px solid rgba(255,255,255,.25); }
    .ls-gap-options .ls-option:nth-child(-n+2) { border-top: 0; }
    .ls-gap-options .ls-option:nth-child(odd)  { border-left: 0; }
    /* Deux rangées de 46px : le bandeau double de hauteur, la réserve aussi. */
    .ls-mcgapfill { padding-bottom: calc(100px + var(--ls-footer-h, 0px)); }
    /* Le bouton de soumission est prioritaire : le lien de sortie cède la
       place et se contente de ce qui reste. Padding resserré des deux côtés
       pour que les deux boutons tiennent sur une ligne. */
    .ls-footerbar-inner { padding: 10px 12px; gap: 10px; }
    .ls-footerbar .ls-btn { padding: 9px 14px; font-size: .92em; }
    .ls-footerbar-home    { flex: 0 1 auto; }
    .ls-footerbar .ls-submit { flex: 0 0 auto; }
}

/* Impression / sans JS : le bandeau redevient un bloc en flux. */
@media print {
    .ls-gap-options { position: static; background: transparent; box-shadow: none; }
    .ls-gap-options .ls-option { color: inherit; }
}

/* -- multiple-text-matching ---------------------------------------------- */
.ls-texts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 8px; }
@media (max-width: 760px) { .ls-texts { grid-template-columns: 1fr; } }
.ls-text-card {
    border: 1px solid var(--border-color, #ddd); border-radius: 10px;
    padding: 12px 14px; font-size: .95em; line-height: 1.55;
    max-height: 320px; overflow-y: auto;
}
.ls-text-label { margin: 0 0 6px; }

/* -- missing-paragraph / missing-sentence -------------------------------- */
.ls-missing-layout { display: grid; grid-template-columns: 3fr 2fr; gap: 18px; }
@media (max-width: 860px) { .ls-missing-layout { grid-template-columns: 1fr; } }
.ls-block { margin: 10px 0; line-height: 1.6; }
.ls-dropzone {
    border: 2px dashed var(--border-color, #bbb); border-radius: 10px;
    min-height: 46px; padding: 8px 12px; margin: 10px 0;
    display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
    background: rgba(31,124,255,.03);
}
.ls-dropzone:focus { outline: 2px solid #1f7cff; }
.ls-drop-hover { border-color: #1f7cff; background: rgba(31,124,255,.10); }
.ls-drop-filled { border-style: solid; border-color: #1f7cff; }
.ls-drop-num {
    font-weight: 700; font-size: .85em;
    background: var(--border-color, #eee); border-radius: 5px; padding: 1px 7px;
}
.ls-drop-content { line-height: 1.5; font-size: .95em; }
.ls-bank { align-self: start; position: sticky; top: 70px; }
.ls-draggable {
    border: 1px solid var(--border-color, #ccc); border-radius: 10px;
    padding: 9px 12px; margin: 8px 0; cursor: grab;
    background: var(--bg-article, #fff); font-size: .93em; line-height: 1.45;
    display: flex; gap: 8px;
}
.ls-draggable:active { cursor: grabbing; }
.ls-drag-key { font-weight: 700; }
.ls-drag-selected { outline: 2px solid #1f7cff; }
.ls-drag-used { opacity: .35; pointer-events: none; }
.ls-noscript-select { font: inherit; padding: 4px; }

/* Le formulaire de tâche : plus de réserve en bas, la barre est dans le flux. */
.ls-taskform { padding-bottom: 0; }

/* -- Barre de pied de tâche (pleine largeur, en bas de la page) ------------- */
/* Passation et entraînement : la barre foncée termine la page. Elle n'est pas
   fixée à la fenêtre (elle masquait la fin du contenu et restait visible en
   permanence) mais placée APRÈS tout le contenu, bord à bord grâce aux mêmes
   marges négatives que .ls-chrome. Aucune règle de mise en page sur <main> ou
   .ls-container : une tentative en flex column pour pousser la barre au bas de
   la fenêtre cassait la hauteur de main sur l'écran de résultats (footer du
   site remonté au milieu de la page). Le margin-bottom négatif annule le
   padding bas de .ls-container pour que la barre touche le bas de page. */
.ls-footerbar {
    position: static;
    margin-top: 24px;                /* respiration avant la barre */
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    margin-bottom: 0;
    background: transparent; color: #fff;
    border: none; border-radius: 0; box-shadow: none;
}
/* Aucun espace sous la barre : on neutralise le padding et la marge bas des
   conteneurs (.ls-container 16px, main.ls-training 16px, et ce que le CSS
   global du site pose sur <main>) pendant qu'une barre est affichée. */
main.ls-has-footerbar,
main.ls-has-footerbar > .ls-container { padding-bottom: 0 !important; margin-bottom: 0 !important; }

/* Textes à trous : le bandeau bleu des choix reste fixé au bas de la fenêtre
   (comme à l'examen). Tant qu'un gap est actif, il recouvrirait la barre Submit
   arrivée en fin de page : on réserve sa hauteur (≈76px) sous la barre, qui
   redevient accessible en défilant jusqu'au bout. Sans gap actif, pas de
   réserve, donc pas de vide. */
main:has(.ls-gap-options.active) .ls-footerbar { margin-bottom: 76px; }
main.ls-has-footerbar:has(.ls-gap-options.active) { padding-bottom: 0 !important; }

/* -- Barre au bas de la FENÊTRE quand le contenu est court -------------------
   Contenu court (page de speaking : instructions + bouton Start) : la barre
   restait au milieu de l'écran, avec un grand vide blanc dessous. Contenu long :
   elle doit rester en fin de contenu, atteinte en défilant. C'est le motif du
   « sticky footer » : la page en colonne flexible avec une hauteur MINIMALE de
   fenêtre, et la barre repoussée en bas par margin-top: auto.

   Pourquoi ça ne recrée pas le bug qui a fait retirer la tentative en flex
   column plus haut : le DÉCLENCHEUR. Ce n'est pas main.ls-has-footerbar — en
   passation, cette classe est posée en permanence, y compris sur l'écran de
   résultats où le pied de page du site est affiché, et c'est là que ça avait
   cassé. C'est la présence effective d'un .ls-footerbar dans la page. Sans
   barre, aucune de ces règles ne s'applique : l'accès, les résultats et le
   panneau « bientôt » gardent leur mise en page d'origine.
   Et c'est min-height, jamais height : un contenu plus haut que la fenêtre
   allonge la page, il n'est pas comprimé.

   La chaîne doit être flexible de body jusqu'à la barre, sinon le
   margin-top: auto n'a rien à pousser. Les deux chaînes réelles :
     entraînement : body → main → form.ls-training-form → .ls-footerbar
     passation    : body → main → .ls-container → form.ls-taskform → .ls-footerbar
   Sélecteurs volontairement précis : les petits formulaires voisins
   (.ls-startform, quitter, réinitialiser) ne sont pas touchés.
   Ajouté le 16/09/2026. */
body:has(main .ls-footerbar) {
    min-height: 100vh;
    min-height: 100dvh;              /* barre d'adresse mobile déduite */
    display: flex; flex-direction: column;
}
main:has(.ls-footerbar) {
    flex: 1 0 auto;
    display: flex; flex-direction: column;
}
main:has(.ls-footerbar) > form.ls-training-form,
main:has(.ls-footerbar) > .ls-container,
main:has(.ls-footerbar) > .ls-container > form.ls-taskform {
    flex: 1 0 auto;
    display: flex; flex-direction: column;
}
main .ls-footerbar {
    margin-top: auto;                /* repoussée en bas… */
    padding-top: 24px;               /* …en gardant la respiration d'avant */
}

/* -- Textes à trous : le bandeau bleu des choix AU-DESSUS de la barre --------
   Ordre voulu, de bas en haut : la barre Retour/Submit au bord de la fenêtre,
   le bandeau fixe des choix juste au-dessus, le texte au-dessus encore.
   Le CSS le prévoyait déjà (.ls-gap-options est à bottom: var(--ls-footer-h)),
   mais la variable valait 0 depuis que la barre suivait le contenu. On lui
   redonne la hauteur de la barre, et on rend cette hauteur DÉTERMINISTE par
   un min-height sur la bande foncée : sans ça, la valeur dépendrait de la
   police et du bouton, et le bandeau tomberait un peu à côté.
   La réserve de 76px sous la barre n'a plus lieu d'être — le bandeau n'est
   plus dessous, il est dessus — et la réserve en bas du texte ne compte plus
   que le bandeau, la barre étant maintenant dans le flux sous lui. */
main:has(.ls-footerbar) { --ls-footer-h: 70px; }
main:has(.ls-footerbar) .ls-footerbar-inner { min-height: 70px; box-sizing: border-box; }
main:has(.ls-footerbar):has(.ls-gap-options.active) .ls-footerbar { margin-bottom: 0; }
main:has(.ls-footerbar) .ls-mcgapfill { padding-bottom: 76px; }
@media (max-width: 640px) {
    main:has(.ls-footerbar) { --ls-footer-h: 58px; }
    main:has(.ls-footerbar) .ls-footerbar-inner { min-height: 58px; }
    /* deux rangées de choix sur mobile : réserve doublée, comme plus haut */
    main:has(.ls-footerbar) .ls-mcgapfill { padding-bottom: 100px; }
}
.ls-footerbar-inner {
    display: flex; justify-content: space-between; align-items: center;
    background: #1f2430;
    box-shadow: 0 -2px 10px rgba(0,0,0,.12);
    padding: 14px max(20px, calc(50vw - 600px));   /* contenu aligné sur la colonne 1200px */
}
.ls-answer-counter { font-variant-numeric: tabular-nums; color: rgba(255,255,255,.75); font-size: .95em; }
/* Barre de pied Writing/Speaking : un seul bouton, aligné à droite. */
.ls-footerbar-inner--end { justify-content: flex-end; }

/* Bouton « Question suivante » dans la barre foncée : contour blanc épuré
   (ghost), plus sobre que le bleu plein. Ne touche pas au bouton Start de
   l'intro (fond clair), qui garde .ls-btn-primary bleu. */
.ls-footerbar .ls-btn-primary {
    background: transparent;
    border: 1.5px solid rgba(255,255,255,.55);
    color: #fff;
}
.ls-footerbar .ls-btn-primary:hover {
    background: rgba(255,255,255,.1);
    border-color: #fff;
    filter: none;
}

/* Lien de sortie, à gauche de la barre de pied (entraînement). Il porte les
   classes .ls-btn .ls-btn-primary : le style ghost lui vient donc de la règle
   .ls-footerbar .ls-btn-primary ci-dessus, exactement comme au bouton de
   soumission. Rien à redéfinir ici — seul le comportement sur écran étroit,
   où l'intitulé long ne doit pas pousser le bouton hors du cadre. */
.ls-footerbar-home {
    /* .ls-btn est inline-block : on le rend compressible et on tronque. */
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* -- Resultats ----------------------------------------------------------- */
.ls-results-title { margin: 18px 0 10px; }
.ls-score-banner {
    text-align: center; padding: 26px 20px; margin: 14px 0 10px;
    border: 2px solid var(--ls-band, #FFD152); border-radius: 14px;
    background: var(--bg-article, #fff);
}
.ls-score-label { text-transform: uppercase; letter-spacing: .08em; font-size: .85em; opacity: .7; }
.ls-score-value { font-size: 3em; font-weight: 800; line-height: 1.2; }
.ls-score-max   { font-size: .4em; font-weight: 500; opacity: .6; margin-left: 6px; }
.ls-score-band  { font-size: 1.2em; margin-top: 4px; }
.ls-score-band strong {
    background: var(--ls-band, #FFD152); color: #14310e;
    border-radius: 8px; padding: 2px 12px;
}
.ls-score-raw { margin-top: 8px; opacity: .75; }
.ls-finish-reason { font-style: italic; opacity: .85; }
.ls-disclaimer {
    font-size: .88em; opacity: .75; border-left: 3px solid var(--border-color, #ccc);
    padding: 6px 12px; margin: 10px 0 22px;
}
.ls-table { width: 100%; border-collapse: collapse; margin: 10px 0 22px; font-size: .95em; }
.ls-table th, .ls-table td {
    border: 1px solid var(--border-color, #ddd); padding: 7px 10px; text-align: left;
    vertical-align: top;
}
.ls-table th { background: rgba(31,124,255,.08); }
.ls-ok td:last-child { color: #2e7d32; font-weight: 700; }
.ls-ko td:last-child { color: #d32f2f; font-weight: 700; }
.ls-expl-row td { font-size: .9em; background: rgba(255,193,7,.08); }
.ls-task-recap { margin: 10px 0; border: 1px solid var(--border-color, #ddd); border-radius: 10px; padding: 6px 14px; }
.ls-task-recap summary { cursor: pointer; font-weight: 600; padding: 6px 0; }
.ls-recap-score { float: right; opacity: .8; }
.ls-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center; margin: 24px 0 40px; }
.ls-actions form { margin: 0; }
.ls-actions .btn-u { cursor: pointer; }

/* -- Impression (recap de resultats) ------------------------------------- */
/* En-tête / pied réservés à l'impression : invisibles à l'écran. */
.ls-print-head, .ls-print-foot { display: none; }

@media print {
    .ls-footerbar, .ls-actions, .ls-chrome { display: none !important; }

    /* Pas de lecteurs audio à l'impression : ils n'ont aucun sens sur papier /
       PDF. Ils restent affichés à l'écran sur la page de résultats. */
    audio, .ls-ya-audio { display: none !important; }

    /* Masquer le bandeau du site (navbar noire + hero) : on le remplace par
       un en-tête d'impression propre (logo + symbole). */
    header, nav, .navbar, .site-header, .hero, footer, .site-footer { display: none !important; }

    /* En-tête d'impression : logo Erosophia + titre (type d'examen). */
    .ls-print-head {
        display: flex !important; align-items: center; justify-content: space-between;
        gap: 20px; padding: 4px 0 16px; margin: 0 0 14px;
        border-bottom: 2px solid #110e23;
    }
    .ls-print-logo   { height: 34px; width: auto; flex: 0 0 auto; }
    .ls-print-titles { display: flex; flex-direction: column; align-items: flex-end; text-align: right; }
    .ls-print-exam {
        font-weight: 800; font-size: 1.15em; color: #110e23; line-height: 1.15;
    }
    .ls-print-sub {
        margin-top: 2px; font-size: .9em; color: #4a5261; font-weight: 600;
    }

    /* Pied d'impression discret. */
    .ls-print-foot {
        display: flex !important; align-items: center; justify-content: center;
        gap: 8px; margin-top: 20px; padding-top: 10px;
        border-top: 1px solid #d8dee8; color: #6b7280; font-size: .8em;
    }
    .ls-print-foot-symbol { width: 18px; height: 18px; flex: 0 0 auto; opacity: .9; }

    /* Débloquer le flux d'impression : certains conteneurs (site + moteur) ont
       overflow/height/position qui piègent le contenu sur une seule page.
       On force le débordement naturel sur plusieurs pages. */
    html, body, main, .ls-container, .ls-review, .ls-ws-review,
    .ls-model-answer, .ls-rubric-details, .ls-rubric-wrap,
    #content, #main, #page, #wrapper,
    .content, .main, .page, .wrapper, .site-wrapper, .page-wrapper, .site-content {
        overflow: visible !important;
        max-height: none !important;
        height: auto !important;
        position: static !important;
    }
    body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

    /* Économie d'encre : à l'impression, on retire tous les fonds colorés
       (beige de la page, jaune de l'évaluation IA, bleu/vert des réponses) —
       fonds blancs, on garde uniquement de fins liserés pour délimiter. */
    html, body, main, .ls-container,
    .ls-review, .ls-ws-review, .ls-card,
    .ls-ai-card, .ls-ai-error,
    .ls-your-answer, .ls-model-answer,
    .ls-ws-overall,
    .ls-ai-table, .ls-ai-table thead th, .ls-ai-table td {
        background: #fff !important;
    }
    /* Les couleurs de TEXTE (scores rouge/vert, titres) restent, elles sont peu
       coûteuses et portent l'information ; seuls les APLATS de fond partent. */

    /* Pas de cadres gris/colorés à l'impression : on retire les bordures et
       ombres décoratives des cartes et blocs de réponse. Le contenu se lit très
       bien sans encadrement ; seuls la grille de rubrique et l'en-tête/pied
       d'impression gardent leurs liserés (traités à part). */
    .ls-card, .ls-review, .ls-ws-review,
    .ls-ai-card, .ls-ai-error, .ls-ai-table,
    .ls-your-answer, .ls-ya-text,
    .ls-model-answer, .ls-ma-text,
    .ls-ws-overall, .ls-task-recap {
        border: 0 !important; box-shadow: none !important; border-radius: 0 !important;
    }
    /* Les traits INTÉRIEURS fins des tableaux d'évaluation (séparateurs de
       lignes) restent utiles à la lecture, on les garde discrets. */
    .ls-ai-table td, .ls-ai-table thead th { border-bottom: 1px solid #e5e5e5 !important; }

    .ls-task-recap { break-inside: avoid; }
    /* Forcer l'affichage du contenu repliable (grille) à l'impression. */
    details.ls-rubric-details { display: block !important; }
    details.ls-rubric-details > summary { list-style: none; font-weight: 700; }
    /* Ne PAS empêcher la coupure des grandes cartes de tâche : sinon une carte
       qui ne tient pas dans la page restante est renvoyée entière à la page
       suivante, laissant un grand vide. On laisse le contenu s'écouler.
       On protège seulement les petites unités (libellé+texte, lignes de table). */
    .ls-your-answer, .ls-model-answer,
    .ls-ma-notes-label, .ls-ma-notes-text,
    .ls-ai-suggestion { break-inside: avoid; }
    .ls-ma-notes-label { break-after: avoid; }
    .ls-review-title { break-after: avoid; }
    /* Les cartes de résultats peuvent se couper entre les pages (contenu long). */
    .ls-card, .ls-review, .ls-ws-review { break-inside: auto; }
    .ls-rubric-table tr, .ls-rubric-table td, .ls-rubric-table th { break-inside: avoid; }
}

/* ============================================================================
   ENTRAINEMENT PAR TACHE (linguaskill-general-training-{en,fr}.php)
   Complete le moteur ci-dessus : reutilise les memes tokens de theme
   (--bg-article / --text-color / --border-color / --shadow-color), le vert
   primaire #1f7cff et les couleurs de bande CECRL. Aucune regle ne redefinit
   les selecteurs existants.
   ========================================================================== */


.ls-training {
    width: 100%; max-width: 1200px; margin: 0 auto; padding: 16px 20px;
    box-sizing: border-box;
    color: var(--text-color);
}
.ls-visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ls-js-only { }                     /* visible par defaut ; masque par JS si besoin */
.ls-task { }                        /* conteneur de tache : herite de .ls-card */
.ls-chrome-label { font-weight: 600; }

/* -- Barre de soumission / navigation ------------------------------------ */
.ls-nav-center { display: flex; justify-content: center; margin: 20px 0; }
.ls-submit { min-width: 220px; }
/* Dans la barre de pied fixe (entraînement comme passation), le bouton suit
   le gabarit ghost de .ls-footerbar .ls-btn-primary : pas de largeur
   minimale imposée, sinon il s'étire hors de l'alignement à droite. */
.ls-footerbar .ls-submit { min-width: 0; }
.ls-nav-bar {
    display: flex; justify-content: space-between; align-items: center;
    gap: 12px; margin: 18px 0 6px;
}
.ls-nav-side { flex: 1; display: flex; }
.ls-nav-right { justify-content: flex-end; }
.ls-nav-home { display: flex; justify-content: center; margin: 8px 0 24px; }
.ls-redo, .ls-restart { }

/* -- Panneau "contenu a venir" ------------------------------------------- */
.ls-soon-panel { text-align: center; }
.ls-soon-msg  { line-height: 1.55; margin: 10px 0; }
.ls-soon-hint { opacity: .7; font-size: .92em; }
.nav-card.ls-soon { opacity: .45; filter: grayscale(.7); }
.nav-card.ls-soon .nav-card-link { cursor: not-allowed; pointer-events: none; }

/* -- Resultat (taches objectives) — grille rigoureuse -------------------- */
.ls-result-head { text-align: center; margin-bottom: 8px; }
.ls-result-head h2 { margin: 0; font-weight: 600; font-size: 1.15em; color: #333a4d; }
.ls-result-score { color: #2e7d32; font-weight: 800; }

/* Anneau de score (SVG) */
.ls-score-ring { display: flex; justify-content: center; margin: 6px 0 22px; }
.ls-ring-num { font: 800 26px -apple-system, "Segoe UI", Roboto, sans-serif; fill: #333a4d; }
.ls-ring-den { font-weight: 600; fill: #98a0ad; font-size: 18px; }
.ls-ring-lbl {
    font: 500 9px -apple-system, "Segoe UI", Roboto, sans-serif; fill: #98a0ad;
    text-transform: uppercase; letter-spacing: .08em;
}

/* Animation d'apparition : l'arc se dessine de 0 jusqu'au score, le texte
   apparaît en fondu juste après. --ls-ring-c = circonférence pleine (arc
   caché) ; --ls-ring-off = offset final (arc révélé), fournis en style inline. */
.ls-ring-arc {
    animation: ls-ring-fill .9s cubic-bezier(.22,.61,.36,1) .15s both;
}
@keyframes ls-ring-fill {
    from { stroke-dashoffset: var(--ls-ring-c); }
    to   { stroke-dashoffset: var(--ls-ring-off); }
}
.ls-ring-num, .ls-ring-lbl {
    animation: ls-ring-text-in .5s ease .55s both;
}
@keyframes ls-ring-text-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .ls-ring-arc { animation: none; stroke-dashoffset: var(--ls-ring-off); }
    .ls-ring-num, .ls-ring-lbl { animation: none; opacity: 1; }
}
.ls-review-title { margin: 0 0 16px; font-weight: 600; }
.ls-review-list { list-style: none; margin: 0; padding: 0; }

/* Colonnes alignées : n° | marque | réponse donnée | correction.
   Toutes les lignes partagent la même grille → lecture verticale nette. */
.ls-review-item {
    display: grid;
    grid-template-columns: 2.4em 1.4em minmax(6em, max-content) 1fr;
    align-items: baseline;
    column-gap: 14px; row-gap: 0;
    padding: 13px 0;
    border: none; border-radius: 0; background: none;
    border-bottom: 1px solid #edeff3;
    font-size: .97em;
}
.ls-review-list .ls-review-item:last-child { border-bottom: none; }

/* N° de question : petite pastille neutre, largeur fixe, centrée. */
.ls-review-qid {
    justify-self: start;
    min-width: 100%; text-align: center;
    background: #f0f2f6; color: #5b6577;
    border-radius: 5px; padding: 2px 0;
    font-size: .82em; font-weight: 700;
    font-variant-numeric: tabular-nums;
}
/* Marqueur ✓/✗ : seul porteur de couleur d'état. */
.ls-review-mark { justify-self: center; font-weight: 700; font-size: 1.05em; }
.ls-review-item.is-correct .ls-review-mark { color: #2e7d32; }
.ls-review-item.is-wrong   .ls-review-mark { color: #c0392b; }

/* Réponse donnée par le candidat (information principale). */
.ls-review-user { color: #1a1a1a; }
.ls-review-item.is-wrong .ls-review-user { color: #8a8f99; }   /* atténuée si fausse */

/* Bonne réponse (secondaire, verte, sur la 4e colonne — alignée à droite du user). */
.ls-review-corr { color: #2e7d32; font-weight: 600; }

/* Explication : sous la ligne, alignée sur la colonne de la réponse (pas à gauche). */
.ls-review-expl { grid-column: 3 / -1; margin-top: 6px; }
.ls-review-expl summary {
    cursor: pointer; font-weight: 500; color: #5b6577; font-size: .86em;
    list-style: none;
}
.ls-review-expl summary::-webkit-details-marker { display: none; }
.ls-review-expl summary::before { content: "\25B8"; margin-right: 6px; font-size: .8em; color: #98a0ad; }
.ls-review-expl[open] summary::before { content: "\25BE"; }
.ls-review-expl p { margin: 6px 0 0; line-height: 1.55; color: #444a57; font-size: .92em; }

/* -- Carte de feedback IA (Writing / Speaking) --------------------------- */
.ls-ai-card {
    border: 1px solid #f0d9a8; border-radius: 0;
    padding: 14px 16px; margin: 14px 0;
    background: #fff9ec;                                   /* évaluation : jaune */
}
.ls-ai-error { background: rgba(211,47,47,.06); border-color: #d32f2f; }
.ls-ai-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.ls-ai-title { font-weight: 700; }
.ls-ai-overall { font-weight: 800; color: #b26a00; font-variant-numeric: tabular-nums; }

/* Tableau des critères d'évaluation — transparent (fond jaune de la carte). */
.ls-ai-table {
    width: 100%; border-collapse: collapse;
    margin: 12px 0 0; font-size: .95em; background: transparent;
    border: 1px solid #ecdcb4;
}
.ls-ai-table thead th {
    text-align: left; font-size: .78em; letter-spacing: .04em; text-transform: uppercase;
    color: #8a6d33; font-weight: 700; padding: 8px 12px; background: transparent;
    border-bottom: 1px solid #ecdcb4;
}
.ls-ai-th-score { text-align: center; white-space: nowrap; }
.ls-ai-table td { padding: 9px 12px; vertical-align: top; border-bottom: 1px solid #f0e4c6; }
.ls-ai-table tbody tr:last-child td { border-bottom: none; }
.ls-ai-td-name { font-weight: 600; white-space: nowrap; }
.ls-ai-td-score { text-align: center; white-space: nowrap; }
.ls-ai-td-comment { color: #5a5443; line-height: 1.5; }

/* Score : chiffre coloré selon le niveau, sans fond. */
.ls-ai-badge {
    display: inline-block; min-width: 38px; text-align: center;
    padding: 2px 8px; font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.ls-ai-row--good .ls-ai-badge { color: #1f7a3d; }
.ls-ai-row--mid  .ls-ai-badge { color: #9a6412; }
.ls-ai-row--low  .ls-ai-badge { color: #b23b3b; }
.ls-ai-row--na   .ls-ai-badge { color: #5b6577; }
.ls-ai-errors { margin-top: 10px; }
.ls-ai-errors-title { font-weight: 600; margin: 0 0 4px; }
.ls-ai-errors ul { margin: 6px 0 0; padding-left: 20px; }
.ls-ai-errors li { margin: 3px 0; line-height: 1.5; }
/* Correction « incorrect → correct » : flèche claire plutôt qu'un deux-points. */
.ls-ai-err-wrong { color: #b23b3b; text-decoration: line-through; text-decoration-thickness: 1px; }
.ls-ai-err-arrow { margin: 0 8px; color: #8a9099; }
.ls-ai-err-right { color: #1f7a3d; font-weight: 600; }
.ls-ai-suggestion { margin: 10px 0 0; line-height: 1.5; }

/* -- Reponse modele : styles partagés avec le test blanc (.ls-model-answer) -- */
/* .ls-model-block / .ls-model / .ls-model-body retirés le 11/07/2026 (§7-C) :
   ls_answer_model_block() supprimée, le training rend via .ls-model-answer.
   .ls-marking et .ls-model-wc restent — ils servent au nouveau template. */
.ls-marking { margin-top: 8px; }
.ls-marking > summary { cursor: pointer; font-weight: 700; }
.ls-model-wc { font-weight: 400; opacity: .7; }

/* ═══ Bilan Writing/Speaking : réponse candidat + réponse modèle ═══════════ */
/* Présentation IDENTIQUE des deux blocs (même libellé, même boîte de texte) ;
   seule la couleur change : BLEU = candidat, VERT = réponse modèle.          */
.ls-your-answer, .ls-model-answer { margin: 0 0 16px; }
.ls-ya-label, .ls-ma-label {
    margin: 0 0 8px; font-size: .82em; letter-spacing: .06em;
    text-transform: uppercase; font-weight: 700;
}
.ls-ya-label { color: #1f6fd6; }                 /* candidat : bleu */
.ls-ma-label { color: #1f7a3d; }                 /* modèle : vert  */
.ls-ma-wc { font-weight: 400; text-transform: none; letter-spacing: 0; opacity: .7; }
.ls-ya-wc { font-weight: 400; text-transform: none; letter-spacing: 0; opacity: .7; }

.ls-ya-text {
    line-height: 1.65; color: #1f2430; white-space: pre-wrap; word-break: break-word;
    text-align: justify; hyphens: auto;
    border-radius: 10px; padding: 14px 16px;
    background: #f0f5fc; border: 1px solid #cfe0f5;       /* candidat : bleu */
}
.ls-ma-text {
    background: #f2faf4; border: 1px solid #cfe8d6;        /* modèle : vert */
    color: #223828;
}
.ls-your-answer--spoken .ls-ya-text { font-style: italic; color: #333a47; }

/* Critères de correction (marking_notes) — rendu texte simple. */
.ls-ma-notes-label {
    margin: 12px 0 4px; font-size: .82em; letter-spacing: .04em; text-transform: uppercase;
    font-weight: 700; color: #1f7a3d;
}
.ls-ma-notes-text { margin: 0; line-height: 1.55; color: #223828; font-size: .95em; }
.ls-ma-notes-list { margin: 0; padding-left: 20px; line-height: 1.55; color: #223828; font-size: .95em; }
.ls-ma-notes-list li { margin: 3px 0; }

/* ═══ Grille d'évaluation officielle (rubrique : critères × niveaux CEFR) ═══ */
.ls-rubric-details { margin: 22px 0 0; }
.ls-rubric-details > summary {
    cursor: pointer; font-weight: 700; padding: 8px 0; color: #1f2430;
}
.ls-rubric-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin-top: 8px; }
.ls-rubric-table {
    border-collapse: collapse; font-size: .82em; table-layout: fixed;
    width: 100%; min-width: 720px;
}
.ls-rubric-table th, .ls-rubric-table td {
    border: 1px solid #d8dee8; padding: 7px 9px; vertical-align: top; line-height: 1.4;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
}
/* Coin + colonne des critères */
.ls-rubric-critcol {
    background: #1a2340; color: #fff; text-align: left; font-weight: 700; width: 130px;
}
.ls-rubric-critname {
    background: #f4f6fb; color: #1a2340; font-weight: 700; text-align: left; width: 130px;
}
/* En-têtes de niveau (colorés selon le rang) */
.ls-rubric-lvl { text-align: center; font-weight: 800; color: #fff; }
.ls-rubric-lvl--good { background: #059669; border-color: #10b981; }
.ls-rubric-lvl--mid  { background: #d97706; border-color: #fbbf24; }
.ls-rubric-lvl--low  { background: #b91c1c; border-color: #ef4444; }
/* Cellules de descripteur (teinte claire assortie au niveau) */
.ls-rubric-cell--good { background: #d1fae5; color: #065f46; border-color: #a7e3c5; }
.ls-rubric-cell--mid  { background: #fef3c7; color: #92400e; border-color: #f2dfa0; }
.ls-rubric-cell--low  { background: #fee2e2; color: #991b1b; border-color: #f4c4c4; }

@media (max-width: 640px) {
    .ls-rubric-table { font-size: .78em; min-width: 620px; }
    .ls-rubric-table th, .ls-rubric-table td { padding: 6px 8px; }
    .ls-rubric-critcol, .ls-rubric-critname { width: 110px; }
}

.ls-ws-noanswer {
    margin: 0 0 16px; padding: 4px 0;
    background: none; border: 0; color: #8a9099; font-style: italic;
}
.ls-ws-results-intro { line-height: 1.55; margin: 0 0 6px; }
.ls-ws-review { margin-bottom: 22px; }

/* -- Writing : consigne + zone de redaction ------------------------------ */
.ls-part-label { font-size: .8em; letter-spacing: .08em; text-transform: uppercase; opacity: .65; margin: 0 0 8px; }
.ls-writing-prompt .ls-w-intro,
.ls-writing-prompt .ls-w-task,
.ls-writing-prompt .ls-w-line { line-height: 1.6; margin: 8px 0; }
.ls-w-notice {
    border: 1px solid var(--border-color, #ddd); border-radius: 8px;
    padding: 12px 14px; margin: 10px 0; background: rgba(255,193,7,.10);
}
.ls-w-notice-title { margin: 0 0 8px; }
.ls-w-line, .ls-w-bullet { margin: 4px 0; line-height: 1.55; }
.ls-w-bullet { padding-left: 6px; }
.ls-w-statement {
    margin: 10px 0; padding: 10px 16px;
    background: rgba(31,124,255,.06); font-style: italic; line-height: 1.55;
    border-radius: 6px;
}
.ls-w-task { font-weight: 600; }
.ls-w-points, .ls-w-views { margin: 8px 0; padding-left: 22px; line-height: 1.6; }
.ls-w-views-intro { margin: 12px 0 4px; }
.ls-w-closing { font-weight: 700; margin-top: 12px; }
.ls-writing-answer { }
.ls-writing-label { display: block; font-weight: 600; margin-bottom: 8px; }
.ls-writing-textarea {
    width: 100%; box-sizing: border-box; font: inherit; line-height: 1.6;
    padding: 12px 14px; border: 1px solid var(--border-color, #ddd);
    border-radius: 8px; background: var(--bg-article, #fff); color: inherit;
    resize: vertical; min-height: 240px;
}
.ls-writing-textarea:focus { outline: 2px solid #1f7cff; outline-offset: 1px; }
.ls-wordcount { margin: 6px 0 0; font-size: .85em; opacity: .7; font-variant-numeric: tabular-nums; }

/* -- Speaking : consigne + enregistreur ---------------------------------- */
.ls-speaking-prompt .ls-s-task { line-height: 1.6; white-space: pre-line; }
.ls-s-timing { margin: 10px 0 0; font-size: .9em; display: flex; gap: 16px; flex-wrap: wrap; opacity: .85; }
.ls-s-prep, .ls-s-speak { font-variant-numeric: tabular-nums; }
.ls-speaking-recorder { text-align: center; }
.ls-rec-hint { margin: 0 0 12px; opacity: .8; }
.ls-rec-controls { display: flex; gap: 10px; justify-content: center; align-items: center; flex-wrap: wrap; }
.ls-rec-start.is-recording { background: #d32f2f; border-color: #d32f2f; color: #fff; }
.ls-rec-status { font-size: .9em; opacity: .8; font-variant-numeric: tabular-nums; }
.ls-rec-playback { display: block; margin: 14px auto 0; width: 100%; max-width: 440px; }

/* -- Listening : lecteur audio + consignes ------------------------------- */
.ls-listening-instr { line-height: 1.5; white-space: pre-line; margin: 0 0 12px; opacity: .9; }
/* ── Hidden audio (exam-style: no visible player at all) ──────────────────── */
/* The whole block is invisible. The <audio> plays automatically via the engine;
   there is no button, no bar, no play/pause. The only thing that can ever show
   is the fallback link, and only if the browser blocked autoplay. */
.ls-audio-hidden {
    /* Collapse to nothing but keep the <audio> in the DOM and playable. */
    display: block; margin: 0; padding: 0; border: 0; height: 0; overflow: hidden;
}
.ls-audio-el { display: none; }

/* Fallback link — hidden by default; revealed only when autoplay is blocked. */
.ls-audio-fallback { display: none; }
.ls-audio-hidden.is-blocked {
    height: auto; overflow: visible; margin: 4px 0 14px;
}
.ls-audio-hidden.is-blocked .ls-audio-fallback {
    display: inline-block;
    padding: 10px 16px; border: none; border-radius: 10px; cursor: pointer;
    background: #1f7cff; color: #fff; font-size: .95em; font-weight: 600;
    box-shadow: 0 3px 10px rgba(31,124,255,.3);
}
.ls-audio-hidden.is-blocked .ls-audio-fallback:hover { background: #1668e0; }

/* sentence-completion (Listening) */
.ls-sc-list { margin: 6px 0 0; padding-left: 26px; }
.ls-sc-item { margin: 12px 0; line-height: 1.8; }
.ls-sc-input { width: 150px; }

/* grid-matching (Listening) */
.ls-grid { margin: 8px 0 4px; }
.ls-grid + .ls-grid { margin-top: 28px; }
.ls-grid-prompt { font-weight: 700; line-height: 1.5; margin: 0 0 12px; font-size: 1.02em; }
.ls-grid-tasknum { color: #1f7cff; font-weight: 800; margin-right: 4px; }
.ls-grid-options { list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ls-grid-opt { display: flex; gap: 10px; align-items: baseline; line-height: 1.45; }
.ls-grid-opt .ls-opt-key {
    flex: 0 0 auto; min-width: 1.4em; text-align: center;
    font-weight: 700; color: #1f7cff;
}

/* Matrix: square corners, thin gridlines, no visible radios. */
.ls-grid-matrix {
    width: 100%; border-collapse: collapse; font-size: .95em;
    border: 1px solid #d7dce6;
    table-layout: fixed;
}
/* La colonne « Speaker » ne prend que la place de son libellé ; les colonnes
   de réponses se partagent le reste à parts égales. */
.ls-grid-matrix col.ls-grid-col-spk,
.ls-grid-matrix th.ls-grid-spk { width: 140px; }
.ls-grid-matrix th, .ls-grid-matrix td {
    padding: 0; text-align: center;
    border: 1px solid #e3e7ef;
}

/* Header row */
/* Header row : les lettres reprennent le bleu de la légende. */
.ls-grid-matrix thead th {
    background: #eef2f8; font-weight: 700; color: #1f7cff;
    padding: 12px 8px;
}
.ls-grid-matrix thead th.ls-grid-spk {
    text-align: center; padding: 12px 8px !important; color: #333a4d;
}

/* Speaker (row header) */
.ls-grid-spk {
    text-align: left; font-weight: 600; white-space: nowrap;
    padding: 0 18px !important; color: #333a4d;
}
.ls-grid-spk .ls-qnum {
    display: inline-block; margin-left: 8px;
    min-width: 1.5em; padding: 1px 6px;
    background: #eef2fb; color: #5b6577; font-size: .78em; font-weight: 700;
    vertical-align: middle;
}

/* The whole cell is the click target. The radio itself is fully hidden. */
.ls-grid-cell {
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; padding: 16px 4px; margin: 0;
    min-height: 22px;
    transition: background .12s ease;
    position: relative;
}
.ls-grid-cell input[type="radio"] {
    position: absolute; opacity: 0; width: 0; height: 0; margin: 0; pointer-events: none;
}
.ls-grid-cell:hover { background: rgba(31,124,255,.08); }

/* Selected: fill the whole cell blue, show a white check, no circle. */
.ls-grid-cell:has(input:checked) { background: #1f7cff; }
.ls-grid-cell:has(input:checked)::after {
    content: "\2713";                 /* ✓ */
    color: #fff; font-size: 1.15em; font-weight: 700; line-height: 1;
}

@media (max-width: 640px) {
    .ls-grid-matrix { font-size: .85em; }
    .ls-grid-matrix col.ls-grid-col-spk,
    .ls-grid-matrix th.ls-grid-spk { width: 90px; }
    .ls-grid-spk { white-space: normal; padding: 0 10px !important; }
    .ls-grid-matrix thead th.ls-grid-spk { padding: 10px 6px !important; }
    .ls-grid-cell { padding: 14px 2px; }

    /* Tableau des critères : nom + score sur une ligne, commentaire dessous. */
    .ls-ai-table thead { display: none; }
    .ls-ai-table, .ls-ai-table tbody, .ls-ai-table tr, .ls-ai-table td { display: block; width: 100%; }
    .ls-ai-table tr { padding: 8px 12px; border-bottom: 1px solid #eceff4; }
    .ls-ai-table tbody tr:last-child { border-bottom: none; }
    .ls-ai-table td { border: none; padding: 2px 0; }
    .ls-ai-td-name { display: inline-block; }
    .ls-ai-td-score { display: inline-block; text-align: left; margin-left: 8px; }
    .ls-ai-td-comment { margin-top: 4px; font-size: .95em; }
}

/* -- Liste "Toutes les taches" ------------------------------------------- */
.ls-alltasks { margin-top: 24px; }
.ls-alltasks > summary { cursor: pointer; font-weight: 700; padding: 8px 0; }
.ls-alltasks-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 10px; margin-top: 8px;
}
.ls-alltasks-col {
    border: 1px solid var(--border-color, #e2e8f8); border-radius: 8px; padding: 10px 12px;
    background: var(--bg-article, #f8faff);
}
.ls-alltasks-cat { font-weight: 700; margin: 0 0 6px; font-size: .85em; }
.ls-alltasks-link {
    display: block; padding: 4px 6px; border-radius: 6px;
    font-size: .88em; text-decoration: none; color: inherit;
}
.ls-alltasks-link:hover { background: rgba(31,124,255,.08); }
.ls-alltasks-link.is-active { background: rgba(31,124,255,.16); font-weight: 700; }

/* -- Reduction d'animation ----------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .ls-rec-start.is-recording { animation: none; }
}

/* -- Complements : conteneurs & elements partages du moteur de rendu ------ */
.ls-review, .ls-listening, .ls-mcgapfill, .ls-missing,
.ls-grid-matching, .ls-sentence-completion { /* heritent de .ls-card / .ls-task */ }
.ls-training-form { margin: 0; width: 100%; }
.ls-opt-text { line-height: 1.45; }
.ls-text-body { line-height: 1.55; }
.ls-drag-text { line-height: 1.4; }
.ls-missing-passage { line-height: 1.6; }
.ls-matching-questions { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.ls-review-qid {
    display: inline-block; min-width: 24px; text-align: center;
    background: var(--border-color, #eee); border-radius: 6px;
    font-weight: 700; font-size: .85em; padding: 1px 5px;
}
.ls-rec-file { }                 /* input file cache ; revele en secours sans JS */
.ls-rec-stop[disabled] { opacity: .5; cursor: not-allowed; }



/* -- Passation Speaking chronométrée (Listen / Read / Think / Speak) ------ */
.ls-passation { text-align: center; }
.ls-p-bar-wrap { max-width: 360px; margin: 8px auto 26px; }
/* Rangée des triangles, calquée sur la disposition des labels : chaque
   triangle est au-dessus de son label ; le JS n'affiche que l'actif. */
.ls-p-tri-row {
    position: relative; height: 14px;
    font-size: 12px; line-height: 1; margin-bottom: 3px;
}
.ls-p-bar-wrap[data-bars="2"] .ls-p-labels { padding: 0; }
/* Triangle unique : sa position horizontale est le centre du label d'index
   --tri-pos parmi --tri-n cellules égales. Il glisse doucement d'une phase à
   l'autre (transition) au lieu de sauter. Couleur posée en inline par le JS. */
.ls-p-tri {
    position: absolute; top: 0; left: 0;
    width: calc(100% / var(--tri-n, 3));
    text-align: center; visibility: hidden;
    transform: translateX(calc(100% * var(--tri-pos, 0)));
    transition: transform .35s cubic-bezier(.4, 0, .2, 1), color .25s ease;
}
@media (prefers-reduced-motion: reduce) {
    .ls-p-tri { transition: none; }
}
.ls-p-labels {
    display: flex; justify-content: space-between;
    font-size: 15px; margin-bottom: 8px; color: #b0b4bb;
}
.ls-p-label { flex: 1; text-align: center; transition: color .25s ease; white-space: nowrap; }
/* La couleur de la phase active est posée en inline par le JS (couleur dédiée à
   l'étape : Listen vert, Think ambre, Speak bleu). On ne force donc pas la
   couleur ici — seulement la graisse. */
.ls-p-label.is-active { font-weight: 700; }
.ls-p-track { display: flex; align-items: center; gap: 12px; }
/* Rail épais, façon examen. La barre colorée se remplit de gauche à droite ;
   pendant Listen elle est pleine (100 %). Couleur = nature de la phase. */
.ls-p-rail {
    flex: 1; position: relative; height: 6px;
    background: #e7e9ee; border-radius: 3px; overflow: hidden;
}
.ls-p-bar {
    position: absolute; left: 0; top: 0; height: 100%; width: 0;
    border-radius: 3px; background: #63c132;
}
.ls-p-secs {
    flex: 0 0 auto; font-size: 13px; color: #555;
    min-width: 78px; text-align: left; font-variant-numeric: tabular-nums;
}

/* Zone de contenu : les blocs sont révélés un par un par le séquenceur. */
.ls-p-content { max-width: 620px; margin: 0 auto; text-align: left; }
.ls-p-block { margin: 0 0 16px; }
.ls-p-block[hidden] { display: none; }

/* Part 3 (recommendation) : le texto et la brochure sont des IMAGES qui
   doivent rester LISIBLES à leur taille native. Elles sont placées dans une
   bande .ls-p-recscroll qui défile horizontalement (ascenseur) au lieu d'être
   comprimées. Le prompt écrit reste hors de la bande, en pleine largeur. */
.ls-p-content[data-ptype="recommendation"] { max-width: 100%; }
.ls-p-recscroll {
    display: flex; flex-wrap: nowrap; align-items: flex-start;
    gap: 24px;
    overflow-x: auto; overflow-y: visible;
    -webkit-overflow-scrolling: touch;
    padding: 4px 2px 12px;
    scrollbar-width: thin;
    cursor: grab;                 /* on peut attraper et pousser à la souris */
    touch-action: pan-x pan-y;    /* tactile : défilement horizontal ET vertical OK */
}
.ls-p-recscroll:active { cursor: grabbing; }
/* Les images de la bande ne sont pas « déplaçables » (ghost drag natif) : ça
   gênerait le glisser-pousser. */
.ls-p-recscroll img { -webkit-user-drag: none; user-select: none; }
.ls-p-recscroll > .ls-p-block {
    flex: 0 0 auto; margin: 0;        /* pas de compression : largeur native */
}
.ls-p-recscroll > .ls-p-block[hidden] { display: none; }
/* Barre de défilement plus visible (WebKit) pour signaler qu'on peut scroller. */
.ls-p-recscroll::-webkit-scrollbar { height: 10px; }
.ls-p-recscroll::-webkit-scrollbar-thumb {
    background: rgba(100,110,125,.45); border-radius: 6px;
}
.ls-p-recscroll::-webkit-scrollbar-track { background: rgba(100,110,125,.10); border-radius: 6px; }
/* Le prompt écrit occupe toute la largeur, sous la bande d'images. */
.ls-p-content[data-ptype="recommendation"] > .ls-p-block[data-block="prompt"] {
    margin: 16px 0 0;
}

/* Consigne / carte de tâche. */
.ls-p-task, .ls-p-card {
    line-height: 1.45; white-space: pre-line; font-size: 1.05em;
}
.ls-p-card {
    background: var(--surface-2, #f6f7f9); border-radius: 10px; padding: 14px 16px;
}

/* Contexte (Part 2.1) — courte phrase d'amorce avant le tutoriel. */
.ls-p-context { font-style: italic; opacity: .85; margin: 0 0 8px; }
.ls-p-prompt { font-weight: 700; margin: 18px 0 0; }

/* Discussion (Part 2.2) — questions successives. */
.ls-p-qwrap { padding: 0; }
.ls-p-qlabel { font-size: 1.15em; font-weight: 700; color: #1a1a1a; margin: 10px 0 0; }
.ls-p-qtext { font-size: 1.05em; line-height: 1.5; margin: 4px 0 0; }

/* Recommandation (Part 3) — images webp du texto et de la brochure, affichées
   à leur LARGEUR NATIVE pour rester lisibles. Les webp sont générées en @2x :
   la largeur d'affichage vaut la moitié de la largeur en pixels. La bande
   parente (.ls-p-content[data-ptype="recommendation"]) défile horizontalement
   si l'ensemble dépasse la largeur disponible. Sur très petit écran, l'image
   ne dépasse pas la fenêtre (max-width fluide) pour éviter un double scroll. */
/* Recommandation (Part 3) — images webp du texto et de la brochure. Elles
   gardent leur LARGEUR NATIVE (y compris sur mobile) : c'est la bande parente
   .ls-p-recscroll qui défile horizontalement. On ne met PAS de max-width en vw,
   sinon l'image serait comprimée sur petit écran et redeviendrait illisible —
   l'utilisateur fait défiler pour lire la brochure en entier. */
.ls-p-img {
    display: block; height: auto;
    border-radius: 0;              /* coins carrés : le style/bordure est dans l'image */
    box-shadow: none;              /* une seule bordure, pas d'ombre ajoutée */
}
.ls-p-img-sms  { width: 400px; }   /* fil de messages, taille native */
.ls-p-img-broc { width: 860px; }   /* brochure large et lisible, défilement si besoin */

/* Apparition du texto (SMS) : léger rebond synchronisé avec le son de
   notification joué par le moteur JS. Désactivé si l'utilisateur préfère
   réduire les animations. */
@keyframes ls-p-pop {
    0%   { transform: translateY(8px) scale(.985); opacity: 0; }
    60%  { transform: translateY(-2px) scale(1.005); opacity: 1; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
}
.ls-p-pop { animation: ls-p-pop .34s cubic-bezier(.22,1,.36,1) both; }
@media (prefers-reduced-motion: reduce) {
    .ls-p-pop { animation: none; }
}

/* Recommandation (Part 3) — texto + brochure. */
.ls-p-sms {
    background: var(--surface-2, #f0f4f8); border-radius: 12px; padding: 12px 16px;
    max-width: 420px;
}
.ls-p-sms-from { font-weight: 600; margin: 0 0 6px; font-size: .9em; opacity: .7; }
.ls-p-sms-line { margin: 0 0 6px; line-height: 1.5; }
.ls-p-sms-line:last-child { margin-bottom: 0; }
.ls-p-brochure {
    border: 1px solid var(--border-color, #e4e7ec); border-radius: 12px;
    padding: 14px 18px; background: var(--surface-1, #fff);
}
.ls-p-broc-title { margin: 0 0 6px; font-size: 1.05em; }
.ls-p-broc-mission { margin: 0 0 12px; opacity: .85; line-height: 1.5; }
.ls-p-broc-h {
    margin: 12px 0 4px; font-size: .78em; letter-spacing: .06em;
    text-transform: uppercase; opacity: .6;
}
.ls-p-broc-list { margin: 0; padding-left: 20px; line-height: 1.6; }
.ls-p-broc-list li { margin: 2px 0; }

/* Indicateur d'enregistrement + avis d'envoi. */
.ls-p-recind {
    display: none; align-items: center; gap: 8px; justify-content: center;
    margin: 18px 0 0; font-size: 14px; color: #d32f2f;
}
.ls-p-recdot {
    width: 10px; height: 10px; border-radius: 50%; background: #d32f2f;
    display: inline-block; animation: ls-recpulse 1s ease-in-out infinite;
}
@keyframes ls-recpulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
.ls-p-uploading {
    display: none; margin: 24px 0 0;
    align-items: center; justify-content: center; gap: 10px;
    font-size: 1.05em; font-weight: 600; color: #378add; font-style: normal;
}
.ls-p-uploading::before {
    content: ""; width: 18px; height: 18px; flex: 0 0 auto;
    border: 2.5px solid rgba(55,138,221,.25); border-top-color: #378add;
    border-radius: 50%; animation: ls-spin .8s linear infinite;
}
@keyframes ls-spin { to { transform: rotate(360deg); } }
/* Clôture « Thank you » : c'est une phase Listen comme les autres — la barre de
   progression, ses libellés et le triangle RESTENT visibles (conforme à
   l'officiel). Seul le contenu de la tâche disparaît, remplacé par le message. */
.ls-passation.is-closing .ls-p-content,
.ls-passation.is-closing .ls-p-micmsg,
.ls-passation.is-closing .ls-p-recind,
.ls-passation.is-closing .ls-nav-center { display: none !important; }
.ls-passation.is-closing .ls-p-thanks {
    display: flex !important; align-items: center; justify-content: center;
    min-height: 120px; margin: 0;
    font-size: 1.35em; font-weight: 700;
}

/* Pendant l'envoi : on masque la barre de progression, les phases, le compteur
   ET le contenu de la tâche (consignes, prompt, brochure). Sinon les blocs
   révélés restent affichés sous « Uploading… », comme si la tâche continuait. */
.ls-passation.is-uploading .ls-p-bar-wrap,
.ls-passation.is-uploading .ls-p-recind,
.ls-passation.is-uploading .ls-p-content,
.ls-passation.is-uploading .ls-p-micmsg,
.ls-passation.is-uploading .ls-p-thanks,
.ls-passation.is-uploading .ls-nav-center { display: none !important; }
.ls-passation.is-uploading .ls-p-uploading {
    display: flex; align-items: center; justify-content: center;
    gap: 10px; min-height: 120px; margin: 0;
}
/* Chrono du bandeau figé (envoi en cours) : on le masque proprement. */
.ls-timer.is-frozen { visibility: hidden; }
/* Bandeau d'instructions pendant l'envoi : plus rien à lire, on l'escamote. */
.ls-chrome.is-uploading { display: none !important; }
@media (prefers-reduced-motion: reduce) {
    .ls-p-uploading::before { animation: none; }
}
.ls-p-thanks { margin: 18px 0 0; font-size: 1.15em; font-weight: 600; color: var(--text-color, #222); }
.ls-p-micmsg {
    max-width: 480px; margin: 16px auto 0; padding: 10px 14px;
    background: #fdecea; color: #b23b3b; border: 1px solid #f5c6cb;
    border-radius: 8px; font-size: 14px; line-height: 1.5;
}
.ls-nav-center { display: flex; justify-content: center; gap: 12px; margin: 22px 0 0; }
@media (prefers-reduced-motion: reduce) {
    .ls-p-recdot { animation: none; }
    .ls-p-bar { transition: none; }
}

/* Note globale de compétence (résultats Writing/Speaking). */
.ls-ws-overall {
    display: flex; align-items: center; gap: 22px;
    border: 1px solid #e3e6ea;
    border-radius: 16px; padding: 20px 24px; margin: 8px 0 24px;
    background: var(--surface-1, #fff);
}
.ls-ws-overall .ls-score-ring { flex: 0 0 auto; margin: 0; }
.ls-ws-overall-body { flex: 1 1 auto; min-width: 0; }
.ls-ws-overall-kicker {
    font-size: .78em; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
    color: #8a9099; margin: 0 0 6px;
}
.ls-ws-overall-lead { font-size: 1.12em; font-weight: 600; color: #1a1a1a; margin: 0 0 8px; line-height: 1.4; }
.ls-ws-overall-detail { font-size: .9em; color: #6b7178; margin: 0; }
.ls-ws-ring-band { font-weight: 800; fill: var(--ws-band, #1a1a1a); }
.ls-ws-ring .ls-ring-num { font-size: 30px; }

/* Réponses modèles multiples (tâche à plusieurs questions). */
.ls-ma-part { margin: 0 0 14px; }
.ls-ma-part:last-child { margin-bottom: 0; }
.ls-ma-part-q { font-weight: 700; color: #1a1a1a; margin: 0 0 4px; }

/* Lecteur audio (réponses candidat + modèles) sur la page résultats. */
.ls-ya-audio { display: block; width: 100%; max-width: 420px; margin: 8px 0; }

/* Bloc par question (tâches à questions multiples). */
.ls-ws-qblock { padding: 14px 0; border-top: 1px solid #eceef1; }
.ls-ws-qblock:first-of-type { border-top: 0; padding-top: 4px; }
.ls-ws-qhead { font-weight: 700; color: #1a1a1a; font-size: 1.05em; margin: 0 0 8px; }

/* Note globale — empilage sur petit écran. */
@media (max-width: 560px) {
    .ls-ws-overall { flex-direction: column; text-align: center; gap: 12px; }
    .ls-ws-overall-body { text-align: center; }
}

/* ============================================================================
   PASSATION ADAPTATIVE — numéros de question masqués (fidélité au test officiel)
   ----------------------------------------------------------------------------
   Le test Reading/Listening est adaptatif : les tâches ne sont pas servies dans
   l'ordre du XML, donc les numéros globaux hérités du XML (6, 7, 11…) n'ont aucun
   sens à l'écran et sautent d'une tâche à l'autre. On les masque VISUELLEMENT ;
   ils restent dans le DOM (name="q[..]", aria-label) pour la soumission POST et
   la correction serveur, qui sont inchangées.
   Portée limitée à la carte de passation .ls-task pour ne pas toucher aux
   écrans de résultats.
   ============================================================================ */
.ls-task .ls-qnum,
.ls-task .ls-gap-num,
.ls-task .ls-drop-num { display: none !important; }

/* Sentence-completion : la liste numérote LOCALEMENT (1..N) via le marqueur <ol>,
   puisque l'attribut value de départ a été retiré côté rendu. On garde donc le
   marqueur de liste visible (numérotation locale voulue), on masque seulement le
   badge inline redondant (déjà couvert par .ls-gap-num ci-dessus). */
.ls-sc-list { list-style: decimal; }

/* Grid matching (two-task) : le repère de sous-tâche « 1. / 2. » (.ls-grid-tasknum)
   est un vrai libellé de sous-tâche de l'officiel, PAS un numéro de question global
   → on le conserve tel quel. */

/* ============================================================================
   BANDEAU DE STATUT AUDIO (Listening) — fidèle à l'écran officiel
   ----------------------------------------------------------------------------
   Pendant le temps de lecture, les secondes s'égrènent (« seconds to read
   1 2 3 … »), les secondes écoulées se grisent. Quand l'enregistrement démarre,
   les secondes disparaissent et le label devient « Playing 1 of 2 », puis
   « Recording finished ». Sans JS, rien ne bouge : le bandeau reste inerte.
   ============================================================================ */
.ls-audio-status {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    margin: 0 0 16px; padding: 10px 14px;
    border: 1px solid var(--border-color, #e3e3e3); border-radius: 10px;
    background: rgba(31, 124, 255, .04);
    font-size: .92em; color: #3a4750;
}
.ls-audio-status-label { font-weight: 700; letter-spacing: .01em; }

/* Les secondes : une pastille par seconde. */
.ls-audio-ticks { display: flex; flex-wrap: wrap; gap: 4px; }
.ls-audio-tick {
    min-width: 22px; padding: 1px 5px; text-align: center;
    border-radius: 5px; font-size: .85em; font-weight: 600;
    background: var(--border-color, #eceef3); color: #5b6770;
    transition: background-color .2s ease, color .2s ease, opacity .2s ease;
}
/* Seconde écoulée : atténuée. */
.ls-audio-tick.is-elapsed { background: #1f7cff; color: #fff; opacity: .55; }

/* Nombre d'écoutes (« ×2 »), toujours visible. */
.ls-audio-plays {
    margin-left: auto; font-weight: 700; color: #5b6770;
    padding: 1px 8px; border-radius: 999px;
    background: var(--border-color, #eceef3);
}

/* Compteur numérique (délais longs : 45 s). */
.ls-audio-count {
    font-weight: 700; font-variant-numeric: tabular-nums;
    padding: 1px 8px; border-radius: 6px;
    background: var(--border-color, #eceef3); color: #5b6770;
}

/* Lecture en cours : on masque les secondes, le label porte l'information. */
.ls-audio-status.is-playing .ls-audio-ticks,
.ls-audio-status.is-playing .ls-audio-count { display: none; }
.ls-audio-status.is-playing { background: rgba(31, 124, 255, .10); }
.ls-audio-status.is-playing .ls-audio-status-label { color: #1f56b0; }

/* Terminé : bandeau discret. */
.ls-audio-status.is-done { background: transparent; opacity: .7; }
.ls-audio-status.is-done .ls-audio-ticks,
.ls-audio-status.is-done .ls-audio-count { display: none; }

/* Le bandeau n'a pas de sens à l'impression. */
@media print { .ls-audio-status { display: none; } }

/* Beaucoup de secondes (45) sur petit écran : on réduit les pastilles. */
@media (max-width: 640px) {
    .ls-audio-tick { min-width: 18px; padding: 1px 3px; font-size: .78em; }
    .ls-audio-plays { margin-left: 0; }
}

/* ============================================================================
   ACCORDÉONS DE RÉSULTATS — ouverture en douceur
   ----------------------------------------------------------------------------
   Un <details> n'anime pas sa hauteur nativement, et l'animer réellement
   exigerait d'envelopper le contenu dans un conteneur (changement de markup).
   On se limite donc à une entrée en fondu + léger glissement du contenu, qui
   suffit à adoucir l'ouverture et fonctionne sur tous les navigateurs.
   Le marqueur triangulaire pivote pour signaler l'état.
   ============================================================================ */
.ls-task-recap > summary,
.ls-rubric-details > summary {
    list-style: none;
    display: flex; align-items: center; gap: 10px;
    transition: color .15s ease;
}
.ls-task-recap > summary::-webkit-details-marker,
.ls-rubric-details > summary::-webkit-details-marker { display: none; }

/* Chevron dessiné en CSS, pivotant à l'ouverture. */
.ls-task-recap > summary::before,
.ls-rubric-details > summary::before {
    content: ""; flex: none;
    width: 7px; height: 7px;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(-45deg); transform-origin: 60% 60%;
    transition: transform .2s ease;
    opacity: .6;
}
.ls-task-recap[open] > summary::before,
.ls-rubric-details[open] > summary::before { transform: rotate(45deg); }

/* Le contenu entre en fondu descendant. */
.ls-task-recap[open] > *:not(summary),
.ls-rubric-details[open] > *:not(summary) {
    animation: ls-details-in .22s ease-out;
}
@keyframes ls-details-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Respect des préférences d'accessibilité. */
@media (prefers-reduced-motion: reduce) {
    .ls-task-recap > summary::before,
    .ls-rubric-details > summary::before { transition: none; }
    .ls-task-recap[open] > *:not(summary),
    .ls-rubric-details[open] > *:not(summary) { animation: none; }
}

/* À l'impression, aucune animation ni marqueur : tout est déjà ouvert (JS). */
@media print {
    .ls-task-recap[open] > *:not(summary),
    .ls-rubric-details[open] > *:not(summary) { animation: none; }
    .ls-task-recap > summary::before,
    .ls-rubric-details > summary::before { display: none; }
    .ls-task-recap, .ls-rubric-details { break-inside: avoid; }
}
