/* ============================================================
   Parcours RGPD — feuille de style
   ============================================================ */

:root {
  --bg: #f5f7fb;
  --bg-soft: #eef1f8;
  --surface: #ffffff;
  --surface-2: #f8fafc;
  --border: #e3e8f0;
  --text: #101828;
  --text-soft: #5a6a85;
  --text-mute: #8a97ab;

  --brand: #4f46e5;
  --brand-2: #7c3aed;
  --brand-soft: #eef2ff;
  --teal: #0d9488;
  --ok: #12b76a;
  --ok-soft: #e7f8ef;
  --warn: #f79009;
  --warn-soft: #fff5e6;
  --danger: #e11d48;
  --danger-soft: #ffeaef;
  --info: #0ea5e9;
  --info-soft: #e8f6fe;

  --accent: var(--brand);

  /* La couleur de Justine. C'est son identité, pas une teinte de page : elle est
     déjà celle de son pull et celle de son médaillon dans les deux escape games
     (`teinte: '#e67e22'` sur sa fiche de personnage). Un jeton pour qu'il n'y
     ait qu'un endroit à changer, et pour qu'elle ne dérive jamais vers la
     teinte du parcours qu'elle présente. */
  --justine: #e67e22;

  --radius: 16px;
  --radius-sm: 10px;
  --radius-lg: 26px;
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .06);
  --shadow: 0 4px 12px rgba(16, 24, 40, .07), 0 12px 28px rgba(16, 24, 40, .06);
  --shadow-lg: 0 18px 50px rgba(16, 24, 40, .14);
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-theme='dark'] {
  --bg: #0c1020;
  --bg-soft: #121729;
  --surface: #161c31;
  --surface-2: #1c2338;
  --border: #2a3350;
  --text: #eef2fb;
  --text-soft: #a8b4cd;
  --text-mute: #7d8aa6;
  --brand: #8b8cff;
  --brand-2: #b78bff;
  --brand-soft: #232a49;
  --ok-soft: #12301f;
  --warn-soft: #33260f;
  --danger-soft: #3a1220;
  --info-soft: #0f2a3d;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow: 0 6px 22px rgba(0, 0, 0, .45);
  --shadow-lg: 0 22px 60px rgba(0, 0, 0, .6);
}

* { box-sizing: border-box; }

/* ── Barres de défilement ───────────────────────────────────────────────────
   Fines, aux couleurs du thème, discrètes jusqu'au survol.
   On ne les MASQUE pas : une page longue sans barre ne dit plus qu'elle est
   longue, ni où l'on en est. C'est le repère de position qu'on perdrait, pas
   une décoration — et sur les modules de sensibilisation, où l'on descend
   parfois beaucoup, savoir qu'il reste du chemin fait partie de la lecture.
   La seule exception reste la page de lancement de l'escape (`html.sans-barres`,
   plus bas) : elle tient dans un écran, sa barre ne servait qu'à hacher un
   bandeau plein cadre.
   `scrollbar-width` est la propriété standard, honorée par Firefox et par
   Chromium ; les règles `::-webkit-scrollbar` couvrent Safari et les Chromium
   plus anciens, qui l'ignorent. Le `border` transparent avec `background-clip`
   est ce qui donne au pouce sa gouttière : sans lui il colle aux bords et
   paraît deux fois plus épais. */
:root { --barre: #c9d1e0; --barre-active: #a4b0c6; }
html[data-theme='dark'] { --barre: #2c3555; --barre-active: #3f4d76; }

* { scrollbar-width: thin; scrollbar-color: var(--barre) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--barre); background-clip: content-box;
  border: 3px solid transparent; border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover { background: var(--barre-active); background-clip: content-box; }

/* Le document occupe au moins la fenêtre ; le corps, lui, s'étire selon son
   contenu. Une hauteur FIXE de 100 % l'aurait figé à la taille de la fenêtre,
   et le pied de page se serait retrouvé au milieu d'une page longue. */
html { height: 100%; }

body {
  margin: 0;
  /* Le pied de page se pose au bas de la FENÊTRE quand le contenu ne la
     remplit pas, et se laisse pousser plus bas quand il déborde. La colonne
     souple s'en charge : le corps prend la hauteur restante, le pied suit.
     Les fenêtres, bannières et confettis sont en position fixe : ils ne
     participent pas à cette colonne. */
  display: flex; flex-direction: column; min-height: 100dvh;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  line-height: 1.6;
  /* Pas de surlignement bleu au double-clic ou au glissé : l'interface est
     faite de zones cliquables, la sélection de texte n'y a pas d'usage. */
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  /* Supprime aussi le flash bleu au toucher sur mobile. */
  -webkit-tap-highlight-color: transparent;
}

/* Les champs de saisie doivent évidemment rester sélectionnables. */
input, textarea, select, [contenteditable] {
  -webkit-user-select: text;
  -moz-user-select: text;
  user-select: text;
}

h1, h2, h3, h4 { line-height: 1.22; margin: 0 0 .5em; font-weight: 700; letter-spacing: -.02em; }
p { margin: 0 0 1em; }
a { color: var(--brand); }
button { font-family: inherit; }

::selection { background: color-mix(in srgb, var(--brand) 25%, transparent); }

/* L'attribut `hidden` doit cacher, quoi qu'en dise la feuille de style.
   La règle par défaut du navigateur — [hidden] { display: none } — a la
   spécificité la plus faible qui soit : n'importe quelle classe qui pose un
   `display` la bat. Un `.btn` masqué par `element.hidden = true` restait donc
   affiché, ce qui est exactement le contraire de ce que le code demandait. */
[hidden] { display: none !important; }

/* ─────────────── Boutons ─────────────── */
/* ── Boutons ────────────────────────────────────────────────────────────────
   Un seul principe partout : la couleur vit dans le trait et le texte, jamais
   en aplat. Au survol, un voile très léger de cette même couleur et un contour
   plus affirmé — assez pour signaler, trop discret pour crier.
   --ton est la couleur du bouton ; --ton-texte en est une version assourdie,
   mêlée à la couleur du texte, qui évite l'effet fluorescent des teintes pures.
   Les variantes ne redéfinissent que --ton. */
.btn {
  --ton: var(--brand);
  --ton-texte: color-mix(in srgb, var(--ton) 70%, var(--text));
  display: inline-flex; align-items: center; justify-content: center; gap: .55em;
  padding: .72em 1.35em; border-radius: 999px;
  font-weight: 600; font-size: .95rem; cursor: pointer;
  background: transparent; color: var(--ton-texte);
  border: 1.5px solid color-mix(in srgb, var(--ton) 34%, var(--border));
  text-decoration: none; /* un <a> stylé en bouton ne doit pas être souligné */
  transition: background .16s, border-color .16s, color .16s, opacity .16s;
}
.btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ton) 9%, transparent);
  border-color: color-mix(in srgb, var(--ton) 62%, var(--border));
}
.btn:active:not(:disabled) { background: color-mix(in srgb, var(--ton) 17%, transparent); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* Bouton PLEIN. L'application n'en avait pas : partout ailleurs, un contour
   suffit parce que le fond est uni. Posé SUR une image, il se dilue — d'où
   cette variante, qui garde la mécanique et la teinte du système et ne change
   que le remplissage. À réserver aux appels à l'action sur fond illustré. */
.btn.plein { background: var(--ton); color: #fff; border-color: var(--ton); }
.btn.plein:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ton) 86%, #000);
  border-color: color-mix(in srgb, var(--ton) 86%, #000);
}
.btn.plein:active:not(:disabled) { background: color-mix(in srgb, var(--ton) 74%, #000); }

/* Variantes : seule la teinte change. */
.btn.ok { --ton: var(--ok); }
.btn.warn { --ton: var(--warn); }
.btn.danger { --ton: var(--danger); }
/* Anneau de focus visible au clavier : nécessaire dès lors qu'il n'y a plus
   d'ombre pour marquer l'état actif. */
.btn:focus-visible, .iconbtn:focus-visible, .opt:focus-visible,
.sort-cat:focus-visible, .match-item:focus-visible, .theme-switch:focus-visible,
.pending-chip:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
/* Bouton secondaire : exactement le même comportement, dans une teinte neutre.
   C'est la couleur qui porte la hiérarchie, pas une différence de mécanique. */
.btn.ghost { --ton: var(--text-mute); color: var(--text-soft); border-color: var(--border); }
.btn.ghost:hover:not(:disabled) { border-color: color-mix(in srgb, var(--text-mute) 65%, var(--border)); }
.btn.ghost.warn-ghost { --ton: var(--warn); }
.btn.ghost.ok-ghost { --ton: var(--ok); }
.btn.ghost.info-ghost { --ton: var(--info); }



.btn.lg { padding: .95em 2em; font-size: 1.05rem; }
.btn.sm { padding: .45em .95em; font-size: .84rem; }
.btn.ghost.danger-ghost { --ton: var(--danger); padding: .45em .7em; }

/* ─────────────── Écran d'authentification ─────────────── */
.auth {
  min-height: 100vh; display: grid; grid-template-columns: 1.05fr .95fr;
}
.auth__brand {
  position: relative; overflow: hidden; color: #fff;
  background: linear-gradient(140deg, #4f46e5 0%, #7c3aed 45%, #0d9488 120%);
  display: flex; flex-direction: column; justify-content: center; padding: 4rem 4.5rem;
}
.auth__brand::before, .auth__brand::after {
  content: ''; position: absolute; border-radius: 50%; opacity: .18;
  background: radial-gradient(circle at 30% 30%, #fff, transparent 65%);
}
.auth__brand::before { width: 520px; height: 520px; top: -160px; right: -140px; animation: float 16s ease-in-out infinite; }
.auth__brand::after { width: 380px; height: 380px; bottom: -120px; left: -100px; animation: float 20s ease-in-out infinite reverse; }
@keyframes float { 50% { transform: translate(26px, -30px) scale(1.06); } }

.auth__logo { display: flex; align-items: center; gap: .75rem; font-weight: 800; font-size: 1.15rem; letter-spacing: -.01em; margin-bottom: 3rem; position: relative; }
/* Le logo est posé sur une pastille blanche : ses roses et ses bleus se
   perdraient sur le dégradé violet du panneau de gauche. */
.auth__logo .mark {
  width: 46px; height: 46px; border-radius: 14px; display: grid; place-items: center; padding: 5px;
  background: #fff; box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
}
.auth__brand h1 { font-size: clamp(2rem, 3.4vw, 3rem); position: relative; margin-bottom: .35em; }
/* 30ch cassait la phrase en trois lignes, dont une très courte. À 56ch elle
   tient en deux, et `balance` les répartit également au lieu de laisser un
   orphelin en bas. */
.auth__brand p {
  font-size: 1.08rem; opacity: .92; max-width: 56ch; position: relative;
  text-wrap: balance;
}
.auth__features { list-style: none; padding: 0; margin: 2.5rem 0 0; display: grid; gap: 1rem; position: relative; }
.auth__features li { display: flex; align-items: flex-start; gap: .85rem; font-size: .98rem; opacity: .95; }
.auth__features .ico {
  flex: none; width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .2);
}

/* Justine se pose dans le coin bas-droit du panneau, coupée par le bord — elle
   fait partie du décor plutôt que d'être une illustration posée dessus. Le
   contenu du panneau étant en `position: relative`, elle passe DERRIÈRE le
   texte : c'est voulu, la colonne de gauche est étroite et le chevauchement
   n'est acceptable que dans ce sens. */
.auth__justine {
  /* Marge positive à droite, jamais négative : sa main ouverte est le bord le
     plus à droite de l'image, et le moindre débordement lui coupe les doigts.
     Le bas, lui, peut déborder — c'est la coupe voulue. */
  position: absolute; right: .5rem; bottom: -1rem; z-index: 0;
  height: min(42vh, 350px); width: auto; pointer-events: none;
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .32));
}
/* En dessous, la colonne se réduit à un bandeau : la figure y prendrait toute
   la place et pousserait le formulaire hors de l'écran. */
@media (max-width: 1180px) { .auth__justine { height: 240px; opacity: .55; } }
/* SOUS 900 PX, L'ENCART SE LIT EN DEUX MOITIÉS : Justine à gauche, la marque à
   droite. Elle y était auparavant sous le texte, centrée et haute de 180 px —
   l'encart mangeait alors l'écran en hauteur pour une figure qu'on voyait à
   peine. Demande de Christophe, le 24 septembre 2026.

   LA MOITIÉ EXACTE : la première colonne fait 50 % de l'encart, et l'encart n'a
   plus ni marge à gauche ni marge en bas. Justine remplit donc sa colonne et
   touche les deux bords — le dessin est cadré au plus près, sans transparence en
   bas, si bien qu'elle s'arrête pile sur la bordure.

   Le `1fr` en dernière rangée, lui, est ce qui garde le logo et le titre serrés
   en haut : sans lui, la hauteur de l'image se répartirait entre les trois
   rangées et ouvrirait un trou entre le nom et la phrase. */
@media (max-width: 900px) {
  .auth__brand {
    display: grid; grid-template-columns: 50% 1fr; grid-template-rows: auto 1fr auto;
    /* La marge droite de l'encart passe de 2 à 1 rem : elle rend au texte une
       partie de ce que son retrait de gauche lui prend. */
    align-items: start; column-gap: 1rem; padding: 2.6rem 1rem 0 0;
  }
  /* Le logo et le nom tiennent la première rangée, sur toute la largeur : ils
     forment l'en-tête de la page et ne partagent pas leur ligne avec l'image.
     Le nom reste donc d'un seul tenant, et son corps se resserre un peu pour
     tenir sur un écran étroit. */
  .auth__logo {
    grid-column: 1 / -1; grid-row: 1; margin: 0 0 1.1rem; padding-left: 1.5rem;
    /* Le corps suit la largeur de l'écran : à 360 px, « Appui Santé Occitanie
       Services » sur une seule ligne déborderait de quelques pixels — et un
       débordement horizontal décale toutes les fenêtres en position fixe. */
    font-size: clamp(.82rem, 3.2vw, 1.02rem); gap: .55rem; white-space: nowrap;
  }
  .auth__logo .mark { width: 40px; height: 40px; border-radius: 12px; }
  .auth__nom span { display: inline; }
  /* La rangée du titre prend toute la place libre entre l'en-tête et la
     phrase, et le titre s'y centre : posé en haut, il laissait sous lui un
     grand vide que la hauteur de Justine, à gauche, rendait très visible. */
  /* Le texte part plus à droite, d'un retrait franc. Le réduire à la largeur de
     sa plus longue ligne ne servait à rien : le titre remplit déjà sa colonne,
     sa « place libre » n'est que le bord irrégulier des lignes, et aucune marge
     automatique n'avait de quoi le pousser. C'est donc le retrait qui décale,
     et la colonne qui s'élargit d'autant pour que rien ne se resserre. */
  .auth__brand h1 {
    grid-column: 2; grid-row: 2; align-self: center; margin-bottom: 0; padding-left: 1.5rem;
  }
  .auth__brand p { grid-column: 2; grid-row: 3; margin: .7rem 0 1.6rem; padding-left: 1.5rem; }
  .auth__justine {
    grid-column: 1; grid-row: 2 / -1; align-self: end;
    position: static; margin: 0; width: 100%; height: auto; opacity: 1;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, .3));
    /* Elle ne se colle pas au bord gauche : un pouce d'écart la pose dans
       l'encart au lieu de la plaquer dessus. Un décalage, non une largeur — sa
       colonne garde sa moitié et le bas du dessin reste sur le bord. */
    transform: translateX(1rem);
  }
}

.auth__panel { display: grid; place-items: center; padding: 2.5rem; background: var(--bg); }
.auth__card h2 { font-size: 1.5rem; }
.auth__card .auth__intro { color: var(--text-soft); font-size: .94rem; }

/* Sur iOS, 100vh dépasse la zone réellement visible (barres du navigateur) :
   le bas de la page s'y retrouve tronqué. 100svh vise la plus petite hauteur
   possible, donc toujours visible. */
@supports (min-height: 100svh) {
  .auth { min-height: 100svh; }
}
.auth__card { width: min(430px, 100%); }
.auth__tabs { display: flex; gap: .3rem; background: var(--bg-soft); padding: .3rem; border-radius: 999px; margin-bottom: 1.8rem; }
.auth__tabs button {
  flex: 1; padding: .65em 1em; border: 0; background: transparent; border-radius: 999px;
  font-weight: 600; color: var(--text-soft); cursor: pointer; transition: .2s;
}
.auth__tabs button.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

.field { margin-bottom: 1.05rem; }
.field label { display: block; font-size: .85rem; font-weight: 600; color: var(--text-soft); margin-bottom: .4rem; }
.field input, .field select, .field textarea {
  width: 100%; padding: .8em 1em; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: .98rem; font-family: inherit; transition: .18s;
}
.field select { cursor: pointer; }
.field input:focus, .field select:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 14%, transparent);
}
.field .hint { font-size: .78rem; color: var(--text-mute); margin-top: .35rem; }

.alert { padding: .8em 1em; border-radius: var(--radius-sm); font-size: .9rem; margin-bottom: 1rem; display: flex; gap: .6em; align-items: flex-start; }
.alert.err { background: var(--danger-soft); color: var(--danger); }
.alert.ok { background: var(--ok-soft); color: var(--ok); }
.alert.info-note {
  background: var(--info-soft); color: color-mix(in srgb, var(--info) 78%, var(--text));
  font-size: .84rem; align-items: flex-start; margin: .2rem 0 1rem;
}

.alert.ok-note {
  background: var(--ok-soft); color: color-mix(in srgb, var(--ok) 82%, var(--text));
  font-size: .84rem; margin: .2rem 0 1rem;
}
.alert.warn-note {
  background: var(--warn-soft); color: color-mix(in srgb, var(--warn) 82%, var(--text));
  font-size: .84rem; margin: .2rem 0 1rem;
}
.alert.warn-note b, .alert.ok-note b { color: inherit; }

/* Écran de confirmation de la dernière question d'une évaluation. */
.quiz-recap {
  border: 1px solid color-mix(in srgb, var(--ok) 35%, var(--border));
  background: color-mix(in srgb, var(--ok) 7%, var(--surface));
  border-radius: var(--radius); padding: 1.1rem 1.2rem; margin-top: 1.2rem;
}
.quiz-recap__titre {
  font-size: .74rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--text-mute); margin-bottom: .6rem;
}
.quiz-recap__ligne { display: flex; align-items: flex-start; gap: .6rem; padding: .25rem 0; font-size: .96rem; }
.quiz-recap__ligne .ok { color: var(--ok); font-weight: 700; flex: 0 0 auto; }
.quiz-recap__aide { font-size: .85rem; color: var(--text-mute); margin: .9rem 0 0; }

/* Fin d'un exercice : le moteur vide sa zone, il faut donc dire ce qui a été
   fait et où se trouve la suite. Le bouton de validation se signale, car il
   tombe souvent sous la ligne de flottaison d'un écran de portable. */
.game-fini {
  display: flex; flex-direction: column; gap: .2rem; text-align: center;
  padding: 1.2rem; border-radius: var(--radius);
  background: color-mix(in srgb, var(--ok) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ok) 32%, var(--border));
}
.game-fini b { font-size: 1.05rem; color: var(--ok); }
.game-fini span { font-size: .88rem; color: var(--text-soft); }
/* Exercice non parfait : teinte d'avertissement + bouton « Recommencer ». */
.game-fini--ko { background: color-mix(in srgb, var(--warn) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--warn) 34%, var(--border)); }
.game-fini--ko b { color: var(--warn); }

.step-nav--pret .btn.lg {
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 55%, transparent);
  animation: pretPulse 1.6s ease-out 3;
}
@keyframes pretPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 45%, transparent); }
  70%  { box-shadow: 0 0 0 14px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
/* Le bouton « Continuer » quand il s'ouvre : mêmes pulsations que la barre
   d'étapes prête, mais posées sur le bouton lui-même — il n'est pas toujours
   dans une barre « prête », et c'est lui qu'on cherche du regard. Trois fois,
   puis plus rien : au-delà, un bouton présent à chaque écran clignoterait en
   permanence et ne signalerait plus rien du tout. */
.btn.btn-pret { animation: pretPulse 1.6s ease-out 3; }
@media (prefers-reduced-motion: reduce) {
  .step-nav--pret .btn.lg, .btn.btn-pret { animation: none; }
}

/* ═══════════════ Cyber Escape Game ═══════════════ */
.esc-entete {
  display: flex; align-items: center; gap: 1.2rem; flex-wrap: wrap;
  padding: .9rem 1.2rem; margin-bottom: 1.4rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  position: sticky; top: .6rem; z-index: 5; box-shadow: var(--shadow-sm);
}
.esc-entete__titre b { display: block; font-size: .96rem; }
.esc-entete__titre span { font-size: .78rem; color: var(--text-mute); }
.esc-entete__droite { margin-left: auto; display: flex; align-items: center; gap: .9rem; }

.esc-jauge { display: flex; gap: .3rem; }
.esc-plot {
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .74rem; font-weight: 700;
  background: var(--bg-soft); color: var(--text-mute); border: 1px solid var(--border);
}
.esc-plot.faite { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.esc-plot.active { background: var(--brand); color: #fff; border-color: var(--brand); }
/* Repères cliquables pour l'administrateur : mêmes pastilles, mais on voit
   qu'elles répondent. */
.esc-jauge--admin .esc-plot { cursor: pointer; font: inherit; font-size: .8rem; }
.esc-jauge--admin .esc-plot:hover { border-color: var(--brand); color: var(--text); }
.esc-jauge--admin .esc-plot:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* Chiffre du code révélé à l'administrateur : présent, mais visiblement pas gagné. */
.esc-case--admin { opacity: .55; border-style: dashed; }

.esc-coffre { display: flex; gap: .25rem; }
.esc-case {
  width: 1.5rem; height: 1.9rem; border-radius: 5px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: 'DejaVu Sans Mono', monospace; font-weight: 700; font-size: .9rem;
  background: var(--bg-soft); color: var(--text-mute); border: 1px dashed var(--border);
}
.esc-case.on { background: color-mix(in srgb, var(--brand) 12%, transparent); color: var(--brand); border: 1px solid var(--brand); }
.esc-chrono {
  font-family: 'DejaVu Sans Mono', monospace; font-size: 1.05rem; font-weight: 700;
  color: var(--text); padding: .2rem .6rem; border-radius: 7px; background: var(--bg-soft);
}

/* ── Intro, salles, résolution ── */
.esc-intro, .esc-final, .esc-fin, .esc-resolu {
  text-align: center; max-width: 640px; margin: 0 auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 2.4rem 2rem;
}
.esc-intro__ico, .esc-final__ico, .esc-fin__ico { font-size: 3rem; }
.esc-intro h2, .esc-final h2, .esc-fin h2 { margin: .6rem 0 1rem; font-size: 1.5rem; }
.esc-intro p, .esc-final p, .esc-fin p { color: var(--text-soft); line-height: 1.65; }
.esc-mission {
  margin: 1.4rem 0 .4rem; font-size: .8rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--brand);
}
.esc-actions { display: flex; gap: .7rem; justify-content: center; margin-top: 1.4rem; flex-wrap: wrap; }

.esc-salle { --ton: var(--brand); }
.esc-salle.ton-danger { --ton: var(--danger); }
.esc-salle.ton-warn { --ton: var(--warn); }
.esc-salle.ton-info { --ton: var(--info); }
.esc-salle.ton-ok { --ton: var(--ok); }
.esc-salle__tete { display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 1rem; }
.esc-salle__ico {
  flex: 0 0 auto; width: 3rem; height: 3rem; border-radius: var(--radius-sm); font-size: 1.5rem;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--ton) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ton) 28%, var(--border));
}
.esc-salle__kicker { font-size: .74rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ton); }
.esc-salle__tete h2 { margin: .1rem 0 .1rem; font-size: 1.4rem; }
.esc-salle__ss { font-size: .9rem; color: var(--text-soft); }
.esc-brief {
  background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--ton);
  border-radius: var(--radius-sm); padding: 1rem 1.1rem; color: var(--text-soft); line-height: 1.65;
  margin-bottom: 1.4rem;
}
.esc-consigne {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  font-weight: 600; margin-bottom: .8rem; flex-wrap: wrap;
}
.esc-compteur {
  font-family: 'DejaVu Sans Mono', monospace; font-weight: 700; color: var(--ton, var(--brand));
  background: color-mix(in srgb, var(--ton, var(--brand)) 11%, transparent);
  padding: .15rem .6rem; border-radius: 99px; font-size: .88rem;
}
.esc-aide { font-size: .85rem; color: var(--text-mute); margin: 0 0 1rem; }

.esc-resolu__chiffre {
  width: 4rem; height: 4rem; margin: 0 auto 1rem; border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  font-family: 'DejaVu Sans Mono', monospace; font-size: 2rem; font-weight: 800;
  color: #fff; background: var(--ok);
}
.esc-resolu b { font-size: 1.15rem; }
.esc-resolu__code { font-size: .9rem; color: var(--text-mute); }

/* ── Message piégé ── */
.esc-mail { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.esc-mail__head { padding: 1rem 1.2rem; background: var(--surface-2); border-bottom: 1px solid var(--border); font-size: .9rem; }
.esc-mail__head > div { display: flex; gap: .6rem; padding: .18rem 0; }
.esc-mail__head .lbl { flex: 0 0 3.4rem; color: var(--text-mute); font-size: .8rem; }
.esc-mail__corps { padding: 1.4rem 1.4rem 1.2rem; font-size: .94rem; line-height: 1.75; color: var(--text-soft); }
.esc-mail__par { margin: 0 0 .95rem; }
.esc-mail__par:last-child { margin-bottom: 0; }
/* La signature se détache, comme sur le papier. */
.esc-mail__par:last-child:not(:only-child) { margin-top: .3rem; }
.esc-frag { cursor: pointer; border-radius: 3px; padding: 0 1px; transition: background .12s; }
.esc-frag:hover { background: color-mix(in srgb, var(--brand) 13%, transparent); }
.esc-frag.trouve {
  background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok);
  font-weight: 600; cursor: default;
}
.esc-frag.ecarte { opacity: .45; cursor: default; }
.esc-mail__corps { word-spacing: .02em; }

/* ── Trouvailles et explications ── */
.esc-detail { display: flex; flex-direction: column; gap: .6rem; margin-top: 1.2rem; }
.esc-trouvaille {
  padding: .8rem 1rem; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ok) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ok) 28%, var(--border));
  animation: escApparait .25s ease-out;
}
.esc-trouvaille.mauvaise {
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--danger) 28%, var(--border));
}
.esc-trouvaille b { display: block; font-size: .93rem; margin-bottom: .2rem; }
.esc-trouvaille p { margin: 0; font-size: .88rem; color: var(--text-soft); line-height: 1.6; }
@keyframes escApparait { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* ── Cartes de réponse ── */
.esc-cartes { display: grid; gap: .55rem; }
.esc-carte {
  display: flex; align-items: center; gap: .8rem; text-align: left; width: 100%;
  padding: .85rem 1rem; border-radius: var(--radius-sm); font-family: inherit; font-size: .94rem;
  background: var(--surface); border: 1px solid var(--border); color: var(--text); cursor: pointer;
  transition: border-color .15s, background .15s;
}
.esc-carte:hover:not(.placee):not(.ecartee) { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 5%, var(--surface)); }
.esc-carte.choisie { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 11%, var(--surface)); font-weight: 600; }
.esc-carte.placee { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 11%, var(--surface)); cursor: default; }
.esc-carte.ecartee { border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); opacity: .5; cursor: default; text-decoration: line-through; }
.esc-carte__rang {
  flex: 0 0 auto; width: 1.6rem; height: 1.6rem; border-radius: 50%; background: var(--ok); color: #fff;
  display: inline-flex; align-items: center; justify-content: center; font-size: .78rem; font-weight: 700;
}
.esc-carte__txt { flex: 1; }

/* ── Tri ── */
.esc-tri__item {
  text-align: center; font-size: 1.02rem; font-weight: 600; padding: 1.6rem 1.2rem;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: .9rem;
}
.esc-tri__cats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .6rem; }
.esc-cat {
  display: flex; flex-direction: column; align-items: center; gap: .3rem; padding: 1rem .8rem;
  border-radius: var(--radius-sm); font-family: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
  background: color-mix(in srgb, var(--cat) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--cat) 32%, var(--border)); color: var(--cat);
}
.esc-cat:hover { background: color-mix(in srgb, var(--cat) 18%, var(--surface)); }
.esc-cat .ico { font-size: 1.3rem; }

/* ── Conversation piégée ──
   Le fil s'allonge à chaque tour et les messages précédents restent lisibles :
   la fraude au virement tient à l'enchaînement, qu'on doit pouvoir relire. */
.esc-fil { display: flex; flex-direction: column; gap: .8rem; margin-bottom: 1.2rem; }
.esc-bulle {
  border: 1px solid var(--border); border-left: 4px solid var(--warn);
  border-radius: var(--radius); background: var(--surface); padding: .9rem 1.1rem; max-width: 88%;
}
.esc-bulle__de { display: flex; align-items: baseline; gap: .6rem; margin-bottom: .5rem; }
.esc-bulle__de b { font-size: .93rem; }
.esc-bulle__de span { font-size: .8rem; color: var(--text-soft); }
.esc-bulle__objet {
  font-size: .82rem; color: var(--text-soft); margin-bottom: .5rem;
  padding-bottom: .5rem; border-bottom: 1px dashed var(--border);
}
.esc-bulle p { margin: 0 0 .5rem; font-size: .92rem; line-height: 1.65; white-space: pre-line; }
.esc-bulle p:last-child { margin-bottom: 0; }
/* Vos réponses : alignées à droite, dans le vert de ce qui est acquis. */
.esc-bulle--moi {
  align-self: flex-end; border-left: 1px solid var(--border); border-right: 4px solid var(--ok);
  background: color-mix(in srgb, var(--ok) 7%, var(--surface));
}

/* ── La fiche de registre ──
   Une ligne par rubrique : l'intitulé à gauche, les mentions possibles à
   droite. Une fois tranchée, la rubrique se fige — on ne revient pas sur un
   registre qu'on vient de remplir. */
.esc-fiche__tete {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .9rem 1.1rem; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: color-mix(in srgb, var(--brand) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--brand) 26%, var(--border)); border-bottom: 0;
}
.esc-fiche__tete b { font-size: 1rem; }
.esc-fiche__tete span { font-size: .8rem; color: var(--text-soft); }
.esc-fiche {
  border: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  overflow: hidden; margin-bottom: 1.2rem;
}
.esc-fiche__ligne {
  display: flex; gap: 1rem; align-items: flex-start; flex-wrap: wrap;
  padding: .8rem 1.1rem; border-bottom: 1px solid var(--border); background: var(--surface);
}
.esc-fiche__ligne:last-child { border-bottom: 0; }
.esc-fiche__ligne.remplie { background: color-mix(in srgb, var(--ok) 8%, var(--surface)); }
.esc-fiche__lbl {
  flex: 0 0 9rem; font-size: .74rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-mute); padding-top: .45rem;
}
.esc-fiche__valeur { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.esc-fiche__ok { font-size: .93rem; font-weight: 600; color: var(--ok); padding: .35rem 0; }
.esc-chip {
  padding: .4rem .8rem; border-radius: 999px; cursor: pointer; text-align: left;
  font-family: inherit; font-size: .86rem; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: border-color .16s, background .16s;
}
.esc-chip:hover:not(.ecartee) { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 8%, var(--surface)); }
.esc-chip.ecartee { opacity: .45; text-decoration: line-through; cursor: default; border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }

/* ── L'arbre de décision ──
   Le chemin parcouru reste affiché : c'est lui qu'il faut retenir, pas la
   conclusion seule. Le filet vertical relie les marches franchies. */
.esc-arbre { position: relative; margin-bottom: 1.2rem; padding-left: .2rem; }
.esc-arbre__noeud {
  position: relative; display: flex; gap: .9rem; align-items: flex-start;
  padding: .9rem 1.1rem .9rem .9rem; margin-bottom: .5rem;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--border); border-radius: var(--radius-sm);
}
.esc-arbre__noeud.franchi { border-left-color: var(--ok); }
.esc-arbre__rang {
  flex: none; width: 1.6rem; height: 1.6rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: .78rem; font-weight: 700; color: #fff; background: var(--brand);
}
.esc-arbre__noeud.franchi .esc-arbre__rang { background: var(--ok); }
.esc-arbre__noeud b { font-size: .97rem; }
.esc-arbre__noeud p { margin: .25rem 0 0; font-size: .89rem; color: var(--text-soft); line-height: 1.6; }
.esc-arbre__reponse {
  display: flex; gap: .45rem; align-items: baseline; margin-top: .55rem;
  padding-top: .55rem; border-top: 1px dashed var(--border); font-size: .9rem; color: var(--ok);
}
.esc-arbre__fin {
  padding: 1rem 1.2rem; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ok) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ok) 32%, var(--border)); border-left: 3px solid var(--ok);
}
.esc-arbre__fin b { display: block; font-size: 1rem; margin-bottom: .25rem; }
.esc-arbre__fin p { margin: 0; font-size: .92rem; line-height: 1.65; color: var(--text-soft); }

@media (max-width: 640px) {
  .esc-fiche__lbl { flex: 1 0 100%; padding-top: 0; }
}

/* ── Écran de rançon ──
   Volontairement laid : terminal noir, texte sans accents, fautes de casse.
   C'est à cela que ressemblent ces messages, et le reconnaître fait partie de
   ce qu'on apprend ici. La couleur ne suit pas le thème — un écran de rançon
   n'a pas de mode clair. */
.esc-rancon {
  border-radius: var(--radius); overflow: hidden; margin-bottom: 1.2rem;
  border: 1px solid #3a0d16; background: #0b0b0f;
}
.esc-rancon__tete {
  display: flex; align-items: center; gap: .7rem; padding: .8rem 1.1rem;
  background: #7a0b1e; color: #ffe8ec; font-weight: 700; letter-spacing: .04em;
}
.esc-rancon__tete .ico { font-size: 1.2rem; }
.esc-rancon__note {
  margin: 0; padding: 1.2rem; overflow-x: auto;
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: .84rem; line-height: 1.75; color: #d8dbe0; white-space: pre-wrap;
}
.esc-rancon__phrase {
  text-align: center; font-size: 1.05rem; font-weight: 600; font-style: italic;
  padding: 1.5rem 1.2rem; margin-bottom: .9rem;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
}
.esc-rancon__choix { display: grid; grid-template-columns: repeat(2, 1fr); gap: .6rem; }
/* Le verdict est rappelé sur chaque ligne du récapitulatif : sans lui, on
   relit une explication sans savoir de quel côté elle penchait. */
.esc-verdict {
  display: inline-block; margin-bottom: .35rem; padding: .1rem .5rem; border-radius: 999px;
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  background: var(--surface-2); color: var(--text-soft); border: 1px solid var(--border);
}

/* ── Saisie du code ── */
.esc-saisies { display: flex; gap: .5rem; justify-content: center; margin: 1.6rem 0 .6rem; }
.esc-saisie {
  width: 3rem; height: 3.6rem; text-align: center; font-size: 1.5rem; font-weight: 800;
  font-family: 'DejaVu Sans Mono', monospace;
  border-radius: var(--radius-sm); border: 1.5px solid var(--border); background: var(--surface); color: var(--text);
}
.esc-saisie:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent); }

/* ── Bilan final ── */
.esc-bilan { display: flex; gap: 1.6rem; justify-content: center; flex-wrap: wrap; margin: 1.6rem 0; }
.esc-bilan div { display: flex; flex-direction: column; }
.esc-bilan b { font-size: 1.6rem; color: var(--brand); }
.esc-bilan span { font-size: .76rem; color: var(--text-mute); }
.esc-fin__soustitre { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute); margin: 1.6rem 0 .9rem; }
.esc-reflexes { display: grid; grid-template-columns: repeat(2, 1fr); gap: .7rem; }
.esc-reflexe {
  padding: 1rem .7rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border);
}
.esc-reflexe .ico { font-size: 1.5rem; display: block; }
.esc-reflexe b { display: block; font-size: .82rem; letter-spacing: .04em; margin: .3rem 0 .2rem; }
.esc-reflexe span { font-size: .78rem; color: var(--text-mute); line-height: 1.45; }

/* ═══════════════ Le bureau des erreurs ═══════════════
   La scène est une image ; les zones cliquables sont des rectangles SVG posés
   par-dessus, en coordonnées de l'image. Aucune n'est visible au repos : la
   salle consiste à regarder, pas à repérer ce qui brille. */
.bureau {
  max-width: 920px; margin: 0 auto;
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; background: var(--surface-2);
}
/* Une image matricielle ne suit pas le thème. Plutôt que d'en produire deux,
   on l'assombrit légèrement : le contraste du texte de la page reste maître. */
html[data-theme='dark'] .bureau { filter: brightness(.86) saturate(.94); }

.bureau-svg { display: block; width: 100%; height: auto; }
.bureau-svg image { pointer-events: none; }

.bureau-zone {
  fill: transparent; stroke: transparent; stroke-width: 5;
  cursor: pointer; transition: fill .15s, stroke .15s;
}
.bureau-zone:hover { fill: color-mix(in srgb, #ffffff 20%, transparent); stroke: rgba(255,255,255,.6); }
.bureau-zone:focus-visible { outline: none; stroke: var(--brand); stroke-width: 6; }
.bureau-zone.trouvee {
  fill: color-mix(in srgb, #12b76a 28%, transparent);
  stroke: #12b76a; cursor: default; pointer-events: none;
}

@media (max-width: 700px) {
  .esc-entete { position: static; }
  .esc-saisie { width: 2.4rem; height: 3rem; font-size: 1.2rem; }
}

/* ─────────────── Familles de parcours ───────────────
   Deux familles ont chacune leur « Niveau 1 » : le regroupement porte la
   distinction, sans quoi deux entrées homonymes se côtoieraient. */
.familles { display: grid; gap: 1.1rem; margin-bottom: 1.6rem; }
.famille__titre {
  font-size: .74rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-mute); margin-bottom: .5rem;
}
.famille .niveaux { margin-bottom: 0; }

/* ─────────────── Modules complémentaires ───────────────
   Une grille de cartes, pas un chemin : les modules se prennent dans
   n'importe quel ordre. La teinte reprend celle du module à l'écran comme
   sur son attestation. */
.mod-intro { color: var(--text-soft); font-size: .9rem; margin: -.4rem 0 1.1rem; }
/* Toutes les cartes à la même taille. Une grille n'égalise les hauteurs qu'au
   sein d'une rangée : un titre sur deux lignes — « Outils numériques de la
   e-Santé » — rendait sa rangée plus haute que la voisine. « grid-auto-rows:
   1fr » donne à chaque rangée la hauteur de la plus haute de toutes. */
.mod-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(285px, 1fr));
  grid-auto-rows: 1fr; gap: .9rem; margin-bottom: 2.4rem;
}

.mod-carte {
  --ton: var(--brand);
  /* Une carte accessible est un <button> : sans largeur explicite, il se
     dimensionne sur son contenu au lieu d'occuper sa case. */
  width: 100%; height: 100%; box-sizing: border-box;
  display: flex; align-items: flex-start; gap: .85rem; text-align: left;
  padding: 1rem 1.05rem; border-radius: var(--radius); font-family: inherit;
  background: var(--surface); border: 1px solid var(--border);
  /* Le liseré porte d'emblée la teinte franche du module, celle qu'il ne
     prenait qu'au survol : atténuée à 55 %, elle faisait des dix cartes une
     rangée grise, et la couleur qui les distingue n'apparaissait qu'une à une. */
  border-left: 3px solid var(--ton);
  cursor: pointer; transition: border-color .18s, box-shadow .18s, transform .18s;
}
.mod-carte.ton-danger { --ton: var(--danger); }
.mod-carte.ton-warn   { --ton: var(--warn); }
.mod-carte.ton-info   { --ton: var(--info); }
.mod-carte.ton-teal   { --ton: var(--teal); }
/* Le survol ne touche plus au liseré, déjà à sa couleur : il entourait aussi
   d'un reflet de la teinte, et c'est ce qui reste. En le forçant à la teinte du
   module, il faisait perdre son vert à une carte validée le temps du passage de
   la souris. */
.mod-carte:hover:not(.verrouille) {
  border-top-color: color-mix(in srgb, var(--ton) 45%, var(--border));
  border-right-color: color-mix(in srgb, var(--ton) 45%, var(--border));
  border-bottom-color: color-mix(in srgb, var(--ton) 45%, var(--border));
  box-shadow: var(--shadow); transform: translateY(-2px);
}
.mod-carte.verrouille { cursor: default; opacity: .55; border-left-color: var(--border); }
.mod-carte.reussi { border-left-color: var(--ok); }

.mod-ico {
  flex: 0 0 auto; width: 2.4rem; height: 2.4rem; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; font-size: 1.25rem;
  background: color-mix(in srgb, var(--ton) 11%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ton) 24%, var(--border));
}
/* L'état — « 10 min environ », « Validé » — se cale au bas de la carte : à
   hauteur égale, il tombe au même niveau d'une carte à l'autre, quelle que soit
   la longueur du titre qui le précède. */
.mod-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; align-self: stretch; }
.mod-txt b { display: block; font-size: 1rem; color: var(--text); line-height: 1.25; }
.mod-ss { display: block; font-size: .82rem; color: var(--text-soft); margin-top: .1rem; }
.mod-etat { display: block; font-size: .75rem; font-weight: 600; color: var(--ton); margin-top: auto; padding-top: .45rem; }
.mod-carte.verrouille .mod-etat { color: var(--text-mute); }
.mod-carte.reussi .mod-etat { color: var(--ok); }

/* Les modules métier sont peu nombreux : en grille, une carte seule se
   calait à gauche sous un titre centré. Ils se rangent au centre, à la
   largeur d'une carte de la grille thématique. */
.mod-grille--centre { display: flex; flex-wrap: wrap; justify-content: center; }
.mod-grille--centre > * { flex: 0 1 320px; }
/* Sous-titre entre le module de sa fonction et ceux des autres métiers. */
.mod-sous-titre {
  font-size: .8rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-mute); margin: -1.2rem 0 .8rem;
}
/* Modules thématiques encore fermés : la section tient sa place, sans cartes. */
.mod-attente {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: 1.1rem 1.3rem; margin-bottom: 2.4rem; border-radius: var(--radius);
  background: var(--surface-2); border: 1px dashed var(--border);
}
.mod-attente__ico { font-size: 1.5rem; }
.mod-attente p { flex: 1; min-width: 14rem; margin: 0; font-size: .92rem; color: var(--text-soft); }
.mod-attente p b { display: block; color: var(--text); margin-bottom: .15rem; }

/* Sur mobile, on resserre les rangées de modules (sous-titre masqué, icône et
   marges réduites) pour montrer plus de modules sans défiler autant. */
@media (max-width: 560px) {
  .mod-grille { gap: .5rem; margin-bottom: 1.6rem; }
  .mod-carte { padding: .7rem .85rem; gap: .7rem; align-items: center; }
  .mod-ico { width: 2rem; height: 2rem; font-size: 1.05rem; }
  .mod-txt b { font-size: .92rem; }
  .mod-ss { display: none; }
  .mod-etat { padding-top: .15rem; font-size: .72rem; }
}

/* Dérogations par module, dans la fiche d'un participant. */
/* ── Fiche d'un participant ──
   La fiche sert à décider : on y lit un état, on y ouvre ou on y referme un
   accès. Le détail chapitre par chapitre y tenait douze grandes lignes pour
   dire « 0/12 » — il est devenu une barre et une rangée de pastilles. */
.modal__body h4 {
  font-size: .78rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--text-mute); margin: 1.7rem 0 .8rem;
  padding-bottom: .45rem; border-bottom: 1px solid var(--border);
}
.modal__body h4:first-of-type { margin-top: 1.3rem; }

/* `fc-` pour la fiche d'un COMPTE. Le préfixe `fp-` était partagé avec la
   fiche réflexe, et ses deux `.fp-resume` se recouvraient : celui de la fiche
   réflexe, plus bas dans la feuille, imposait son italique et sa bordure au
   bandeau de chiffres d'un compte. */
.fc-resume {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.3rem;
  font-size: .86rem; color: var(--text-soft);
}
.fc-resume b { color: var(--text); font-size: 1rem; }

.fp-niveau { margin-bottom: 1.1rem; }
.fp-niveau__tete { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin-bottom: .4rem; }
.fp-niveau__tete b { font-size: .92rem; }
.fp-niveau__tete span { font-size: .8rem; color: var(--text-mute); }
.fp-niveau .mini-bar { display: block; width: 100%; margin-bottom: .5rem; }

.fp-chips { display: flex; flex-wrap: wrap; gap: .3rem; }
.fp-chip {
  min-width: 1.65rem; height: 1.65rem; padding: 0 .35rem; border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .74rem; font-weight: 600; cursor: default;
  background: var(--bg-soft); border: 1px solid var(--border); color: var(--text-mute);
}
.fp-chip.on { background: color-mix(in srgb, var(--ok) 16%, transparent); border-color: color-mix(in srgb, var(--ok) 45%, transparent); color: var(--ok); }
.fp-chip.partiel { background: color-mix(in srgb, var(--warn) 14%, transparent); border-color: color-mix(in srgb, var(--warn) 42%, transparent); color: var(--warn); }
.fp-chip--mod { min-width: 1.85rem; height: 1.85rem; font-size: .95rem; }
.fp-chip--mod:not(.on):not(.partiel) { filter: grayscale(1); opacity: .45; }

.fp-bascule {
  display: flex; align-items: center; gap: .6rem; padding: .5rem .55rem;
  border-radius: var(--radius-sm); cursor: pointer;
}
.fp-bascule { border: 1px solid var(--border); margin-bottom: .9rem; }
.fp-bascule:hover { background: var(--surface-2); }
.fp-bascule input { width: 1rem; height: 1rem; accent-color: var(--brand); cursor: pointer; flex: 0 0 auto; }
.fp-bascule .ico { font-size: 1rem; flex: 0 0 auto; }
.fp-bascule .nom { display: block; }
.fp-bascule .nom b { display: block; font-size: .9rem; }
.fp-bascule .nom small { display: block; font-size: .76rem; color: var(--text-mute); margin-top: .1rem; }

.fp-aide { font-size: .8rem; color: var(--text-mute); line-height: 1.5; margin: 0 0 .5rem; }

/* Ce qui sépare les deux natures d'interrupteur : proposer un niveau qui
   n'existe pas sans cela, et déroger au prérequis d'un objet ouvert à tous.
   Sans cette césure, six cases identiques laissaient croire à six actions de
   même portée. */
.fp-separateur {
  display: flex; align-items: center; gap: .7rem;
  margin: 1.3rem 0 .9rem;
  font-size: .72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-mute);
}
.fp-separateur::after {
  content: ''; flex: 1; height: 1px; background: var(--border);
}

/* ── La fiche d'un compte ────────────────────────────────────────────────
   L'identité coiffe les deux colonnes ; en dessous, les cartes. Ce qu'on met
   en regard avant de décider — la progression et ce qui est ouvert — tient
   désormais sur un écran, sans défilement entre les deux. */
.fc-tete {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: .6rem 1.5rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.1rem 1.3rem; margin-bottom: 1rem;
}
.fc-tete__qui b { display: block; font-size: 1.15rem; }
.fc-tete__qui span { font-size: .84rem; color: var(--text-mute); }
/* Le rattachement, réglable depuis l'en-tête. Largeur bornée : un menu qui
   s'étirerait sur toute la place disponible ferait concurrence au nom. */
.fc-tete__structure { flex: 0 1 20rem; min-width: 12rem; }
.fc-tete__structure .field { margin: 0; }
.fc-tete__structure label { margin-bottom: .25rem; font-size: .78rem; text-align: center; }
.fc-tete__structure select { width: 100%; }
/* Le métier, rangée pleine largeur sous l'identité. */
.fc-tete__metiers {
  flex: 1 1 100%; padding-top: .8rem; margin-top: .2rem;
  border-top: 1px solid var(--border);
}
.fc-tete__metiers .field { margin: 0; }
.fc-metiers__tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; }
.fc-metiers__tete label { margin: 0; }
.fc-metiers__tete .fp-aide { margin: 0; }

/* Pas d'`align-items: start` : les deux cartes d'une rangée s'étirent à la
   hauteur de la plus haute et alignent leur pied. Chacune à sa hauteur propre,
   la colonne la plus courte laissait un décroché sous elle. */
/* Le crayon, posé sur le nom qu'il modifie. Discret au repos : une coquille
   se corrige rarement, ce geste n'a pas à se présenter comme la première chose
   à faire sur une fiche. */
.fc-crayon {
  appearance: none; border: 0; background: none; padding: 0 0 0 .5rem;
  font-size: .82rem; line-height: 1; cursor: pointer;
  opacity: .35; transition: opacity .14s; vertical-align: middle;
}
.fc-crayon:hover, .fc-crayon:focus-visible, .fc-crayon.actif { opacity: 1; }
.fc-crayon:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }

/* Le formulaire, dans sa fenêtre : les trois champs l'un sous l'autre. Une
   fenêtre est étroite — les mettre en colonnes y serrerait « Adresse e-mail »
   sur un tiers de la largeur. */
.fc-identite__form { display: grid; gap: .9rem; margin-top: 1.2rem; }
.fc-identite__form .field { margin: 0; }
.fc-identite__actions {
  display: flex; gap: .7rem; justify-content: flex-end; margin-top: .4rem;
}

.fp-grille { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 1000px) {
  .fp-grille { grid-template-columns: 1fr; }
}
.fp-carte {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.2rem 1.3rem;
}
.fp-carte--pleine { grid-column: 1 / -1; }
.fp-carte__t {
  margin: 0 0 1rem; font-size: .74rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute);
}

.fp-evals { display: flex; flex-direction: column; gap: .1rem; }
.fp-eval {
  display: flex; align-items: baseline; gap: .6rem; padding: .5rem .2rem;
  border-bottom: 1px solid var(--border); font-size: .86rem;
}
.fp-eval:last-child { border-bottom: 0; }
.fp-eval .st { flex: 0 0 auto; }
.fp-eval .quoi { flex: 1; min-width: 0; color: var(--text); }
.fp-eval b { color: var(--text); }
.fp-eval .quand { font-size: .78rem; color: var(--text-mute); }
.fp-vide { color: var(--text-mute); font-size: .88rem; }

/* Le contenu peut dépasser : la barre reste discrète, comme dans le tableau. */
.modal { scrollbar-color: var(--barre) transparent; }
.modal::-webkit-scrollbar { width: 8px; }
.modal::-webkit-scrollbar-track { background: transparent; }
.modal::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }
.modal::-webkit-scrollbar-thumb:hover { background: var(--text-mute); }

/* ─────────────── Réglages de l'organisme ───────────────
   Formulaire long et rarement ouvert : on privilégie la lisibilité de chaque
   champ à la densité. Deux colonnes seulement sur grand écran. */
.rg-form { max-width: 780px; }
.rg-groupe { margin-bottom: 2rem; }
.rg-groupe h3 {
  font-size: .82rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-mute); margin: 0 0 1rem;
  padding-bottom: .5rem; border-bottom: 1px solid var(--border);
}
.rg-champs { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1.4rem; }
.rg-champs .field { margin-bottom: 1.3rem; }
.rg-champs .hint { font-size: .78rem; color: var(--text-mute); margin-top: .35rem; line-height: 1.45; }
.rg-champs textarea { resize: vertical; min-height: 2.6em; }

/* Une mention imposée par la loi et laissée vide doit se voir dès le
   formulaire, pas seulement sur la page publique. */
.rg-tag {
  margin-left: .5em; padding: .05em .55em; border-radius: 999px;
  font-size: .66rem; font-weight: 600; letter-spacing: .02em; text-transform: none;
  color: var(--text-mute); background: var(--bg-soft); border: 1px solid var(--border);
}
.rg-tag--manque {
  color: var(--warn); background: var(--warn-soft);
  border: 1px dashed color-mix(in srgb, var(--warn) 45%, transparent);
}

.rg-ecarts { list-style: none; margin: .2rem 0 0; padding: 0; font-size: .82rem; }
.rg-ecarts li { padding: .15rem 0; }
.rg-ecarts i { font-style: normal; padding: 0 .35em; border-radius: 4px; background: color-mix(in srgb, var(--warn) 14%, transparent); }

.rg-actions { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: .4rem; }
.rg-maj { font-size: .78rem; color: var(--text-mute); margin-top: 1.2rem; }

@media (max-width: 700px) {
  .rg-champs { grid-template-columns: 1fr; }
}

/* ─────────────── Shell applicatif ─────────────── */
.topbar {
  /* Fond opaque plutôt que translucide + flou : le flou était recalculé à
     chaque défilement, sur toutes les pages. */
  position: sticky; top: 0; z-index: 40; background: var(--surface);
  border-bottom: 1px solid var(--border);
}
/* La barre occupe toute la largeur, mais son contenu de GAUCHE reste aligné
   sur la colonne de contenu : le retrait à gauche vaut exactement la marge de
   centrage d'un bloc de 1180 px, et le bloc de droite se cale contre le bord
   de la page.
   Le calcul porte sur `50%` de la barre — la largeur réellement disponible —
   et non sur `100vw`, qui compte la barre de défilement et décalerait le logo
   de quelques pixels par rapport au contenu situé dessous. */
.topbar__in {
  max-width: none; margin: 0;
  /* 590px = la moitié des 1180px de `main`, et 1.4rem son retrait interne :
     le logo tombe ainsi sur le bord du TEXTE de la page, pas sur celui de sa
     boîte. Vingt-deux pixels d'écart se voient, sur une verticale aussi longue. */
  padding: .7rem 1.4rem .7rem max(1.4rem, calc(50% - 590px + 1.4rem));
  display: flex; align-items: center; gap: 1.2rem; min-width: 0;
}
.topbar__logo {
  display: flex; align-items: center; gap: .6rem; font-weight: 800; cursor: pointer;
  letter-spacing: -.02em; min-width: 0;
  /* C'est un bouton : il n'en prend ni le fond, ni la bordure, ni la police. */
  border: 0; background: none; padding: 0; color: inherit; font: inherit; font-weight: 800;
}
.topbar__logo .brand-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Le chemin : les ancêtres cliquables, des chevrons, l'endroit où l'on est.
   Il va jusqu'à trois échelons — Ressources › Fiches réflexes › Hameçonnage —,
   ce qui dispense les pages profondes d'un bouton de retour à elles.
   Quand l'écran rétrécit, le chemin se raccourcit par la GAUCHE : d'abord les
   ancêtres lointains (marqués --loin), puis le parent direct. Le logo ramène de
   toute façon à l'accueil, et savoir où l'on est prime sur savoir d'où l'on vient. */
.topbar__ou { display: inline-flex; align-items: baseline; gap: .45rem; min-width: 0; }
.topbar__parent { color: var(--text-mute); text-decoration: none; white-space: nowrap; }
.topbar__parent:hover { color: var(--text); text-decoration: underline; }
.topbar__sep { color: var(--text-mute); opacity: .6; }
/* L'ancêtre lointain accepte de se faire couper : c'est lui, et non l'endroit
   où l'on est, qui rogne la place quand l'intitulé est long. */
.topbar__parent--loin { overflow: hidden; text-overflow: ellipsis; min-width: 3rem; }
.topbar__ici { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 1040px) {
  .topbar__parent--loin, .topbar__sep.topbar__parent--loin { display: none; }
}
@media (max-width: 760px) {
  /* Plus de chemin dans la barre : sa longueur varie d'une page à l'autre, et
     avec le nom du compte en face, il ne restait de place ni pour l'un ni pour
     l'autre. Le chemin vit dans le menu, où il tient sur plusieurs lignes. */
  .topbar__ou { display: none; }
}
.topbar__logo .mark {
  width: 36px; height: 36px; display: grid; place-items: center; flex: none;
}

/* Le logo garde ses couleurs propres : pas de fond ni de teinte appliquée. */
.logo-img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* L'intitulé de la section, à la place des six boutons d'autrefois. */
.topbar__ou {
  margin-left: .9rem; padding-left: .9rem; border-left: 1px solid var(--border);
  font-size: .88rem; font-weight: 600; color: var(--text-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.topbar__spacer { flex: 1 1 0; min-width: 0; }


.iconbtn {
  width: 38px; height: 38px; border-radius: 50%; border: 1.5px solid var(--border); background: transparent;
  cursor: pointer; display: grid; place-items: center; font-size: 1rem; color: var(--text-soft);
  transition: background .16s, border-color .16s, color .16s; flex: none;
}
.iconbtn:hover {
  background: color-mix(in srgb, var(--brand) 9%, transparent);
  color: color-mix(in srgb, var(--brand) 70%, var(--text));
  border-color: color-mix(in srgb, var(--brand) 62%, var(--border));
}

/* ── Sélecteur bascule jour / nuit ── */
.theme-switch {
  position: relative; flex: none; width: 64px; height: 32px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); padding: 0;
  display: flex; align-items: center; justify-content: space-around;
}
.theme-switch .ico { position: relative; z-index: 1; font-size: .8rem; line-height: 1; transition: opacity .2s, transform .2s; }
.theme-switch .knob {
  position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%; z-index: 0;
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform .3s cubic-bezier(.3, 1.5, .5, 1);
}
html[data-theme='dark'] .theme-switch .knob { transform: translateX(34px); }
html[data-theme='light'] .theme-switch .moon,
html[data-theme='dark'] .theme-switch .sun { opacity: .3; transform: scale(.85); }
.theme-switch:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--border)); }

/* Pastille du compte : le nom en toutes lettres, et un chevron qui annonce le
   menu. Volontairement discrète — l'une est
   un état qu'on regarde, l'autre une commande qu'on ouvre. Les deux au même
   niveau de contraste feraient une barre à deux titres concurrents. */
.compte-chip {
  display: flex; align-items: center; gap: .45rem; flex: none;
  max-width: 15rem; height: 38px; padding: 0 .55rem 0 .8rem;
  border-radius: 999px; cursor: pointer; font-family: inherit;
  font-size: .86rem; font-weight: 700; color: var(--text-soft);
  background: transparent; border: 1px solid transparent; transition: .15s;
}
.compte-chip .nom, .compte-chip .prenom {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Le prénom seul est le repli des écrans étroits : masqué par défaut. */
.compte-chip .prenom { display: none; }
.compte-chip .chevron {
  flex: none; width: .5em; height: .5em; margin-bottom: .2em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); opacity: .55;
}
.compte-chip:hover, .compte-chip:focus-visible {
  background: var(--surface-2); border-color: var(--border); color: var(--text);
}
.compte-chip:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* position: fixed — la barre haute est collante, le menu doit rester ancré à
   l'écran. Son décalage exact est calculé à l'ouverture depuis l'avatar, la
   hauteur de la barre variant selon la largeur d'écran. */
.menu {
  position: fixed; right: 1.4rem; top: 62px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); min-width: 230px; padding: .45rem; z-index: 60;
  animation: pop .16s ease-out;
  max-height: calc(100vh - 76px); overflow-y: auto;
}
.menu .who { padding: .7rem .8rem; border-bottom: 1px solid var(--border); margin-bottom: .3rem; }
.menu .who b { display: block; font-size: .93rem; }
.menu .who span { font-size: .8rem; color: var(--text-mute); word-break: break-all; }
.menu button {
  width: 100%; text-align: left; border: 0; background: transparent; padding: .6em .8em; border-radius: 8px;
  cursor: pointer; color: var(--text); font-size: .9rem; display: flex; gap: .6em; align-items: center;
}
.menu button:hover { background: var(--surface-2); }
@keyframes pop { from { opacity: 0; transform: translateY(-6px) scale(.98); } }

/* 5 rem de marge basse repoussaient le pied de page très loin sous le contenu,
   et suffisaient à eux seuls à faire apparaître une barre de défilement sur un
   portable. La mesure de lecture se resserre au passage. */
main { max-width: 1080px; width: 100%; box-sizing: border-box; margin: 0 auto; padding: 1.2rem 1.4rem 1rem; flex: 1 0 auto; }
/* Le suivi des participants compte huit colonnes : la mesure de lecture confortable
   d'un texte ne lui convient pas. Sur grand écran, il prend la place disponible
   plutôt que d'imposer un défilement horizontal. */
main.large { max-width: 1500px; }
/* À l'inverse : un formulaire court n'a rien à gagner à s'étaler. Étalé, son
   intitulé se retrouve loin du champ qu'il nomme. */
main.etroit { max-width: 760px; }

/* ─────────────── Accueil / carte du parcours ─────────────── */
.hero-home {
  position: relative; overflow: hidden; border-radius: var(--radius-lg); padding: 2.4rem 2.6rem;
  background: linear-gradient(135deg, #4f46e5, #7c3aed 55%, #0d9488); color: #fff; margin-bottom: 2.2rem;
  box-shadow: var(--shadow);
}
.hero-home::after {
  content: ''; position: absolute; width: 420px; height: 420px; border-radius: 50%; right: -120px; top: -180px;
  background: radial-gradient(circle, rgba(255,255,255,.22), transparent 68%);
}
/* ── Page de lancement d'un parcours ───────────────────────────────────────
   Même principe que celle de l'escape game : le décor occupe toute la largeur
   de la fenêtre, entier et calé à droite ; ce qui reste à gauche n'est pas du
   vide perdu, c'est exactement la place du médaillon et du titre. Ces images
   sont composées pour ça — leur tiers gauche est déjà sombre —, le voile ne
   fait donc que tenir la lisibilité quand le cadre se resserre.
   `contain` et non `cover` : l'image doit rester ENTIÈRE, c'est la règle posée
   pour l'escape et elle vaut ici. Le panneau prend donc les proportions du
   décor, et sa hauteur est bornée pour qu'on ne défile pas avant le bouton. */
.para-lancement {
  position: relative; overflow: hidden; background: #0c1020;
  width: 100vw; margin-left: calc(50% - 50vw);
  --h: min(calc(100vh - 11rem), calc(100vw / 1.777));
  height: var(--h);
  margin-bottom: 2rem;
}

/* ── Raccorder un décor à la nuit du site ───────────────────────────────────
 *
 * Ces décors sont en « contain », calés à droite : ils laissent à gauche une
 * bande de fond, et leur propre bord gauche est peint dans un noir plus froid
 * et plus sombre que le site (#000415 contre #0c1020). La couture se voyait —
 * une tache noire posée sur du bleu nuit.
 *
 * Le voile de gauche ne fond donc pas un bord : il REMPLACE le vide du décor,
 * à la couleur exacte du fond, et ne s'éteint qu'à l'approche du sujet. Sa
 * course est propre à chaque image — le contenu commence à 12 % sur la
 * formation, à 42 % sur le niveau avancé —, d'où « --vide », posé par le
 * script à partir de mesures faites sur les fichiers.
 *
 * Le bord gauche du décor se calcule : l'image étant en 16/9, la largeur
 * qu'elle occupe vaut sa hauteur × 1,777.
 */
.para-lancement::before, .esc-lancement::before {
  content: ''; position: absolute; top: 0; bottom: 0; z-index: 1;
  left: calc(100% - var(--h) * 1.777);
  width: calc(var(--h) * 1.777 * var(--vide, .2)); margin-left: -1rem;
  background: linear-gradient(to right,
    #0c1020 0%, #0c1020 46%, rgba(12, 16, 32, .93) 60%,
    rgba(12, 16, 32, .68) 74%, rgba(12, 16, 32, .34) 87%,
    rgba(12, 16, 32, .12) 95%, rgba(12, 16, 32, 0) 100%);
  pointer-events: none;
}
/* Le bas, lui, est toujours au même endroit : l'image occupe toute la hauteur
   du panneau, quelle que soit la fenêtre. Le masque du décor l'éteint déjà,
   mais il le rend TRANSPARENT sans le fondre dans quoi que ce soit — le fond
   apparaissait d'un coup. Ce voile ajoute la transition. */
.para-lancement::after, .esc-lancement::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  height: min(10rem, 26%);
  background: linear-gradient(to top,
    #0c1020 0%, rgba(12, 16, 32, .96) 12%, rgba(12, 16, 32, .86) 26%,
    rgba(12, 16, 32, .62) 45%, rgba(12, 16, 32, .34) 65%,
    rgba(12, 16, 32, .12) 82%, rgba(12, 16, 32, 0) 100%);
  pointer-events: none;
}
.para-lancement__decor {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: right center;
  /* Les bords du fichier traçaient deux lignes en travers du panneau : on les
     éteint vers la nuit, qui est la même. */
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, rgba(0, 0, 0, .5) 4%, #000 12%,
    #000 88%, rgba(0, 0, 0, .5) 96%, transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0%, rgba(0, 0, 0, .5) 4%, #000 12%,
    #000 88%, rgba(0, 0, 0, .5) 96%, transparent 100%);
}
.para-lancement__corps {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; justify-content: center;
  gap: clamp(1rem, 3vh, 2rem); color: #fff;
  padding: clamp(1rem, 3vh, 2.4rem);
  padding-left: max(clamp(1.4rem, 4vw, 2.6rem), calc(50vw - 590px));
  background: linear-gradient(100deg,
    rgba(12, 16, 32, .96) 24%, rgba(12, 16, 32, .74) 38%,
    rgba(12, 16, 32, .22) 56%, rgba(12, 16, 32, 0) 72%);
}
.para-lancement__tete { display: flex; align-items: center; gap: 1.6rem; flex-wrap: wrap; }
.para-lancement__mots { max-width: 34ch; }
.para-lancement__mots h2 { margin: 0 0 .4rem; font-size: clamp(1.4rem, 3.4vw, 2rem); }
.para-lancement__mots p { margin: 0; color: rgba(255, 255, 255, .82); }
/* Le médaillon a la taille de ceux de l'escape : c'est le même personnage qui
   parlera dedans, et une vidéo y prendra la place du portrait. */
.para-medaillon {
  position: relative; width: clamp(7rem, 17vh, 13rem); aspect-ratio: 1; flex: none;
  border-radius: 50%; overflow: hidden; background: #0c1020;
  border: 3px solid var(--justine);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45),
              0 0 22px color-mix(in srgb, var(--justine) 28%, transparent);
}
.para-medaillon img, .para-medaillon video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
/* Le fondu passe par le noir dans les DEUX sens : ce qui part s'efface tout de
   suite, ce qui arrive attend que la place soit libre. Le fond du rond étant
   déjà `#0c1020`, il n'y a pas de voile à ajouter — c'est lui qu'on voit
   pendant le battement.
   `avec-film` conditionne la bascule : sans ce marqueur, la règle viderait le
   médaillon des pages qui n'ont pas encore leur clip. */
.para-medaillon img { transition: opacity .28s ease .24s; }
/* Le plan envoyé à Hedra a beaucoup d'air au-dessus de la tête — c'est ce qui
   l'empêche de dériver —, mais dans un cercle cette marge se voit : le buste
   s'arrête avant le bord et laisse un vide en bas. Mesuré sur les clips : le
   sujet n'occupe que 62 % de la hauteur contre 86 % sur la vignette fixe.
   ×1,5 le sort du cadre, et le décalage vers le bas évite de rogner le chignon
   au passage. Ce sont les valeurs des médaillons de l'escape, qui partent du
   même plan. */
.para-medaillon__film {
  opacity: 0; transition: opacity .28s ease;
  transform: translateY(6%) scale(1.5);
}
.para-medaillon.avec-film.joue img { opacity: 0; transition-delay: 0s; }
.para-medaillon.avec-film.joue .para-medaillon__film { opacity: 1; transition-delay: .24s; }
/* Le lecteur occupe TOUT le médaillon et n'a aucun fond : une pastille posée
   au milieu du visage le barre, et un cercle translucide sur une joue se lit
   comme un défaut. Il ne reste que le symbole, blanc et large, porté par une
   ombre qui le détache de l'image quelle qu'elle soit.
   Pendant la lecture il disparaît et ne revient qu'au survol — et il montre
   alors la PAUSE, jamais la lecture : proposer « lire » à quelqu'un qui écoute
   déjà n'a aucun sens. */
.para-medaillon__play {
  position: absolute; inset: 0; z-index: 1;
  border: 0; background: none; cursor: pointer;
  display: grid; place-items: center; color: #fff; transition: opacity .2s;
}
.para-medaillon__play svg {
  width: 30%; height: 30%; fill: currentColor;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, .75)) drop-shadow(0 0 3px rgba(0, 0, 0, .5));
}
/* Caché au repos comme en lecture : à l'ouverture de la page, on doit voir
   Justine, pas une commande posée sur elle. Il n'apparaît qu'au survol — sauf
   si la lecture automatique a été refusée (`bloque`), auquel cas plus rien ne
   parle et il faut bien que quelque chose le dise. */
.para-medaillon__play { opacity: 0; }
.para-medaillon:hover .para-medaillon__play { opacity: 1; }
.para-medaillon.bloque .para-medaillon__play { opacity: 1; }
/* Sans survol possible, le bouton reste visible tant que rien ne joue : sur un
   écran tactile, une commande qui n'apparaît qu'au survol n'apparaît jamais. */
@media (hover: none) {
  .para-medaillon__play { opacity: 1; }
  .para-medaillon.joue .para-medaillon__play { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .para-medaillon img, .para-medaillon__film { transition: none; }
}
/* Sur l'écran de résultat, le médaillon coiffe le score et se centre. */
.score-hero .para-medaillon { margin: 0 auto 1.2rem; width: clamp(6rem, 14vw, 9rem); }
/* Le bouton se centre sous le MÉDAILLON, et non sur le bord gauche du bloc.
   Aligné à gauche, son centre tombait plus à gauche que celui de Justine —
   il est plus étroit qu'elle —, et le décalage se voyait d'autant plus que
   rien d'autre ne se trouve sur cette ligne.
   La boîte reprend donc exactement la largeur du médaillon (le même clamp,
   écrit à l'identique) et le bouton s'y centre. */
.para-lancement__actions {
  display: flex; gap: .7rem; flex-wrap: wrap; align-items: center;
  width: clamp(7rem, 17vh, 13rem); justify-content: center;
}
/* Écran court ou étroit : on renonce à la hauteur imposée, le panneau reprend
   celle de son contenu et la page défile un peu. `relative`, surtout PAS
   `static` : le décor est en position absolue, et un élément positionné se
   peint au-dessus de ce qui ne l'est pas — le texte passerait dessous et le
   bouton deviendrait inopérant. */
@media (max-height: 760px), (max-width: 1100px) {
  /* Le panneau remplit l'écran sous la barre (le pied de page reste visible),
     et le bloc de texte s'étire sur toute sa hauteur : son voile sombre
     couvre alors tout le décor. À la hauteur du seul texte, le panneau
     laissait le bas de la page noir, et le voile s'arrêtait avant l'image en
     traçant un trait sous le médaillon. */
  .para-lancement {
    height: auto; display: flex;
    /* La fenêtre, moins la barre du haut (mesurée par le script), les marges
       de la page (1,2 + 1 rem), celle du panneau (2 rem) et le pied (3,4 rem) :
       pile la hauteur visible, sans barre de défilement. */
    min-height: max(20rem, calc(100dvh - var(--topbar-h, 3.75rem) - 7.6rem));
  }
  .para-lancement__corps { flex: 1; }
  /* « --h » ne dit plus où commence l'image : les voiles se poseraient de
     travers, et le décor passe de toute façon en « cover ». */
  .para-lancement::before, .para-lancement::after { display: none; }
  /* Le médaillon rétrécit jusqu'à 7 rem, moins que le bouton : centré dans une
     boîte plus étroite que lui, il déborderait des deux côtés et sortirait de
     la marge gauche. Il reprend donc l'alignement du bloc. */
  .para-lancement__actions { width: auto; justify-content: flex-start; }
  .para-lancement__corps { position: relative; z-index: 1; min-width: 0; }
  /* `cover` et non `contain` dans ce repli. Le panneau prend ici la hauteur de
     son texte, pas celle du décor : une image CONTENUE s'y affiche à sa propre
     proportion, donc bien plus étroite que le panneau, et trace un bord net au
     milieu de la page — c'est le découpage incohérent entre Justine et le fond.
     En la faisant remplir, on perd le haut et le bas de la scène, ce qui est
     sans conséquence : ces décors ont leur sujet au centre et à droite. */
  .para-lancement__decor { object-fit: cover; }
}

/* Écrans courts — un portable à 100 % de zoom. Les portes s'aplatissent et le
   médaillon se réduit : quatre cartes en 16/9 plus un portrait ne tiennent pas
   dans 800 px de haut, et l'utilisateur ne devrait pas avoir à dézoomer pour
   voir sa page d'accueil entière. Invisible au-dessus de cette hauteur. */
@media (max-height: 820px) {
  .hub-porte { aspect-ratio: 2 / 1; }
  .hub-portes--3 .hub-porte { aspect-ratio: 3 / 2; }
  .hub-medaillon { width: clamp(5rem, 11vh, 7.5rem); }
  .hub-tete h1 { font-size: 1.35rem; }
  .hub-scene { gap: .7rem; }
}
.hero-home h1 { font-size: clamp(1.6rem, 3vw, 2.3rem); margin-bottom: .3em; position: relative; }
.hero-home p { opacity: .93; max-width: 56ch; position: relative; margin-bottom: 1.4rem; }
.hero-home .row { display: flex; flex-wrap: wrap; gap: 2rem; position: relative; align-items: center; }
.hero-home .hero-text { flex: 1; min-width: 260px; }
.hero-home .hero-actions { display: flex; gap: .7rem; flex-wrap: wrap; }
/* Sur le dégradé, même principe en blanc : contour et texte blancs, voile
   blanc très léger au survol. */
.hero-home .btn {
  background: transparent; color: #fff;
  border: 1.5px solid rgba(255, 255, 255, .55);
}
.hero-home .btn:hover:not(:disabled) { background: rgba(255, 255, 255, .16); border-color: rgba(255, 255, 255, .9); color: #fff; }
.hero-home .btn:active:not(:disabled) { background: rgba(255, 255, 255, .26); }
.hero-home .btn:focus-visible { outline-color: #fff; }

.ring { --p: 0; width: 104px; height: 104px; flex: none; position: relative; display: grid; place-items: center; }
.ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(#fff calc(var(--p) * 1%), rgba(255,255,255,.22) 0);
  mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 8px));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 8px));
  transition: background .8s cubic-bezier(.2,.8,.2,1);
}
/* ── La carte de reprise ──────────────────────────────────────────────────
   Trois éléments et pas une phrase : Justine accueille à gauche, l'anneau dit
   l'avancement à droite, le bouton se pose dessous. Justine reste visible sur
   téléphone — contrairement aux autres bannières, où elle s'efface — parce
   qu'ici elle occupe une place que rien d'autre ne réclame. */
/* Deux classes, et non une : trois blocs plus bas dans la feuille resserrent
   « .hero-home » selon la largeur, et un simple « .hero-home--reprise »
   perdait contre eux. Le retrait du bas vaut zéro pour que Justine touche
   vraiment le bord de la carte. */
.hero-home.hero-home--reprise { padding: 1.5rem 1.6rem 0; }
.hero-home--reprise h1 { margin: 0 0 .4rem; }
.reprise-row { position: relative; display: flex; align-items: flex-end; gap: 1rem; }
.reprise-row .hero-justine { display: block; height: 200px; margin: 0; align-self: flex-end; }
.reprise-cote { margin: 0 0 1.4rem auto; display: grid; justify-items: center; gap: .9rem; }
@media (max-width: 760px) {
  .hero-home.hero-home--reprise { padding: 1.3rem 1.2rem 0; }
  .reprise-row .hero-justine { height: 168px; }
  .reprise-cote { margin-bottom: 1.1rem; gap: .7rem; }
}
@media (max-width: 400px) {
  .hero-home.hero-home--reprise { padding: 1.1rem 1rem 0; }
  .reprise-row .hero-justine { height: 146px; }
  .reprise-cote { margin-bottom: .9rem; }
}

.ring b { font-size: 1.5rem; font-weight: 800; line-height: 1; }
/* Le décompte sort du cercle : dedans, il poussait le pourcentage vers le haut
   et l'on ne lisait plus le chiffre au centre. */
.ring-bloc { display: grid; justify-items: center; gap: .35rem; }
.ring-legende { font-size: .72rem; opacity: .85; text-align: center; }

.section-title { display: flex; align-items: baseline; justify-content: center; flex-wrap: wrap; text-align: center; gap: .3rem .8rem; margin: 2.4rem 0 1.1rem; }
.section-title h2 { font-size: 1.28rem; margin: 0; }
.section-title span { font-size: .87rem; color: var(--text-mute); }

/* ── Interrupteur de la voix de Justine ───────────────────────────────────
   Posé à gauche du titre du parcours, qui lui est centré : d'où le calage en
   absolu, seul moyen de garder le titre au milieu de la page plutôt qu'au
   milieu de ce qu'il reste. En dessous de 760 px la place manque, il repasse
   dans le flux, au-dessus du titre. */
.section-title--voix { position: relative; }
.voix-bascule {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .34rem .5rem .34rem .7rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface);
  font-size: .82rem; font-weight: 600; color: var(--text-soft);
  cursor: pointer; transition: border-color .18s, color .18s;
}
.voix-bascule:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--border)); color: var(--text); }
.voix-bascule__ico { font-size: .95rem; line-height: 1; }
.voix-bascule__rail {
  position: relative; flex: none; width: 34px; height: 18px; border-radius: 999px;
  background: var(--border); transition: background .22s;
}
.voix-bascule__rail i {
  position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform .26s cubic-bezier(.3, 1.5, .5, 1);
}
.voix-bascule[aria-pressed='true'] { color: var(--text); }
.voix-bascule[aria-pressed='true'] .voix-bascule__rail { background: var(--brand); }
.voix-bascule[aria-pressed='true'] .voix-bascule__rail i { transform: translateX(16px); }
/* Éteinte, l'icône s'efface sans disparaître : l'interrupteur doit rester
   trouvable par qui veut rallumer. */
.voix-bascule[aria-pressed='false'] .voix-bascule__ico { opacity: .55; }
@media (max-width: 760px) {
  /* Repassé dans le flux, l'interrupteur s'aligne sur le bord gauche comme le
     titre qu'il précède, et garde ses distances : collé, il se lisait comme
     une partie du titre du parcours. */
  .voix-bascule { position: static; transform: none; margin: 0 0 1.3rem; align-self: flex-start; }
  .section-title--voix { flex-direction: column; align-items: flex-start; text-align: left; }
}

/* ── Console des références ───────────────────────────────────────────────
   La liste mêle deux natures : ce que le contenu cite tout seul et ce qu'on a
   saisi. Les pastilles disent laquelle, sans quoi « supprimer » n'aurait pas
   le même sens d'une ligne à l'autre. */
.ref-adm-groupe { margin-bottom: 1.8rem; }
.ref-adm-groupe h3 {
  display: flex; align-items: baseline; gap: .5rem; margin: 0 0 .6rem;
  font-size: .96rem; color: var(--text-soft);
}
.ref-adm-groupe h3 span {
  font-size: .78rem; font-weight: 600; color: var(--text-mute);
  background: var(--bg-soft); border-radius: 99px; padding: .05rem .45rem;
}
.ref-adm-url {
  display: block; font-size: .78rem; color: var(--text-mute);
  overflow-wrap: anywhere; margin-top: .1rem;
}
.ref-adm-pastilles { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .35rem; }
/* Retirée de la page : la ligne reste lisible mais s'efface, pour qu'on voie
   d'un coup d'œil ce qui ne s'affiche plus. */
.faq-admin-ligne.est-masquee { opacity: .58; }
/* Le contexte saisi en console, sur la page publique : il tient la place des
   renvois pour une référence que le contenu ne cite pas. */
.res-note { font-style: italic; color: var(--text-mute); }

.path { display: grid; gap: 1rem; }
.node {
  display: flex; gap: 1.2rem; align-items: center; background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.1rem 1.3rem; cursor: pointer; position: relative;
  transition: background .2s, border-color .2s, box-shadow .22s;
}
/* Au survol : un voile de la couleur du chapitre envahit la carte, la bordure
   se teinte, l'icône s'intensifie et le titre prend la couleur. Quatre signaux
   discrets qui, ensemble, se remarquent nettement — sans aucun aplat. */
.node:hover:not(.locked) {
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  box-shadow: var(--shadow);
}
.node:hover:not(.locked) .node__badge {
  background: color-mix(in srgb, var(--accent) 24%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
}
.node:hover:not(.locked) .node__body h3 { color: color-mix(in srgb, var(--accent) 55%, var(--text)); }
.node__badge, .node__body h3 { transition: background .2s, border-color .2s, color .2s; }
/* Verrouillée : éteinte, et inerte — le curseur ne promet rien. */
.node.locked { opacity: .55; cursor: default; }
.node__badge {
  flex: none; width: 58px; height: 58px; border-radius: 18px; display: grid; place-items: center; font-size: 1.6rem;
  background: color-mix(in srgb, var(--accent) 14%, var(--surface)); border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  position: relative;
}
.node__num {
  position: absolute; top: -7px; left: -7px; width: 24px; height: 24px; border-radius: 50%; font-size: .72rem;
  display: grid; place-items: center; color: #fff; font-weight: 700; background: var(--accent); border: 2px solid var(--surface);
}
.node__body { flex: 1; min-width: 0; }
.node__body h3 { font-size: 1.05rem; margin: 0 0 .15em; }
/* Deux lignes réservées d'office pour le sous-titre : sur un téléphone, une
   accroche de quarante caractères tient sur une ligne et celle de soixante en
   prend deux, et les cartes de la liste n'avaient plus la même hauteur. Au-delà
   de deux lignes, le texte se coupe proprement — aucune accroche du parcours
   n'y arrive aujourd'hui, la plus longue faisant soixante-quatre caractères. */
.node__body p {
  margin: 0; font-size: .88rem; color: var(--text-soft);
  min-height: 3.2em;                      /* deux lignes à 1,6 d'interligne */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.node__meta { display: flex; gap: .8rem; margin-top: .45rem; font-size: .78rem; color: var(--text-mute); flex-wrap: wrap; }
.node__state { flex: none; font-size: 1.5rem; }
.node.done { border-color: color-mix(in srgb, var(--ok) 40%, var(--border)); }
.node.done .node__badge { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.node.current { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }

/* Sur mobile, les cartes de chapitre sont compactées : badge et marges réduits,
   accroche limitée à 2 lignes, indicateurs de progression resserrés pour qu'ils
   tiennent sans s'étaler sur trop de lignes. */
@media (max-width: 560px) {
  .path { gap: .7rem; }
  .node { gap: .8rem; padding: .8rem .85rem; align-items: flex-start; }
  .node__badge { width: 44px; height: 44px; border-radius: 13px; font-size: 1.25rem; }
  .node__num { width: 19px; height: 19px; font-size: .6rem; top: -5px; left: -5px; }
  .node__body h3 { font-size: .96rem; line-height: 1.25; }
  .node__body p { font-size: .8rem; margin-top: .12rem;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .node__meta { gap: .28rem .55rem; font-size: .72rem; margin-top: .4rem; }
  .node__meta .pill { font-size: .64rem; padding: .12em .5em; }
  .node__state { font-size: 1.2rem; align-self: center; }
  /* (Le hero et l'anneau sont compactés dans le bloc @media 760px plus bas, qui
     l'emporte sur celui-ci en ordre source.) */
  /* Encart Évaluation finale compacté. */
  .finale { padding: 1.1rem 1.1rem; gap: .9rem; }
  .finale .em { font-size: 2rem; }
  .finale .grow { min-width: 0; }
  .finale p { font-size: .84rem; }
}

.pill {
  display: inline-flex; align-items: center; gap: .35em; padding: .2em .65em;
  border-radius: 999px; font-size: .74rem; font-weight: 700;
  /* Sur une seule ligne : une pastille coupée en deux déséquilibre la hauteur
     des lignes du tableau. */
  white-space: nowrap;
}
.pill.ok { background: var(--ok-soft); color: var(--ok); }
.pill.warn { background: var(--warn-soft); color: var(--warn); }
.pill.info { background: var(--info-soft); color: var(--info); }
.pill.brand { background: var(--brand-soft); color: var(--brand); }
.pill.neutre { background: color-mix(in srgb, var(--text-mute) 20%, transparent); color: var(--text-soft); }
/* Pastilles de statut de ticket : libellé en majuscules. */
.pill-statut { text-transform: uppercase; letter-spacing: .03em; }

/* ── Sélecteur de niveau ── */
.niveaux { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: .8rem; margin-bottom: 1.8rem; }
.niveau {
  display: flex; align-items: center; gap: .9rem; text-align: left; width: 100%;
  padding: 1rem 1.2rem; border-radius: var(--radius); cursor: pointer; font-family: inherit;
  background: transparent; border: 1.5px solid var(--border); color: var(--text);
  transition: background .16s, border-color .16s;
}
.niveau:hover:not(:disabled) {
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  border-color: color-mix(in srgb, var(--brand) 55%, var(--border));
}
/* Le niveau consulté se distingue par un contour affirmé, pas par un aplat. */
.niveau.on {
  border-color: color-mix(in srgb, var(--brand) 70%, var(--border));
  background: color-mix(in srgb, var(--brand) 6%, transparent);
}
/* Même convention partout : ce qui est fermé s'estompe et reste inerte. */
.niveau.verrouille { opacity: .6; cursor: default; }
.niveau .niv-icone { font-size: 1.5rem; flex: none; }
.niveau .niv-texte { flex: 1; min-width: 0; display: block; }
.niveau .niv-texte b { display: block; font-size: .96rem; margin-bottom: .1rem; }
.niveau .niv-texte span { font-size: .8rem; color: var(--text-mute); }

/* ── La médaille d'un parcours validé ──
   Posée sur le coin de la carte, à côté du bouton et non dedans : la carte est
   elle-même un bouton, et l'imbrication rendrait le clavier inopérant.
   Elle mène à l'attestation ; le lien du bas de page reste en place. */
.carte-medaillee { position: relative; display: flex; height: 100%; }
.carte-medaillee > .niveau,
.carte-medaillee > .mod-carte { flex: 1; padding-right: 3.1rem; }
.medaille {
  position: absolute; top: .55rem; right: .55rem;
  display: flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; padding: 0; line-height: 1; font-size: 1.15rem;
  border-radius: 50%; cursor: pointer;
  background: transparent; border: 1px solid transparent;
  transition: background .16s, border-color .16s, transform .16s;
}
.medaille:hover {
  background: color-mix(in srgb, var(--ok) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--ok) 45%, var(--border));
  transform: scale(1.1);
}
.medaille:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* La carte de niveau est centrée verticalement : la médaille l'accompagne. */
.carte-medaillee > .niveau ~ .medaille { top: 50%; right: .7rem; transform: translateY(-50%); }
.carte-medaillee > .niveau ~ .medaille:hover { transform: translateY(-50%) scale(1.1); }

/* ── Sources d'un écran ── */
.sources {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .9rem;
  margin-top: 1.2rem; padding-top: .9rem; border-top: 1px solid var(--border);
  font-size: .82rem;
}
.sources .src-titre { color: var(--text-mute); font-weight: 700; }
.sources a {
  color: var(--text-soft); text-decoration: none;
  border-bottom: 1px dashed color-mix(in srgb, var(--brand) 45%, transparent);
}
.sources a:hover { color: var(--brand); border-bottom-color: var(--brand); }
.sources a::after { content: ' ↗'; font-size: .85em; opacity: .7; }

/* ── Progression par niveau dans le tableau d'administration ── */
/* ── Tableau d'administration : une colonne par objet de progression ──
   Chaque cellule tient sur UNE ligne. La pastille porte à la fois la valeur et
   l'avancement — son fond se remplit à proportion —, là où une barre séparée
   doublait la largeur pour dire la même chose. */
.data--admin th { white-space: nowrap; }
.data--admin td { vertical-align: middle; }
/* L'alignement vaut pour l'intitulé comme pour la cellule : appliqué à la
   seule cellule, le titre restait collé à gauche d'une colonne centrée. */
.data--admin th.c-num, .c-num { text-align: center; white-space: nowrap; }
.data--admin th.c-droite { text-align: right; }
.c-vide { color: var(--text-mute); font-size: .82rem; }
.c-date { font-size: .8rem; color: var(--text-mute); white-space: nowrap; }
.c-eval { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.c-eval b { font-size: .95rem; }
.c-eval .c-vide { font-size: .74rem; }

.prog-chip {
  --p: 0%;
  display: inline-block; padding: .22rem .55rem; border-radius: 999px;
  font-size: .8rem; font-weight: 600; color: var(--text); white-space: nowrap;
  border: 1px solid var(--border);
  /* Le remplissage EST la barre de progression : dégradé net, arrêté au ratio. */
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--brand) 26%, var(--surface)) var(--p),
    var(--surface-2) var(--p));
}
.prog-chip.plein {
  border-color: color-mix(in srgb, var(--ok) 45%, var(--border));
  background: color-mix(in srgb, var(--ok) 16%, var(--surface));
  color: var(--ok);
}
.prog-jeux { display: inline-flex; gap: .3rem; }
.prog-jeux .prog-chip { background: var(--surface-2); }

/* Onze colonnes ne tiennent pas sur un écran de portable étroit. Plutôt qu'une
   barre de défilement horizontale — que la lecture ligne à ligne rend pénible —
   les colonnes les moins critiques s'effacent : elles restent dans la fiche du
   compte et dans l'export. L'ordre d'effacement suit l'usage réel. */
@media (max-width: 1360px) {
  .data--admin th, .data--admin td { padding-left: .55rem; padding-right: .55rem; }
  .data--admin { font-size: .9rem; }
}
@media (max-width: 1220px) {                    /* la dernière activité s'en va */
  .data--admin th:nth-child(10), .data--admin td:nth-child(10) { display: none; }
}
@media (max-width: 1060px) {                    /* puis la structure */
  .data--admin th:nth-child(3), .data--admin td:nth-child(3) { display: none; }
}
@media (max-width: 1000px) {                    /* enfin, on resserre tout */
  .data--admin { font-size: .84rem; }
  .data--admin th, .data--admin td { padding: .55rem .4rem; }
  .data--admin td:first-child div { font-size: .74rem; }
  .prog-chip { padding: .2rem .4rem; font-size: .76rem; }
}
.niv-tag {
  font-size: .64rem; font-weight: 800; color: var(--text-mute);
  border: 1px solid var(--border); border-radius: 5px; padding: 0 .25rem;
}

/* Carte finale : questionnaire */
.finale {
  margin-top: 1rem; border-radius: var(--radius); padding: 1.6rem 1.8rem; border: 1px dashed var(--border);
  background: linear-gradient(135deg, var(--surface), var(--surface-2)); display: flex; gap: 1.4rem; align-items: center; flex-wrap: wrap;
}
.finale.ready { border: 1px solid transparent; background: linear-gradient(135deg, #101828, #2b1c5a); color: #fff; box-shadow: var(--shadow); }
.finale.ready p { color: rgba(255,255,255,.85); }
.finale .em { font-size: 2.6rem; }
.finale h3 { margin: 0 0 .2em; }
.finale p { margin: 0; font-size: .9rem; color: var(--text-soft); }
.finale .grow { flex: 1; min-width: 220px; }

/* Badges */
.badges { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: .9rem; }
.badge {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.1rem .9rem;
  text-align: center; transition: .2s;
}
.badge .em { font-size: 2rem; display: block; filter: grayscale(1); opacity: .35; transition: .3s; }
.badge b { display: block; font-size: .85rem; margin-top: .35rem; }
.badge small { font-size: .74rem; color: var(--text-mute); display: block; margin-top: .2rem; line-height: 1.35; }
.badge.on { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); background: linear-gradient(180deg, var(--warn-soft), var(--surface)); }
.badge.on .em { filter: none; opacity: 1; animation: badgePop .5s cubic-bezier(.2,1.4,.4,1); }
@keyframes badgePop { 0% { transform: scale(.4) rotate(-16deg); } 60% { transform: scale(1.18) rotate(6deg); } }
/* Sur mobile : grille dense 3 colonnes (description masquée, gardée en info-bulle)
   pour un mur de badges compact plutôt que de grandes tuiles espacées. */
@media (max-width: 560px) {
  .badges { grid-template-columns: repeat(3, 1fr); gap: .5rem; }
  .badge { padding: .7rem .4rem; }
  .badge .em { font-size: 1.7rem; }
  .badge b { font-size: .72rem; margin-top: .3rem; line-height: 1.15; }
  .badge small { display: none; }
}

/* ─────────────── Chapitre ─────────────── */
.chapter-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.4rem; }
/* Le titre s'aligne à gauche depuis que la pastille de retour a disparu :
   centré, il flottait entre un bord vide et le pictogramme. */
.chapter-head .info { flex: 1; min-width: 0; }
.chapter-head .info b { font-size: .78rem; text-transform: uppercase; letter-spacing: .09em; color: var(--accent); }
.chapter-head .info h2 { margin: .1rem 0 0; font-size: 1.2rem; }

.steps-bar { display: flex; gap: 6px; margin-bottom: 1.8rem; }
.steps-bar i {
  height: 5px; flex: 1; border-radius: 99px; background: var(--border); transition: background .35s, transform .35s;
}
.steps-bar i.done { background: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.steps-bar i.on { background: var(--accent); transform: scaleY(1.7); }
/* Les segments déjà traversés sont cliquables : ils remplacent le bouton
   « Précédent ». Cinq pixels de haut ne se visent pas au doigt, d'où la zone
   de clic élargie, invisible, qui déborde de dix-huit pixels en hauteur. */
.steps-bar i.libre { cursor: pointer; position: relative; }
.steps-bar i.libre::after { content: ''; position: absolute; left: 0; right: 0; top: -9px; bottom: -9px; }
.steps-bar i.libre:hover { background: color-mix(in srgb, var(--accent) 75%, var(--border)); transform: scaleY(1.9); }
.steps-bar i.libre:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.step { animation: stepIn .42s cubic-bezier(.2,.8,.2,1); }
@keyframes stepIn { from { opacity: 0; transform: translateY(16px); } }

.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 2.2rem 2.4rem; box-shadow: var(--shadow-sm);
}
.card h2 { font-size: 1.55rem; }
.card h3 { font-size: 1.15rem; }
.card .intro { color: var(--text-soft); font-size: 1rem; white-space: pre-line; }  /* « \n » : retour à la ligne */

.step-hero { text-align: center; padding: 3.2rem 2.4rem; }
.step-hero .kicker { font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); font-weight: 700; }
.step-hero .em { font-size: 3.6rem; display: block; margin-bottom: .6rem; animation: bob 3s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateY(-9px); } }
.step-hero h2 { font-size: clamp(1.5rem, 3.2vw, 2.1rem); margin: .3em 0 .45em; }
.step-hero p { font-size: 1.06rem; color: var(--text-soft); max-width: 60ch; margin: 0 auto; }

/* Cartes en grille */
.grid-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1rem; margin-top: 1.4rem; }
.mini {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.3rem;
  transition: transform .2s, box-shadow .2s;
}
/* Ces cartes ne sont pas cliquables : aucun effet de survol, pour ne pas
   suggérer une interaction qui n'existe pas. */
.grid-cards.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cards.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.mini .em { font-size: 1.7rem; display: block; margin-bottom: .5rem; }
.mini b { display: block; margin-bottom: .3rem; font-size: 1rem; }
.mini p { margin: 0 0 .6rem; font-size: .9rem; color: var(--text-soft); }
.mini p:last-child { margin-bottom: 0; }
.mini .src {
  margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--border);
  font-size: .78rem; color: var(--text-mute);
}

/* Définition */
.defbox {
  border-left: 4px solid var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--surface-2));
  border-radius: 0 var(--radius) var(--radius) 0; padding: 1.5rem 1.7rem; margin-top: 1.2rem;
}
.defbox .term { font-size: 1.25rem; font-weight: 700; }
.defbox .src { font-size: .78rem; color: var(--text-mute); margin-bottom: .8rem; }
.defbox .txt { font-size: 1.02rem; }
.chips { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem; }
.chips span {
  background: var(--surface); border: 1px solid var(--border); padding: .32em .8em; border-radius: 999px; font-size: .84rem; color: var(--text-soft);
}
/* Modules : les « exemples » sont des phrases (avec termes-loupe). On les rend
   en liste fluide plutôt qu'en pastilles — la loupe y reste un lien coloré. */
.def-notes { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .55rem; }
.def-notes li { position: relative; padding-left: 1.15rem; font-size: .96rem; line-height: 1.55; color: var(--text-soft); }
.def-notes li::before {
  content: ''; position: absolute; left: 0; top: .62em;
  width: .5rem; height: 2px; border-radius: 2px; background: var(--accent); opacity: .7;
}

/* ═══════════ Formation : compagnon Sophie, scénario-décision, verrou ═══════════ */
.sophie { display: flex; gap: .9rem; align-items: flex-start; }
.sophie__ava {
  flex: none; width: 78px; height: 78px; border-radius: 50%; overflow: hidden;
  background: var(--surface-2); border: 2px solid #e67e22;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
}
.sophie__ava svg, .sophie__ava img { width: 100%; height: 100%; display: block; object-fit: cover; }
.module-scene { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--radius); margin-bottom: 1.2rem; box-shadow: 0 6px 22px rgba(0, 0, 0, .14); display: block; }
/* Vidéo compagnon (Justine qui parle) : masquée en cercle, fond nuit de l'appli,
   liseré orange comme les avatars. Un seul bouton ▶/⏸ custom au centre. */
.compagnon-video {
  position: relative; width: 100%; max-width: 300px; aspect-ratio: 1 / 1;
  margin: 0 auto 1.2rem; border-radius: 50%; overflow: hidden; background: #0c1020;
  border: 3px solid #e67e22; box-shadow: 0 8px 26px rgba(0, 0, 0, .4);
}
/* Zoom du cercle : le start-frame est un plan large (62 %, anti-dérive longform),
   donc on rezoome pour remplir le médaillon. Origine centrée (le personnage est
   centré verticalement) : tête et buste tiennent dans le cercle. */
.compagnon-video__el { width: 100%; height: 100%; object-fit: cover; display: block; cursor: pointer; transform: translateY(8%) scale(1.35); transform-origin: center; }
.compagnon-video__play {
  position: absolute; inset: 0; margin: auto; width: 66px; height: 66px;
  border: 0; border-radius: 50%; background: rgba(12, 16, 32, .62); color: #fff;
  display: grid; place-items: center; cursor: pointer;
  transition: opacity .25s, transform .15s, background .2s;
}
.compagnon-video__play:hover { transform: scale(1.07); background: rgba(12, 16, 32, .72); }
.compagnon-video__play svg { width: 30px; height: 30px; fill: #fff; }
.compagnon-video__play svg.ico-play { margin-left: 3px; }   /* triangle optiquement centré */
.compagnon-video__play .ico-pause { display: none; }
/* En lecture : le bouton s'efface, réapparaît (en pause) au survol du cercle. */
.compagnon-video.playing .compagnon-video__play { opacity: 0; }
.compagnon-video.playing:hover .compagnon-video__play { opacity: 1; }
/* Tactile (pas de vrai survol) : après le tap, le :hover « colle » et laisserait
   le bouton pause affiché. On le garde masqué en lecture — un tap sur la vidéo
   met en pause (le bouton ▶ réapparaît alors, la vidéo n'étant plus en lecture). */
@media (hover: none) {
  .compagnon-video.playing:hover .compagnon-video__play { opacity: 0; }
}
.compagnon-video.playing .compagnon-video__play .ico-play { display: none; }
.compagnon-video.playing .compagnon-video__play .ico-pause { display: block; }

/* Bannière du module + vidéo circulaire incrustée dans un coin. */
.module-scene-wrap { position: relative; margin-bottom: 1.2rem; }
.module-scene-wrap .module-scene { margin-bottom: 0; }
.compagnon-video--coin {
  position: absolute; width: 30%; max-width: 172px; margin: 0;
  border-width: 2px; box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
}
.compagnon-video--coin.bl { left: 3.5%; bottom: 4%; }
.compagnon-video--coin.br { right: 3.5%; bottom: 4%; }
.compagnon-video--coin.tl { left: 3.5%; top: 4%; }
.compagnon-video--coin.tr { right: 3.5%; top: 4%; }
.compagnon-video--coin .compagnon-video__play { width: 46px; height: 46px; }
.compagnon-video--coin .compagnon-video__play svg { width: 22px; height: 22px; }
@media (max-width: 560px) { .compagnon-video--coin { width: 34%; } }

/* Médaillon « dialogue » d'un scénario : cercle centré + nom du locuteur. */
.dialogue-video { display: flex; flex-direction: column; align-items: center; margin-bottom: 1.2rem; }
.dialogue-video .compagnon-video { max-width: 300px; margin: 0; }
.dialogue-video__nom { margin-top: .6rem; font-weight: 700; color: var(--accent); letter-spacing: .02em; }
/* Scénario SMS : capture téléphone + bouton voix off. */
.sms-media { display: flex; flex-direction: column; align-items: center; gap: 1rem; margin-bottom: 1.2rem; }
.sms-media__img { max-height: 540px; max-width: 100%; border-radius: 22px; box-shadow: 0 12px 32px rgba(0, 0, 0, .5); }
/* Justine, figure mi-corps à gauche de la bannière de formation. En flux flex :
   elle RÉSERVE sa propre place (pas de chevauchement, et la ligne se réagence
   proprement en paysage mobile grâce à flex-wrap). Ancrée en bas, bas coupé
   affleurant le bord, incrustée dans le bleu. */
.hero-justine {
  height: 235px; width: auto; flex: none; align-self: flex-end; margin: 0 .2rem -2.5rem 0;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .28)); pointer-events: none;
}
@media (max-width: 760px) { .hero-justine { height: 160px; } }
@media (max-width: 520px) { .hero-justine { display: none; } }
.scenario__texte { color: var(--text-soft); }
.scenario__texte p { margin: .6rem 0 0; }
.scenario__texte p:first-child { margin-top: 0; }
.scenario__texte .sophie-cite {
  font-style: italic; padding-left: .8rem;
  border-left: 2px solid color-mix(in srgb, var(--accent) 45%, var(--border));
}
/* Bouton d'ouverture de l'épreuve : verrouillé tant que l'échange n'est pas vu. */
.scenario__go { display: block; margin: 1.4rem auto 0; font-size: 1rem; }
.scenario__go:disabled { opacity: .5; cursor: not-allowed; }
.scenario__go.pret { animation: goPulse 1.8s ease-out infinite; }
@keyframes goPulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  70% { box-shadow: 0 0 0 12px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.scenario__suite { display: block; margin: 1.3rem auto 0; }
/* Cartes de formation révélées une par une. */
/* Masquage d'un élément pas encore dévoilé.
   `!important` est ici le bon outil, et non un raccourci : plusieurs blocs
   révélables (niveaux de pyramide, lignes de liste, éléments de checklist)
   déclarent leur propre `display: flex` dans des règles de spécificité égale ou
   supérieure, écrites plus bas dans cette feuille. Elles l'emportaient, si bien
   que ces blocs restaient tous affichés : la découverte pas à pas ne masquait
   rien, donc ne révélait rien — et l'on croyait à une animation absente.
   Une classe utilitaire dont l'unique rôle est de masquer doit gagner. */
.carte-cachee { display: none !important; }
/* `transition: none` pendant l'entrée : certains blocs révélables (les niveaux
   de la pyramide, avec leur liseré coloré) portent leur propre transition sur
   `transform`, qui se déclenche en même temps que l'animation et peut l'écraser
   selon le moteur de rendu. */
.carte-apparait { animation: carteIn .34s ease-out; transition: none; }
@keyframes carteIn { from { opacity: 0; transform: translateY(14px); } }
.cartes-plus { display: block; margin: 1.2rem auto 0; }
/* L'épreuve en modale : un peu d'aération autour de la question/réponses. */
/* L'épreuve passée brute à openModal n'a pas le padding du .modal__body :
   on le remet ici pour décoller la question/les options des bords arrondis. */
.modal .scenario__epreuve { padding: 1.4rem 1.5rem 1.7rem; }
.modal .scenario__epreuve .scenario__q { margin-top: .2rem; padding-right: 2.2rem; }

.outro-ava { width: 88px; height: 88px; margin: 0 auto .5rem; }
/* Justine narratrice : avatar à gauche, contenu (page ou exercice) à droite. */
.jeu-perso, .lecture-perso { display: flex; gap: 1rem; align-items: flex-start; }
.jeu-perso > .sophie__ava, .lecture-perso > .sophie__ava { position: sticky; top: 1.2rem; }
.jeu-perso > .game, .lecture-perso > :not(.sophie__ava) { flex: 1; min-width: 0; }
@media (max-width: 560px) {
  .jeu-perso, .lecture-perso { flex-direction: column; align-items: center; }
  .jeu-perso > .game, .lecture-perso > :not(.sophie__ava) { width: 100%; }
}
/* Phishing de formation : les indices ne doivent PAS être surlignés au repos
   (sinon on les repère sans lire). Tout le message paraît cliquable ; seuls le
   vert « trouvé » et le rouge « raté » apparaissent après coup. */
.jeu-perso .mailbox__body { cursor: pointer; }
.jeu-perso .clue:not(.found):not(.miss) { background: transparent; border-bottom-color: transparent; }
.jeu-perso .clue:hover:not(.found):not(.miss) { background: transparent; }
.sophie__bulle {
  position: relative; flex: 1; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 14px; padding: .8rem 1rem;
}
.sophie__bulle::before {
  content: ''; position: absolute; left: -8px; top: 18px; width: 14px; height: 14px;
  background: var(--surface-2); border-left: 1px solid var(--border); border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}
.sophie__bulle p { margin: .6rem 0 0; }
.sophie__bulle p:first-child { margin-top: 0; }
.sophie__bulle .sophie-cite {
  font-style: italic; color: var(--text-soft);
  padding-left: .8rem; border-left: 2px solid color-mix(in srgb, var(--accent) 45%, var(--border));
}
.sophie__heure, .scenario__heure {
  display: inline-block; font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: .14em .6em; border-radius: 999px;
}
.sophie__heure { margin-bottom: .45rem; }
.step-hero--sophie .em { display: none; }
/* Intro de module : aligné à gauche, texte pleine largeur (l'avatar a été retiré). */
.step-hero--sophie { text-align: left; padding: 2.2rem 2.4rem; }
.hero-texte { margin-top: 1rem; }
.hero-texte p { margin: .7rem 0 0; font-size: 1.06rem; color: var(--text-soft); max-width: none; }
.hero-texte p:first-child { margin-top: 0; }
.hero-texte .sophie-cite {
  font-style: italic; color: var(--text-soft);
  padding-left: .8rem; border-left: 2px solid color-mix(in srgb, var(--accent) 45%, var(--border));
}

.step-scenario .scenario__heure { margin-bottom: .8rem; }
.scenario__q { margin: 1.1rem 0 .7rem; font-size: 1.08rem; }
.scenario__opts { display: grid; gap: .6rem; }
.scenario__opt {
  width: 100%; text-align: left; padding: .8rem 1rem; border-radius: 12px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font: inherit; cursor: pointer; transition: border-color .15s, background .15s;
}
.scenario__opt:hover:not(.lock) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
.scenario__opt.lock { cursor: default; }
.scenario__opt.ok { border-color: var(--ok); background: var(--ok-soft); }
.scenario__opt.ko { border-color: var(--danger); background: var(--danger-soft); }
.scenario__opt.bonne:not(.ok) { border-color: var(--ok); box-shadow: inset 0 0 0 1px var(--ok); }
.scenario__opt.bonne::after { content: ' ✓'; color: var(--ok); font-weight: 700; }
.scenario__fb {
  max-height: 0; overflow: hidden; opacity: 0; font-size: .93rem;
  transition: max-height .25s, opacity .25s, margin .25s;
}
.scenario__fb.on { max-height: 560px; opacity: 1; margin: .5rem 0 .3rem .2rem; padding-left: .8rem; border-left: 3px solid var(--border); }
.scenario__fb.fb-ok { border-left-color: var(--ok); }
.scenario__fb.fb-ko { border-left-color: var(--danger); }

/* Voix de Justine sur une révélation (feedback de scénario, explication d'un
   vrai/faux) : une pastille discrète, qui ne conditionne jamais la suite. */
.voix-rv {
  display: inline-flex; align-items: center; gap: .35rem; margin: .5rem 0 0;
  padding: .22rem .6rem .22rem .4rem; border-radius: 999px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--text-soft); font: inherit; font-size: .78rem; line-height: 1.5;
}
.voix-rv:hover { border-color: var(--accent); color: var(--text); }
.voix-rv__ico { display: grid; place-items: center; width: 16px; height: 16px; color: var(--accent); }
.voix-rv__ico svg { width: 16px; height: 16px; fill: currentColor; }
.voix-rv.joue { border-color: var(--accent); color: var(--text); }
.voix-rv.joue .voix-rv__ico { animation: voix-pulse 1.1s ease-in-out infinite; }
@keyframes voix-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .55; transform: scale(.86); } }
@media (prefers-reduced-motion: reduce) { .voix-rv.joue .voix-rv__ico { animation: none; } }
/* Sur sa propre ligne : accolée à la fin d'un paragraphe, la pastille se
   confondait avec le texte de la réponse. */
.feedback .voix-rv, .scenario__fb .voix-rv { display: flex; width: fit-content; }

/* Narration MP3 d'une étape de formation : la progression suit l'audio réel
   (l'ancienne jauge d'attente au chrono a été retirée). */
.narration {
  display: flex; align-items: center; gap: .8rem; margin: 0 0 1.1rem;
  padding: .5rem .8rem .5rem .5rem; border: 1px solid var(--border);
  border-radius: 999px; background: var(--surface);
}
.narration__play {
  flex: none; width: 40px; height: 40px; border-radius: 50%; border: 0; padding: 0;
  background: var(--accent); color: #fff; display: grid; place-items: center; cursor: pointer;
}
.narration__play svg { width: 18px; height: 18px; fill: currentColor; }
.narration__play .ico-pause { display: none; }
.narration.playing .narration__play .ico-play { display: none; }
.narration.playing .narration__play .ico-pause { display: block; }
.narration__corps { flex: 1; min-width: 0; }
.narration__txt { display: block; font-size: .84rem; color: var(--text-soft); margin-bottom: .35rem; }
.narration__jauge { height: 5px; border-radius: 999px; background: var(--border); overflow: hidden; }
.narration__jauge i { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 999px; }
.narration.fini .narration__jauge i { background: var(--ok); }
.narration__temps { flex: none; font-size: .82rem; color: var(--text-soft); font-variant-numeric: tabular-nums; }
.narration__recul {
  flex: none; border: 1px solid var(--border); background: transparent; color: var(--text-soft);
  font: inherit; font-size: .78rem; padding: .3rem .55rem; border-radius: 999px; cursor: pointer;
}
.narration__recul:hover { border-color: var(--accent); color: var(--text); }
@media (max-width: 560px) { .narration__recul { display: none; } }
.blist-ex { margin: .35rem 0 0; font-size: .9rem; color: var(--text-soft); padding-left: .7rem; border-left: 3px solid color-mix(in srgb, var(--accent) 45%, var(--border)); }
.blist-ex b { color: var(--text); }

/* Horloge du fil rouge (formation) */
.horloge { display: inline-flex; align-items: center; gap: .55rem; margin-bottom: 1rem; color: var(--accent); }
.horloge__face { width: 42px; height: 42px; flex: none; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .12)); }
.horloge__face svg { width: 100%; height: 100%; display: block; }
.horloge__txt { font-weight: 800; font-size: 1.2rem; letter-spacing: .01em; }

/* Callout */
.callout { border-radius: var(--radius); padding: 1.4rem 1.6rem; display: flex; gap: 1rem; margin-top: 1.2rem; }
.callout .em { font-size: 1.5rem; flex: none; }
.callout b { display: block; margin-bottom: .3rem; }
.callout p { margin: 0; font-size: .96rem; }
.callout.info { background: var(--info-soft); color: color-mix(in srgb, var(--info) 75%, var(--text)); }
.callout.warn { background: var(--warn-soft); color: color-mix(in srgb, var(--warn) 78%, var(--text)); }
.callout.danger { background: var(--danger-soft); color: color-mix(in srgb, var(--danger) 78%, var(--text)); }
.callout.ok { background: var(--ok-soft); color: color-mix(in srgb, var(--ok) 78%, var(--text)); }
/* Un encadré peut porter deux paragraphes : le second énonce souvent la règle
   à retenir, il ne doit pas se coller au premier. */
.callout p + p { margin-top: .7em; }
/* Sur un téléphone, la colonne du pictogramme coûtait quarante pixels de
   largeur sur trois cent soixante, et le texte tombait à six ou sept mots par
   ligne. Le pictogramme passe donc DANS le texte, en début de première ligne :
   le paragraphe reprend toute la largeur dès la deuxième. Les retraits de la
   carte et de l'encadré se resserrent d'autant. */
@media (max-width: 560px) {
  .callout { display: block; padding: 1.1rem 1rem; }
  .callout .em { float: left; margin: -.1rem .55rem .1rem 0; font-size: 1.3rem; line-height: 1.2; }
  .callout b { margin-bottom: .2rem; }
}

/* Timeline */
.timeline { position: relative; margin-top: 1.6rem; padding-left: 2.2rem; }
.timeline::before { content: ''; position: absolute; left: 9px; top: 8px; bottom: 8px; width: 2px; background: var(--border); }
.tl-item { position: relative; padding-bottom: 1.7rem; }
.tl-item::before {
  content: ''; position: absolute; left: -2.2rem; top: 5px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--surface); border: 3px solid var(--border);
}
.tl-item.hl::before { border-color: var(--accent); box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 15%, transparent); }
.tl-item .date { font-size: .78rem; font-weight: 800; color: var(--accent); letter-spacing: .04em; }
.tl-item b { display: block; margin: .1rem 0 .25rem; }
.tl-item p { margin: 0; font-size: .93rem; color: var(--text-soft); }

/* Pyramide */
.pyramid { display: grid; gap: .8rem; margin-top: 1.4rem; }
.pyr-level {
  border-radius: var(--radius); padding: 1.2rem 1.4rem; border: 1px solid var(--border); background: var(--surface-2);
  display: flex; gap: 1rem; align-items: flex-start; transition: transform .2s;
  border-left: 5px solid var(--lvl);
}

.pyr-level .em { font-size: 1.6rem; flex: none; }
.pyr-level b { display: block; color: var(--lvl); margin-bottom: .25rem; }
.pyr-level p { margin: 0; font-size: .92rem; color: var(--text-soft); }

/* Split 2 colonnes */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 1.4rem; }
.split .col { border-radius: var(--radius); padding: 1.4rem 1.5rem; border: 1px solid var(--border); background: var(--surface-2); }
.split .col .em { font-size: 1.7rem; display: block; margin-bottom: .5rem; }
.split .col b { display: block; font-size: 1.05rem; margin-bottom: .5rem; }
.split .col p { font-size: .93rem; color: var(--text-soft); margin: 0; white-space: pre-line; }
.split .col ul { margin: .3rem 0 0; padding-left: 1.1rem; font-size: .92rem; color: var(--text-soft); }
.split .col ul li { margin-bottom: .45rem; }
.split .col.danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 22%, transparent); }
.split .col.ok { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 22%, transparent); }
.split .col.warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 22%, transparent); }

/* Listes à puces enrichies */
.blist { display: grid; gap: .8rem; margin-top: 1.4rem; }
.blist .row { display: flex; gap: 1rem; padding: 1rem 1.2rem; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--border); }
.blist .row .n {
  flex: none; width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; font-size: .95rem;
  background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); font-weight: 700;
}
.blist .row b { display: block; margin-bottom: .15rem; }
.blist .row p { margin: 0; font-size: .92rem; color: var(--text-soft); }

/* Grille 6 bases légales */
.grid6 { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: .9rem; margin-top: 1.4rem; }
.grid6 .cell {
  border: 1px solid var(--border); border-radius: var(--radius); padding: 1.2rem; background: var(--surface-2);
  transition: .2s; position: relative; overflow: hidden;
}
.grid6 .cell::before {
  content: ''; position: absolute; inset: 0; background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent 60%);
  opacity: 0; transition: .25s;
}

.grid6 .cell:hover::before { opacity: 1; }
.grid6 .em { font-size: 1.5rem; display: block; margin-bottom: .4rem; position: relative; }
.grid6 b { display: block; margin-bottom: .25rem; position: relative; }
.grid6 p { margin: 0; font-size: .88rem; color: var(--text-soft); position: relative; }

/* Checklist */
.checklist { display: grid; gap: .6rem; margin-top: 1.4rem; }
.checklist .item {
  display: flex; gap: .9rem; align-items: flex-start; padding: .95rem 1.2rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border); cursor: pointer; transition: .18s;
}
.checklist .item:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.checklist .item .box {
  flex: none; width: 22px; height: 22px; border-radius: 7px; border: 2px solid var(--border); display: grid; place-items: center;
  font-size: .8rem; color: transparent; transition: .18s; margin-top: 1px;
}
.checklist .item.on { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.checklist .item.on .box { background: var(--ok); border-color: var(--ok); color: #fff; }
.checklist .item span { font-size: .95rem; }

/* Stats */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin-top: 1.4rem; }
.stat { text-align: center; padding: 1.4rem 1rem; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--border); }
.stat b { display: block; font-size: 2.2rem; font-weight: 800; letter-spacing: -.03em; color: var(--accent); }
.stat span { font-size: .84rem; color: var(--text-soft); }

/* Outro */
.outro { text-align: center; padding: 3rem 2.4rem; }
.outro .em { font-size: 4rem; display: block; animation: bob 3s ease-in-out infinite; }
.outro .mail {
  display: inline-block; margin-top: 1rem; padding: .6em 1.3em; border-radius: 999px;
  background: var(--brand-soft); color: var(--brand); font-weight: 600; text-decoration: none;
}

/* Navigation de chapitre */
/* La barre du bas ne porte plus qu'une action depuis que « Précédent » a cédé
   la place à la barre d'étapes : elle se pose au centre, sous le contenu. */
.step-nav { display: flex; gap: .8rem; align-items: center; justify-content: center; margin-top: 1.6rem; }
.step-nav .grow { flex: 1; }
.chapter-head__ico { font-size: 1.8rem; flex: none; }

/* En portrait mobile, l'intérieur d'un chapitre est resserré : l'en-tête ne doit
   plus couper « chapitre X / Y » ni le titre, et la barre du bas doit garder ses
   deux boutons sur une ligne (le compteur d'étapes, redondant avec la barre de
   progression du haut, est masqué). */
@media (max-width: 560px) {
  .chapter-head { gap: .55rem; margin-bottom: 1rem; }
  .chapter-head .info b { font-size: .64rem; letter-spacing: .04em; }
  .chapter-head .info h2 { font-size: 1.02rem; line-height: 1.25; margin-top: .15rem; }
  .chapter-head__ico { font-size: 1.35rem; }
  .steps-bar { margin-bottom: 1.2rem; }
  .step-nav { gap: .5rem; margin-top: 1.2rem; }
  .step-nav .grow { display: none; }
  .step-nav .btn { padding: .58em .9em; font-size: .86rem; }
  .step-nav .btn.lg { padding: .62em 1em; font-size: .88rem; }
  /* Carton de jeu resserré. */
  .game { padding: 1.1rem 1rem; }
  .game__head h3 { font-size: 1.08rem; }
  .game__head .em { font-size: 1.3rem; }
  .game .intro { font-size: .86rem; margin-bottom: 1rem; }
  .tf-card { padding: 1.2rem 1rem; font-size: 1rem; min-height: 84px; margin-bottom: 1rem; }
  .tf-actions { gap: .6rem; }
  .tf-actions .btn { min-width: 0; flex: 1; }
}

/* ─────────────── Loupe / mots-clés ─────────────── */
.kw {
  display: inline; cursor: pointer; font-weight: 600; color: var(--accent);
  border-bottom: 1.5px dashed color-mix(in srgb, var(--accent) 55%, transparent);
  padding: 0 .1em; border-radius: 3px; transition: background .18s;
  position: relative;
}
.kw::after { content: '\00a0🔍'; font-size: .78em; opacity: .75; }
.kw:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.kw.seen { color: var(--text-soft); border-bottom-color: var(--border); }
.kw.seen::after { opacity: .4; }

.overlay {
  /* Pas de backdrop-filter : flouter toute la page sous la modale coûtait des
     images à 158 ms. Un voile un peu plus dense produit le même effet de mise
     en retrait, sans recalcul du fond à chaque image. */
  position: fixed; inset: 0; background: rgba(10, 14, 30, .64);
  display: grid; place-items: center; z-index: 100; padding: 1.4rem; animation: fade .18s;
  /* minmax(0, …) empêche la colonne d'être dimensionnée sur le contenu :
     sans cela le pourcentage de largeur de la modale peut déborder. */
  grid-template-columns: minmax(0, 1fr);
}
@keyframes fade { from { opacity: 0; } }
.modal {
  background: var(--surface); border-radius: var(--radius-lg);
  width: min(560px, 100%); max-width: 100%; justify-self: center;
  max-height: 86vh; overflow: auto; overflow-wrap: break-word;
  box-shadow: var(--shadow-lg); animation: modalIn .28s cubic-bezier(.2,.9,.3,1.1); position: relative;
}
@keyframes modalIn { from { opacity: 0; transform: translateY(24px) scale(.96); } }
.modal__head {
  padding: 1.6rem 1.8rem 1.2rem; background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 14%, var(--surface)), var(--surface));
  border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 2;
}
/* Le z-index n'est pas décoratif : sans lui, tout contenu du corps portant un
   `filter` ou une `opacity` — qui créent un contexte d'empilement — se peint
   au-dessus du bandeau collant, parce qu'il le suit dans le document. C'est ce
   qui faisait passer les pastilles de modules à travers le nom. */
.modal__head .lens { font-size: 1.7rem; }
/* Sans pictogramme, l'en-tête n'a plus besoin de la hauteur qu'il réservait. */
.modal__head--compact { padding-top: 1.15rem; padding-bottom: 1rem; }
.modal__head--compact h3 { margin-top: 0; }
.modal__head h3 { margin: .3rem 0 .1rem; font-size: 1.35rem; }
.modal__head .sub { font-size: .82rem; color: var(--text-mute); }
.modal__body { padding: 1.4rem 1.8rem 1.8rem; }
.modal__body p { font-size: .98rem; }
.modal__body ul { padding-left: 1.1rem; margin: .8rem 0 0; }
.modal__body li { font-size: .93rem; color: var(--text-soft); margin-bottom: .55rem; }
/* La croix suit le défilement, elle ne s'en va pas avec lui.
   En « absolute », elle était positionnée par rapport au contenu de la modale
   — qui est le conteneur qui défile — et disparaissait vers le haut dès qu'on
   descendait, alors que l'en-tête, lui, restait collé. En « sticky » elle reste
   à sa place. La marge basse négative annule la hauteur qu'elle occuperait
   dans le flux : l'en-tête démarre toujours au bord supérieur. */
.modal__close {
  position: sticky; top: 1rem; z-index: 3;
  display: block; margin: 1rem 1rem -3rem auto;
  width: 32px; height: 32px; border-radius: 50%; border: 0;
  background: var(--surface-2); cursor: pointer; font-size: 1rem; color: var(--text-soft);
}
.modal__close:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.modal__close:hover { background: var(--border); }
.modal .srcline { margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--border); font-size: .8rem; color: var(--text-mute); }

/* ─────────────── Mini-jeux ─────────────── */
.game { border: 2px solid color-mix(in srgb, var(--accent) 25%, var(--border)); border-radius: var(--radius-lg); padding: 2rem 2.2rem; background: var(--surface); }
.game__head { display: flex; align-items: center; gap: .8rem; margin-bottom: .4rem; }
/* Une fois l'exercice fini, l'en-tête ne porte plus que Justine : elle se
   centre au-dessus du résultat qu'elle commente, et prend la place que le
   titre et le compteur laissent libre — c'est elle le sujet de cet écran. */
.game__head--fini { justify-content: center; margin-bottom: 1rem; }
.game__head--fini .jus-medaillon { width: 5.5rem; height: 5.5rem; border-width: 3px; }

/* La barre de l'écran de bilan : un seul bouton, centré sous le résultat. */
/* Dernier écran et écran de bilan : même centrage que partout ailleurs. */
/* Tant que l'exercice final n'est pas réussi, la barre est vide : elle ne doit
   pas laisser sa marge sous la carte. */
.step-nav:empty { display: none; margin: 0; }

/* ── Le bouton qui récompense ─────────────────────────────────────────────
   Il n'apparaît qu'après un sans-faute : c'est le seul endroit du parcours où
   l'application a quelque chose à fêter. Trois gestes qui se composent sans
   rien redessiner — une entrée en ressort, un halo qui respire, un éclat qui
   traverse. `transform` et `opacity` seulement : ce sont les propriétés que le
   navigateur compose sans repasser sur la mise en page. */
.btn-fete {
  position: relative; overflow: hidden;
  animation: feteEntree .5s cubic-bezier(.2, 1.4, .4, 1) both,
             feteHalo 2.4s ease-in-out .5s infinite;
}
@keyframes feteEntree {
  from { transform: scale(.82); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
@keyframes feteHalo {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand) 55%, transparent); }
  70%      { box-shadow: 0 0 0 12px color-mix(in srgb, var(--brand) 0%, transparent); }
}
/* L'éclat : une bande claire en biais qui traverse le bouton, puis attend.
   Elle vit dans un pseudo-élément, donc rien du contenu ne bouge. */
.btn-fete::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 42%, rgba(255, 255, 255, .38) 50%, transparent 58%);
  transform: translateX(-130%);
  animation: feteEclat 3.6s ease-in-out 1s infinite;
}
@keyframes feteEclat {
  0%, 55% { transform: translateX(-130%); }
  85%, 100% { transform: translateX(130%); }
}
/* Rien de tout cela n'est nécessaire à la compréhension : sous réglage de
   confort, le bouton reste un bouton. */
@media (prefers-reduced-motion: reduce) {
  .btn-fete, .btn-fete::after { animation: none; }
}
@media (max-width: 700px) {
  .game__head--fini .jus-medaillon { width: 4.5rem; height: 4.5rem; }
}
/* Justine, à la place du pictogramme de manette. Elle interroge pendant qu'on
   cherche, rassure, alerte ou félicite selon la réponse — c'est le même
   mécanisme que dans les scénarios de la formation, ici rendu disponible aux
   exercices de tous les parcours. */
.jus-medaillon {
  flex: none; width: 3rem; height: 3rem; border-radius: 50%;
  overflow: hidden; background: var(--surface-2);
  border: 2px solid color-mix(in srgb, var(--brand) 45%, transparent);
}
/* L'image est carrée, le médaillon rond : les quatre coins tombent hors du
   cercle, et c'est là que se trouvent les poings levés de l'expression de
   joie. Un léger retrait rentre le dessin dans le disque inscrit. */
/* Le dessin est réduit pour rentrer dans le disque, puis DESCENDU : cadré au
   centre, son buste laissait un vide sous lui et sa chevelure frôlait le bord
   haut. Le sujet vit dans la moitié supérieure de l'image, pas en son
   milieu. */
.jus-medaillon img {
  width: 100%; height: 100%; object-fit: contain; display: block;
  transform: scale(.9) translateY(7%);
}
/* Chaque dessin a son propre centre de gravité : celui de la joie penche à
   droite — les deux poings ne sont pas symétriques —, et cadré comme les
   autres, Justine s'y retrouvait décentrée. */
.jus-medaillon[data-expr="bravo"] img { transform: scale(.9) translate(-4%, 7%); }
@media (max-width: 700px) {
  .jus-medaillon { width: 2.4rem; height: 2.4rem; }
}
.game__head .em { font-size: 1.5rem; }
.game__head h3 { margin: 0; font-size: 1.25rem; }
.game__head .score { margin-left: auto; font-weight: 800; color: var(--accent); font-size: .95rem; }
.game .intro { color: var(--text-soft); font-size: .94rem; margin-bottom: 1.4rem; }

/* Vrai / faux */
.tf-card {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.8rem;
  text-align: center; font-size: 1.12rem; font-weight: 600; margin-bottom: 1.2rem; min-height: 110px;
  display: grid; place-items: center; animation: stepIn .3s;
}
.tf-actions { display: flex; gap: 1rem; justify-content: center; }
.tf-actions .btn { min-width: 140px; }
/* Vrai / Faux : mêmes teintes que Valider / Refuser, même comportement. */
.tf-actions .btn.vrai { --ton: var(--ok); }
.tf-actions .btn.faux { --ton: var(--danger); }
/* Réponse retenue : le même repère que pour un choix unique. Sans lui, revenir
   en arrière sur une question vrai/faux ne montrait pas ce qu'on avait coché —
   « soft » se distinguait à peine de l'état non choisi. */
.tf-actions .btn.sel {
  background: color-mix(in srgb, var(--ton) 13%, var(--surface));
  border-color: var(--ton);
  box-shadow: inset 0 0 0 1px var(--ton);
  font-weight: 700;
}

.feedback { border-radius: var(--radius); padding: 1.1rem 1.3rem; margin-top: 1.2rem; animation: stepIn .3s; font-size: .94rem; }
.feedback.good { background: var(--ok-soft); color: color-mix(in srgb, var(--ok) 80%, var(--text)); }
.feedback.bad { background: var(--danger-soft); color: color-mix(in srgb, var(--danger) 80%, var(--text)); }
.feedback b { display: block; margin-bottom: .25rem; }

/* ── La révélation, posée par-dessus l'exercice sur les petits écrans ──────
   Écrite sous les propositions, elle tombait hors de l'écran d'un téléphone :
   on faisait défiler pour la lire, puis encore pour atteindre « Suivant ».
   Ici, elle arrive devant, et l'appui suivant enchaîne. */
/* Sur un téléphone, la correction prend la place des propositions : elle
   paraît là où le doigt vient d'appuyer, l'énoncé reste au-dessus, et un appui
   dessus passe à la suite. Elle est donc un bouton, et le dit au toucher. */
.feedback--tactile {
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, filter .12s ease;
}
.feedback--tactile:active { transform: scale(.985); filter: brightness(1.06); }
.feedback--tactile:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.revel-nav { margin-top: 1rem; text-align: right; }

/* QCM */
.opt {
  display: flex; gap: .85rem; align-items: flex-start; width: 100%; text-align: left; padding: 1rem 1.2rem; margin-bottom: .6rem;
  border-radius: var(--radius-sm); border: 1.5px solid var(--border); background: var(--surface-2); cursor: pointer;
  font-size: .96rem; color: var(--text); transition: .16s; font-family: inherit;
}
.opt:hover:not(:disabled) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--surface-2)); }
.opt .let {
  flex: none; width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; font-weight: 700; font-size: .82rem;
  background: var(--surface); border: 1px solid var(--border);
}
.opt.good { background: var(--ok-soft); border-color: var(--ok); }
.opt.good .let { background: var(--ok); color: #fff; border-color: var(--ok); }
.opt.bad { background: var(--danger-soft); border-color: var(--danger); }
.opt.bad .let { background: var(--danger); color: #fff; border-color: var(--danger); }
.opt.sel { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, var(--surface-2)); }
.opt.sel .let { background: var(--accent); color: #fff; border-color: var(--accent); }
.opt:disabled { cursor: default; }

/* Tri (sort) */
.sort-item {
  background: var(--surface); border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 1rem 1.3rem;
  font-weight: 600; text-align: center; margin-bottom: 1.2rem; box-shadow: var(--shadow-sm); animation: stepIn .28s;
}
.sort-cats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .8rem; }
/* Bordure pleine, et non en pointillés : ce sont des boutons à cliquer,
   pas des zones de dépôt — le pointillé suggérait un glisser-déposer. */
.sort-cat {
  border: 2px solid color-mix(in srgb, var(--c) 45%, var(--border)); border-radius: var(--radius); padding: 1.2rem .9rem;
  text-align: center; cursor: pointer; background: color-mix(in srgb, var(--c) 7%, var(--surface-2)); transition: .18s;
  font-family: inherit; color: var(--text); width: 100%; box-shadow: var(--shadow-sm);
}
.sort-cat:hover:not(:disabled) {
  background: color-mix(in srgb, var(--c) 15%, var(--surface-2));
  border-color: color-mix(in srgb, var(--c) 75%, var(--border));
}
.sort-cat .em { font-size: 1.5rem; display: block; margin-bottom: .3rem; }
.sort-cat b { font-size: .88rem; color: var(--c); }
.sort-cat:disabled { opacity: .5; cursor: default; }

/* Association (match) */
.match-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.match-col { display: grid; gap: .55rem; align-content: start; }
.match-col h4 { font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-mute); margin: 0 0 .2rem; }
.match-item {
  padding: .85rem 1rem; border-radius: var(--radius-sm); border: 1.5px solid var(--border); background: var(--surface-2);
  cursor: pointer; font-size: .9rem; text-align: left; transition: .16s; font-family: inherit; color: var(--text);
}
.match-item:hover:not(:disabled) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface-2)); }
.match-item.sel { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent); }
.match-item.paired { opacity: .45; cursor: default; border-style: dashed; }
.match-item.flash-good { border-color: var(--ok); background: var(--ok-soft); }
.match-item.flash-bad { border-color: var(--danger); background: var(--danger-soft); animation: shake .35s; }
@keyframes shake { 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }

/* Remise en ordre */
.order-list { display: grid; gap: .6rem; }
.order-item {
  display: flex; align-items: center; gap: .9rem; padding: .95rem 1.1rem; border-radius: var(--radius-sm);
  border: 1.5px solid var(--border); background: var(--surface-2); cursor: grab; font-size: .94rem;
}
.order-item.dragging { opacity: .4; }
.order-item .handle { color: var(--text-mute); font-size: 1.1rem; cursor: grab; }
.order-item .arrows { margin-left: auto; display: flex; gap: .25rem; }
.order-item .arrows button {
  width: 28px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface);
  cursor: pointer; color: var(--text-soft); font-size: .8rem;
}
.order-item .arrows button:hover { background: var(--brand-soft); color: var(--brand); }
.order-item.good { border-color: var(--ok); background: var(--ok-soft); }
.order-item.bad { border-color: var(--danger); background: var(--danger-soft); }

/* Phishing */
.mailbox { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface-2); }
.mailbox__head { padding: 1rem 1.3rem; border-bottom: 1px solid var(--border); background: var(--surface); font-size: .88rem; }
.mailbox__head .line { display: flex; gap: .5rem; margin-bottom: .3rem; }
.mailbox__head .line .lbl { color: var(--text-mute); min-width: 62px; }
.mailbox__head .subject { font-weight: 700; font-size: 1rem; margin-top: .5rem; }
.mailbox__body { padding: 1.4rem 1.3rem; font-size: .95rem; white-space: pre-wrap; line-height: 1.7; }
.clue {
  cursor: pointer; border-radius: 4px; padding: .05em .25em; background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-bottom: 2px dotted color-mix(in srgb, var(--text-mute) 60%, transparent); transition: .16s;
}
.clue:hover { background: color-mix(in srgb, var(--warn) 25%, transparent); }
.clue.found { background: var(--ok-soft); border-bottom-color: var(--ok); color: color-mix(in srgb, var(--ok) 75%, var(--text)); font-weight: 600; }
.clue.miss { background: var(--danger-soft); }
.clue-list { margin-top: 1.2rem; display: grid; gap: .5rem; }
.clue-list .found-item {
  padding: .8rem 1rem; border-radius: var(--radius-sm); background: var(--ok-soft); font-size: .89rem;
  color: color-mix(in srgb, var(--ok) 80%, var(--text)); animation: stepIn .3s;
}
.clue-counter { text-align: center; font-weight: 700; color: var(--accent); margin: 1rem 0 0; }

/* Testeur de mot de passe */
.pw-box { margin-top: 1.4rem; }
.pw-box input {
  width: 100%; padding: 1em 1.2em; border-radius: var(--radius-sm); border: 1.5px solid var(--border);
  background: var(--surface); color: var(--text); font-size: 1.05rem; font-family: ui-monospace, monospace;
}
.pw-box input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent); }
.pw-meter { height: 9px; border-radius: 99px; background: var(--border); margin: .9rem 0 .6rem; overflow: hidden; }
.pw-meter i { display: block; height: 100%; width: 0; border-radius: 99px; transition: width .35s, background .35s; }
.pw-verdict { font-weight: 700; font-size: .95rem; margin-bottom: .8rem; }
/* Champ mot de passe avec bouton afficher/masquer */
.pw-input { position: relative; }
.pw-input input { padding-right: 3rem; }
.pw-eye {
  position: absolute; right: .5rem; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border: 0; background: transparent; cursor: pointer;
  border-radius: 8px; font-size: 1rem; line-height: 1;
}
.pw-eye:hover { background: var(--surface-2); }

.pw-rules { display: grid; gap: .4rem; }
.pw-rules.pw-rules--hidden { display: none; }
.pw-rules div { font-size: .88rem; color: var(--text-mute); display: flex; gap: .5rem; align-items: center; }
.pw-rules div.on { color: var(--ok); }

/* Résultat de mini-jeu */
.game-done {
  text-align: center; padding: 1.8rem; border-radius: var(--radius); margin-top: 1.4rem;
  background: linear-gradient(135deg, var(--brand-soft), var(--surface-2)); animation: stepIn .4s;
}
.game-done .em { font-size: 2.6rem; display: block; }
.game-done b { display: block; font-size: 1.15rem; margin: .4rem 0 .2rem; }
.game-done p { margin: 0; font-size: .9rem; color: var(--text-soft); }
/* Reprendre l'exercice, offert là où l'on lit son score. Le bouton reste en
   retrait : il propose, il n'appelle pas — la suite du chapitre se décide plus
   bas, dans la barre de navigation. */
.game-done__rejouer { margin-top: 1.1rem; font-size: .86rem; padding: .5rem 1rem; }

/* ─────────────── Questionnaire final ─────────────── */
.quiz-shell { max-width: 800px; margin: 0 auto; }
.quiz-top { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.4rem; }
.quiz-top .prog { flex: 1; height: 8px; border-radius: 99px; background: var(--border); overflow: hidden; }
.quiz-top .prog i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand), var(--teal)); transition: width .4s; }
.quiz-top .cnt { font-size: .85rem; font-weight: 700; color: var(--text-soft); white-space: nowrap; }
.quiz-q { font-size: 1.22rem; font-weight: 600; margin: .3rem 0 1.4rem; }
.quiz-theme { font-size: .74rem; text-transform: uppercase; letter-spacing: .09em; font-weight: 800; color: var(--accent); }
.quiz-multi-hint { font-size: .82rem; color: var(--text-mute); margin-bottom: .8rem; }

.score-hero { text-align: center; padding: 3rem 2rem 2.4rem; }
.big-ring { --p: 0; --col: var(--brand); width: 180px; height: 180px; margin: 0 auto 1.4rem; position: relative; display: grid; place-items: center; }
.big-ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--col) calc(var(--p) * 1%), var(--border) 0);
  mask: radial-gradient(farthest-side, transparent calc(100% - 15px), #000 calc(100% - 14px));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 15px), #000 calc(100% - 14px));
  transition: background 1.2s cubic-bezier(.2,.8,.2,1);
}
.big-ring > div { text-align: center; }
.big-ring b { display: block; font-size: 2.8rem; font-weight: 800; letter-spacing: -.04em; line-height: 1.1; }
.big-ring small { display: block; font-size: .8rem; color: var(--text-mute); }

.theme-bars { display: grid; gap: .8rem; margin-top: 1.4rem; }
.theme-bar { display: flex; align-items: center; gap: 1rem; }
.theme-bar .nm { width: 150px; font-size: .88rem; font-weight: 600; flex: none; }
.theme-bar .tr { flex: 1; height: 10px; border-radius: 99px; background: var(--border); overflow: hidden; }
.theme-bar .tr i { display: block; height: 100%; border-radius: 99px; transition: width .8s; }
.theme-bar .vl { font-size: .82rem; color: var(--text-mute); width: 46px; text-align: right; flex: none; }

.review-item { border: 1px solid var(--border); border-radius: var(--radius); padding: 1.2rem 1.4rem; margin-bottom: .8rem; }
.review-item.ko { border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); background: color-mix(in srgb, var(--danger) 3%, var(--surface)); }
.review-item .qh { display: flex; gap: .7rem; align-items: flex-start; margin-bottom: .7rem; }
.review-item .qh .st { font-size: 1.1rem; }
.review-item .qh b { font-size: .98rem; font-weight: 600; }
.review-item .ans { font-size: .88rem; display: grid; gap: .3rem; margin-bottom: .6rem; }
.review-item .ans .lbl { color: var(--text-mute); }
.review-item .ans .good { color: var(--ok); font-weight: 600; }
.review-item .ans .bad { color: var(--danger); font-weight: 600; }
.review-item .why { font-size: .88rem; color: var(--text-soft); background: var(--surface-2); border-radius: var(--radius-sm); padding: .7rem .9rem; }

/* ═══════════════ Attestation ═══════════════
   Un document, pas un écran : fond blanc, tout centré, et AUCUN cadre — ni
   bordure d'ensemble, ni filet, ni pastille cerclée. Ce qui structure la page
   est l'espacement et la hiérarchie typographique, comme sur un vrai document.
   Les couleurs sont écrites en dur : une attestation imprimée ne suit pas le
   thème de qui la consulte, et doit rester lisible en noir et blanc. */
.cert {
  --or: #4f46e5;                      /* couleur du parcours, RGPD par défaut */
  max-width: 760px; margin: 0 auto;
  background: #fff; color: #101828; text-align: center;
  padding: 3rem 3rem 2.6rem;
  border-radius: 4px; box-shadow: var(--shadow);

  /* Une page, pas un bloc qui s'arrête où le contenu s'arrête. La hauteur
     minimale est celle d'un A4 à cette largeur ; l'espace en trop se répartit
     entre les blocs au lieu de s'accumuler en bas. Une attestation sans module
     et une attestation qui en porte huit occupent ainsi la même page, l'une
     aérée, l'autre dense. */
  min-height: 1074px;                 /* 760 px × √2 */
  display: flex; flex-direction: column; justify-content: space-between;
  gap: 1.4rem;
}
/* Sur un écran étroit, forcer la proportion créerait un vide absurde : le
   document reprend sa hauteur naturelle. « screen » est indispensable — une
   page A4 mesure environ 740 px de large, elle tomberait sinon dans cette
   règle et l'attestation imprimée cesserait de se répartir. */
@media screen and (max-width: 820px) {
  .cert { min-height: 0; display: block; }
  .cert-bloc + .cert-bloc, .cert .meta, .cert-modules, .cert-pied { margin-top: 2rem; }
}
/* Chaque bloc reste solidaire : l'espace se glisse entre eux, jamais dedans. */
.cert-bloc { flex: none; }
.cert--cyber { --or: #0b7285; }

/* ── En-tête : logo et émetteur, centrés ── */
.cert-head { display: flex; flex-direction: column; align-items: center; gap: .5rem; flex: none; }
.cert-logo { width: 52px; height: 52px; object-fit: contain; }
.cert-head__txt { display: flex; flex-direction: column; line-height: 1.35; }
.cert-head__txt b { font-size: 1rem; color: #101828; letter-spacing: .01em; }
.cert-head__txt span { font-size: .78rem; color: #6b7688; }

/* ── Titre ── */
.cert-eyebrow {
  font-size: .74rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: #8a93a6;
}
.cert h2 {
  font-size: 2.5rem; line-height: 1.1; margin: .35rem 0 .4rem;
  letter-spacing: -.01em; color: var(--or);
}
.cert .sub { color: #5a6a85; font-size: .9rem; }
/* Le niveau, sans pastille cerclée : la couleur et la graisse suffisent. */
.cert .cert-niveau {
  display: block; margin-top: 1rem; color: var(--or);
  font-size: .82rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
}

/* ── Le corps de l'attestation ── */
.cert-formule { font-size: .88rem; color: #6b7688; font-style: italic; }
.cert .who {
  font-size: 1.75rem; font-weight: 800; margin: .35rem 0 .2rem; color: #101828;
  letter-spacing: -.01em;
}
.cert-structure { font-size: .92rem; color: #5a6a85; }
.cert-corps { max-width: 46ch; margin: 1.1rem auto 0; font-size: .95rem; line-height: 1.7; color: #3d4759; }

.cert .meta { display: flex; justify-content: center; gap: 3rem; flex-wrap: wrap; flex: none; }
.cert .meta div b { display: block; font-size: 1.25rem; color: var(--or); }
.cert .meta div span { font-size: .74rem; color: #6b7688; text-transform: uppercase; letter-spacing: .06em; }

/* ── Modules complémentaires ──
   Des lignes, pas des encadrés : le point coloré tient lieu de puce. */
.cert-modules { flex: none; }
.cert-modules__titre {
  font-size: .74rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: #8a93a6; margin-bottom: .7rem;
}
.cert-modules ul { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: .28rem; max-width: 40rem; }
.cert-modules li {
  display: flex; align-items: baseline; gap: .45rem; flex-wrap: wrap;
  font-size: .85rem; color: #3d4759; text-align: left;
}
.cert-modules li::before {
  content: ''; flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: var(--or); transform: translateY(-2px);
}
.cert-modules li b { font-weight: 700; }
.cert-modules li .ss { color: #6b7688; flex: 1; min-width: 0; }
.cert-modules li .sc { margin-left: auto; white-space: nowrap; color: var(--or); font-weight: 700; font-size: .79rem; }

/* ── Pied : la référence, centrée ── */
.cert-pied { display: flex; flex-direction: column; align-items: center; gap: .12rem; flex: none; }
.cert-pied .lbl { font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; color: #8a93a6; }
.cert-pied .val.mono {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: .84rem; font-weight: 600; color: #101828; letter-spacing: .04em;
}

/* ═══════════════ Une fiche réflexe à l'écran ═══════════════
   Même contenu que le PDF, mise en page de l'application. La couleur vient de
   la famille, par --ton, comme sur les cartes. */
.fiche-page {
  --ton: var(--brand);
  background: var(--surface); border: 1px solid var(--border);
  border-top: 4px solid var(--ton); border-radius: var(--radius); padding: 1.8rem 2rem 1.6rem;
}
.fiche-page.ton-danger { --ton: var(--danger); }
.fiche-page.ton-teal   { --ton: var(--teal); }

.fp-tete { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: 1.2rem; }
.fp-ico {
  flex: 0 0 auto; width: 3rem; height: 3rem; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; font-size: 1.5rem;
  background: color-mix(in srgb, var(--ton) 11%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ton) 24%, var(--border));
}
.fp-titre { flex: 1; min-width: 0; }
.fp-kicker {
  font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ton); margin-bottom: .2rem;
}
.fp-titre h2 { font-size: 1.5rem; margin: 0; line-height: 1.2; letter-spacing: -.01em; }
.fp-ss { font-size: .92rem; color: var(--text-soft); margin-top: .15rem; }
.fp-pdf { flex: 0 0 auto; white-space: nowrap; }
.fp-resume {
  font-style: italic; color: var(--text-soft); font-size: .95rem; line-height: 1.65;
  padding-bottom: 1.2rem; margin: 0 0 1.4rem; border-bottom: 1px solid var(--border);
}

.fb { margin-bottom: 1.6rem; }
.fb h3 {
  font-size: .74rem; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--text-mute); margin: 0 0 .7rem;
  padding-bottom: .4rem; border-bottom: 1px solid var(--border);
}
.fb p { margin: .15rem 0 0; font-size: .92rem; line-height: 1.65; color: var(--text-soft); }
.fb li { font-size: .93rem; line-height: 1.6; }

/* Le bandeau d'urgence : la première chose lue. */
.fb-urgence {
  display: flex; gap: .8rem; align-items: flex-start; margin-bottom: 1.6rem;
  padding: .9rem 1.1rem; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--danger) 30%, var(--border));
  border-left: 3px solid var(--danger);
}
.fb-urgence__ico {
  flex: none; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--danger);
  color: #fff; font-weight: 800; font-size: .85rem; display: flex; align-items: center; justify-content: center;
}
.fb-urgence p { margin: 0; color: var(--text); font-weight: 500; }

.fb-cles { margin: 0; padding: 0; list-style: none; counter-reset: cle; display: grid; gap: .6rem; }
.fb-cles li {
  counter-increment: cle; display: flex; gap: .7rem; align-items: flex-start;
  padding-bottom: .6rem; border-bottom: 1px dashed var(--border);
}
.fb-cles li:last-child { border-bottom: 0; padding-bottom: 0; }
.fb-cles li::before {
  content: counter(cle); flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: var(--ton); color: #fff; font-size: .76rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}

.fb-puces, .fb-interdits { margin: 0; padding: 0; list-style: none; display: grid; gap: .45rem; }
.fb-puces li, .fb-interdits li { display: flex; gap: .6rem; align-items: flex-start; }
.fb-puces li::before { content: '•'; color: var(--ton); font-weight: 800; flex: none; }
.fb-interdits li::before { content: '✕'; color: var(--danger); font-weight: 700; font-size: .85rem; flex: none; }

.fb-etapes { display: grid; gap: .1rem; }
.fb-etape {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: .7rem 0; border-bottom: 1px solid var(--border);
}
.fb-etape:last-child { border-bottom: 0; }
.fb-etape__quand {
  flex: 0 0 7rem; font-size: .7rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ton); padding-top: .15rem;
}
.fb-etape b { font-size: .95rem; }

.fb-tableau { overflow-x: auto; }
.fb-tableau table { width: 100%; border-collapse: collapse; font-size: .89rem; }
.fb-tableau th {
  text-align: left; font-size: .7rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-mute); padding: 0 .8rem .5rem 0; border-bottom: 1px solid var(--border);
}
.fb-tableau td { padding: .55rem .8rem .55rem 0; border-bottom: 1px solid var(--border); vertical-align: top; color: var(--text-soft); }
.fb-tableau td:first-child { color: var(--text); font-weight: 600; white-space: nowrap; }

.fb-encart {
  --e: var(--info);
  margin: 0 0 1.6rem; padding: .9rem 1.1rem; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--e) 7%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--e) 26%, var(--border));
  border-left: 3px solid var(--e);
}
.fb-encart.ton-danger { --e: var(--danger); }
.fb-encart.ton-warn   { --e: var(--warn); }
.fb-encart.ton-ok     { --e: var(--ok); }
.fb-encart > b { display: block; font-size: .93rem; margin-bottom: .25rem; }

.fb-contacts { display: grid; gap: .4rem; }
.fb-contacts > div {
  display: flex; flex-wrap: wrap; gap: .3rem 1rem; align-items: baseline;
  padding: .55rem .9rem; border-radius: var(--radius-sm); background: var(--surface-2);
}
.fb-contacts .lbl { flex: 0 0 15rem; font-size: .78rem; color: var(--text-mute); }
.fb-contacts a { color: var(--ton); }

.fb-sources { margin: 0; padding: 0; list-style: none; display: grid; gap: .55rem; }
.fb-sources b { display: block; font-size: .9rem; }
.fb-url { font-size: .84rem; color: var(--ton); word-break: break-all; }

.fp-pied {
  margin: 1.8rem 0 0; padding-top: 1rem; border-top: 1px solid var(--border);
  font-size: .76rem; color: var(--text-mute); text-align: center;
}

/* Les autres fiches de la même famille, pour ne pas repasser par la liste. */
.fp-suite { margin-top: 1.8rem; }
.fp-suite > span {
  display: block; font-size: .74rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-mute); margin-bottom: .7rem;
}
.fp-liens { display: flex; flex-wrap: wrap; gap: .5rem; }
.fp-lien {
  --ton: var(--brand);
  display: inline-flex; align-items: center; gap: .45rem; text-decoration: none;
  padding: .45rem .85rem; border-radius: 999px; font-size: .85rem; color: var(--text);
  background: var(--surface); border: 1px solid var(--border);
  transition: border-color .16s, background .16s;
}
.fp-lien.ton-danger { --ton: var(--danger); }
.fp-lien.ton-teal   { --ton: var(--teal); }
.fp-lien:hover { border-color: var(--ton); background: color-mix(in srgb, var(--ton) 8%, var(--surface)); }

@media (max-width: 700px) {
  .fiche-page { padding: 1.3rem 1.1rem; }
  /* Le bouton PDF passe sous le titre : coincé à côté, il réduisait celui-ci
     à une colonne de trois mots. */
  .fp-tete { flex-wrap: wrap; }
  .fp-titre { flex: 1 1 calc(100% - 4rem); }
  .fp-pdf { order: 3; margin-left: auto; }
  .fp-etape { flex-direction: column; }
  .fb-etape { flex-direction: column; gap: .2rem; }
  .fb-etape__quand { flex: none; }
  .fb-contacts .lbl { flex: 1 0 100%; }
}

/* Les escape games dans la fiche d'un compte : deux lignes, à côté des
   modules, parce que c'est là qu'on les cherche. */
.fp-jeux { display: grid; gap: .4rem; margin-top: .5rem; }
.fp-jeu {
  display: flex; align-items: center; gap: .7rem;
  padding: .55rem .8rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border); opacity: .6;
}
.fp-jeu.joue { opacity: 1; border-color: color-mix(in srgb, var(--ok) 35%, var(--border)); background: color-mix(in srgb, var(--ok) 7%, var(--surface)); }
.fp-jeu .ico { font-size: 1.1rem; flex: none; }
.fp-jeu > div { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.fp-jeu b { font-size: .88rem; }
.fp-jeu > div span { font-size: .78rem; color: var(--text-mute); }
.fp-jeu__score { flex: none; font-size: .88rem; font-weight: 700; color: var(--ok); }

/* ═══════════════════ Le hub ═══════════════════
   L'accueil ne présente plus que les sections. Le bandeau de reprise passe
   avant les cartes : le geste le plus fréquent ne doit pas coûter un clic de
   plus que les autres. */
.hub-tete { margin-bottom: 1.4rem; }
.hub-tete h1 { font-size: 1.7rem; margin: 0 0 .2rem; letter-spacing: -.02em; }
.hub-tete p { margin: 0; color: var(--text-soft); font-size: .95rem; }

.hub-reprise {
  display: flex; align-items: center; gap: 1rem; text-decoration: none;
  padding: 1rem 1.2rem; margin-bottom: 1.6rem; border-radius: var(--radius);
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--brand) 12%, var(--surface)),
    color-mix(in srgb, var(--brand2) 10%, var(--surface)));
  border: 1px solid color-mix(in srgb, var(--brand) 32%, var(--border));
  transition: border-color .16s, transform .16s, box-shadow .16s;
}
.hub-reprise:hover { border-color: var(--brand); transform: translateY(-1px); box-shadow: var(--shadow); }
.hub-reprise__ico {
  flex: none; width: 2.4rem; height: 2.4rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 1rem;
  background: var(--brand); color: #fff;
}
.hub-reprise__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hub-reprise__txt b { font-size: 1rem; color: var(--text); }
.hub-reprise__txt span {
  font-size: .86rem; color: var(--text-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hub-reprise__fleche { flex: none; color: var(--brand); font-size: 1.2rem; }

/* Trois colonnes, donc deux rangées de trois : le nombre de colonnes est fixé
   plutôt que déduit d'une largeur minimale. Avec « auto-fill », six cartes se
   répartissaient en quatre puis deux — une grille bancale, alors que 3 × 2 se
   lit d'un coup d'œil. */
/* ── L'accueil, une fois connecté ───────────────────────────────────────────
   Justine en haut, centrée, puis deux portes côte à côte. L'accueil ne pose
   plus qu'une question, il ne mérite donc qu'une colonne. */
.hub-scene {
  position: relative; display: flex; flex-direction: column; align-items: center;
  /* Marges resserrées : cette scène occupe déjà toute la fenêtre, l'air qu'elle
     laissait au-dessus et au-dessous ne servait qu'à créer une barre de
     défilement sur un portable. */
  gap: clamp(.9rem, 2.4vh, 1.5rem); margin-bottom: .8rem;
  /* Pleine largeur de fenêtre : `50% - 50vw` fait sortir le panneau de la
     colonne de lecture centrée sans toucher à sa structure. */
  width: 100vw; margin-left: calc(50% - 50vw);
  padding: clamp(.7rem, 2vh, 1.4rem) clamp(1.2rem, 4vw, 3.5rem);
  background: var(--bg);
}
.hub-medaillon { flex: none; width: clamp(6rem, 14vh, 10rem); }
.hub-tete { margin-bottom: 0; text-align: center; }

/* Les deux portes. Elles ne ressemblent volontairement à aucune autre carte de
   l'application : ce sont les seules qui portent une image, et leur taille dit
   qu'il n'y a rien d'autre à décider sur cette page. */
.hub-portes {
  display: grid; grid-template-columns: repeat(var(--n, 2), 1fr);
  gap: clamp(1rem, 2.5vw, 2rem);
  width: min(100%, 1040px); align-items: stretch;
}
/* Le nombre de cartes est porté par une classe, et la feuille de style en tire
   la largeur du bloc et la taille du titre. Trois cartes sur une ligne sont
   forcément plus étroites : on leur donne un peu plus de place au total et un
   titre d'un cran plus petit, sinon il se casse en quatre lignes. */
.hub-portes--3 { --n: 3; width: min(100%, 1220px); }
.hub-portes--3 .hub-porte b { font-size: clamp(1rem, 1.5vw, 1.28rem); }
.hub-portes--3 .hub-porte { aspect-ratio: 4 / 3; }
.hub-portes--1 { --n: 1; width: min(100%, 620px); }
/* Quatre portes : deux rangées de deux. En file indienne elles seraient trop
   basses pour porter un décor, et sur une seule ligne trop étroites pour leur
   titre. */
.hub-portes--4 { --n: 2; width: min(100%, 1100px); }
/* Titre d'une seule ligne, en casse normale : il tient sans se casser, et un
   cran plus bas que les portes du rez-de-chaussée, où le nom de la famille
   est le sujet. Ici c'est une destination, pas une enseigne. */
.hub-portes--4 .hub-porte b { font-size: clamp(1.05rem, 1.7vw, 1.42rem); text-wrap: balance; }

/* La vignette d'une destination : même objet que la porte, en format bas.
   Deux fois et demie plus large que haute — assez pour qu'une photo se lise,
   pas au point de prendre la page comme le ferait une porte. */
/* Deux fois plus large que haute. C'était 5/2 : les décors, tous cadrés en
   16/10, y perdaient plus du tiers de leur hauteur — on ne voyait plus la
   scène, seulement une bande prise en son milieu. Deux tiers de moins de
   rognage suffisent à leur rendre leur décor. */
.hub-porte--compacte {
  aspect-ratio: 2 / 1; padding: clamp(.9rem, 1.8vw, 1.3rem);
  border-radius: var(--radius);
}
.hub-porte--compacte b { font-size: clamp(.98rem, 1.4vw, 1.18rem); }
/* Le compte sous le titre — « 62 notions », « 18 fiches » : c'est ce qui reste
   des trois lignes de l'ancienne carte, et la seule qui apprenait quelque
   chose. Plus discret que sur une grande porte, où il annonce un verrou. */
.hub-porte--compacte .hub-porte__etat { margin-top: .2rem; font-size: .78rem; }
.hub-porte--compacte .hub-porte__fleche {
  right: clamp(.9rem, 1.8vw, 1.3rem); bottom: clamp(.9rem, 1.8vw, 1.3rem);
  width: 1.75rem; height: 1.75rem; font-size: .92rem;
}
/* Sans décor, la vignette n'est pas un rectangle vide : elle prend un dégradé
   à la teinte de sa famille. C'est aussi ce qui s'affiche le temps que l'image
   arrive. */
.hub-porte--compacte {
  background:
    radial-gradient(120% 140% at 100% 0%,
      color-mix(in srgb, var(--ton) 42%, transparent) 0%,
      transparent 62%),
    #0c1020;
}
/* ── L'étiquette « Sur demande » ─────────────────────────────────────────────
   Une bande en diagonale, ancrée dans l'angle supérieur droit de la carte, que
   l'« overflow: hidden » de .hub-porte coupe net sur les deux bords : c'est ce
   qui lui donne son air d'étiquette collée en travers, et non de badge posé
   dessus.

   Le carré porteur est plus grand que la bande, et sa diagonale (45°) donne
   l'angle : la bande se contente d'être assez large pour dépasser des deux
   côtés du carré avant d'être rognée. Le rouge est celui des alertes de la
   charte, en dégradé pour que le texte blanc reste lisible sur toute la bande,
   y compris sur un décor clair.

   Elle est purement décorative — « aria-hidden » — parce que l'état écrit sous
   le titre dit déjà la même chose à qui n'a pas l'image. */
.hub-porte__ruban {
  position: absolute; z-index: 3; top: 0; right: 0;
  width: 9.5rem; height: 9.5rem; overflow: hidden; pointer-events: none;
}
.hub-porte__ruban span {
  position: absolute; display: block; width: 14rem;
  top: 2.1rem; right: -3.3rem; transform: rotate(45deg);
  padding: .34rem 0; text-align: center;
  font-family: inherit; font-weight: 800; font-size: .78rem;
  letter-spacing: .16em; text-transform: uppercase; color: #fff;
  box-shadow: 0 .35rem .9rem rgba(0, 0, 0, .38);
  /* Les deux liserés donnent le pli du papier ; sans eux, la bande est un
     simple rectangle posé de travers. */
  border-top: 1px solid rgba(255, 255, 255, .38);
  border-bottom: 1px solid rgba(0, 0, 0, .18);
}
/* Ardoise : la porte est ouverte, mais elle ne donne que sur les premiers
   chapitres. Ni rouge — rien n'est refusé, rien n'est à demander — ni orange,
   qui dit « pas encore » sur une carte fermée. Une teinte hors charte, exprès :
   indigo, turquoise, ambre et violet appartiennent chacune à une famille, et un
   ruban à l'une d'elles se confondrait avec la carte qui la porte.
   Contraste du blanc sur #334155 : 9,9:1. */
.hub-porte__ruban--essai span {
  background: linear-gradient(90deg, #1e293b 0%, #334155 50%, #1e293b 100%);
}

/* Rouge : il faut s'adresser à quelqu'un. */
.hub-porte__ruban--demande span {
  background: linear-gradient(90deg, #be123c 0%, #e11d48 50%, #be123c 100%);
}
/* Ardoise : il suffit de travailler. Pas une teinte de la charte, et c'est
   voulu — indigo, turquoise, ambre et violet appartiennent chacune à une
   famille, et un ruban turquoise sur la carte du Cyber Escape Game se serait
   confondu avec la teinte de sa propre carte. Le neutre ne dispute rien à
   personne, et se distingue du rouge par son calme même : l'un appelle une
   démarche, l'autre dit « pas encore, et cela ne dépend que de vous ».
   Contraste du blanc sur #334155 : 9,9:1, très au-dessus du seuil. */
/* L'orange de Justine — la variable qui cercle son médaillon, reprise telle
   quelle. Elle porte déjà, partout dans l'application, celle qui accompagne et
   explique : un ruban à sa couleur dit « ce n'est pas refusé, c'est devant
   vous », là où le rouge dit « adressez-vous à quelqu'un ».

   Le TEXTE y est foncé, et c'est ce qui rend la couleur utilisable : en blanc,
   cet orange ne donne que 2,85:1 — illisible à 0,72 rem. En brun très sombre,
   il monte à 5,9:1 au centre et 4,8:1 sur les bords assombris. L'inversion
   n'est pas un pis-aller : elle distingue les deux rubans à distance, avant
   même qu'on ait lu ce qu'ils disent. */
.hub-porte__ruban--attente span {
  background: linear-gradient(90deg, #d06f14 0%, var(--justine) 50%, #d06f14 100%);
  color: #fff;
  /* Le pli du papier : sur une bande claire, c'est l'ombre du bas qui le donne,
     l'inverse exact de la bande sombre qu'elle remplace. */
  border-top-color: rgba(255, 255, 255, .5);
  border-bottom-color: rgba(0, 0, 0, .22);
}
/* Sur une carte compacte, le ruban prend la moitié de la vignette : il se
   réduit dans les mêmes proportions. */
.hub-porte--compacte .hub-porte__ruban { width: 6.6rem; height: 6.6rem; }
.hub-porte--compacte .hub-porte__ruban span {
  width: 10rem; top: 1.4rem; right: -2.6rem; font-size: .62rem; letter-spacing: .12em;
}
.hub-porte {
  position: relative; overflow: hidden; text-decoration: none;
  display: flex; flex-direction: column; justify-content: flex-end;
  /* 16/9 plutôt que 16/10 : quatre portes en deux rangées, un médaillon et un
     titre ne tiennent pas dans la hauteur d'un portable autrement. */
  aspect-ratio: 16 / 9; padding: clamp(1rem, 2.4vw, 1.8rem);
  border-radius: var(--radius-lg); border: 1px solid var(--border);
  background: #0c1020;
  color: #fff;
  /* AUCUNE transition, et aucun déplacement au survol. Le zoom de l'image, puis
     le soulèvement de la carte, ont tous deux saccadé : agrandir ou déplacer un
     élément à coins arrondis contenant une image oblige à recalculer le
     découpage image par image, et ce masque ne se délègue pas au processeur
     graphique. Le survol change donc d'état d'un coup — ce qui ne peut pas
     saccader, par construction. */
}
/* Le décor est calé à DROITE : ces images ont été composées avec un tiers
   gauche vide, et c'est ce vide qui accueille le titre. */
.hub-porte__decor {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: right center;
  /* Ces décors ont été dessinés pour être vus SOUS un panneau de texte, donc
     très sombres. Ici ils sont le sujet : on les remonte. */
  filter: brightness(1.55) saturate(1.12);
}
/* Le voile monte du bas : le titre reste lisible quel que soit ce qu'il
   recouvre, et le haut de la scène garde sa lumière. */
/* Le voile ne couvre plus que le pied de la carte, juste ce qu'il faut pour
   porter le titre. Au-dessus, l'image est laissée intacte : c'est elle qu'on
   vient regarder. */
.hub-porte__voile {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(12, 16, 32, .9) 0%, rgba(12, 16, 32, .55) 20%, rgba(12, 16, 32, 0) 46%);
}
.hub-porte b, .hub-porte__etat { position: relative; }
/* Le chevron, dans le coin opposé au titre. Un disque à la teinte de la
   famille, effacé au repos.
   SEULE l'opacité est animée : c'est la propriété que le navigateur compose
   sans jamais rien redessiner. Un déplacement, même de quelques pixels, ferait
   recalculer le masque à coins arrondis de la carte à chaque image — c'est
   exactement ce qui avait fait saccader le zoom du décor. */
.hub-porte__fleche {
  position: absolute; right: clamp(1rem, 2.4vw, 1.8rem); bottom: clamp(1rem, 2.4vw, 1.8rem);
  width: 2.1rem; height: 2.1rem; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 1.05rem; font-weight: 700; line-height: 1;
  color: #fff; background: color-mix(in srgb, var(--ton) 88%, transparent);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
  opacity: 0; transition: opacity .16s ease;
}
.hub-porte:hover .hub-porte__fleche,
.hub-porte:focus-visible .hub-porte__fleche { opacity: 1; }
/* Au clavier, le survol n'existe pas : le chevron accompagne aussi le focus,
   et la carte garde son halo. */
.hub-porte:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--ton) 75%, transparent);
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--ton) 60%, transparent),
    0 0 18px color-mix(in srgb, var(--ton) 38%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  .hub-porte__fleche { transition: none; }
}
.hub-porte.ton-brand  { --ton: var(--brand); }
.hub-porte.ton-brand2 { --ton: var(--brand-2); }
.hub-porte.ton-teal   { --ton: var(--teal); }
.hub-porte.ton-warn   { --ton: var(--warn); }
/* Le halo est porté par la carte elle-même, en `box-shadow` : un pseudo-élément
   débordant aurait été rogné par l'`overflow: hidden` qui contient l'image.
   Trois couches — un liseré net, une lueur large à la teinte de la famille, et
   une ombre portée qui décolle la carte du fond. */
.hub-porte:not(.verrouille):hover {
  border-color: color-mix(in srgb, var(--ton) 75%, transparent);
  /* Un liseré net et une lueur modérée. Un flou large coûte un redessin de
     toute la zone à chaque entrée et sortie du pointeur. */
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--ton) 60%, transparent),
    0 0 18px color-mix(in srgb, var(--ton) 38%, transparent);
}
/* L'image accompagne le geste : un souffle d'agrandissement et un peu plus de
   lumière. Sans lui, seul le cadre réagit et le survol paraît plaqué. */
/* PAS de zoom sur l'image au survol. Trois tentatives — fichier taillé à la
   bonne dimension, promotion sur le processeur graphique, retrait du
   `will-change` — n'ont pas suffi : agrandir une image dans un cadre à coins
   arrondis oblige le navigateur à recalculer le découpage à chaque image, et ce
   masque-là ne se délègue pas. L'effet ne valait pas la saccade.
   Reste l'opacité du voile, la seule propriété visuelle qui se compose sans
   jamais rien redessiner : l'image paraît s'éclairer, pour rien. */
.hub-porte__decor { transform: translateZ(0); }
/* Les décors de l'administration ne réservent pas de vide à gauche — leur
   titre se pose sur le voile du bas, comme partout, et non sur un tiers noir.
   Ils sont donc cadrés au centre, et n'ont pas besoin du même coup de fouet en
   luminosité que les décors du rez-de-chaussée, dessinés pour être vus sous un
   panneau de texte. */
/* Ces décors-là sont pris en plein jour : aucune correction à l'affichage,
   un coup de fouet les brûlerait. Le survol passe donc par le voile, qui
   s'allège — sur une image claire, c'est lui qui fait l'effet, pas la
   luminosité, qui n'a plus de marge. */
/* La classe est DOUBLÉE à dessein. À spécificité égale, c'est la dernière
   règle écrite qui l'emporte — et `.hub-porte:hover .hub-porte__decor`, plus
   bas dans cette feuille, pèse exactement autant : c'est elle qui gagnait, et
   le décor reprenait son brightness(1.78) au survol. */
.hub-porte__decor.hub-porte__decor--centre { object-position: center; filter: none; }
/* Ni surcroît de lumière, ni voile qui s'ouvre, au survol : sur une image déjà
   en plein soleil, l'éclaircissement passait pour un défaut d'affichage. Le
   halo à la teinte de la famille suffit à dire que la carte répond. */
.hub-porte:not(.verrouille):hover .hub-porte__decor.hub-porte__decor--centre { filter: none; }
.hub-porte:not(.verrouille):hover .hub-porte__decor--centre + .hub-porte__voile { opacity: 1; }
.hub-porte:not(.verrouille):hover .hub-porte__voile { opacity: .62; }
/* Et l'image gagne un cran de lumière. Sans transition, comme le reste : un
   changement instantané ne peut pas saccader, et sous le déplacement du
   pointeur on ne perçoit de toute façon pas la différence entre les deux. */
.hub-porte:not(.verrouille):hover .hub-porte__decor { filter: brightness(1.78) saturate(1.08); }


/* Une porte fermée ne réagit à rien : ni au survol, ni au doigt. Elle
   s'éclaircissait légèrement au survol, et sur un téléphone cet état reste
   accroché après l'appui — la carte semblait donc répondre alors qu'elle ne
   mène nulle part. Le curseur reste neutre : « interdit » désigne une erreur,
   or il n'y en a pas, la porte n'est simplement pas encore ouverte. */
.hub-porte.verrouille { opacity: .5; cursor: default; }
.hub-porte.verrouille .hub-porte__decor { filter: brightness(1.55) grayscale(.55); }
/* L'étiquette « Sur demande » échappe à l'atténuation de la carte.
   L'opacité d'un parent ne se rattrape pas sur un enfant : on ne peut pas la
   remonter à 1 sur le ruban. On compense donc EN SENS INVERSE — la carte
   fermée qui porte un ruban est moins éteinte que les autres, et son décor
   l'est davantage, pour que le contraste reste le même sous le titre. C'est ce
   qui rend l'étiquette franche là où elle doit l'être : sur une porte fermée,
   la seule qui appelle une démarche. */
.hub-porte.verrouille:has(.hub-porte__ruban) { opacity: .82; }
.hub-porte.verrouille:has(.hub-porte__ruban) .hub-porte__decor { filter: brightness(1.3) grayscale(.72); }
.hub-porte.verrouille:has(.hub-porte__ruban) .hub-porte__voile { opacity: .8; }
/* Décor déjà éclairé — celui du Cyber Escape Game fermé, redessiné pour que le
   ruban ne couvre plus personne. Les décors de nuit reçoivent un coup de fouet
   en luminosité (× 1,55, et × 1,3 avec un grisé quand la carte est fermée) :
   appliqué à une image prise en plein éclairage, il la brûlerait. Il ne reste
   donc que le grisé, plus léger, qui suffit à dire « fermé » sans délaver.
   Écrite APRÈS les règles de .verrouille : à spécificité égale, c'est la
   dernière qui gagne. */
.hub-porte__decor.hub-porte__decor--clair { object-position: center; filter: none; }
.hub-porte.verrouille .hub-porte__decor.hub-porte__decor--clair,
.hub-porte.verrouille:has(.hub-porte__ruban) .hub-porte__decor.hub-porte__decor--clair {
  filter: grayscale(.32) brightness(.96);
}
.hub-porte:not(.verrouille):hover .hub-porte__decor.hub-porte__decor--clair { filter: none; }

.hub-porte.verrouille .hub-porte__etat { color: rgba(255, 255, 255, .92); }
.hub-porte b {
  font-size: clamp(1.15rem, 2.1vw, 1.7rem); line-height: 1.2; letter-spacing: -.01em;
  text-shadow: 0 2px 12px rgba(0, 0, 0, .5);
}
.hub-porte__etat {
  position: relative; margin-top: .45rem; font-size: .85rem; font-weight: 600;
  color: rgba(255, 255, 255, .8);
}

/* Un bloc de deux colonnes, réutilisé par la page de choix du niveau. */
.hub-bloc { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; }

@media (max-width: 980px) {
  .hub-portes, .hub-portes--3 { --n: 1; }
  .hub-portes--3 .hub-porte { aspect-ratio: 16 / 9; }
  .hub-porte { aspect-ratio: 16 / 9; }
  .hub-bloc { grid-template-columns: 1fr; }
}

.hub-grille {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 1rem; margin-bottom: 2.4rem;
}
@media (max-width: 940px) { .hub-grille { grid-template-columns: repeat(2, 1fr); } }
.hub-carte {
  --ton: var(--brand);
  display: flex; flex-direction: column; gap: .25rem; text-decoration: none;
  padding: 1.2rem 1.2rem 1.1rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border);
  border-top: 3px solid color-mix(in srgb, var(--ton) 60%, var(--border));
  transition: border-color .18s, box-shadow .18s, transform .18s;
}
.hub-carte.ton-brand  { --ton: var(--brand); }
.hub-carte.ton-brand2 { --ton: var(--brand-2); }
.hub-carte.ton-teal   { --ton: var(--teal); }
.hub-carte.ton-danger { --ton: var(--danger); }
.hub-carte.ton-warn   { --ton: var(--warn); }
.hub-carte.ton-info   { --ton: var(--info); }
.hub-carte:not(.verrouille):hover {
  border-color: color-mix(in srgb, var(--ton) 50%, var(--border));
  border-top-color: var(--ton);
  box-shadow: var(--shadow); transform: translateY(-2px);
}

.hub-carte.verrouille { opacity: .58; }
.hub-carte__haut { display: flex; align-items: center; justify-content: space-between; margin-bottom: .5rem; }
.hub-carte__ico {
  width: 2.8rem; height: 2.8rem; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; font-size: 1.4rem;
  background: color-mix(in srgb, var(--ton) 11%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ton) 24%, var(--border));
}
.hub-carte__ok { font-size: 1.1rem; }
.hub-carte > b { font-size: 1.05rem; color: var(--text); line-height: 1.25; }
.hub-carte__ss { font-size: .84rem; color: var(--text-soft); }
.hub-carte__jauge {
  height: 5px; border-radius: 999px; margin-top: .7rem;
  background: color-mix(in srgb, var(--ton) 16%, var(--surface-2)); overflow: hidden;
}
.hub-carte__jauge i { display: block; height: 100%; background: var(--ton); transition: width .4s; }
.hub-carte__etat { margin-top: .5rem; font-size: .78rem; font-weight: 600; color: var(--ton); }

/* Sur téléphone, six cartes en pleine largeur font beaucoup à faire défiler :
   on garde deux colonnes et on compacte les cartes (sous-titre masqué, icône
   et marges réduites) pour ramener les six cartes à trois rangées courtes. */
@media (max-width: 560px) {
  .hub-grille { gap: .6rem; margin-bottom: 1.6rem; }
  .hub-carte { padding: .75rem .8rem; gap: .15rem; }
  .hub-carte__haut { margin-bottom: .35rem; }
  .hub-carte__ico { width: 2rem; height: 2rem; font-size: 1rem; border-radius: 8px; }
  .hub-carte__ok { font-size: .95rem; }
  .hub-carte > b { font-size: .9rem; line-height: 1.2; }
  .hub-carte__ss { display: none; }
  .hub-carte__jauge { margin-top: .45rem; }
  .hub-carte__etat { margin-top: .35rem; font-size: .72rem; }
  .hub-tete { margin-bottom: 1rem; }
  .hub-tete h1 { font-size: 1.4rem; }
  .hub-tete p { font-size: .88rem; }
}

/* Fil d'Ariane des pages de section : le retour au hub, et où l'on est. */

/* Retour à la vue quittée. Poussé à droite : le fil d'Ariane dit la place dans
   l'arborescence, ce bouton dit d'où l'on vient — deux gestes distincts. */
.retour-prec {
  margin-left: auto; padding: .35rem .8rem; border-radius: 999px; cursor: pointer;
  font-family: inherit; font-size: .82rem; font-weight: 600; color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--brand) 30%, var(--border));
  white-space: nowrap; max-width: 22rem; overflow: hidden; text-overflow: ellipsis;
  transition: background .16s, border-color .16s;
}
.retour-prec:hover { background: color-mix(in srgb, var(--brand) 15%, var(--surface)); border-color: var(--brand); }
.savoir-onglets .retour-prec { align-self: center; }
@media (max-width: 560px) { .retour-prec { max-width: 11rem; } }

/* ═══════════════ Ressources ═══════════════
   Glossaire et ressources partagent une carte au hub : une barre d'onglets
   les sépare ici, plutôt que deux entrées concurrentes dans la navigation. */
.savoir-onglets { display: flex; gap: .4rem; margin-bottom: 1.4rem; }
.savoir-onglet {
  padding: .5rem 1rem; border-radius: 999px; text-decoration: none; cursor: pointer;
  font-size: .88rem; font-weight: 600; color: var(--text-soft);
  background: var(--surface); border: 1px solid var(--border);
  transition: border-color .16s, background .16s, color .16s;
}
.savoir-onglet:hover { border-color: var(--brand); color: var(--text); }
.savoir-onglet.on {
  background: color-mix(in srgb, var(--brand) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--brand) 55%, var(--border));
  color: var(--brand);
}

.res-barre { margin-bottom: 1.6rem; }
.res-recherche {
  width: 100%; padding: .85em 1em; border-radius: 999px; font-size: .96rem;
  background: var(--surface); border: 1px solid var(--border); color: var(--text);
}
.res-recherche:focus { outline: none; border-color: var(--brand); }

.res-liste { display: grid; gap: .7rem; }
/* Sections rétractables. <details> porte l'état ; on n'habille que l'apparence.
   Fermées par défaut, les dix thèmes tiennent sur un écran : la page devient
   un sommaire d'où l'on ouvre ce dont on a besoin. */
.res-groupe {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden;
}
.res-groupe[open] { border-color: color-mix(in srgb, var(--brand) 32%, var(--border)); }
.res-groupe__tete {
  display: flex; align-items: flex-start; gap: .8rem; cursor: pointer;
  padding: .9rem 1.1rem; list-style: none; user-select: none;
}
.res-groupe__tete::-webkit-details-marker { display: none; }
.res-groupe__tete:hover { background: var(--surface-2); }
.res-groupe[open] .res-groupe__tete { border-bottom: 1px solid var(--border); }
.res-chevron {
  flex: none; color: var(--text-mute); font-size: 1.1rem; line-height: 1;
  transition: transform .2s; align-self: center;
}
.res-groupe[open] .res-chevron { transform: rotate(180deg); }
.res-groupe > ul { padding: .9rem 1.1rem 1.1rem; }
.res-groupe__tete .ico { font-size: 1.3rem; flex: none; }
.res-groupe__tete > div { flex: 1; min-width: 0; }
.res-groupe__tete h3 { margin: 0; font-size: 1.05rem; }
.res-groupe__tete p { margin: .1rem 0 0; font-size: .85rem; color: var(--text-soft); }
.res-compte {
  flex: none; padding: .15rem .55rem; border-radius: 999px;
  font-size: .78rem; font-weight: 700; color: var(--text-mute);
  background: var(--surface-2); border: 1px solid var(--border);
}

.res-items { list-style: none; margin: 0; display: grid; gap: .7rem; }
.res-items li {
  padding: .7rem .9rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: border-color .16s;
}
.res-items li:hover { border-color: color-mix(in srgb, var(--brand) 40%, var(--border)); }
.res-lien { display: block; text-decoration: none; }
.res-lien b { display: block; font-size: .93rem; color: var(--text); line-height: 1.35; }
.res-url { display: block; font-size: .78rem; color: var(--info); word-break: break-all; margin-top: .1rem; }

.res-meta { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .5rem; align-items: center; }
.res-emetteur {
  font-size: .7rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-mute); padding-right: .3rem;
}
/* D'où la référence est citée : cliquable, elle y ramène. */
.res-cite {
  font-size: .74rem; text-decoration: none; color: var(--text-soft);
  padding: .12rem .5rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.res-cite:hover { border-color: var(--brand); color: var(--brand); }
.res-vide { color: var(--text-mute); text-align: center; padding: 2rem 0; }
/* En portrait mobile : sommaire sobre. En-tête de thème sur une ligne (sans
   intro) ; chaque référence réduite à son libellé + chevron (URL et sources
   masquées), le lien ouvrant directement la source. */
@media (max-width: 560px) and (orientation: portrait) {
  .res-groupe__tete p { display: none; }
  .res-groupe__tete h3 { font-size: .95rem; }
  .res-items { gap: .4rem; }
  .res-items li { padding: .62rem 1.9rem .62rem .85rem; position: relative; }
  .res-url, .res-meta { display: none; }
  .res-lien b { font-size: .88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .res-items li::after { content: '›'; position: absolute; right: .8rem; top: 50%; transform: translateY(-50%);
    color: var(--text-mute); font-size: 1.2rem; line-height: 1; }
}

/* ─────────────── Glossaire ─────────────── */
.gl-search { position: relative; margin-bottom: 1.4rem; }
.gl-search input { width: 100%; padding: .9em 1em .9em 2.8em; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: .98rem; }
.gl-search input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 14%, transparent); }
.gl-search .ico { position: absolute; left: 1.1em; top: 50%; transform: translateY(-50%); color: var(--text-mute); }
.gl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; }
.gl-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.3rem 1.4rem; cursor: pointer; transition: .2s; }
.gl-card:hover { border-color: color-mix(in srgb, var(--brand) 50%, var(--border)); box-shadow: var(--shadow-sm); }
.gl-card b { display: block; font-size: 1.02rem; margin-bottom: .2rem; }
.gl-card .sub { font-size: .76rem; color: var(--text-mute); margin-bottom: .5rem; }
.gl-card p { margin: 0; font-size: .88rem; color: var(--text-soft); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
/* En portrait mobile : une ligne unique par terme (le libellé seul), sobre.
   Sous-titre et définition masqués ; la définition complète reste accessible
   d'un tap, via la loupe. */
@media (max-width: 560px) and (orientation: portrait) {
  .gl-grid { display: block; }
  .gl-card { display: flex; align-items: center; padding: .72rem 2rem .72rem .9rem;
    margin-bottom: .4rem; position: relative; }
  .gl-card b { margin: 0; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .gl-card .sub, .gl-card p { display: none; }
  .gl-card::after { content: '›'; position: absolute; right: .9rem; top: 50%; transform: translateY(-50%);
    color: var(--text-mute); font-size: 1.3rem; line-height: 1; }
}

/* ─────────────── Admin ─────────────── */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
/* Le défilement reste possible sur fenêtre étroite, mais sa barre n'a pas à
   s'imposer : elle apparaît discrète, et seulement quand elle est nécessaire. */
.table-wrap { scrollbar-color: var(--barre) transparent; }
.table-wrap::-webkit-scrollbar { height: 8px; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }
.table-wrap::-webkit-scrollbar-thumb:hover { background: var(--text-mute); }
table.data { width: 100%; border-collapse: collapse; font-size: .89rem; min-width: 860px; }
table.data th {
  text-align: left; padding: .9rem 1rem; font-size: .74rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-mute); border-bottom: 1px solid var(--border); background: var(--surface-2); white-space: nowrap;
}
table.data td { padding: .85rem .8rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.data th:first-child, table.data td:first-child { padding-left: 1rem; }
table.data th:last-child, table.data td:last-child { padding-right: 1rem; }
table.data tr:last-child td { border-bottom: 0; }
table.data tr:hover td { background: var(--surface-2); }
/* Colonne d'actions : boutons carrés de gabarit strictement identique, sur une
   seule ligne. Icône seule et infobulle : avec des libellés, quatre boutons
   s'empilaient sur quatre lignes et faisaient exploser la hauteur des lignes. */
.cell-actions { white-space: nowrap; width: 1%; }
.row-actions { display: flex; gap: .35rem; justify-content: flex-end; flex-wrap: nowrap; }
.act {
  flex: none; width: 32px; height: 32px; padding: 0; border-radius: 9px;
  border: 1.5px solid var(--border); background: transparent;
  cursor: pointer; font-size: .92rem; line-height: 1;
  display: grid; place-items: center; font-family: inherit; color: var(--text-soft);
  transition: background .16s, border-color .16s, color .16s;
  /* La loupe de la fiche d'un compte est devenue un <a> — Ctrl+clic ouvre la
     fiche dans un onglet. Le soulignement du lien n'a pas sa place ici. */
  text-decoration: none;
}
.act { --ton: var(--brand); }
.act.ok-ghost { --ton: var(--ok); }
.act.warn-ghost { --ton: var(--warn); }
.act.danger-ghost { --ton: var(--danger); }
/* Bascule fermée : la commande reste lisible mais s'efface, pour qu'un coup
   d'œil au tableau distingue les comptes concernés des autres. */
.act.act-off { opacity: .38; }
.act.act-off:hover { opacity: 1; }
.act:hover {
  background: color-mix(in srgb, var(--ton) 10%, transparent);
  border-color: color-mix(in srgb, var(--ton) 62%, var(--border));
}
.act:active { background: color-mix(in srgb, var(--ton) 18%, transparent); }
.act:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.mini-bar { width: 84px; height: 7px; border-radius: 99px; background: var(--border); overflow: hidden; display: inline-block; vertical-align: middle; margin-right: .5rem; }
.mini-bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand), var(--teal)); }


/* ── Le tableau de bord d'administration ────────────────────────────────── */

/* ── Le suivi des participants ───────────────────────────────────────────
   La population à gauche, en grand : c'est elle qui donne son sens à tout le
   reste. Puis une part par parcours, avec sa fraction et sa barre — « 1 sur 5 »
   se lit, « 1 » ne dit rien. Chaque part porte la teinte de sa famille, ce qui
   permet de la retrouver sans lire son intitulé. */
.suivi {
  display: flex; flex-wrap: wrap; align-items: stretch; gap: 1.4rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 1rem 1.3rem; margin-bottom: 1.6rem;
}
.suivi__qui {
  display: flex; flex-direction: column; justify-content: center;
  padding-right: 1.4rem; border-right: 1px solid var(--border); min-width: 9rem;
}
.suivi__qui b { font-size: 2.3rem; font-weight: 800; line-height: 1; letter-spacing: -.03em; margin-top: .1rem; }
.suivi__invit { margin-top: .5rem; font-size: .76rem; font-weight: 600; color: var(--text-mute); }
.suivi__invit.alerte { color: var(--warn); }
.suivi__parts {
  flex: 1 1 auto; display: grid; gap: .5rem 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}
.suivi-part { --ton: var(--brand); display: flex; flex-direction: column; gap: .25rem; justify-content: center; }
.suivi-part.ton-brand2 { --ton: var(--brand-2); }
.suivi-part.ton-teal   { --ton: var(--teal); }
.suivi-part.ton-warn   { --ton: var(--warn); }
.suivi-part.ton-ok     { --ton: var(--ok); }
/* Un compteur sans part : ni teinte de famille — rien à repérer parmi les
   parcours —, ni barre. Sa place est réservée quand même, sans quoi le bloc
   remontait et son intitulé ne s'alignait plus sur les autres. */
.suivi-part.ton-neutre { --ton: var(--text); }
.suivi-part.ton-neutre .suivi-part__n b { font-weight: 700; }
/* Le texte qui remplace la barre d'un compte seul : même rôle que le libellé
   sous le nombre de participants, même discrétion. */
.suivi-part__sous { display: block; font-size: .72rem; color: var(--text-mute); line-height: 1.2; }
.suivi-part__t {
  font-size: .7rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-mute);
}
/* Le chiffre atteint domine, le total reste en retrait : c'est la part qu'on
   lit, pas la division. */
.suivi-part__n { line-height: 1; }
.suivi-part__n b { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em; color: var(--ton); }
.suivi-part__n span { font-size: .9rem; color: var(--text-mute); }
.suivi-part__bar {
  display: block; height: 4px; border-radius: 99px;
  background: color-mix(in srgb, var(--ton) 16%, transparent); overflow: hidden;
}
.suivi-part__bar i { display: block; height: 100%; background: var(--ton); border-radius: 99px; }
@media (max-width: 700px) {
  .suivi__qui { border-right: 0; border-bottom: 1px solid var(--border); padding: 0 0 .9rem; width: 100%; }
}

/* ── Le bandeau de chiffres ─────────────────────────────────────────────────
   Une seule ligne, des filets pour séparer, pas de cartes. Les grandes cartes
   d'indicateurs occupaient tout l'écran avant qu'on atteigne les portes : ce
   sont des repères, pas la raison d'être de la page. */
.stat-bande {
  display: flex; flex-wrap: wrap; align-items: stretch;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden; margin-bottom: 1.6rem;
}
.stat {
  flex: 1 1 auto; min-width: 8.5rem; padding: .8rem 1.1rem;
  display: flex; flex-direction: column; gap: .1rem;
  text-decoration: none; color: inherit;
  /* Le filet vient de la gauche pour ne pas laisser de trait pendant en bout
     de ligne quand le bandeau se replie sur un écran étroit. */
  border-left: 1px solid var(--border);
}
.stat:first-child { border-left: 0; }
.stat b { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.stat span { font-size: .78rem; color: var(--text-mute); }
/* Le seul chiffre qui appelle un geste porte la teinte de l'alerte, et se
   clique. Les autres ne sont que des repères. */
.stat--alerte b { color: var(--warn); }
a.stat:hover { background: var(--surface-2); }
a.stat:hover span { color: var(--text); }


.admin-page {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.5rem 1.6rem 1.7rem;
}
/* Le contenu vient des anciennes fenêtres : il gardait sa taille de texte du
   corps de modale, qui ne s'applique plus ici. */
.admin-page p { font-size: .98rem; }
.admin-page ul { padding-left: 1.1rem; margin: .8rem 0 0; }
.admin-page li { font-size: .93rem; color: var(--text-soft); margin-bottom: .55rem; }
/* ── Le tableau de bord : portes et pied de page ────────────────────────── */
/* Les portes de l'administration reprennent celles de l'accueil — même décor,
   même halo à la teinte de la famille. La grille est simplement centrée dans
   la colonne, sans le panneau pleine largeur du rez-de-chaussée : ici les
   chiffres qui les précèdent font déjà l'entrée. */
.admin-portes { margin: 1.8rem auto 1.6rem; }

/* ── Les cartes d'une famille d'administration ──────────────────────────── */
/* Deux colonnes fixes, pas une grille automatique : à quatre cartes, l'ajustement
   automatique en tenait trois par rangée et laissait la quatrième seule.
   Même règle que les cartes de la page des ressources. */
.outil-grille { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Trois cartes de gestion tiennent sur une rangée dans la largeur du tableau
   des participants ; en deux colonnes, la troisième restait seule. */
.outil-grille--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Sans section autour d'elles, les cartes portent leur propre respiration. */
.outil-grille--gerer { margin-bottom: .4rem; }
@media (max-width: 1100px) {
  .outil-grille--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .outil-grille, .outil-grille--3 { grid-template-columns: 1fr; }
}

/* ── Sélection multiple dans le tableau ──────────────────────────────────
   La barre n'existe que lorsqu'il y a une sélection : une rangée de commandes
   grisées en permanence occuperait la place sans jamais rien proposer. */
.c-select { width: 2.4rem; text-align: center; padding-right: 0 !important; }
.c-select input { width: 1rem; height: 1rem; accent-color: var(--brand); cursor: pointer; }
.c-select input:disabled { cursor: not-allowed; opacity: .35; }
/* La ligne choisie se teinte : sur trente lignes, retrouver ses cases cochées
   d'un coup d'œil vaut mieux que de les compter. */
.data--admin tr.ligne-choisie > td { background: var(--brand-soft); }
/* Toute la ligne coche : le curseur le dit, sinon rien ne laisse deviner
   qu'elle est cliquable. La colonne d'actions garde le sien — ses boutons font
   autre chose. */
/* Pas de sélection de texte sur une ligne cochable : Maj+clic en surlignerait
   des mots par-dessus la plage de lignes qu'on désigne. */
.data--admin tr.ligne-cochable > td { cursor: pointer; user-select: none; }
/* Sauf l'adresse, qu'on veut pouvoir copier. */
.data--admin tr.ligne-cochable > td:nth-child(2) { user-select: text; }
.data--admin tr.ligne-cochable > td.cell-actions { cursor: default; }
.data--admin tr.ligne-cochable:hover > td { background: var(--surface-2); }
.data--admin tr.ligne-cochable.ligne-choisie:hover > td { background: var(--brand-soft); }

/* La barre FLOTTE au-dessus de la page, elle n'est pas dans le flux : insérée
   entre les filtres et le tableau, elle poussait tout vers le bas à la première
   case cochée — et le tableau sautait sous le pointeur au moment précis où
   l'on visait la case suivante. Posée en bas de l'écran, elle ne déplace rien
   et reste sous la main même après avoir fait défiler cent lignes. */
.sel-barre {
  position: fixed; z-index: 60;
  left: 50%; bottom: 1.4rem; transform: translateX(-50%);
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  max-width: calc(100vw - 2rem);
  padding: .7rem .7rem .7rem 1.2rem;
  border: 1px solid color-mix(in srgb, var(--brand) 55%, var(--border));
  border-radius: 999px; background: var(--surface);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .35);
  animation: selBarreIn .18s ease-out;
}
@keyframes selBarreIn {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .sel-barre { animation: none; }
}
/* Flottante, la barre n'a plus de largeur à remplir : le ressort qui écartait
   le compteur des boutons n'a plus lieu d'être. */
.sel-barre .grow { display: none; }
.sel-compte { font-size: .88rem; font-weight: 700; color: var(--text); white-space: nowrap; }
@media (max-width: 600px) {
  .sel-barre { left: 1rem; right: 1rem; transform: none; max-width: none; justify-content: center; }
  @keyframes selBarreIn { from { opacity: 0; } to { opacity: 1; } }
}

/* ── Import de participants ──────────────────────────────────────────────
   Le rapport se lit avant d'agir : les chiffres d'abord, le détail des écarts
   replié en dessous. Une liste de cinquante lignes déroulée d'emblée noierait
   la seule information qui décide — combien de comptes seront créés. */
.imp-depot {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  margin: 1.2rem 0; padding: 1.1rem 1.2rem;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--surface-2);
}
.imp-nom { font-size: .86rem; color: var(--text-mute); }

.imp-bilan { display: flex; gap: 1.6rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
.imp-bilan__n b { display: block; font-size: 1.7rem; font-weight: 800; line-height: 1.1; }
.imp-bilan__n span { font-size: .8rem; color: var(--text-mute); }
.imp-bilan__n--ok b { color: var(--ok); }

.imp-details > summary {
  cursor: pointer; font-size: .86rem; font-weight: 600; color: var(--text-soft);
  padding: .5rem 0;
}
.imp-details > summary:hover { color: var(--text); }
.imp-ecarts { display: grid; gap: .9rem; margin-top: .5rem; }
.imp-motif > b { font-size: .84rem; color: var(--warn); }
.imp-motif ul { margin: .35rem 0 0; padding-left: 1.1rem; }
.imp-motif li { font-size: .82rem; color: var(--text-mute); margin-bottom: .2rem; }
/* Le numéro de ligne est ce qu'on cherche pour corriger : il se repère. */
.imp-ligne {
  font-weight: 700; color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

.admin-note { margin-top: 1.2rem; color: var(--text-mute); font-size: .86rem; }
.admin-actions { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: 1.2rem; }
.admin-actions--fin { justify-content: flex-end; margin-top: 1.4rem; }
/* Une rangée d'action posée EN TÊTE : elle suit un encart d'avertissement et
   précède la liste, d'où des marges resserrées — la marge du bas la sépare de
   ce qu'elle commande, celle du haut la rattache à l'encart. */
.admin-actions--tete { margin-top: 1.4rem; margin-bottom: .5rem; }
.admin-note--tete { margin-top: 0; margin-bottom: 1.4rem; }
@media (max-width: 700px) {
  .admin-page { padding: 1.2rem 1.1rem 1.4rem; }
}

/* ── File d'attente des demandes de compte ── */
.queue { display: grid; gap: .7rem; margin-bottom: 1.6rem; }
.queue-item {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  background: var(--surface); border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--border));
  border-left: 4px solid var(--warn); border-radius: var(--radius); padding: 1rem 1.2rem;
}
.queue-who { flex: 1; min-width: 200px; }
.queue-who b { display: block; font-size: 1rem; }
.queue-meta { font-size: .82rem; color: var(--text-mute); }
.queue-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

table.data tr.row-muted td { background: var(--surface-2); }
table.data tr.row-muted td:first-child b { color: var(--text-soft); }

/* ── Pastille-compteur des demandes en attente ── */
.topbar__right { display: flex; align-items: center; gap: .55rem; flex: none; }

.pending-chip {
  display: flex; align-items: center; gap: .3rem; flex: none;
  height: 36px; padding: 0 .7rem 0 .55rem; border-radius: 999px; cursor: pointer;
  background: transparent; border: 1.5px solid color-mix(in srgb, var(--warn) 55%, transparent);
  color: color-mix(in srgb, var(--warn) 85%, var(--text));
  font-family: inherit; font-weight: 800;
  transition: background .16s, border-color .16s, color .16s;
  animation: pcPop .4s cubic-bezier(.2, 1.5, .4, 1);
}
.pending-chip:hover {
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-color: color-mix(in srgb, var(--warn) 75%, var(--border));
}
.pending-chip .pc-ico { font-size: .95rem; line-height: 1; animation: pcRing 3.5s ease-in-out infinite; transform-origin: 50% 10%; }
.pending-chip .pc-n { font-size: .88rem; font-variant-numeric: tabular-nums; }
@keyframes pcPop { from { opacity: 0; transform: scale(.7); } }
/* Sonnerie discrète, très espacée : signaler sans harceler. */
@keyframes pcRing {
  0%, 88%, 100% { transform: rotate(0); }
  90% { transform: rotate(11deg); }
  92% { transform: rotate(-9deg); }
  94% { transform: rotate(7deg); }
  96% { transform: rotate(-5deg); }
}
@media (prefers-reduced-motion: reduce) {
  .pending-chip, .pending-chip .pc-ico { animation: none; }
}
@media (max-width: 600px) {
  .pending-chip { height: 32px; padding: 0 .55rem 0 .45rem; }
  .pending-chip .pc-n { font-size: .82rem; }
}


/* ─────────────── Divers ─────────────── */
.toast-wrap { position: fixed; bottom: 1.6rem; left: 50%; transform: translateX(-50%); z-index: 200; display: grid; gap: .6rem; }
.toast {
  background: var(--text); color: var(--bg); padding: .85em 1.4em; border-radius: 999px; font-size: .92rem; font-weight: 600;
  box-shadow: var(--shadow-lg); animation: toastIn .3s cubic-bezier(.2,1.2,.3,1); display: flex; gap: .6em; align-items: center;
}
@keyframes toastIn { from { opacity: 0; transform: translateY(20px) scale(.9); } }
.toast.badge-toast { background: linear-gradient(135deg, #f79009, #e11d48); color: #fff; }

/* ── Toasts en mode « notification téléphone » sur mobile ──
 * Desktop inchangé (pastille en bas au centre). Sur petit écran, la notif
 * descend du haut, pleine largeur, alignée à gauche avec son icône, et
 * respecte l'encoche (safe-area). */
@media (max-width: 640px) {
  .toast-wrap {
    top: calc(env(safe-area-inset-top, 0px) + .5rem); bottom: auto;
    left: .5rem; right: .5rem; transform: none; gap: .5rem;
  }
  body.has-install-banner .toast-wrap { bottom: auto; }
  .toast {
    width: 100%; border-radius: 16px; padding: .9em 1.05em; text-align: left;
    justify-content: flex-start; font-weight: 600; line-height: 1.3;
    background: var(--surface); color: var(--text); border: 1px solid var(--border);
    animation: toastDrop .32s cubic-bezier(.2,1.1,.3,1);
  }
  .toast > span:first-child { font-size: 1.3em; flex: none; }
  .toast.badge-toast { color: #fff; border-color: transparent; }
}
@keyframes toastDrop { from { opacity: 0; transform: translateY(-16px) scale(.97); } }

/* ── Guide visuel d'installation (iOS et navigateurs sans invite) ── */
.modal__head .head-icon {
  width: 44px; height: 44px; border-radius: 12px; display: block; margin-bottom: .2rem;
}
.steps-visual { display: grid; gap: .7rem; }
.step-row {
  display: flex; align-items: center; gap: .9rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .9rem 1rem;
}
.step-row .step-num {
  flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand); color: #fff; font-size: .78rem; font-weight: 800;
}
.step-row .step-ico { flex: none; width: 30px; height: 30px; color: var(--brand); display: block; }
.step-row .step-ico svg { width: 100%; height: 100%; display: block; }
.step-row b { display: block; font-size: .96rem; line-height: 1.3; }
.step-row span { font-size: .8rem; color: var(--text-mute); }
.step-note {
  margin: 1rem 0 0; font-size: .84rem; color: color-mix(in srgb, var(--warn) 80%, var(--text));
  background: var(--warn-soft); border-radius: var(--radius-sm); padding: .7rem .9rem;
}

/* ── Retour en haut de page ──
   Discret : un disque translucide qui flotte au-dessus du contenu, et qui
   n'existe qu'une fois la page descendue. Il glisse vers le bas quand il
   s'efface, plutôt que de disparaître d'un coup. */
.haut {
  position: fixed; right: 1.4rem; z-index: 80;
  bottom: max(1.4rem, env(safe-area-inset-bottom));
  width: 2.7rem; height: 2.7rem; border-radius: 50%; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.15rem; line-height: 1; cursor: pointer; color: var(--brand);
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  opacity: 0; visibility: hidden; transform: translateY(12px);
  transition: opacity .22s, transform .22s, visibility .22s, border-color .16s, background .16s;
}
.haut.on { opacity: 1; visibility: visible; transform: none; }
.haut:hover {
  border-color: color-mix(in srgb, var(--brand) 55%, var(--border));
  background: color-mix(in srgb, var(--brand) 10%, var(--surface));
}
.haut:active { transform: scale(.94); }
.haut:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
/* Pas de retour en haut sur un écran tactile : le geste y est déjà immédiat —
   un balayage, ou la barre d'état qu'on tape sur iOS — et la pastille flottante
   ne fait que recouvrir le contenu, souvent au-dessus d'un bouton.
   Le critère est le POINTEUR, pas la largeur : une tablette en paysage est
   large, et un portable à écran tactile branché sur une souris garde le
   bouton. */
@media (hover: none), (pointer: coarse) {
  .haut { display: none; }
}
/* La bannière d'installation occupe toute la largeur en bas : le bouton monte
   au-dessus d'elle tant qu'elle est affichée, au lieu de passer dessous. */
body:has(.install-banner) .haut { bottom: max(5.6rem, calc(env(safe-area-inset-bottom) + 4.2rem)); }
@media (max-width: 560px) { .haut { right: .9rem; width: 2.5rem; height: 2.5rem; } }
@media (prefers-reduced-motion: reduce) {
  .haut { transition: opacity .15s, visibility .15s; transform: none; }
  .haut.on { transform: none; }
}

/* ── Bannière d'invitation à installer l'application ── */
.install-banner {
  position: fixed; left: .7rem; right: .7rem; bottom: .7rem; z-index: 90;
  display: flex; align-items: center; gap: .9rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: .85rem 1rem;
  padding-bottom: max(.85rem, env(safe-area-inset-bottom));
  animation: ibIn .35s cubic-bezier(.2, 1, .3, 1);
}
@keyframes ibIn { from { opacity: 0; transform: translateY(28px); } }
.install-banner.out { opacity: 0; transform: translateY(28px); transition: opacity .25s, transform .25s; }
.install-banner .ib-icon { width: 42px; height: 42px; flex: none; border-radius: 11px; }
.install-banner .ib-text { flex: 1; min-width: 0; }
.install-banner .ib-text b { display: block; font-size: .92rem; line-height: 1.3; }
.install-banner .ib-text span { font-size: .78rem; color: var(--text-mute); }
.install-banner .ib-actions { display: flex; flex-direction: column; gap: .35rem; flex: none; }

/* La bannière occupe le bas de l'écran : on remonte les notifications. */
body.has-install-banner .toast-wrap { bottom: 7.5rem; }

@media (max-width: 420px) {
  .install-banner { flex-wrap: wrap; }
  .install-banner .ib-actions { flex-direction: row; width: 100%; }
  .install-banner .ib-actions .btn { flex: 1; }
}

.empty { text-align: center; padding: 3rem 1rem; color: var(--text-mute); }
.empty .em { font-size: 3rem; display: block; margin-bottom: .6rem; opacity: .6; }

.spinner {
  width: 34px; height: 34px; border-radius: 50%; border: 3px solid var(--border); border-top-color: var(--brand);
  animation: spin .8s linear infinite; margin: 3rem auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.confetti { position: fixed; inset: 0; pointer-events: none; z-index: 150; overflow: hidden; }
.confetti i {
  position: absolute; width: 9px; height: 15px; opacity: .9; top: -20px;
  animation: fall linear forwards;
}
@keyframes fall { to { transform: translateY(105vh) rotate(720deg); opacity: .2; } }

.footer { margin-top: auto; text-align: center; padding: .9rem 1rem 1.1rem; font-size: .8rem; color: var(--text-mute); }
.footer a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border); transition: .18s; }
.footer a:hover { color: var(--brand); border-bottom-color: var(--brand); }

/* ─────────────── Fiches réflexes ───────────────
   Chaque carte est un lien vers son PDF : la cible est la fiche entière, pas
   un bouton minuscule au fond de la carte. La teinte reprend celle du PDF, de
   sorte qu'on reconnaisse à l'écran la fiche qu'on a en main sur papier. */
/* Une couleur par famille, portée par l'intitulé comme par les cartes : le
   classement se voit alors sans avoir à être expliqué. */
.fiche-groupe { --ton: var(--brand); margin-bottom: 2.4rem; }
.fiche-groupe.ton-danger { --ton: var(--danger); }
.fiche-groupe.ton-teal   { --ton: var(--teal); }
.fiche-famille {
  display: flex; align-items: baseline; gap: .8rem;
  font-size: .82rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ton); margin: 0 0 1rem;
  padding-bottom: .5rem; border-bottom: 2px solid color-mix(in srgb, var(--ton) 35%, var(--border));
}
.fiche-famille span { font-size: .74rem; font-weight: 500; letter-spacing: 0; text-transform: none; }

/* Trois par ligne, comme le hub : neuf fiches par famille donnent trois
   rangées nettes, là où « auto-fill » produisait un reliquat bancal. */
.fiche-grille { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 940px) { .fiche-grille { grid-template-columns: repeat(2, 1fr); } }

.fiche-carte {
  --ton: var(--brand);
  display: flex; flex-direction: column; text-decoration: none;
  background: var(--surface); border: 1px solid var(--border);
  border-top: 3px solid color-mix(in srgb, var(--ton) 55%, var(--border));
  border-radius: var(--radius); padding: 1.1rem 1.15rem 1rem;
  transition: border-color .18s, box-shadow .18s, transform .18s;
}
.fiche-carte.ton-danger { --ton: var(--danger); }
.fiche-carte.ton-teal   { --ton: var(--teal); }
.fiche-carte:hover {
  border-color: color-mix(in srgb, var(--ton) 45%, var(--border));
  border-top-color: var(--ton);
  box-shadow: var(--shadow); transform: translateY(-2px);
}

.fiche-haut { display: flex; align-items: center; justify-content: space-between; margin-bottom: .7rem; }
.fiche-ico {
  width: 2.3rem; height: 2.3rem; border-radius: var(--radius-sm); font-size: 1.2rem;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--ton) 11%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ton) 24%, var(--border));
}
.fiche-num {
  font-size: .68rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ton);
}
.fiche-titre { display: block; font-size: 1.04rem; color: var(--text); line-height: 1.25; }
.fiche-ss { font-size: .82rem; color: var(--text-soft); margin-top: .18rem; }
.fiche-resume {
  font-size: .83rem; color: var(--text-mute); line-height: 1.55;
  margin: .7rem 0 0; flex: 1;
}
.fiche-pdf {
  margin-top: .9rem; padding-top: .7rem; border-top: 1px solid var(--border);
  font-size: .78rem; font-weight: 600; color: var(--ton);
}
/* En portrait mobile : une ligne unique par fiche (le titre seul + chevron),
   sobre. Icône, numéro, sous-titre, résumé et pied masqués ; la fiche s'ouvre
   au tap. Un fin liseré à gauche garde le repère de couleur de la famille.
   Placé APRÈS les règles de base : à spécificité égale, la dernière l'emporte. */
@media (max-width: 560px) and (orientation: portrait) {
  .fiche-grille { grid-template-columns: 1fr; gap: .4rem; }
  .fiche-groupe { margin-bottom: 1.4rem; }
  .fiche-carte { flex-direction: row; align-items: center; position: relative;
    padding: .72rem 2rem .72rem .9rem; border-top: 0;
    border-left: 3px solid color-mix(in srgb, var(--ton) 55%, var(--border)); }
  .fiche-haut, .fiche-ss, .fiche-resume, .fiche-pdf { display: none; }
  .fiche-titre { font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .fiche-carte::after { content: '›'; position: absolute; right: .9rem; top: 50%; transform: translateY(-50%);
    color: var(--text-mute); font-size: 1.3rem; line-height: 1; }
}

/* ─────────────── Mentions légales ───────────────
   Page de lecture : mesure de ligne resserrée et interlignage généreux. Ces
   textes ne se parcourent pas, ils se lisent — parfois pour vérifier un point
   précis, d'où les titres de section bien détachés. */
.legal { max-width: 62ch; }
.legal-tabs { display: inline-flex; gap: .3rem; background: var(--bg-soft); padding: .3rem; border-radius: 999px; margin-bottom: 1.6rem; }
.legal-tabs .tab {
  padding: .55em 1.1em; border: 0; background: transparent; border-radius: 999px;
  font-weight: 600; font-size: .9rem; font-family: inherit; color: var(--text-soft); cursor: pointer; transition: .2s;
}
.legal-tabs .tab:hover { color: var(--text); }
.legal-tabs .tab.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

.legal-chapo { color: var(--text-soft); font-size: .92rem; font-style: italic; margin-bottom: 2rem; }
.legal-sec { margin-bottom: 2.1rem; }
.legal-sec h3 {
  font-size: 1.02rem; margin-bottom: .7rem; color: var(--text);
  padding-bottom: .45rem; border-bottom: 1px solid var(--border);
}
.legal-sec p { color: var(--text-soft); line-height: 1.72; margin-bottom: .85rem; font-size: .93rem; }
.legal-sec b { color: var(--text); font-weight: 600; }
.legal-sec a { color: var(--brand); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--brand) 35%, transparent); }
.legal-sec a:hover { border-bottom-color: var(--brand); }

.legal-liste { list-style: none; margin: 0 0 .95rem; padding: 0; }
.legal-liste li {
  position: relative; padding-left: 1.1rem; margin-bottom: .55rem;
  color: var(--text-soft); line-height: 1.7; font-size: .93rem;
}
.legal-liste li::before {
  content: ''; position: absolute; left: 0; top: .68em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--border);
}

.legal-champs { display: grid; grid-template-columns: minmax(9rem, 14rem) 1fr; gap: .1rem 1.2rem; margin: 0 0 1rem; }
.legal-champs dt { font-size: .8rem; font-weight: 600; color: var(--text-mute); padding: .45rem 0; }
.legal-champs dd { margin: 0; padding: .45rem 0; color: var(--text-soft); font-size: .93rem; line-height: 1.6; }
.legal-champs dt + dd { border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.legal-champs dt { border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }

/* Champ que seul l'organisme peut renseigner. Signalé sans ambiguïté plutôt
   que masqué : une mention légale incomplète doit se voir. */
.a-completer {
  display: inline-block; padding: .1em .55em; border-radius: 999px;
  font-size: .78rem; font-weight: 600; color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border: 1px dashed color-mix(in srgb, var(--warn) 45%, transparent);
}

.legal-maj { font-size: .8rem; color: var(--text-mute); margin-top: 2.4rem; padding-top: 1rem; border-top: 1px solid var(--border); }

/* Consultation sans compte : pas de bandeau applicatif, une simple page. */
.legal-page { max-width: 780px; margin: 0 auto; padding: 2rem 1.4rem 4rem; }
.legal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding-bottom: 1.4rem; margin-bottom: 2rem; border-bottom: 1px solid var(--border);
}

/* Information affichée sur le formulaire d'inscription lui-même. */
/* Lien « mot de passe oublié » : sous le formulaire, discret mais trouvable —
   c'est le premier motif de blocage d'un compte. */
.auth__oubli { margin: .9rem 0 0; text-align: center; font-size: .88rem; }
.auth__oubli a { color: var(--text-soft); text-decoration: none; border-bottom: 1px solid var(--border); }
.auth__oubli a:hover { color: var(--brand); border-bottom-color: var(--brand); }

.auth__legal {
  font-size: .78rem; line-height: 1.6; color: var(--text-mute);
  margin: .2rem 0 1rem; padding-left: .85rem;
  border-left: 2px solid var(--border);
}
.auth__legal a { color: var(--brand); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--brand) 35%, transparent); }
.auth__legal a:hover { border-bottom-color: var(--brand); }
.auth__foot { margin-top: 1.4rem; text-align: center; font-size: .8rem; }
.auth__foot a { color: var(--text-mute); text-decoration: none; border-bottom: 1px solid var(--border); }
.auth__foot a:hover { color: var(--brand); border-bottom-color: var(--brand); }

/* ─────────────── Activation d'une invitation ───────────────
 * Rappel de l'identité rattachée au lien, avant que la personne ne pose son
 * mot de passe : elle doit pouvoir vérifier que le lien lui était destiné. */
.activation-qui {
  display: grid; gap: .55rem; margin: 0 0 1.5rem;
  padding: .95rem 1.1rem; border-radius: var(--radius-sm);
  background: var(--bg-soft); border: 1px solid var(--border);
}
.activation-qui > div { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.activation-qui span { font-size: .8rem; color: var(--text-mute); }
.activation-qui b { font-size: .92rem; text-align: right; word-break: break-word; }

/* ─────────────── Tri et filtres du tableau des participants ─────────────── */
/* La barre appartient au TABLEAU, pas à ce qui la précède : elle s'en approche
   et s'écarte des cartes. Placée à mi-distance, elle se lisait comme un
   prolongement des cartes de gestion. */
.grille-filtres {
  display: flex; gap: .6rem; flex-wrap: wrap; align-items: center;
  /* Presque rien sous la barre : elle commande le tableau, elle se pose
     dessus. Tout l'air est au-dessus, pour la détacher des cartes de gestion —
     placée à mi-distance, elle se lisait comme un prolongement de celles-ci. */
  margin: 2.6rem 0 .5rem;
}
/* `:not([type=checkbox])` : sans cette réserve, la case du filtre booléen
   héritait du gabarit d'un champ de saisie — 0,8 em de rembourrage autour
   d'une case de 13 px. */
.grille-filtres input:not([type="checkbox"]), .grille-filtres select {
  padding: .55em .8em; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: .9rem; font-family: inherit;
}
/* L'interrupteur à trois positions. Il a le gabarit d'un menu voisin — même
   hauteur, même cadre — pour se lire comme un filtre de plus, et c'est la
   POSITION qui porte le sens : cadenas fermé à gauche, repos au centre,
   cadenas ouvert à droite. */
.filtre-tri {
  display: inline-flex; align-items: center; gap: .15rem;
  padding: .3em .5em .3em .8em; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
  font-size: .9rem; color: var(--text-soft); user-select: none;
}
.filtre-tri__t { margin-right: .45rem; }
.filtre-tri__p {
  display: grid; place-items: center; width: 1.9rem; height: 1.7rem;
  border-radius: calc(var(--radius-sm) - 2px); cursor: pointer;
  font-size: .95rem; line-height: 1; transition: background .14s, color .14s;
}
/* La case native est masquée sans quitter le document : elle reste la cible du
   clavier et des lecteurs d'écran, et les flèches parcourent le groupe. */
.filtre-tri__p input { position: absolute; opacity: 0; width: 0; height: 0; }
.filtre-tri__p:hover { background: var(--surface-2); }
/* La position au repos est discrète tant qu'elle n'est pas choisie : son point
   ne doit pas se lire comme un état actif. */
.filtre-tri__p.p-neutre { font-size: 1.3rem; color: var(--text-mute); }
.filtre-tri__p:has(input:checked) { background: var(--brand-soft); color: var(--text); }
.filtre-tri__p.p-sans:has(input:checked) { background: color-mix(in srgb, var(--warn) 22%, transparent); }
.filtre-tri__p.p-avec:has(input:checked) { background: color-mix(in srgb, var(--ok) 22%, transparent); }
.filtre-tri__p:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 1px; }
.grille-filtres input:focus, .grille-filtres select:focus {
  outline: 0; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft);
}
.grille-q { flex: 1 1 15rem; min-width: 0; max-width: 24rem; }
/* Largeur réservée : le libellé passe de « 12 participants » à « 3 sur 12 »,
   et sans cette réserve tout ce qui précède se décalait à chaque coup de
   filtre. */
.grille-compte {
  margin-left: auto; font-size: .85rem; color: var(--text-mute);
  white-space: nowrap; min-width: 9rem; text-align: right;
}
/* Ce qui garde sa place sans se montrer. `hidden` retirait le bouton du flux
   et faisait sauter la barre entière à chaque activation de filtre. */
.grille-filtres .invisible { visibility: hidden; pointer-events: none; }
.grille-compte.actif { color: var(--brand); font-weight: 600; }
/* Pas de `display: flex` sur cette cellule : une case de tableau ainsi
   déclarée sort de l'algorithme de largeur du tableau et cesse de couvrir la
   rangée — le message se retrouvait collé à gauche au lieu d'être centré.
   Une cellule ordinaire et `text-align` suffisent, le bouton étant en ligne. */
.grille-vide { text-align: center; padding: 2.2rem 1rem; color: var(--text-mute); }
/* Le bouton reste sur la ligne du message : la phrase et le geste qu'elle
   propose se lisent d'un trait, et la rangée vide ne prend pas trois lignes. */
.grille-vide .btn { margin-left: .7rem; vertical-align: middle; }

/* En-tête triable. La flèche occupe sa place en permanence, en réserve
   d'opacité : la faire apparaître au clic décalait tous les intitulés d'un
   demi-caractère, et la ligne entière tressautait à chaque tri. */
.data--admin th.triable { cursor: pointer; user-select: none; white-space: nowrap; }
.data--admin th.triable:hover { color: var(--brand); }
.data--admin th .fleche {
  display: inline-block; width: .75em; margin-left: .25em;
  font-style: normal; opacity: 0; transition: opacity .15s;
}
.data--admin th .fleche::before { content: '▲'; font-size: .62em; vertical-align: .1em; }
.data--admin th.triable:hover .fleche { opacity: .35; }
.data--admin th.trie { color: var(--brand); }
.data--admin th.trie .fleche { opacity: 1; }
.data--admin th.trie.desc .fleche::before { content: '▼'; }
/* Colonne centrée : la flèche suit l'intitulé sans casser le centrage. */
.data--admin th.c-num.triable, .data--admin th.c-droite.triable { white-space: nowrap; }

@media (max-width: 760px) {
  .grille-compte { margin-left: 0; }
  .grille-q { flex-basis: 100%; max-width: none; }
}

/* ─────────────── Page pédagogique d'un clic de faux-phishing ───────────────
   Ton apaisé, jamais alarmant : la personne vient de « se faire prendre » à un
   test, la page l'instruit sans la brusquer. Fond neutre, carte centrée. */
.hamec {
  min-height: 100vh; display: grid; place-items: center;
  padding: 2rem 1.2rem; background: var(--bg);
}
.hamec-carte {
  width: 100%; max-width: 720px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); padding: 2.4rem 2.6rem;
}
.hamec-tete { display: flex; align-items: center; gap: 1.1rem; margin-bottom: 1.4rem; }
.hamec-ico {
  flex: none; width: 3.4rem; height: 3.4rem; border-radius: 1rem; font-size: 1.7rem;
  display: grid; place-items: center;
  background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
}
.hamec-sur { font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--warn); }
.hamec-tete h1 { margin: .1rem 0 0; font-size: 1.5rem; letter-spacing: -.02em; }
.hamec-intro { color: var(--text-soft); line-height: 1.6; margin-bottom: 1.6rem; }

.hamec-bloc { margin-top: 1.4rem; padding: 1.1rem 1.3rem; border-radius: var(--radius); border: 1px solid var(--border); }
.hamec-bloc h2 { font-size: .82rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; margin: 0 0 .7rem; }
.hamec-bloc ul { margin: 0; padding-left: 1.2rem; }
.hamec-bloc li { margin: .5rem 0; line-height: 1.55; }
.hamec-indices { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 25%, transparent); }
.hamec-indices h2 { color: color-mix(in srgb, var(--warn) 75%, var(--text)); }
.hamec-conseils { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 25%, transparent); }
.hamec-conseils h2 { color: color-mix(in srgb, var(--ok) 72%, var(--text)); }
.hamec-pied { margin-top: 1.8rem; }
.hamec-pied p { color: var(--text-soft); }
.hamec-actions { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: 1rem; justify-content: center; }
/* Les deux boutons partagent le même style contour : le principal se distingue
   par sa teinte de marque (indigo), le secondaire reste en gris neutre — sans
   à-plat de couleur, plus sobre et cohérent. */

@media (max-width: 560px) {
  .hamec-carte { padding: 1.6rem 1.3rem; }
  .hamec-tete h1 { font-size: 1.25rem; }
  /* Sur mobile, les deux boutons s'empilent, même largeur et bien centrés :
     un pied de page net, sans bouton qui déborde ni largeurs disparates. */
  .hamec-actions { flex-direction: column; align-items: center; }
  .hamec-actions .btn { width: 100%; max-width: 320px; }
}

/* ─────────────── Campagnes de faux-phishing (console) ─────────────── */
.phish-liste { display: grid; gap: .8rem; }
.phish-carte {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1rem 1.2rem; font-family: inherit; transition: .15s;
  /* La carte est devenue un <a> — pour qu'un Ctrl+clic ouvre la campagne dans
     un onglet. Elle garde la couleur du texte de la page, et perd le
     soulignement que le lien apporte avec lui. */
  color: var(--text); text-decoration: none;
}
.phish-carte:hover { border-color: var(--brand); box-shadow: var(--shadow-sm); }
.phish-carte__tete { display: flex; align-items: center; gap: .8rem; }
.phish-modele { font-size: 1.4rem; flex: none; }
.phish-carte__titre { flex: 1; min-width: 0; }
.phish-carte__titre b { display: block; }
.phish-carte__titre span { font-size: .84rem; color: var(--text-mute); }
.phish-carte__chiffres { display: flex; gap: 2rem; margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--border); }
.phish-carte__chiffres b { font-size: 1.3rem; font-weight: 800; }
.phish-carte__chiffres span { font-size: .78rem; color: var(--text-mute); margin-left: .35rem; }
.phish-taux-haut b { color: var(--warn); }

.phish-recid-liste { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .7rem; }

/* Palette des domaines de lien : le nom d'hôte prend la place, la pastille
   dit son rôle, l'action (retirer) suit. Réutilise la grille du référentiel. */
.ref-ligne .ref-domaine {
  flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .9rem; word-break: break-all;
}

.phish-apercu { font-size: .84rem; color: var(--text-mute); margin-top: .4rem; font-style: italic; }

/* Sélecteur de cibles */
.phish-cible-barre { display: flex; align-items: center; gap: .9rem; margin-bottom: .5rem; }
.phish-cible-compte { font-size: .84rem; color: var(--text-mute); }
.phish-cibles {
  max-height: 15rem; overflow-y: auto; display: grid; gap: .2rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .5rem;
}
/* `.phish-cible` est un <label> DANS un .field : la règle `.field label`
   (display:block, margin) le prendrait de vitesse. On monte la spécificité
   pour remettre la ligne à l'horizontale et annuler la marge héritée. */
.phish-cibles label.phish-cible {
  display: flex; align-items: center; gap: .6rem; margin: 0;
  padding: .4rem .5rem; border-radius: var(--radius-sm); cursor: pointer;
}
.phish-cibles label.phish-cible:hover { background: var(--surface-2); }
/* La règle globale .field input impose width:100% : sans cette réserve, la
   case à cocher s'étirait sur toute la ligne et fuyait à droite de l'intitulé. */
.phish-cible input[type="checkbox"] {
  width: auto; flex: none; margin: 0; padding: 0;
  accent-color: var(--brand); width: 1.05rem; height: 1.05rem;
}
.phish-cible span { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.phish-cible small { color: var(--text-mute); }

/* Détail : résumé + liste des cibles */
.phish-resume { display: flex; gap: 1.4rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem; }
.phish-resume b { font-size: 1.05rem; }
.phish-cibles-detail { display: grid; gap: .4rem; }
.phish-cible-ligne {
  display: grid; grid-template-columns: 1fr auto auto; gap: .8rem; align-items: center;
  padding: .6rem .8rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.phish-cible-ligne.a-clique { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); background: color-mix(in srgb, var(--warn-soft) 55%, transparent); }
.phish-cible-qui b { display: block; }
.phish-cible-qui small { color: var(--text-mute); }
.phish-cible-actions { display: flex; gap: .4rem; flex-wrap: wrap; }

@media (max-width: 620px) {
  .phish-cible-ligne { grid-template-columns: 1fr; }
  .phish-cible-actions { justify-content: flex-start; }
}

/* ─────────────── Référentiel des structures ─────────────── */
.ref-liste { display: grid; gap: .5rem; }
.ref-ligne {
  display: grid; grid-template-columns: 7.5rem 1fr auto auto;
  gap: .6rem; align-items: center;
}
.ref-ligne .ref-type, .ref-ligne .ref-nom {
  padding: .5em .7em; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: .92rem; font-family: inherit;
}
.ref-ligne .ref-nom:focus, .ref-ligne .ref-type:focus {
  outline: 0; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft);
}
.ref-vide { color: var(--text-mute); font-size: .92rem; margin: 0; }

/* Création d'une structure, en place dans un autre formulaire. */
.creation-structure { margin-top: .7rem; }
.creation-ligne { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.creation-ligne select, .creation-ligne input {
  padding: .5em .7em; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: .92rem; font-family: inherit;
}
.creation-ligne input { flex: 1 1 12rem; min-width: 0; }

/* Colonne « Structure » du tableau : le type devant, le nom derrière. */
.cell-structure { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.cell-structure .pill { flex: none; }

@media (max-width: 620px) {
  .ref-ligne { grid-template-columns: 1fr auto; }
  .ref-ligne .ref-type { grid-column: 1 / -1; }
}

/* ═══════════════ Animations en direct ═══════════════ */
/* ── Participant (téléphone) ── */
.live { min-height: 100vh; background: var(--bg); display: flex; flex-direction: column; }
.live--join { align-items: center; justify-content: center; padding: 1.5rem; }
.live-join {
  width: 100%; max-width: 26rem; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 2rem 1.8rem; text-align: center;
}
.live-join-marque { display: inline-flex; align-items: center; gap: .5rem; font-weight: 800; margin-bottom: 1.2rem; }
.live-join-marque .mark { width: 30px; height: 30px; }
.live-join h1 { font-size: 1.4rem; margin: 0 0 .3rem; }
.live-join p { color: var(--text-soft); margin: 0 0 1.2rem; }
.live-code-input {
  width: 100%; text-align: center; font-size: 2rem; font-weight: 800; letter-spacing: .3em;
  text-transform: uppercase; padding: .6em .3em; margin-bottom: 1rem;
  border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.live-code-input:focus { outline: 0; border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft); }

.live-topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .8rem 1.1rem; background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 5;
}
.live-topbar__marque { display: flex; align-items: center; gap: .5rem; font-weight: 700; font-size: .95rem; }
.live-topbar__marque .mark { width: 26px; height: 26px; }
.live-topbar__etape { font-size: .82rem; font-weight: 700; color: var(--brand); white-space: nowrap; }

.live-centre { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 2rem 1.5rem; }
.live-centre__ico { font-size: 3.5rem; margin-bottom: 1rem; }
.live-centre h2 { font-size: 1.5rem; margin: 0 0 .4rem; }
.live-centre p { color: var(--text-soft); margin: 0; }

/* Large sur PC : inutile de brider la question à une colonne de téléphone. Le
   plafond garde les lignes lisibles sur très grand écran, sans plus. */
.live-q { flex: 1; padding: 1.6rem clamp(1.2rem, 4vw, 3rem) 2.5rem; max-width: min(96vw, 70rem); width: 100%; margin: 0 auto; }
/* L'explication reste à une largeur de lecture confortable, centrée dans la
   colonne large — boîte et texte. */
.live-explic { max-width: 52rem; margin-left: auto; margin-right: auto; text-align: center; }
.live-q__txt { font-size: 1.5rem; line-height: 1.25; margin: .3rem 0 1.2rem; }
.live-q__consigne { color: var(--text-soft); margin: -0.6rem 0 1rem; }
.live-options { display: grid; gap: .7rem; }
.live-opt {
  display: flex; align-items: center; gap: .8rem; width: 100%; text-align: left;
  padding: 1.05rem 1.1rem; border: 2px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font-size: 1.05rem; font-family: inherit; cursor: pointer; transition: .12s;
}
.live-opt__puce { flex: none; width: 1.4rem; height: 1.4rem; border-radius: 50%; border: 2px solid var(--border); display: flex; align-items: center; justify-content: center; font-size: .82rem; font-weight: 800; line-height: 1; color: #fff; }
.live-opt.on { border-color: var(--brand); background: var(--brand-soft); }
.live-opt.on .live-opt__puce { border-color: var(--brand); background: var(--brand); box-shadow: inset 0 0 0 3px var(--surface); }
/* Après révélation : cartes figées, pastille verte (juste) ou rouge (faux). */
.live-opt--fige { cursor: default; }
.live-opt__txt { flex: 1; min-width: 0; }
.live-opt.bon { border-color: var(--ok); background: var(--ok-soft); }
.live-opt.bon .live-opt__puce { border-color: var(--ok); background: var(--ok); box-shadow: none; }
.live-opt.mauvais { border-color: var(--danger); background: var(--danger-soft); }
.live-opt.mauvais .live-opt__puce { border-color: var(--danger); background: var(--danger); box-shadow: none; }
/* Bonne réponse OUBLIÉE : verte, mais pastille creuse — on la distingue d'une
   bonne réponse qu'on avait bien cochée. */
.live-opt.oubli { border-style: dashed; }
.live-opt.oubli .live-opt__puce { background: transparent; color: var(--ok); }
/* Étiquette de droite : « Votre réponse » ou « Oubliée ». */
.live-opt__tag { flex: none; font-size: .72rem; font-weight: 700; padding: .18rem .6rem; border-radius: 999px; white-space: nowrap; }
.live-opt.bon .live-opt__tag.vous { background: var(--ok); color: #fff; }
.live-opt.mauvais .live-opt__tag.vous { background: var(--danger); color: #fff; }
.live-opt__tag.oubli { background: var(--warn-soft); color: color-mix(in srgb, var(--warn) 80%, var(--text)); border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent); }
.live-q__pied { margin-top: 1.3rem; }
.live-q__attente { text-align: center; color: var(--ok); font-weight: 600; }

/* Après révélation (participant) */
.live-verdict { text-align: center; font-size: 1.2rem; font-weight: 800; padding: .6rem; border-radius: var(--radius-sm); margin-bottom: 1rem; }
.live-verdict.juste { color: var(--ok); background: var(--ok-soft); }
.live-verdict.faux { color: var(--danger); background: var(--danger-soft); }
.live-res { margin-bottom: .7rem; }
.live-res__tete { display: flex; justify-content: space-between; gap: 1rem; font-size: .95rem; margin-bottom: .25rem; }
.live-res.bon .live-res__tete { color: var(--ok); font-weight: 700; }
.live-res.mien-faux .live-res__tete { color: var(--danger); }
.live-res__bar { height: 8px; background: var(--bg-soft); border-radius: 99px; overflow: hidden; }
.live-res__bar i { display: block; height: 100%; background: var(--text-mute); border-radius: 99px; }
.live-res.bon .live-res__bar i { background: var(--ok); }
.live-explic { margin-top: 1rem; padding: .9rem 1.1rem; background: var(--info-soft); border-radius: var(--radius-sm); color: color-mix(in srgb, var(--info) 75%, var(--text)); line-height: 1.5; }

.live-mot-ligne { display: flex; gap: .6rem; }
.live-mot-input { flex: 1; padding: 1rem 1.1rem; font-size: 1.1rem; border: 2px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
.live-mot-input:focus { outline: 0; border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft); }
.live-mesmots { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.live-monmot { padding: .35rem .8rem; background: var(--brand-soft); color: var(--brand); border-radius: 99px; font-weight: 600; font-size: .9rem; }

/* ── Animateur (écran projeté) ── */
.animer { min-height: 100vh; background: var(--bg); display: flex; flex-direction: column; }
.animer-tete { display: flex; align-items: center; gap: 1.2rem; padding: 1rem 1.6rem; border-bottom: 1px solid var(--border); background: var(--surface); }
.animer-tete__titre { display: flex; align-items: center; gap: .6rem; font-weight: 800; font-size: 1.15rem; flex: 1; }
.animer-tete__titre .mark { width: 32px; height: 32px; }
.animer-tete__code { display: flex; align-items: center; gap: .6rem; font-size: .9rem; color: var(--text-soft); }
.animer-tete__code b { font-size: 1.5rem; letter-spacing: .15em; color: var(--brand); font-family: ui-monospace, monospace; }
.animer-tete__n { color: var(--text-mute); }
.animer-quitter { border: 0; background: var(--bg-soft); color: var(--text-soft); width: 34px; height: 34px; border-radius: 50%; cursor: pointer; font-size: 1rem; }
.animer-quitter:hover { background: var(--danger-soft); color: var(--danger); }
.animer-corps { flex: 1; display: flex; padding: 2rem 2.5rem; overflow: hidden; }
.animer-zone { flex: 1; display: flex; flex-direction: column; justify-content: center; min-height: 0; }

/* Écran d'attente projeté : le code en très grand */
.animer-attente { text-align: center; }
.animer-rejoindre p { font-size: 1.6rem; color: var(--text-soft); margin: .3rem 0; }
.animer-url { font-size: 2.4rem; font-weight: 800; color: var(--text); margin: .3rem 0; }
.animer-bigcode {
  font-size: 7rem; font-weight: 900; letter-spacing: .1em; line-height: 1.1;
  color: var(--brand); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.animer-count { font-size: 1.3rem; color: var(--text-mute); margin-top: 1.5rem; }

.animer-q { width: 100%; }
.animer-q__num { font-size: 1rem; font-weight: 700; color: var(--brand); text-transform: uppercase; letter-spacing: .08em; }
.animer-q h1 { font-size: 2.4rem; line-height: 1.15; margin: .4rem 0 1.4rem; }
.animer-q__consigne { font-size: 1.2rem; color: var(--text-soft); margin: -0.8rem 0 1.2rem; }
.animer-barres { display: grid; gap: .9rem; }
.animer-barre { display: grid; grid-template-columns: 1fr auto; gap: .2rem 1rem; align-items: center; }
.animer-barre__txt { grid-column: 1; font-size: 1.4rem; font-weight: 600; }
.animer-barre__n { grid-column: 2; grid-row: 1 / 3; font-size: 2rem; font-weight: 800; color: var(--text-soft); min-width: 2.5rem; text-align: right; }
.animer-barre__jauge { grid-column: 1; height: 18px; background: var(--bg-soft); border-radius: 99px; overflow: hidden; }
.animer-barre__jauge i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand), var(--brand-2)); border-radius: 99px; transition: width .5s cubic-bezier(.2,.8,.2,1); }
.animer-barre.bon .animer-barre__txt { color: var(--ok); }
.animer-barre.bon .animer-barre__jauge i { background: var(--ok); }
.animer-barre.pale { opacity: .5; }
.animer-q__pied { margin-top: 1.4rem; display: flex; flex-direction: column; gap: .6rem; font-size: 1.1rem; color: var(--text-mute); }
.animer-explic { font-size: 1.15rem; color: color-mix(in srgb, var(--info) 75%, var(--text)); background: var(--info-soft); padding: 1rem 1.3rem; border-radius: var(--radius); line-height: 1.5; margin: 0; }

.animer-nuage-wrap { text-align: center; width: 100%; }
.animer-nuage-titre { font-size: 2rem; margin: 0 0 1.5rem; }
.animer-nuage { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; align-items: center; justify-content: center; line-height: 1.15; }
.animer-mot { font-weight: 800; }
.animer-nuage-vide { font-size: 1.4rem; color: var(--text-mute); }

/* ══════════════ Escape game : les personnages ══════════════
   Le médaillon circulaire est celui de la formation — l'application n'invente
   pas un second vocabulaire pour dire la même chose. */
/* Taille de PRÉSENTATION, volontairement plus grande que `--esc-rond` : c'est
   celle de Justine, qui ouvre et referme le jeu. Elle ne joue pas une scène et
   l'on n'a aucune émotion à lire sur son visage — elle s'adresse au joueur, et
   sa place à l'écran dit qu'on l'écoute avant de commencer. Les personnages
   pris dans l'histoire, eux, passent tous par `--esc-rond`. Ne pas fondre les
   deux : ce sont deux rôles, pas deux réglages. */
.esc-media {
  position: relative; width: clamp(7rem, 17vh, 13rem); aspect-ratio: 1; flex: none;
  border-radius: 50%; overflow: hidden; background: #0c1020;
  border: 3px solid var(--teinte, var(--brand));
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
.esc-media__el {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transform-origin: center; transition: opacity .28s ease;
}
/* Le second plan attend dessous, à zéro : le fondu bascule les opacités pendant
   que les deux clips tournent, ce qui évite le flash noir d'un changement de
   source et le saut de tête d'une coupe franche. */
.esc-media__el--dessous { opacity: 0; }
.esc-media.bascule .esc-media__el { opacity: 0; }
.esc-media.bascule .esc-media__el--dessous { opacity: 1; }
/* Même principe que le médaillon des parcours : aucun fond, le symbole seul,
   blanc et détaché par son ombre. Le voile sombre qui couvrait le cercle
   assombrissait le visage à chaque survol pour ne rien apporter. */
.esc-media__play {
  position: absolute; inset: 0; display: grid; place-items: center; cursor: pointer;
  border: 0; background: none; color: #fff; transition: opacity .2s;
}
.esc-media__play svg {
  width: 30%; height: 30%; fill: currentColor;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, .75)) drop-shadow(0 0 3px rgba(0, 0, 0, .5));
}
.esc-media__play { opacity: 0; }
.esc-media:hover .esc-media__play { opacity: 1; }
.esc-media.bloque .esc-media__play { opacity: 1; }
@media (hover: none) {
  .esc-media__play { opacity: 1; }
  .esc-media.joue .esc-media__play { opacity: 0; }
}

/* Une salle dont quelqu'un porte la parole : le médaillon remplace l'icône, et
   la ligne de titre s'aère pour l'accueillir. */
.esc-salle__tete.avec-media { align-items: center; gap: 1.2rem; }
.esc-salle__tete.avec-media .esc-media { width: var(--esc-rond); }

/* ── La taille d'un médaillon ──────────────────────────────────────────────
   UNE seule valeur, pour tous les visages qui parlent : pastille de voix off,
   pastille posée dans un message, médaillon vidéo en tête de salle. Ce n'est
   pas une décision de mise en page, c'est ce qui décide si l'on LIT l'émotion
   ou non. Le prestataire est surpris salle 5, affirmatif salle 7 ; Karine est
   concentrée puis rassurante. Laisser la taille varier d'un endroit à l'autre,
   c'est rendre ces visages illisibles une fois sur deux — donc les avoir
   produits pour rien.
   Deux exceptions, et elles se justifient par le RÔLE, pas par la place :
   Justine, qui présente le jeu et n'a pas d'émotion à faire lire (voir
   `.esc-media` plus haut), et les vignettes du casting — cinq personnes
   alignées, visage neutre, rien à y lire non plus. */
:root { --esc-rond: clamp(6rem, 13vw, 8.5rem); }

/* Voix off : un portrait fixe, un libellé qui dit d'où vient le son, un bouton.
   La carte reste sobre — elle accompagne le texte de la salle, elle ne le
   concurrence pas. */
/* Centrées et espacées : à cette taille, les médaillons sont des visages, pas
   des puces de liste. Deux visages collés au bord gauche se lisent comme un
   sommaire ; posés au centre avec de l'air entre eux, ils se lisent comme deux
   personnes qui viennent d'entrer dans la pièce. */
.esc-voix-liste {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 1.6rem; margin: 1.2rem 0 1.6rem;
}
.esc-voix {
  display: flex; align-items: center; gap: 1rem; padding: .7rem 1.1rem .7rem .7rem;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  flex: 1 1 22rem; max-width: 30rem; transition: border-color .16s;
}
.esc-voix.joue { border-color: var(--teinte); }
.esc-voix__rond {
  width: var(--esc-rond); height: var(--esc-rond); flex: none; border-radius: 50%;
  overflow: hidden; border: 3px solid var(--teinte); background: #0c1020;
}
.esc-voix__rond { position: relative; }
.esc-voix__rond img, .esc-voix__film {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
/* Le fondu passe par le noir dans les DEUX sens : ce qui part s'efface tout de
   suite, ce qui arrive attend que la place soit libre. Le fond du rond étant
   déjà `#0c1020`, il n'y a pas de voile à ajouter — c'est lui qu'on voit
   pendant le battement. */
.esc-voix__rond img { transition: opacity .28s ease .24s; }
.esc-voix__film { opacity: 0; transition: opacity .28s ease; }
.esc-voix.avec-film.joue .esc-voix__rond img { opacity: 0; transition-delay: 0s; }
.esc-voix.avec-film.joue .esc-voix__film { opacity: 1; transition-delay: .24s; }
@media (prefers-reduced-motion: reduce) {
  .esc-voix__rond img, .esc-voix__film { transition: none; }
}
.esc-voix__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.esc-voix__txt b { font-size: 1rem; }
.esc-voix__txt span { font-size: .82rem; color: var(--text-mute); }
.esc-voix__btn {
  flex: none; width: 2.2rem; height: 2.2rem; border-radius: 50%; cursor: pointer;
  border: 0; background: var(--teinte); color: #fff; display: grid; place-items: center;
}
.esc-voix__btn svg { width: 1rem; height: 1rem; fill: currentColor; }
.esc-voix.joue .esc-voix__btn { animation: voix-pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .esc-voix.joue .esc-voix__btn { animation: none; } }

/* Écoute posée dans l'en-tête d'un message : discrète, alignée sur la ligne de
   l'expéditeur, elle appartient au message et non à la salle. */
.esc-ecoute { display: inline-flex; align-items: center; gap: .45rem; margin-left: auto; }
/* Une pastille SEULE dans l'en-tête ne signale pas un message : elle EST le
   message. Le tour n'a alors ni expéditeur ni objet — quelqu'un parle, et c'est
   tout. Elle prend donc la taille des médaillons vidéo et se cale à gauche,
   comme les personnages qui parlent en face. La règle se déclenche sur la
   position, pas sur une classe : dès qu'un expéditeur la précède, on retombe
   sur la petite pastille alignée à droite, qui n'est qu'un accessoire du
   message et ne doit pas lui voler la vedette. */
.esc-ecoute:first-child { margin-left: 0; }

/* Assez grand pour qu'on lise l'expression : le prestataire est SURPRIS à ce
   moment-là, et une vignette de 27 px n'en montrait rien. C'est la seule chose
   que ce portrait a à dire — autant qu'elle se voie. */
.esc-ecoute__rond {
  position: relative; display: block; flex: none;
  width: var(--esc-rond); height: var(--esc-rond); border-radius: 50%; overflow: hidden;
  border: 3px solid var(--teinte); background: #0c1020;
}
.esc-ecoute__ava, .esc-ecoute__film {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.esc-ecoute__ava { transition: opacity .28s ease .24s; }
.esc-ecoute__film { opacity: 0; transition: opacity .28s ease; }
.esc-ecoute.avec-film.joue .esc-ecoute__ava { opacity: 0; transition-delay: 0s; }
.esc-ecoute.avec-film.joue .esc-ecoute__film { opacity: 1; transition-delay: .24s; }
@media (prefers-reduced-motion: reduce) {
  .esc-ecoute__ava, .esc-ecoute__film { transition: none; }
}
.esc-ecoute__btn {
  width: 2.2rem; height: 2.2rem; flex: none; border-radius: 50%; cursor: pointer;
  border: 0; background: var(--teinte); color: #fff; display: grid; place-items: center;
}
.esc-ecoute__btn svg { width: 1rem; height: 1rem; fill: currentColor; }
.esc-ecoute__txt { font-size: .78rem; color: var(--text-mute); }
.esc-ecoute.joue .esc-ecoute__btn { animation: voix-pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .esc-ecoute.joue .esc-ecoute__btn { animation: none; } }
.esc-bulle__de { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
/* Voix sans visage : un symbole, jamais le portrait d'un autre personnage. */
.esc-voix__anonyme { display: grid; place-items: center; width: 100%; height: 100%; font-size: 1.1rem; }

/* Écran final : le médaillon de Justine prend la place de la coupe. */
.esc-fin .esc-media { margin: 0 auto 1.2rem; width: clamp(7rem, 16vw, 10rem); }

/* Page de lancement : le décor derrière, le texte et l'équipe devant. */
/* Le panneau prend les proportions du décor (1672×941). Sans cela, `cover`
   remplit la largeur, déborde en hauteur, et il faut amputer quelque chose —
   les pieds si l'on ancre en haut, les têtes si l'on ancre en bas. En donnant au
   cadre la forme de l'image, il n'y a plus rien à couper.
   `max-height` garde la main sur les très grands écrans : au-delà, un léger
   recadrage revient, et il mord alors sur le plafond. */
/* Pleine largeur, image ENTIÈRE, sans défilement horizontal. Les trois à la
   fois imposent une seule solution : le panneau prend toute la fenêtre, sa
   hauteur est bornée par la fenêtre, et le décor est contenu — donc calé sur la
   HAUTEUR quand l'écran est large. La plus grande image entière possible fait
   alors `hauteur × 1,78` ; ce qui reste à gauche n'est pas perdu, c'est
   exactement la place du titre et du médaillon.
   `50% - 50vw` fait sortir le panneau de la colonne de lecture centrée. */
/* Hauteur calculée pour que la page ne défile PAS : la fenêtre, moins tout ce
   qui l'occupe déjà — la barre de l'application, les marges de la vue et le pied
   de page, que j'avais d'abord oublié et qui laissait 69 px de débordement.
   En dessous de cette limite, c'est le ratio du décor qui commande. */
.esc-lancement {
  position: relative; overflow: hidden; background: #0c1020;
  width: 100vw; margin-left: calc(50% - 50vw);
  /* La hauteur passe par une variable parce que le voile de gauche en dépend :
     l'image étant en « contain » au format 16/9, la largeur qu'elle occupe vaut
     sa hauteur × 1,777, et son bord gauche tombe donc à cette distance du bord
     droit du panneau. C'est la seule façon de placer un dégradé exactement sur
     la couture, dont la position bouge avec la fenêtre. */
  --h: min(calc(100vh - 15.5rem), calc(100vw / 1.777));
  height: var(--h);
}
.esc-lancement__corps {
  position: absolute; inset: 0; z-index: 1; justify-content: space-between;
  padding: clamp(.8rem, 2.4vh, 2.2rem);
  padding-left: max(clamp(1.4rem, 4vw, 2.6rem), calc(50vw - 590px));
  gap: clamp(.5rem, 1.6vh, 1.6rem);
}
/* `cover` : le décor remplit tout le panneau, quitte à être recadré — c'est ce
   remplissage qui fait l'atmosphère, une image posée au milieu de vide n'en
   produit aucune. Ancrée HAUT à droite, à 8 % près : sur un écran large, le panneau est bien plus
   allongé que l'image et `cover` rogne beaucoup en hauteur. Ancrer en bas
   décapitait les personnages ; ancrer en haut sacrifie le bas des jambes, ce que
   personne ne regarde. Entre un visage et une chaussure, le choix est vite fait. */
/* `contain` : l'image n'est jamais rognée. Calée à DROITE, elle laisse à gauche
   une bande de nuit dans laquelle le contenu se pose — au lieu d'un vide. */
.esc-lancement__decor {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  opacity: .95; object-position: right center;
  /* Les bords du fichier traçaient deux lignes nettes en travers du panneau :
     l'image s'arrêtait, la nuit reprenait, et la couture se voyait. Un masque
     éteint les premiers et les derniers pour-cent vers le fond, qui est la même
     nuit — l'image ne se termine plus, elle s'y dissout.
     Les bandes sont courtes et symétriques : plus bas on mangerait les pieds des
     personnages, plus haut on entamerait le plafond de la pièce. */
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, rgba(0, 0, 0, .5) 4%, #000 12%,
    #000 88%, rgba(0, 0, 0, .5) 96%, transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0%, rgba(0, 0, 0, .5) 4%, #000 12%,
    #000 88%, rgba(0, 0, 0, .5) 96%, transparent 100%);
}
/* Le voile ne sert qu'à rendre le texte lisible : il est donc franc à GAUCHE,
   où se trouvent le titre et le médaillon, et s'efface complètement à droite
   pour laisser le décor exister. */
.esc-lancement__corps {
  position: relative; padding: clamp(1.4rem, 4vw, 2.6rem);
  background: linear-gradient(100deg,
    rgba(12,16,32,.96) 24%, rgba(12,16,32,.74) 38%,
    rgba(12,16,32,.22) 56%, rgba(12,16,32,0) 72%);
  display: flex; flex-direction: column; gap: 1.6rem;
}
.esc-lancement__tete { display: flex; align-items: center; gap: 1.6rem; flex-wrap: wrap; }
.esc-lancement__tete h2 { margin: 0 0 .4rem; font-size: clamp(1.4rem, 3.4vw, 2rem); }
.esc-lancement__tete p { margin: 0; color: var(--text-soft); max-width: 34ch; }

/* Trois vignettes sur la première ligne, les deux dernières en dessous et
   centrées : la seconde rangée se cale alors dans les intervalles de la
   première, ce qui tient l'ensemble groupé au lieu de l'étirer. La largeur est
   bornée pour que le bloc reste dans la bande de nuit, à gauche du décor. */
/* Écran court ou étroit : on renonce à faire tenir la scène dans une hauteur
   imposée — un médaillon, cinq vignettes et un bouton ne rentrent pas dans
   450 px. Le panneau reprend alors la hauteur de son contenu, le décor passe
   derrière en remplissage, et la page défile un peu. Mieux vaut un défilement
   qu'un bouton coupé. */
@media (max-height: 820px), (max-width: 1200px) {
  .esc-lancement { height: auto; min-height: 24rem; }
  /* Le panneau reprend la hauteur de son contenu : « --h » ne dit plus où
     l'image commence, et le voile se poserait n'importe où. */
  .esc-lancement::before, .esc-lancement::after { display: none; }
  /* `relative`, surtout PAS `static` : le décor est en position absolue, et un
     élément positionné se peint au-dessus de ce qui ne l'est pas. En sortant le
     contenu du positionnement, l'image passait par-dessus le texte et
     interceptait les clics — le bouton devenait inopérant. */
  .esc-lancement__corps { position: relative; z-index: 1; }
  /* `contain` ici aussi : l'image doit rester entière, c'est la règle posée. */
  .esc-lancement__decor { object-fit: contain; }
}

.esc-casting {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(.5rem, 1.4vh, 1.1rem);
  max-width: 34rem; align-self: flex-start;
}
.esc-perso {
  flex: 0 0 calc((100% - 2 * clamp(.5rem, 1.4vh, 1.1rem)) / 3); text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: .35rem;
  opacity: 0; transform: translateY(10px); transition: opacity .45s ease, transform .45s ease;
}
.esc-perso.la { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .esc-perso { transition: none; } }
.esc-perso__rond {
  width: clamp(3rem, 8vh, 6.2rem); aspect-ratio: 1; border-radius: 50%; overflow: hidden;
  border: 2px solid var(--teinte); background: #0c1020;
}
.esc-perso__rond img { width: 100%; height: 100%; object-fit: cover; transform-origin: center; }
.esc-perso b { font-size: .92rem; }
.esc-perso span { font-size: .76rem; color: var(--text-mute); line-height: 1.35; }
/* Le personnage qu'on incarne se signale : sans cela, on cherche qui l'on est. */
.esc-perso--vous b::after {
  content: 'vous'; margin-left: .4rem; font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
  background: var(--teinte); color: #fff; padding: .1rem .4rem; border-radius: 999px; vertical-align: middle;
}

/* ══════════ Plan de séance : pupitre du formateur & page projetée ══════════ */
/* Deux colonnes : le sommaire de la séance à gauche (on y clique pour envoyer
   une page), l'écran en cours à droite. */
/* Une séance a un sommaire long : il doit défiler DANS sa colonne, sinon c'est
   toute la console qui s'allonge et le formateur perd sa barre de commandes. */
.animer--plan { height: 100vh; }
.animer-corps--plan { gap: 1.6rem; min-height: 0; }
.animer-plan {
  flex: 0 0 clamp(15rem, 24vw, 22rem); overflow-y: auto; min-height: 0;
  padding-right: .6rem; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: .25rem;
}
.animer-plan__ligne {
  display: flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  width: 100%; text-align: left; padding: .45rem .65rem; border-radius: 10px;
  border: 1px solid transparent; background: transparent; color: var(--text-soft);
  font: inherit; font-size: .88rem; line-height: 1.35; cursor: pointer;
}
.animer-plan__ligne--titre { flex-direction: row; align-items: center; }
.animer-plan__ligne:hover { background: var(--surface-2); color: var(--text); }
.animer-plan__ligne.on { background: color-mix(in srgb, var(--brand) 16%, transparent); border-color: var(--brand); color: var(--text); }
.animer-plan__ligne--titre { flex: 1; margin-top: 0; font-weight: 800; color: var(--text); font-size: .95rem; gap: .5rem; }
/* Le titre replie sa partie ; le bouton ▶ à côté l'envoie à la salle. */
.animer-plan__section { display: flex; align-items: center; gap: .2rem; margin-top: .7rem; }
.animer-plan__chevron { flex: none; width: .9rem; color: var(--text-mute); font-size: .8rem; }
.animer-plan__compte {
  flex: none; margin-left: auto; font-size: .72rem; color: var(--text-mute);
  background: var(--surface-2); border-radius: 999px; padding: .05rem .45rem;
}
.animer-plan__envoyer {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--text-mute); font-size: .7rem;
}
.animer-plan__envoyer:hover { border-color: var(--brand); color: var(--brand); background: var(--surface-2); }
.animer-plan__num {
  flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand); font-size: .78rem; font-weight: 800;
}
.animer-plan__txt { width: 100%; }
.animer-plan__src { width: 100%; font-size: .72rem; color: var(--text-mute); }
.animer-plan__exo { display: flex; flex-direction: column; }
.animer-plan__chips { display: flex; flex-wrap: wrap; gap: .3rem; padding: .1rem 0 .4rem 2.1rem; }
.animer-plan__chip {
  width: 1.7rem; height: 1.7rem; border-radius: 50%; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-soft); font: inherit; font-size: .76rem; cursor: pointer;
}
.animer-plan__chip:hover { border-color: var(--brand); color: var(--text); }
.animer-plan__chip.on { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 800; }

/* La page projetée : le contenu du parcours, tel quel, dans la zone centrale. */
.animer-page { flex: 1; overflow-y: auto; min-height: 0; }
.animer-page__src { font-size: .82rem; color: var(--text-mute); margin-bottom: .6rem; }
.animer-titre-ecran { text-align: center; }
.animer-titre-ecran span { text-transform: uppercase; letter-spacing: .12em; font-size: .8rem; color: var(--text-mute); }
.animer-titre-ecran h1 { font-size: clamp(1.8rem, 4vw, 3.2rem); margin-top: .6rem; }
.animer-q__completude { color: var(--text-mute); }
.animer-q__stats { display: inline-flex; gap: .7rem; margin-left: auto; }
.animer-q__stats .ok { color: var(--ok); }
.animer-q__stats .ko { color: var(--danger); }

/* Séance nominative : la salle, nom par nom, sous la question projetée. */
.animer-nominatif { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 1rem; }
.animer-nominatif__vide { color: var(--text-mute); font-size: .9rem; align-self: center; }
.animer-nominatif__voir {
  border: 1px solid var(--border); background: transparent; color: var(--text-soft);
  font: inherit; font-size: .82rem; padding: .25rem .7rem; border-radius: 999px; cursor: pointer;
}
.animer-nominatif__voir:hover { border-color: var(--brand); color: var(--text); }
.animer-qui {
  padding: .22rem .7rem; border-radius: 999px; font-size: .85rem;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-soft);
}
.animer-qui.juste { border-color: var(--ok); color: var(--ok); background: var(--ok-soft); }
.animer-qui.faux { border-color: var(--danger); color: var(--danger); background: var(--danger-soft); }

/* Relevé de séance : une ligne par personne, une colonne par question. */
.releve-wrap { overflow-x: auto; }
/* `separate` et non `collapse` : une colonne collante au-dessus d'un tableau à
   bordures fusionnées laisse passer le fond, et les cellules défilent sous le
   nom au lieu de disparaître derrière. */
.releve { border-collapse: separate; border-spacing: 0; width: 100%; font-size: .88rem; }
.releve th, .releve td { padding: .4rem .5rem; border-bottom: 1px solid var(--border); text-align: center; }
/* Le nom reste visible quand on fait défiler trente colonnes vers la droite :
   sans cela, on lit une ligne de croix sans savoir de qui elle est. */
.releve th:first-child, .releve td:first-child { text-align: left; white-space: nowrap; }
/* Seules la case « Participant » (qui coiffe les deux étages d'en-tête) et les
   cellules de nom sont collantes. Viser `th:first-child` tout court rendrait
   AUSSI collante la première case de la rangée des numéros, qui viendrait se
   garer par-dessus le nom. */
.releve__parties th:first-child, .releve tbody td:first-child {
  position: sticky; left: 0; z-index: 2; background: var(--surface);
  box-shadow: 1px 0 0 var(--border);
}
.releve thead th { color: var(--text-mute); font-weight: 600; font-size: .78rem; }
.releve td.ok { color: var(--ok); font-weight: 700; }
.releve td.ko { color: var(--danger); font-weight: 700; }
.releve td.vide { color: var(--text-mute); }
.releve tbody tr:hover { background: var(--surface-2); }
/* Repère de partie : un filet qui traverse tout le tableau au changement de
   chapitre, et le titre de la partie en chapeau des colonnes concernées. */
.releve th.debut-partie, .releve td.debut-partie { border-left: 2px solid var(--brand); }
.releve__parties th {
  font-size: .75rem; font-weight: 700; color: var(--text-soft);
  padding: .35rem .5rem; border-bottom: 1px solid var(--border);
  max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.releve__parties th:first-child, .releve__parties th:last-child { color: var(--text-mute); font-weight: 600; }

/* Écran de connexion à une séance réservée : titre, bouton et avis forment un
   seul bloc centré, sans vide au milieu. */
.live-connexion { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 1.5rem 1.2rem 2.5rem; }
.live-connexion .live-centre { flex: none; padding: 0 0 1.4rem; }
.live-connexion .live-notice { margin-top: 1.4rem; }

/* Avis « séance nominative » chez le participant : sobre, mais devant les yeux
   au moment où il rejoint — c'est là que se joue l'information des personnes. */
.live-notice {
  max-width: 34rem; margin: 1.6rem auto 0; padding: .9rem 1.1rem;
  border: 1px solid var(--border); border-radius: 14px; background: var(--surface-2);
  color: var(--text-soft); font-size: .86rem; line-height: 1.5;
}
.live-notice b { color: var(--text); }
.live-notice p { margin: .45rem 0 0; }
.live-notice a { display: inline-block; margin-top: .5rem; color: var(--brand); }

/* Page du parcours affichée chez le participant pendant une séance. */
.live-page { flex: 1; overflow-y: auto; padding: 1.2rem clamp(1rem, 4vw, 2.5rem) 2.5rem; max-width: min(96vw, 60rem); width: 100%; margin: 0 auto; }
.live-page__src { font-size: .8rem; color: var(--text-mute); margin-bottom: .7rem; }

@media (max-width: 900px) {
  /* Sur un petit écran, le pupitre passe au-dessus et se limite en hauteur :
     la page en cours doit rester lisible. */
  .animer-corps--plan { flex-direction: column; }
  .animer-plan { flex: none; max-height: 32vh; border-right: 0; border-bottom: 1px solid var(--border); padding-right: 0; }
}

.animer-commandes { display: flex; gap: .7rem; padding: 1rem 1.6rem; border-top: 1px solid var(--border); background: var(--surface); flex-wrap: wrap; justify-content: center; }
.animer-commandes .btn { font-size: 1.05rem; }

/* Gestionnaire d'animations (console) */
.anim-liste { display: grid; gap: .5rem; }
.anim-ligne { display: flex; align-items: center; gap: 1rem; padding: .8rem 1rem; border: 1px solid var(--border); border-radius: var(--radius); flex-wrap: wrap; }
.anim-ligne__ico { font-size: 1.7rem; flex: none; }
.anim-ligne__txt { flex: 1; min-width: 8rem; }
.anim-ligne__txt b { display: block; }
.anim-ligne__txt span { font-size: .82rem; color: var(--text-mute); }
.anim-ligne__actions { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
/* ══════════ Éditeur d'un plan de séance ══════════ */
.plan-section { margin: 1.4rem 0 .6rem; font-size: .95rem; }
.plan-liste { display: flex; flex-direction: column; gap: .35rem; }
.plan-item {
  display: flex; align-items: center; gap: .7rem; padding: .55rem .75rem;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
}
.plan-item--titre { background: var(--surface-2); }
.plan-plier { flex: none; color: var(--text-mute); }
.plan-item__compte { flex: none; font-size: .74rem; color: var(--text-mute); white-space: nowrap; }
/* Section repliée : le titre garde son relief, mais le bloc est visiblement clos. */
.plan-item--titre.plie { border-style: dashed; }
.plan-item--absent { border-color: var(--warn); }
.plan-item__num {
  flex: none; width: 1.6rem; height: 1.6rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand); font-size: .8rem; font-weight: 800;
}
.plan-item__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.plan-item__txt b { font-size: .92rem; }
.plan-item__txt span { font-size: .78rem; color: var(--text-mute); }
.plan-item__outils { flex: none; display: flex; gap: .25rem; }
.plan-titre__champ {
  flex: 1; border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 700;
  padding: .2rem .3rem; border-bottom: 1px dashed var(--border);
}
.plan-titre__champ:focus { outline: none; border-bottom-color: var(--brand); }
.plan-outils { margin-top: .7rem; }
.plan-ajout { border: 1px solid var(--border); border-radius: 12px; padding: .8rem; background: var(--surface-2); }
.plan-ajout__ligne { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .7rem; }
.plan-sel {
  flex: 1; min-width: 12rem; padding: .45rem .6rem; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: .88rem;
}
.plan-ecrans { display: flex; flex-direction: column; gap: .2rem; max-height: 16rem; overflow-y: auto; }
.plan-ecran { display: flex; align-items: center; gap: .55rem; padding: .35rem .5rem; border-radius: 8px; cursor: pointer; font-size: .85rem; }
.plan-ecran:hover { background: var(--surface); }
.plan-ecran__n { flex: none; color: var(--text-mute); font-size: .78rem; width: 4.6rem; }
.plan-ecran__t { flex: 1; min-width: 0; }
.plan-ecran__type { flex: none; font-size: .74rem; color: var(--text-mute); }

.anim-nouveau { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: 1.2rem; }

/* Éditeur de quiz */
.edq { border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.1rem; margin: .9rem 0; background: var(--surface-2); }
.edq__tete { display: flex; align-items: center; justify-content: space-between; margin-bottom: .6rem; }
.edq__outils { display: flex; gap: .3rem; }
.edq__options { margin: .3rem 0 .8rem; }
.edq__type { display: inline-flex; gap: .2rem; padding: .2rem; background: var(--bg-soft); border-radius: 999px; margin-bottom: .6rem; }
.edq__type-btn { border: 0; background: transparent; color: var(--text-soft); font-family: inherit; font-size: .84rem; font-weight: 600; padding: .4em .9em; border-radius: 999px; cursor: pointer; }
.edq__type-btn.on { background: var(--surface); color: var(--brand); box-shadow: var(--shadow-sm); }
.edq__hint { font-size: .82rem; color: var(--text-mute); margin-bottom: .5rem; }
.edq__opt-case input[type="radio"] { accent-color: var(--ok); }
.edq__opt { display: flex; align-items: center; gap: .5rem; margin-bottom: .45rem; }
.edq__opt-case { flex: none; display: inline-flex; align-items: center; cursor: pointer; }
.edq__opt-case input { width: 1.15rem; height: 1.15rem; accent-color: var(--ok); }
.edq__opt-txt { flex: 1; min-width: 0; padding: .55em .8em; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); font-family: inherit; }
.edq__opt-txt:focus { outline: 0; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.edq textarea, .edq input[type="text"] { font-family: inherit; }
.act[disabled] { opacity: .35; cursor: default; }

@media (max-width: 640px) {
  .animer-corps { padding: 1.2rem; }
  .animer-q h1 { font-size: 1.6rem; }
  .animer-barre__txt { font-size: 1.1rem; }
  .animer-bigcode { font-size: 4rem; }
  .animer-url { font-size: 1.6rem; }
}

/* ─────────────── Documents support (console) ─────────────── */
.doc-liste { display: grid; gap: .5rem; }
.doc-ligne {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .7rem .9rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface);
}
.doc-info { flex: 1; min-width: 0; }
.doc-nom { font-weight: 600; color: var(--text); text-decoration: none; word-break: break-all; }
.doc-nom:hover { color: var(--brand); }
.doc-meta { font-size: .8rem; color: var(--text-mute); margin-top: .15rem; }
.doc-actions { display: flex; align-items: center; gap: .5rem; flex: none; }

/* Dossiers de classement : une barre de pastilles au-dessus de la liste. Deux
   vues fixes (Tous, Non classés), puis les dossiers, puis l'ajout. */
.doc-dossiers { display: flex; flex-wrap: wrap; gap: .45rem; margin: 1.2rem 0 .8rem; }
.doc-puce {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .38rem .8rem; border-radius: 999px; cursor: pointer; font: inherit;
  font-size: .85rem; font-weight: 600; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: background .15s, border-color .15s, color .15s;
}
.doc-puce:hover { color: var(--text); border-color: color-mix(in srgb, var(--brand) 45%, var(--border)); }
.doc-puce.on { color: var(--brand); border-color: var(--brand); background: color-mix(in srgb, var(--brand) 13%, var(--surface)); }
.doc-puce__n {
  font-size: .72rem; font-weight: 700; min-width: 1.4rem; text-align: center;
  padding: .05rem .35rem; border-radius: 999px; background: var(--surface); color: var(--text-mute);
}
.doc-puce.on .doc-puce__n { color: var(--brand); }
.doc-puce--ajout { border-style: dashed; background: transparent; }
.doc-dossier-outils:empty { display: none; }
.doc-dossier-outils { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: 0 0 .8rem; }
.doc-dossier-outils__nom { font-weight: 700; margin-right: .3rem; }
.danger-txt { color: var(--danger); }
.doc-classer {
  max-width: 13rem; padding: .32rem .5rem; font: inherit; font-size: .82rem; cursor: pointer;
  color: var(--text); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
/* Même réaction que les boutons « ghost » voisins (Télécharger) : un voile et
   un contour qui s'affirment au survol. */
.doc-classer { transition: background .16s, border-color .16s; }
.doc-classer:hover {
  background: color-mix(in srgb, var(--text-mute) 9%, var(--surface-2));
  border-color: color-mix(in srgb, var(--text-mute) 65%, var(--border));
}
.doc-classer:focus-visible { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent); }
@media (max-width: 560px) { .doc-classer { max-width: 100%; flex: 1 1 100%; } }

/* ─────────────── Lien d'invitation, dans la console ─────────────── */
.lien-invit-label {
  display: block; font-size: .85rem; font-weight: 600;
  color: var(--text-soft); margin: 1.2rem 0 .4rem;
}
.lien-invit {
  width: 100%; padding: .75em .9em; border-radius: var(--radius-sm);
  border: 1px solid var(--brand); background: var(--brand-soft); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82rem; word-break: break-all;
}

/* ─────────────── Responsive ─────────────── */
@media (max-width: 900px) {
  .legal-champs { grid-template-columns: 1fr; gap: 0; }
  .legal-champs dt { border-bottom: 0; padding-bottom: 0; }
  .legal-champs dd { padding-top: .15rem; padding-bottom: .8rem; }
  .auth { grid-template-columns: 1fr; }
  /* Ni à gauche ni en bas : c'est Justine qui tient ces deux bords. */
  .auth__brand { padding: 2.6rem 2rem 0 0; }
  .auth__features { display: none; }
  .auth__brand h1 { font-size: clamp(1.5rem, 6.2vw, 2.3rem); }
  .auth__brand p { font-size: .95rem; max-width: none; }
  .split, .match-grid { grid-template-columns: 1fr; }
  .grid-cards.cols-2, .grid-cards.cols-3 { grid-template-columns: 1fr; }
  .card { padding: 1.6rem 1.3rem; }
  .game { padding: 1.4rem 1.2rem; }
  /* Sous 560 px, chaque retrait se paie en mots par ligne : la gouttière de la
     page, celle de la carte et celle de l'encadré se resserrent ensemble. */
  @media (max-width: 560px) {
    main { padding: 1rem .9rem .8rem; }
    .card { padding: 1.3rem 1rem; }
    .game { padding: 1.2rem .9rem; }
  }
  .hero-home { padding: 1.8rem 1.5rem; }
  .cert { padding: 1.8rem 1.2rem; }
  .theme-bar .nm { width: 108px; font-size: .82rem; }
}

/* ── Portrait étroit : la barre haute doit tenir sans déborder ──
   Un débordement horizontal élargit la fenêtre de mise en page, ce qui décale
   et tronque tous les éléments en position fixed (modales, bandeaux). */
@media (max-width: 600px) {
  .topbar__in { padding: .55rem .8rem; gap: .55rem; }
  .topbar__logo .brand-name { display: none; }
  .theme-switch { width: 56px; height: 30px; }
  .theme-switch .knob { width: 22px; height: 22px; }
  html[data-theme='dark'] .theme-switch .knob { transform: translateX(28px); }
  /* Sur téléphone, « Prénom NOM » mangerait la barre. Le prénom seul y tient,
     reste lisible, et vaut mieux qu'un nom coupé en son milieu. */
  .compte-chip { max-width: 8rem; height: 34px; padding: 0 .5rem 0 .7rem; }
  .compte-chip .nom { display: none; }
  .compte-chip .prenom { display: block; }
  .overlay { padding: .8rem; }
  .modal__head { padding: 1.2rem 1.3rem 1rem; }
  .modal__body { padding: 1.1rem 1.3rem 1.4rem; }
  /* Marges internes resserrées : ~13 px de largeur utile gagnés pour le titre. */
  .hero-home { padding: 1.6rem 1.1rem; }

  /* Écran de connexion : le bandeau de présentation ne doit pas repousser le
     bouton hors de l'écran. Titre resserré, accroche masquée. */
  /* Toujours sans marge à gauche ni en bas : Justine tient les deux bords. */
  .auth__brand { padding: 1.4rem 1.2rem 0 0; }
  .auth__logo { margin-bottom: 1.1rem; }
  .auth__logo .mark { width: 38px; height: 38px; border-radius: 11px; }
  .auth__brand h1 { font-size: 1.3rem; margin-bottom: 0; }
  .auth__brand p { display: none; }
  .auth__panel { padding: 1.3rem 1.2rem 1.8rem; }
  .auth__tabs { margin-bottom: 1.2rem; }
  .auth__card h2 { font-size: 1.25rem; }
}

/* ── Encart de reprise compact sur mobile ──
   Auparavant l'anneau passait SOUS le texte (encart très haut, mangeant tout
   l'écran). On le garde À CÔTÉ du texte (texte à gauche, anneau à droite), en
   version resserrée, pour une carte beaucoup plus courte. */
@media (max-width: 760px) {
  .hero-home { padding: 1.2rem 1.15rem; }
  .hero-home .row { flex-wrap: nowrap; flex-direction: row; align-items: center; gap: 1rem; }
  .hero-home .hero-text { flex: 1; min-width: 0; width: auto; text-align: left; }
  .hero-home h1 { font-size: clamp(1.15rem, 4.6vw, 1.5rem); }
  .hero-home p { font-size: .86rem; margin-bottom: .9rem; }
  .hero-home .hero-text p { margin-left: 0; margin-right: 0; max-width: none; }
  .hero-home .hero-actions { justify-content: flex-start; }
  .hero-home .btn.lg { padding: .55em 1.1em; font-size: .9rem; }
  .ring { width: 84px; height: 84px; }
  .ring b { font-size: 1.1rem; }
  .ring small { font-size: .54rem; letter-spacing: -.02em; }
}

/* ── Écrans peu hauts : petits téléphones, ou paysage ──
   Le critère pertinent est la hauteur, pas la largeur : c'est elle qui décide
   si le bouton de connexion tient à l'écran. S'applique donc aussi en paysage. */
@media (max-height: 640px) {
  .auth__brand { padding: 1rem 1.2rem 0 0; }
  .auth__logo { margin-bottom: .7rem; font-size: 1rem; }
  .auth__logo .mark { width: 34px; height: 34px; border-radius: 10px; }
  .auth__brand h1 { font-size: 1.15rem; margin-bottom: 0; }
  .auth__brand p { display: none; }
  .auth__panel { padding: 1rem 1.2rem 1.4rem; }
  .auth__card h2 { font-size: 1.15rem; margin-bottom: .3rem; }
  .auth__card .auth__intro { display: none; }
  .auth__tabs { margin-bottom: .9rem; }
  .auth .field { margin-bottom: .75rem; }
}

/* Peu haut mais large — typiquement un téléphone en paysage : on remet le
   bandeau à côté du formulaire au lieu de l'empiler dessus. La hauteur
   disponible est alors entièrement dédiée au formulaire. */
@media (max-height: 640px) and (min-width: 680px) {
  .auth { grid-template-columns: 1fr 1fr; }
  .auth__brand h1 { font-size: 1.4rem; }
  .auth__brand p { display: block; font-size: .9rem; max-width: 32ch; }
}

@media print {
  .topbar, .footer, .btn, .no-print { display: none !important; }
  body { background: #fff; }
  main { padding: 0; max-width: none; }
  /* Une attestation s'imprime sur une page A4, et les aplats de couleur doivent
     survivre au reglage « economiser l'encre » des navigateurs — sans quoi le
     filet, la pastille et le lisere disparaissent. */
  @page { size: A4 portrait; margin: 12mm; }
  .cert {
    box-shadow: none; max-width: none; padding: 1.8rem 2.2rem 1.4rem;
    break-inside: avoid; -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  /* Le pire cas — huit modules complementaires — doit tenir sur la page :
     l'attestation se resserre a l'impression, sans rien retirer. */
  .cert h2 { font-size: 2rem; }
  .cert .who { font-size: 1.45rem; }
  /* La hauteur de la page imprimée, et non celle calculée pour l'écran :
     l'attestation remplit la feuille quel que soit son contenu. */
  .cert { min-height: 265mm; gap: 1rem; }
  .cert-corps { margin-top: .7rem; }
  .cert .cert-niveau { margin-top: .7rem; }
  .cert-modules ul { gap: .2rem; }
  .cert-modules li { break-inside: avoid; font-size: .8rem; }
}

/* ══════════════════════════ Fonds d'écran ══════════════════════════ */
/* Trois fonds par écran, calés sur la largeur du rail plutôt que sur une taille
   fixe : la troisième vignette s'arrête donc exactement au bord, sans quatrième
   tronquée. La soustraction est celle des deux gouttières qui les séparent. */
.fond-piste { grid-auto-columns: calc((100% - 2 * 1.1rem) / 3); }
/* Sous 860 px, trois vignettes deviendraient illisibles : on repasse à une
   seule, largement débordante, ce qui rend le balayage évident. */
@media (max-width: 860px) { .fond-piste { grid-auto-columns: min(82%, 22rem); } }
.fond-carte {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}
.fond-carte:hover {
  box-shadow: var(--shadow);
  transform: translateY(-2px);
  border-color: var(--fa);
}
.fond-apercu {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
  border-bottom: 3px solid var(--fa);
  overflow: hidden;
}
.fond-apercu img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .3s ease;
}
.fond-carte:hover .fond-apercu img { transform: scale(1.03); }
.fond-info {
  padding: .85rem 1rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .35rem;
  flex: 1;            /* occupe le reste de la carte : sans quoi rien à pousser */
}
.fond-saison {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--fa);
}
.fond-slogan {
  font-size: 1rem;
  line-height: 1.25;
  color: var(--text);
}
/* « margin-top: auto » colle le bouton au bas de la carte : un slogan sur deux
   lignes ne le fait plus danser d'une vignette à l'autre. Le retrait vient d'un
   padding, qu'une marge automatique effacerait. */
.fond-dl {
  margin-top: auto;
  padding-top: .55rem;
  align-self: flex-start;
  text-decoration: none;
}

/* ══════════════════════ Planches de bande dessinée ══════════════════════ */
/* Les quatre planches tiennent ensemble : la largeur se calcule sur celle du
   rail, gouttières déduites, plutôt que sur une taille fixe qui laissait la
   quatrième coupée au bord. Elles sont en portrait, donc plus étroites que les
   fonds — et la vignette garde son format A4, un 16/9 aurait rogné le pied de
   page, là où se trouvent le QR et l'adresse.
   Quatre tiennent de front ; depuis la cinquième planche, les suivantes se
   rejoignent en faisant défiler, et le rail a retrouvé ses flèches. */
.bd-piste { grid-auto-columns: calc((100% - 3 * 1.1rem) / 4); }
@media (max-width: 860px) { .bd-piste { grid-auto-columns: min(78%, 19rem); } }

.bd-carte {
  margin: 0; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden;
  display: flex; flex-direction: column;
  transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}
.bd-carte:hover { box-shadow: var(--shadow); transform: translateY(-2px); border-color: var(--fa); }

.bd-apercu {
  display: block; aspect-ratio: 210 / 297; background: var(--surface-2);
  border-bottom: 3px solid var(--fa); overflow: hidden;
}
.bd-apercu img {
  width: 100%; height: 100%; object-fit: cover; object-position: top;
  display: block; transition: transform .3s ease;
}
.bd-carte:hover .bd-apercu img { transform: scale(1.02); }

.bd-info { padding: .85rem 1rem 1rem; display: flex; flex-direction: column; gap: .35rem; flex: 1; }
.bd-num {
  font-size: .78rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fa);
}
.bd-titre { font-size: 1.05rem; line-height: 1.25; color: var(--text); }
.bd-accroche { margin: 0; font-size: .88rem; line-height: 1.45; color: var(--text-soft); }
.bd-fiche {
  font-size: .82rem; font-weight: 600; color: var(--brand);
  text-decoration: none; margin-top: .15rem;
}
.bd-fiche:hover { text-decoration: underline; }
/* Même calage que les fonds d'écran : l'accroche d'une planche fait deux ou
   trois lignes selon la planche. */
.bd-dl { margin-top: auto; padding-top: .55rem; align-self: flex-start; text-decoration: none; }

/* ═══════════════════════════════ Livre d'or ═══════════════════════════════ */
.lo-banniere {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.1rem 1.2rem 1.2rem; margin-bottom: 1.6rem;
}
.lo-banniere__tete { display: flex; align-items: baseline; gap: .8rem; margin-bottom: .7rem; }
.lo-banniere__tete h3 { margin: 0; font-size: 1.02rem; }
/* La jauge dit la place qui reste : cinq au plus, c'est le format de la bannière. */
.lo-jauge {
  font-size: .8rem; font-weight: 700; font-variant-numeric: tabular-nums;
  padding: .1rem .55rem; border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 12%, transparent); color: var(--brand);
}
.lo-jauge.plein { background: color-mix(in srgb, var(--ok) 15%, transparent); color: var(--ok); }
.lo-publies { list-style: none; margin: .9rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; }
.lo-publie {
  display: grid; grid-template-columns: 2.2rem minmax(0, 1fr); gap: .6rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .7rem .8rem;
}
.lo-publie__rang { display: flex; flex-direction: column; align-items: center; gap: .15rem; }
.lo-publie__rang b { font-size: .95rem; }
.lo-publie__corps { display: flex; flex-direction: column; gap: .45rem; min-width: 0; }
.lo-publie__pied { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.lo-tiret { color: var(--text-mute); }
/* La signature se lit, elle ne se saisit pas : c'est celle que l'auteur a
   acceptée. Un cadenas discret dit pourquoi on ne peut pas la corriger. */
.lo-signature {
  flex: 1; min-width: 10rem; font-size: .9rem; font-weight: 600; color: var(--text-soft);
}
.lo-signature::after { content: ' 🔒'; font-size: .72rem; opacity: .55; }
.lo-compteur { font-size: .74rem; color: var(--text-mute); font-variant-numeric: tabular-nums; }
.lo-note { display: inline-flex; align-items: center; gap: .3rem; }
.lo-note .imp-etoiles { transform: scale(.78); transform-origin: left center; }

.lo-filtres { display: flex; flex-direction: column; gap: .6rem; margin-bottom: 1rem; }
.lo-filtres .savoir-onglets { margin-bottom: 0; flex-wrap: wrap; }
.lo-compte { font-size: .82rem; color: var(--text-mute); }
.lo-cartes { display: flex; flex-direction: column; gap: .6rem; margin-bottom: 1rem; }
.lo-carte {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .8rem 1rem;
}
/* Un avis déjà publié se reconnaît dans la liste : on ne le choisit pas deux fois. */
.lo-carte.en-banniere { border-color: color-mix(in srgb, var(--ok) 55%, var(--border)); }
.lo-carte__tete { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.lo-provenance { font-size: .8rem; color: var(--text-soft); }
.lo-date { margin-left: auto; font-size: .76rem; color: var(--text-mute); }
.lo-carte__texte { margin: .45rem 0 .6rem; font-size: .95rem; line-height: 1.5; }
.lo-carte__pied { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.lo-auteur { font-size: .8rem; color: var(--text-soft); margin-right: auto; }
.lo-badge { font-size: .8rem; font-weight: 700; color: var(--ok); }

/* ── La bannière, sur l'écran de connexion ────────────────────────────────
   Discrète : elle accompagne la carte de connexion, elle ne la concurrence
   pas. Une citation à la fois, qui entre en fondu. */
.temoins {
  /* La largeur de la carte de connexion, à l'identique : le panneau centre ses
     enfants à la taille de leur contenu, et la bannière changeait de largeur
     à chaque citation. Alignée sur la carte, elle forme un bloc avec elle. */
  width: min(430px, 100%); box-sizing: border-box;
  margin-top: 1.4rem; padding: 1rem 1.2rem;
  /* Le liseré prend la teinte de la carte affichée, et change avec elle au
     rythme du glissement : la couleur accompagne la carte au lieu de sauter. */
  border-left: 3px solid var(--temoin-teinte, var(--justine));
  transition: border-color 1.2s cubic-bezier(.22, .8, .3, 1);
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  border-radius: var(--radius-sm);
}
.temoins__titre {
  display: block; margin-bottom: .45rem;
  font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-mute);
}
/* Toutes les citations dans la même case : la case prend la hauteur de la plus
   longue, et rien autour ne bouge quand elles se succèdent.
   Le passage de l'une à l'autre est un GLISSEMENT : la courante sort par un
   côté pendant que la suivante entre par l'autre. La case coupe ce qui déborde,
   pour que les cartes n'apparaissent qu'une fois dans le cadre.
   La courbe démarre vite et freine à l'arrivée — une carte qui glisse à vitesse
   constante s'arrête d'un coup, et l'œil le perçoit comme un à-coup. */
.temoins__pile { display: grid; overflow: hidden; }
.temoins__citation {
  grid-area: 1 / 1; margin: 0;
  opacity: 0; visibility: hidden;
  transform: translateX(var(--depart, 100%));
  transition: transform 1.2s cubic-bezier(.22, .8, .3, 1), opacity .9s ease,
              visibility 0s linear 1.2s;
}
.temoins__citation.on {
  opacity: 1; visibility: visible; transform: translateX(0);
  transition: transform 1.2s cubic-bezier(.22, .8, .3, 1), opacity .9s ease .2s, visibility 0s;
}
.temoins__citation.sort { transform: translateX(var(--sortie, -100%)); }
/* Replacement instantané d'une carte avant son entrée : voir montrer(). */
.temoins__citation.sans-transition { transition: none !important; }
.temoins__citation p { margin: 0; font-size: .95rem; line-height: 1.5; font-style: italic; color: var(--text); }
.temoins__citation footer { margin-top: .4rem; font-size: .82rem; color: var(--text-soft); }
.temoins__points { display: flex; gap: .4rem; margin-top: .6rem; }
.temoins__point {
  width: .55rem; height: .55rem; padding: 0; border: 0; border-radius: 50%; cursor: pointer;
  background: color-mix(in srgb, var(--text-mute) 40%, transparent);
}
.temoins__point { transition: background-color .4s ease; }
.temoins__point.on { background: var(--temoin-teinte, var(--justine)); }
/* Sans animation demandée : pas de glissement, la citation change sur place. */
@media (prefers-reduced-motion: reduce) {
  .temoins__citation, .temoins__citation.on, .temoins__citation.sort { transition: none; transform: none; }
  .temoins, .temoins__point { transition: none; }
}

/* Pastille d'une réponse de fréquence (Jamais → Toujours), dans les
   commentaires des statistiques. */
.fbq-pill {
  font-size: .78rem; font-weight: 600; padding: .15rem .6rem; border-radius: 999px;
  color: #fff; background: var(--ton-c, var(--info));
}
.fbq-pill.ton-danger { --ton-c: var(--danger); }
.fbq-pill.ton-warn   { --ton-c: var(--warn); }
.fbq-pill.ton-info   { --ton-c: var(--info); }
.fbq-pill.ton-ok     { --ton-c: var(--ok); }

/* ══════════════ Statistiques et avancement ═══════════════════════════════
   Les marques sont fines et les axes discrets : ce qu'on lit, ce sont les
   longueurs, pas la décoration. Les valeurs sont écrites en clair à côté de
   chaque barre — la couleur ne porte jamais seule une information. */
/* ─────────── Le suivi d'une structure (responsable) ───────────
   Une page en lecture : des chiffres, un tableau, et la mention de ce qui ne
   s'y trouve pas. Rien n'y est cliquable, d'où l'absence totale d'effet. */
.struc-jauge {
  height: 7px; border-radius: 99px; background: var(--border); overflow: hidden; min-width: 72px;
}
.struc-jauge i { display: block; height: 100%; border-radius: 99px; background: var(--brand); transition: width .5s ease; }
.data td small { display: block; margin-top: .2rem; font-size: .72rem; color: var(--text-mute); }
.struc-vide { color: var(--text-mute); }
.vide { color: var(--text-mute); font-size: .92rem; }
/* Un compte qui n'a pas encore activé son accès : la ligne s'estompe, elle
   n'attend pas un résultat mais une relance. */
.struc-inactif td { opacity: .62; }
.struc-inactif .pill { opacity: 1; }

.stat-tuiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .8rem; margin: 1.2rem 0 1.8rem;
}
.stat-tuile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .9rem 1rem;
  display: flex; flex-direction: column; gap: .15rem;
}
.stat-tuile b { font-size: 1.7rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-tuile span { font-size: .82rem; color: var(--text-soft); }
.stat-tuile.ton-danger b { color: var(--danger); }
.stat-tuile.ton-ok b { color: var(--ok); }

.stat-bloc { margin-bottom: 2.2rem; }
.stat-bloc h3 {
  display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  font-size: 1rem; margin: 0 0 .8rem;
}
.stat-bloc__n { font-size: .82rem; font-weight: 400; color: var(--text-mute); }

/* ── Une barre de pourcentage ────────────────────────────────────────────
   4 px d'arrondi à l'extrémité des données, la piste à peine teintée, et un
   repère vertical au seuil de validation : sans lui, « 58 % » ne se juge pas. */
.stat-barre { display: flex; align-items: center; gap: .6rem; }
.stat-barre__piste {
  position: relative; flex: 1; height: .55rem; min-width: 4rem;
  background: var(--surface-2); border-radius: 999px; overflow: hidden;
}
.stat-barre__part {
  height: 100%; border-radius: 0 4px 4px 0; background: var(--brand);
  transition: width .3s ease;
}
.stat-barre__part.sous-seuil { background: var(--danger); }
.stat-barre__seuil {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: color-mix(in srgb, var(--text-mute) 55%, transparent);
}
.stat-barre__val { font-size: .85rem; font-variant-numeric: tabular-nums; min-width: 3ch; text-align: right; }

/* ── Les sections rétractables ────────────────────────────────────────────
   Un seul dessin, deux noms : « stat-section » dans les statistiques,
   « pliable » ailleurs dans l'administration — aujourd'hui « Participants et
   suivi ». Une page tient en quelques titres : tout est là, rien ne s'impose. */
.stat-section, .pliable {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); margin-bottom: 1rem;
}
.stat-section > summary, .pliable > summary {
  display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  padding: 1rem 1.2rem; cursor: pointer; list-style: none;
}
.stat-section > summary::-webkit-details-marker,
.pliable > summary::-webkit-details-marker,
.stat-groupe > summary::-webkit-details-marker,
.stat-chap > summary::-webkit-details-marker { display: none; }
/* Le chevron dit « ceci se déplie », et tourne avec l'état. */
.stat-section > summary::before, .pliable > summary::before, .stat-groupe > summary::before {
  content: '›'; display: inline-block; width: 1ch; color: var(--text-mute);
  font-weight: 700; transition: transform .16s;
}
.stat-section[open] > summary::before, .pliable[open] > summary::before, .stat-groupe[open] > summary::before { transform: rotate(90deg); }
.stat-section__t, .pliable__t { font-weight: 700; font-size: 1.02rem; }
.stat-section__r, .pliable__r { font-size: .82rem; color: var(--text-mute); }
.stat-section__corps, .pliable__corps { padding: 0 1.2rem 1.2rem; }

/* ── Une session ─────────────────────────────────────────────────────────── */
.stat-groupe { border-top: 1px solid var(--border); }
.stat-groupe > summary {
  display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap;
  padding: .8rem 0; cursor: pointer; list-style: none;
}
.stat-groupe__t { font-weight: 600; }
.stat-groupe__r { font-size: .8rem; color: var(--text-mute); }
.stat-alerte {
  font-size: .75rem; font-weight: 700; color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-radius: 999px; padding: .08rem .5rem;
}

/* ── Un chapitre : une ligne, qui se déplie ────────────────────────────────
   Quatre colonnes alignées d'un chapitre à l'autre, pour que l'œil compare en
   descendant : la même grille sert à la ligne d'en-tête. */
.stat-ligne {
  display: grid; grid-template-columns: minmax(0, 2.2fr) minmax(8rem, 1.6fr) 7.5rem 3.2rem;
  align-items: center; gap: .9rem; padding: .5rem .6rem;
}
.stat-ligne--entete {
  font-size: .72rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-mute); padding-top: 0; padding-bottom: .25rem;
}
.stat-chap { border-radius: var(--radius-sm); border-left: 3px solid transparent; }
.stat-chap > summary { cursor: pointer; list-style: none; border-radius: var(--radius-sm); }
.stat-chap > summary:hover { background: var(--surface-2); }
.stat-chap[open] { background: var(--surface-2); }
.stat-chap--alerte { border-left-color: var(--danger); }
.stat-ligne__titre { display: flex; align-items: center; gap: .4rem; font-size: .92rem; min-width: 0; }
.stat-ligne__titre .stat-alerte { padding: 0 .3rem; }
.stat-ligne__note { display: flex; align-items: center; gap: .35rem; font-variant-numeric: tabular-nums; }
.stat-ligne__note .imp-etoiles { transform: scale(.82); transform-origin: left center; }
.stat-ligne__coms { font-size: .8rem; color: var(--brand); text-align: right; white-space: nowrap; }
.stat-vide { font-size: .8rem; color: var(--text-mute); }

.stat-detail { padding: .2rem .8rem 1rem 1.2rem; }
.stat-detail__chiffres {
  display: flex; gap: .4rem 1.2rem; flex-wrap: wrap;
  font-size: .8rem; color: var(--text-soft); margin-bottom: .6rem;
}
.stat-chap__sans { margin: .6rem 0 0; font-size: .85rem; color: var(--text-mute); }

/* ── Les commentaires ────────────────────────────────────────────────────── */
.stat-coms { list-style: none; margin: .8rem 0 .6rem; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.stat-com {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: .6rem .8rem;
}
.stat-com__tete { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.stat-com__tete .imp-etoiles { transform: scale(.75); transform-origin: left center; }
.stat-com__qui { margin-left: auto; font-size: .76rem; color: var(--text-mute); }
/* La corbeille ferme la ligne, discrète au repos : c'est un geste rare, qui ne
   doit pas se proposer avant qu'on le cherche. */
.stat-com__sup { flex: none; opacity: .45; transition: opacity .16s; }
.stat-com:hover .stat-com__sup, .stat-com__sup:focus-visible { opacity: 1; }
.stat-com__txt { margin: .35rem 0 0; font-size: .9rem; line-height: 1.45; }
.stat-com__prec { font-style: italic; color: var(--text-soft); }

/* ══════════════ Résultats par structure et par session — le datagrid ═════════
   Trois niveaux dans un seul tableau : le fond et la graisse disent le niveau
   avant même l'indentation, pour qu'on ne confonde jamais une structure avec
   une session qu'on vient de déplier. */
.grid-res__barre { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.grid-res__barre .savoir-onglets { margin-bottom: 0; }
.grid-res__filtre { flex: 1; min-width: 14rem; padding: .55em .9em; font-size: .9rem; }
.grid-res__compte { font-size: .8rem; color: var(--text-mute); margin-left: auto; }

.grid-res__table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.grid-res__table th {
  position: sticky; top: 0; z-index: 1; background: var(--surface);
  font-size: .74rem; letter-spacing: .03em; text-transform: uppercase; white-space: nowrap;
}
.grid-res__table th.triable { cursor: pointer; user-select: none; }
.grid-res__table th.triable:hover { color: var(--brand); }
.grid-res__table th.trie { color: var(--brand); }
.grid-res__fleche { font-size: .7rem; }
.grid-res__table td { padding: .45rem .6rem; vertical-align: top; border-bottom: 1px solid var(--border); }
.grid-res__table td.num, .grid-res__table th.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Niveau 1 : le groupe principal, franc. Niveau 2 : teinté. Niveau 3 : la personne, léger. */
.grid-res__l--1 { background: var(--surface-2); }
.grid-res__l--1 td { font-weight: 700; }
.grid-res__l--2 { background: color-mix(in srgb, var(--surface-2) 45%, var(--surface)); }
.grid-res__l--2 td { font-weight: 600; }
.grid-res__l--3 td { font-weight: 400; }
.grid-res__table tbody tr:hover { background: color-mix(in srgb, var(--brand) 6%, var(--surface)); }

.grid-res__cellnom { display: flex; align-items: flex-start; gap: .35rem; min-width: 14rem; }
.grid-res__plier {
  flex: none; width: 1.4rem; height: 1.4rem; padding: 0; border: 0; background: none;
  border-radius: 4px; cursor: pointer; color: var(--text-soft); font-size: .8rem; line-height: 1.4rem;
}
.grid-res__plier:hover { background: color-mix(in srgb, var(--brand) 12%, transparent); color: var(--brand); }
.grid-res__plier--vide { cursor: default; }
.grid-res__email { display: block; font-size: .74rem; color: var(--text-mute); font-weight: 400; }
/* Le statut ne passe pas par la couleur seule : l'icône ⚠️ le dit aussi. */
.grid-res__bas { color: var(--danger); }
.grid-res__fragiles { font-size: .8rem; color: var(--text-soft); font-weight: 400 !important; min-width: 12rem; }
.grid-res__plus { color: var(--text-mute); }

.stat-bloc h4 {
  display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  font-size: .95rem; margin: .4rem 0 .6rem;
}

@media (max-width: 700px) {
  /* Sur un écran étroit, la ligne passe sur deux rangs : le titre seul, puis
     les mesures — quatre colonnes n'y tiendraient pas. */
  .stat-ligne { grid-template-columns: 1fr auto auto; }
  .stat-ligne__titre { grid-column: 1 / -1; }
  .stat-ligne--entete { display: none; }
}

/* ── Répartition d'une échelle ordonnée ──────────────────────────────────
   Barre empilée plutôt que camembert : une longueur se compare, une part de
   disque non — et un camembert ne sait pas porter un ordre. Deux pixels de
   fond séparent les segments, faute de quoi deux teintes voisines se lisent
   comme une seule. */
.stat-freq { margin-top: .9rem; }
.stat-freq__barre { display: flex; gap: 2px; height: 1.35rem; border-radius: 4px; overflow: hidden; }
.stat-freq__part {
  display: grid; place-items: center; min-width: 1.6rem;
  font-size: .76rem; font-weight: 700; color: #fff;
  background: var(--ton-c, var(--info));
}
.stat-freq__legende { display: flex; gap: .9rem; flex-wrap: wrap; margin-top: .4rem; font-size: .78rem; color: var(--text-soft); }
.stat-freq__cle { display: inline-flex; align-items: center; gap: .35rem; }
.stat-puce { width: .6rem; height: .6rem; border-radius: 2px; background: var(--ton-c, var(--info)); }
/* Teintes validées (bande de luminosité, plancher de chroma, séparation pour
   les daltonismes, contraste sur le fond) — mesurées par le validateur de la
   charte de dataviz, jamais choisies à l'œil.
   Elles sont posées SOUS « .stat-freq » et non sur « .ton-danger » nu : ces
   classes de ton servent déjà ailleurs dans cette feuille, et une variable
   déclarée globalement s'y serait invitée. */
.stat-freq .ton-danger { --ton-c: #e11d48; }
.stat-freq .ton-warn   { --ton-c: #f79009; }
.stat-freq .ton-info   { --ton-c: #0ea5e9; }
.stat-freq .ton-ok     { --ton-c: #12b76a; }
/* Le sombre n'est pas un renversement du clair : ce sont d'autres pas de la
   même rampe, validés contre le fond sombre de l'application — les teintes du
   mode clair y sortaient de la bande de luminosité. */
html[data-theme='dark'] .stat-freq .ton-danger { --ton-c: #e11d48; }
html[data-theme='dark'] .stat-freq .ton-warn   { --ton-c: #d97706; }
html[data-theme='dark'] .stat-freq .ton-info   { --ton-c: #0284c7; }
html[data-theme='dark'] .stat-freq .ton-ok     { --ton-c: #059669; }

/* ── L'entonnoir ─────────────────────────────────────────────────────────── */
.stat-entonnoir { display: flex; flex-direction: column; gap: .35rem; }
.stat-etape { display: flex; align-items: center; gap: .6rem; }
.stat-etape__r {
  width: 1.5rem; flex: none; text-align: right;
  font-size: .78rem; color: var(--text-mute); font-variant-numeric: tabular-nums;
}
.stat-etape__n { min-width: 2.5ch; font-size: .85rem; font-variant-numeric: tabular-nums; }

/* ── Les métiers ──────────────────────────────────────────────────────────
   Pastille dans le tableau et le rapport d'import, cases dans la fiche et la
   pré-inscription : un seul vocabulaire visuel pour le référentiel. */
.pill.metier {
  background: color-mix(in srgb, var(--teal) 12%, var(--surface));
  color: var(--text-soft); border: 1px solid color-mix(in srgb, var(--teal) 30%, var(--border));
}
/* Les fonctions en tags à bascule : la case reste dans le formulaire (et au
   clavier), mais c'est la pastille qui montre l'état. Intitulé court à
   l'écran, libellé complet au survol. */
.metier-cases { display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem; margin: .4rem 0; }
.field .metier-case {
  position: relative; display: inline-flex; align-items: center; margin: 0;
  padding: .32rem .75rem; border-radius: 999px; cursor: pointer; user-select: none;
  font-size: .82rem; font-weight: 600; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: background .15s, border-color .15s, color .15s;
}
.metier-case input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.field .metier-case:hover { border-color: color-mix(in srgb, var(--teal) 50%, var(--border)); color: var(--text); }
.field .metier-case:has(input:checked) {
  color: var(--teal); border-color: var(--teal);
  background: color-mix(in srgb, var(--teal) 14%, var(--surface));
}
.field .metier-case:has(input:checked)::after { content: '✓'; margin-left: .4rem; font-weight: 700; }
.metier-case:has(input:focus-visible) { outline: 2px solid var(--teal); outline-offset: 2px; }
/* Sur PC, chaque ligne de tags occupe toute la largeur : les pastilles
   s'étirent pour combler l'espace plutôt que de laisser un vide à droite. */
@media (min-width: 720px) {
  .field .metier-case { flex: 1 1 auto; justify-content: center; }
}
.imp-metiers { display: flex; flex-wrap: wrap; gap: .35rem; margin: .6rem 0 0; }
.imp-format { margin: .8rem 0 0; font-size: .88rem; color: var(--text-soft); }
.imp-format summary { cursor: pointer; font-weight: 600; color: var(--brand); }
.imp-format p { margin: .5rem 0 0; }
.imp-format__metiers { margin: .4rem 0 0; padding-left: 1.2rem; columns: 2; }
@media (max-width: 560px) { .imp-format__metiers { columns: 1; } }

/* ══════════════ Avis de fin de chapitre ══════════════════════════════════ */
.fb-page {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: clamp(1.2rem, 3vw, 2rem); margin-top: 1.2rem;
}
.fb-tete { display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 1.6rem; }
.fb-ico {
  flex: none; width: 2.8rem; height: 2.8rem; border-radius: 50%;
  display: grid; place-items: center; font-size: 1.4rem;
  background: color-mix(in srgb, var(--brand) 12%, var(--surface));
}
.fb-tete h3 { margin: 0 0 .25rem; font-size: 1.15rem; }
.fb-tete p { margin: 0; color: var(--text-soft); font-size: .92rem; }

.fb-form { display: flex; flex-direction: column; gap: 1.4rem; }
.fb-bloc { display: flex; flex-direction: column; gap: .5rem; }
/* Le commentaire n'apparaît que si la note n'est pas maximale. Il se glisse
   entre deux blocs déjà en place, sans liseré : sur un téléphone, le retrait
   coupait le libellé en deux lignes pour trois pixels de couleur. */
.fb-bloc--conditionnel { animation: fade .18s; }
.fb-lbl { font-weight: 600; font-size: .95rem; }
/* Le témoignage : proposé à qui a aimé le chapitre. */
.fb-bloc--temoignage { animation: fade .18s; }
/* La case d'accord : le texte est long parce qu'il dit exactement à quoi l'on
   consent — il s'aligne donc sous la case, pas autour. */
.fb-accord {
  display: grid; grid-template-columns: auto 1fr; gap: .55rem; align-items: start;
  font-size: .86rem; color: var(--text-soft); line-height: 1.4; cursor: pointer; margin-top: .2rem;
}
.fb-accord input { margin-top: .18rem; width: 1rem; height: 1rem; accent-color: var(--ok); }
.fb-obl {
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--danger); background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-radius: 999px; padding: .12rem .5rem; vertical-align: .08em;
}
/* La mention « (facultatif) » passe sous son libellé : accolée, elle le
   faisait déborder sur deux lignes dès qu'on lit sur un téléphone. */
.fb-opt { display: block; font-size: .8rem; font-weight: 400; color: var(--text-mute); }
.fb-aide, .fb-question { margin: 0; font-size: .88rem; color: var(--text-soft); line-height: 1.45; }
.fb-question { font-style: italic; }
.fb-texte {
  width: 100%; box-sizing: border-box; resize: vertical;
  font-family: inherit; font-size: .95rem; line-height: 1.5;
  padding: .7rem .8rem; border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  max-height: 11lh;                   /* dix lignes de saisie, pas davantage */
}
.fb-texte:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand); }
.fb-actions { display: flex; gap: .7rem; justify-content: space-between; align-items: center; flex-wrap: wrap; }

/* ── Le curseur de fréquence ──────────────────────────────────────────────
   Quatre crans, et la teinte de la réponse court du rouge au vert. Les
   libellés sont des BOUTONS : le curseur se manie à la souris, au clavier et
   au doigt, mais cliquer le mot voulu reste le geste le plus direct — et c'est
   le seul qui fonctionne sur un écran tactile étroit. */
.freq { display: flex; flex-direction: column; gap: .55rem; }
.freq-piste {
  -webkit-appearance: none; appearance: none; width: 100%; height: .5rem;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border);
  cursor: pointer; accent-color: var(--brand);
}
.freq-piste.ton-danger { background: color-mix(in srgb, var(--danger) 32%, var(--surface-2)); }
.freq-piste.ton-warn   { background: color-mix(in srgb, var(--warn) 34%, var(--surface-2)); }
.freq-piste.ton-info   { background: color-mix(in srgb, var(--info) 34%, var(--surface-2)); }
.freq-piste.ton-ok     { background: color-mix(in srgb, var(--ok) 34%, var(--surface-2)); }
.freq-piste::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 1.25rem; height: 1.25rem; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--text-soft);
  box-shadow: var(--shadow-sm); cursor: grab;
}
.freq-piste::-moz-range-thumb {
  width: 1.25rem; height: 1.25rem; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--text-soft); cursor: grab;
}
.freq-crans { display: grid; grid-template-columns: repeat(4, 1fr); gap: .4rem; }
.freq-cran {
  padding: .4rem .3rem; border-radius: var(--radius-sm); cursor: pointer;
  font-size: .85rem; font-weight: 600; color: var(--text-soft);
  background: var(--surface); border: 1.5px solid var(--border);
  transition: border-color .16s, color .16s, background .16s;
}
.freq-cran:hover { border-color: var(--ton-c, var(--brand)); color: var(--text); }
.freq-cran.ton-danger { --ton-c: var(--danger); }
.freq-cran.ton-warn   { --ton-c: var(--warn); }
.freq-cran.ton-info   { --ton-c: var(--info); }
.freq-cran.ton-ok     { --ton-c: var(--ok); }
.freq-cran.on {
  color: #fff; background: var(--ton-c); border-color: var(--ton-c);
}
.freq-valeur { font-size: .9rem; color: var(--text-mute); }
.freq-valeur.ton-danger { color: var(--danger); }
.freq-valeur.ton-warn   { color: var(--warn); }
.freq-valeur.ton-info   { color: var(--info); }
.freq-valeur.ton-ok     { color: var(--ok); }
@media (max-width: 560px) {
  .freq-crans { grid-template-columns: repeat(2, 1fr); }
  .fb-actions { flex-direction: column-reverse; align-items: stretch; }
}

/* ── Accès d'essai ──────────────────────────────────────────────────────── */
/* Le bandeau annonce la limite en tête de parcours ; il emprunte l'habillage
   des notes d'information plutôt que celui des avertissements — un essai n'est
   pas un incident. */
.essai-bandeau { margin: 1.2rem 0 .2rem; }
.essai-bandeau b { display: block; }
/* L'adresse où s'adresser, dans la fenêtre de fin d'essai. */
.essai-contact {
  margin-top: 1rem; padding: .7rem .9rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: .92rem;
}
.essai-contact a { color: var(--brand); font-weight: 600; }

/* ══════════════════ Rail : une galerie qui défile de côté ══════════════════ */
.rail { position: relative; margin-top: 1rem; padding: 0 2.4rem; }
/* Les flèches disparaissant sur tactile, la réserve qui les accueille n'a plus
   lieu d'être : la piste reprend toute la largeur. */
@media (max-width: 860px) { .rail { padding: 0; } }
/* Rail sans flèches : rien à réserver sur les côtés. */
.rail--nu { padding: 0; }
.rail__piste {
  display: grid; grid-auto-flow: column; gap: 1.1rem;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity; scroll-padding: 0 .2rem;
  padding: .3rem .25rem 1rem;
}
.rail__piste > * { scroll-snap-align: start; }
/* Le défilement se fait au doigt, à la molette ou aux flèches : la barre reste
   visible mais discrète, elle est aussi le seul repère de position au clavier. */
.rail__piste { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }

.rail__fleche {
  position: absolute; top: calc(50% - 2.2rem); z-index: 2;
  width: 2.6rem; height: 2.6rem; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-size: 1.5rem; line-height: 1; padding-bottom: .18em;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  transition: border-color .16s, color .16s, opacity .16s;
}
.rail__fleche--prec { left: 0; }
.rail__fleche--suiv { right: 0; }
.rail__fleche:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
.rail__fleche:disabled { opacity: .25; cursor: default; }
/* Sur écran tactile, le geste suffit — et une flèche posée sur la première
   vignette masquerait une partie de ce qu'on est venu regarder. */
@media (max-width: 860px) { .rail__fleche { display: none; } }

/* Barre d'onglets de la page « À afficher » : le bouton du diaporama se range
   à droite, sur la même ligne, plutôt que d'occuper une ligne pour lui seul. */
.afficher-barre { align-items: center; flex-wrap: wrap; }
.bloc-action { margin-left: auto; }

/* ══════════════════════ Kiosque : le diaporama en grand ════════════════════ */
/* Au-dessus des modales (z-index 100/200) : c'est un plein écran, rien ne doit
   passer devant. Fond très sombre — un visuel se regarde sans halo blanc. */
.kio {
  position: fixed; inset: 0; z-index: 300;
  background: #0a0d16; display: flex; flex-direction: column;
  animation: fade .18s;
}
/* « grid-template-rows: minmax(0, 1fr) » n'est pas une coquetterie : une rangée
   en « auto » prend la taille de l'image, si bien que « max-height: 100% » ne
   contraint plus rien et que le visuel déborde PAR-DESSUS la barre du bas — la
   légende, les points, le compteur et la pause disparaissaient dessous. La
   rangée bornée rend au 100 % la hauteur qu'il croyait avoir. */
.kio__scene {
  position: relative; flex: 1; min-height: 0; padding: 1.1rem;
  display: grid; grid-template-rows: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
  place-items: center;
}
.kio__img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  background: #fff; border-radius: 3px; box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, .55);
  opacity: 0; transition: opacity .35s ease;
}
.kio__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 3rem; height: 3rem; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; font-size: 1.8rem; line-height: 1; padding-bottom: .18em;
  color: #fff; background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .22);
  transition: background .16s;
}
.kio__nav:hover { background: rgba(255, 255, 255, .24); }
.kio__nav--prec { left: 1rem; }
.kio__nav--suiv { right: 1rem; }

.kio__barre {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; flex: none;
  padding: .75rem 1.1rem; color: #fff;
  background: rgba(255, 255, 255, .07); border-top: 1px solid rgba(255, 255, 255, .12);
}
.kio__leg { display: flex; flex-direction: column; gap: .15rem; min-width: 12rem; margin-right: auto; }
.kio__titre { font-size: 1rem; }
.kio__detail { font-size: .85rem; color: rgba(255, 255, 255, .72); }
.kio__points { display: flex; gap: .4rem; }
.kio__point {
  width: .6rem; height: .6rem; padding: 0; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .3); border: 0; transition: background .16s, transform .16s;
}
.kio__point.actif { background: #fff; transform: scale(1.35); }
.kio__compteur { font-size: .82rem; color: rgba(255, 255, 255, .72); font-variant-numeric: tabular-nums; }
.kio__btn {
  width: 2.4rem; height: 2.4rem; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; font-size: 1rem;
  color: #fff; background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .22);
}
.kio__btn:hover { background: rgba(255, 255, 255, .24); }
.kio__dl { --ton: var(--brand); text-decoration: none; }
/* La sortie doit se voir : c'est le geste qu'on cherche quand on veut récupérer
   son écran, et « Échap » ne s'invente pas. */
.kio__fermer:hover { background: rgba(225, 29, 72, .55); border-color: rgba(225, 29, 72, .8); }

@media (max-width: 640px) {
  .kio__leg { min-width: 100%; }
  .kio__nav { width: 2.4rem; height: 2.4rem; font-size: 1.4rem; }
}

/* ─────────────── Modèles de leurre (bibliothèque) ─────────────── */
.modele-liste { display: flex; flex-direction: column; gap: .7rem; }
.modele-carte {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: .9rem 1rem; background: var(--surface-2);
  display: flex; flex-direction: column; gap: .6rem;
}
.modele-tete { display: grid; grid-template-columns: auto 1fr; gap: .3rem .8rem; align-items: center; }
.modele-ico { grid-row: span 2; font-size: 1.7rem; width: 2.6rem; text-align: center; }
.modele-titre { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.modele-desc { grid-column: 2; margin: 0; font-size: .85rem; color: var(--text-soft); line-height: 1.45; }
.modele-actions { display: flex; gap: .5rem; flex-wrap: wrap; }
.modele-ta {
  width: 100%; box-sizing: border-box; resize: vertical;
  font-family: inherit; font-size: .92rem; line-height: 1.5;
  padding: .6rem .7rem; border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
}
.modele-ta:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand); }

/* Filtre par structure dans le sélecteur de cibles d'une campagne */
.phish-cible-filtre { margin-bottom: .5rem; }
.phish-cible-filtre select { width: 100%; }
.phish-cible-struct {
  display: inline-block; margin-top: .15rem;
  font-size: .74rem; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 999px; padding: .05rem .5rem;
}

/* Modale de confirmation (remplace confirm() natif) — s'empile au-dessus */
.overlay--confirm { z-index: 200; }
.modal--confirm { width: min(440px, 100%); }
.confirm-msg { white-space: pre-line; line-height: 1.55; color: var(--text-soft); margin: 0; }
.confirm-actions { display: flex; gap: .7rem; justify-content: flex-end; margin-top: 1.4rem; flex-wrap: wrap; }

/* Éditeur WYSIWYG du corps de leurre */
.wz { border: 1.5px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.wz-barre { display: flex; gap: .3rem; align-items: stretch; flex-wrap: wrap; padding: .4rem; background: var(--surface-2); border-bottom: 1px solid var(--border); }
.wz-btn { border: 1px solid var(--border); background: var(--surface); border-radius: 6px; padding: .28rem .6rem; font-size: .85rem; font-weight: 600; cursor: pointer; color: var(--text); line-height: 1.2; }
.wz-btn:hover { border-color: var(--brand); color: var(--brand); }
.wz-sep { width: 1px; background: var(--border); margin: .1rem .25rem; }
.wysiwyg { min-height: 190px; max-height: 380px; overflow-y: auto; padding: .7rem .85rem; font-size: .95rem; line-height: 1.55; outline: none; color: var(--text); }
.wysiwyg:focus { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--brand) 30%, transparent); }
.wysiwyg ul { margin: .3rem 0; padding-left: 1.4rem; }
.wysiwyg p { margin: 0 0 .6rem; }

/* Champ de saisie dans les modales confirmer()/demander() */
.confirm-label { display: block; font-weight: 600; font-size: .88rem; margin: .7rem 0 .1rem; color: var(--text); }
/* Le champ de `demander()` : même apparence que les champs de formulaire. Sans
   cela, le navigateur affichait son champ brut, blanc à fin liseré noir. */
.confirm-input {
  width: 100%; box-sizing: border-box; margin-top: .4rem;
  padding: .75em 1em; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: .98rem; font-family: inherit; transition: .18s;
}
.confirm-input::placeholder { color: var(--text-mute); }
.confirm-input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 14%, transparent);
}

/* ─────────────────────────── FAQ ─────────────────────────── */
.faq-liste { display: flex; flex-direction: column; gap: .6rem; }
.faq-item { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.faq-item summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 1rem; padding: 1rem 1.1rem; font-weight: 600; color: var(--text); }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-q { flex: 1; }
.faq-chevron { transition: transform .18s; color: var(--text-mute); font-size: 1.2rem; }
.faq-item[open] .faq-chevron { transform: rotate(180deg); }
.faq-reponse { padding: 0 1.1rem 1.1rem; color: var(--text-soft); line-height: 1.6; white-space: pre-line; }
.faq-support-cta { margin-top: 1.6rem; padding: 1.1rem 1.3rem; border: 1px dashed var(--border); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; color: var(--text-soft); }
.faq-admin-liste { display: flex; flex-direction: column; gap: .5rem; }
.faq-admin-ligne { display: flex; align-items: center; gap: .8rem; padding: .7rem .9rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
.faq-admin-txt { flex: 1; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.faq-admin-actions { display: flex; gap: .35rem; }
.faq-publie { display: flex; align-items: center; gap: .5rem; margin-top: .3rem; font-weight: 600; cursor: pointer; }

/* ─────────────────────────── Support / tickets ─────────────────────────── */
.tk-barre { margin-bottom: 1rem; }
/* Le filtre de statut vit hors d'un .field : on lui redonne le style des selects
   de l'appli, plus un chevron maison (le natif varie d'un navigateur à l'autre). */
.tk-barre select {
  appearance: none; -webkit-appearance: none;
  min-width: 230px; cursor: pointer;
  padding: .62em 2.3em .62em 1em;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23808a9c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .85em center; background-size: .7em;
  color: var(--text); font-size: .95rem; font-family: inherit; transition: border-color .16s, box-shadow .16s;
}
.tk-barre select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 14%, transparent); }
.tk-liste { display: flex; flex-direction: column; gap: .6rem; }
/* Regroupement des demandes par catégorie parente, en accordéon. L'en-tête est
   une barre discrète ; les cartes gardent leur apparence sous celle-ci. */
.tk-cat { display: block; }
.tk-cat__tete {
  display: flex; align-items: center; gap: .6rem; padding: .65rem .9rem;
  cursor: pointer; list-style: none; user-select: none;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .15s;
}
.tk-cat__tete::-webkit-details-marker { display: none; }
.tk-cat__tete:hover { border-color: color-mix(in srgb, var(--brand) 35%, var(--border)); }
.tk-cat__nom { flex: 1; font-weight: 700; font-size: .92rem; color: var(--text); }
.tk-cat__n {
  min-width: 1.5rem; padding: .05rem .5rem; border-radius: 999px; text-align: center;
  font-size: .78rem; font-weight: 700; color: var(--text-soft);
  background: var(--surface); border: 1px solid var(--border);
}
.tk-cat__chevron { color: var(--text-mute); font-size: 1.05rem; line-height: 1; transition: transform .2s; }
.tk-cat[open] .tk-cat__chevron { transform: rotate(180deg); }
.tk-cat__corps { display: flex; flex-direction: column; gap: .6rem; margin-top: .6rem; }
.tk-carte { text-align: left; width: 100%; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  padding: .9rem 1.1rem; cursor: pointer; display: flex; flex-direction: column; gap: .5rem; transition: border-color .15s, box-shadow .15s; }
.tk-carte:hover { border-color: color-mix(in srgb, var(--brand) 40%, var(--border)); box-shadow: var(--shadow-sm); }
/* Ticket avec un message non lu : liseré vert et pastille verte dans l'en-tête
   (vert = quelque chose de nouveau à lire). */
.tk-carte--nonlu { border-color: color-mix(in srgb, var(--ok) 50%, var(--border)); }
.tk-carte--nonlu .tk-carte__tete b { font-weight: 800; }
.tk-dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--ok); flex: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 24%, transparent); }
.tk-carte__tete { display: flex; align-items: center; gap: .7rem; }
.tk-carte__tete b { flex: 1; color: color-mix(in srgb, var(--brand) 45%, var(--text)); }
.tk-carte__id { color: var(--text-mute); font-size: .85rem; }
.tk-carte__bas { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; font-size: .82rem; color: var(--text-soft); }
/* Aperçu du premier message : occupe la place restante, tronqué sur une ligne. */
.tk-apercu { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tk-carte__maj { flex: none; white-space: nowrap; }
.tk-nouveau { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 1.2rem 1.3rem; }
.tk-nouveau h3 { margin: 0 0 .8rem; }
.tk-form .field { margin-bottom: .8rem; }
.tk-detail-tete { margin: .4rem 0 1rem; }
.tk-detail-tete h2 { margin: 0 0 .4rem; }
.tk-detail-meta { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
/* Attributs du ticket en petites pastilles neutres ; le libellé de date en gras discret. */
.tk-meta { display: inline-flex; align-items: center; gap: .35rem; padding: .28rem .7rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border); font-size: .8rem; color: var(--text-soft); }
.tk-meta em { font-style: normal; font-weight: 600; color: var(--text-mute); }
.tk-fil { display: flex; flex-direction: column; gap: .7rem; margin-bottom: 1.2rem; }
.tk-msg { max-width: 82%; padding: .7rem .9rem; border-radius: var(--radius); border: 1px solid var(--border); }
.tk-msg--autre { align-self: flex-start; background: var(--surface-2); }
.tk-msg--moi { align-self: flex-end; background: color-mix(in srgb, var(--brand) 10%, var(--surface)); border-color: color-mix(in srgb, var(--brand) 28%, var(--border)); }
.tk-msg-tete { display: flex; justify-content: space-between; gap: 1rem; font-size: .78rem; color: var(--text-soft); margin-bottom: .3rem; }
/* Libellé d'auteur : teinte de marque (jamais noir stark), lisible dans les deux
   thèmes. Côté « Support », un peu plus soutenu pour distinguer les intervenants. */
.tk-msg-tete b { color: color-mix(in srgb, var(--brand) 55%, var(--text)); font-weight: 700; }
.tk-msg--autre .tk-msg-tete b { color: color-mix(in srgb, var(--brand) 78%, var(--text)); }
.tk-msg-corps { white-space: pre-wrap; line-height: 1.55; }
.tk-repondre { border-top: 1px solid var(--border); padding-top: 1rem; }

/* Pastille des tickets de support : teinte marque (indigo), pour la distinguer
   de la pastille ambre des demandes d'accès. */
.tickets-chip {
  border-color: color-mix(in srgb, var(--brand) 55%, transparent);
  color: color-mix(in srgb, var(--brand) 85%, var(--text));
}
.tickets-chip:hover { background: color-mix(in srgb, var(--brand) 12%, transparent); border-color: var(--brand); }
.tickets-chip .pc-ico { animation: none; }

/* Ticket clôturé, côté utilisateur : plus de réponse, invite à en ouvrir une nouvelle. */
.tk-close-note { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: 1rem 1.2rem; border: 1px dashed var(--border); border-radius: var(--radius); color: var(--text-soft); }

/* Pastilles de filtre par statut (liste des tickets, admin & utilisateur).
   Elles réutilisent .pill (couleur du statut) ; l'active gagne un anneau de sa
   propre couleur (currentColor). */
.tk-filtres { display: flex; gap: .45rem; flex-wrap: wrap; align-items: center; margin-bottom: 1rem; }
.tk-fpill { appearance: none; cursor: pointer; border: 0; font: inherit; font-size: .8rem; font-weight: 700; padding: .34em .85em; opacity: .62; transition: opacity .15s, box-shadow .15s; text-transform: uppercase; letter-spacing: .03em; }
.tk-fpill:hover { opacity: 1; }
.tk-fpill.actif { opacity: 1; box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 42%, transparent); }
.tk-fpill-n { opacity: .7; font-weight: 600; }
/* Saut de ligne des filtres : neutre sur desktop, force la 2ᵉ ligne sur mobile. */
.tk-filtres__br { display: none; }
@media (max-width: 640px) {
  .tk-filtres { justify-content: center; }
  .tk-filtres__br { display: block; flex-basis: 100%; height: 0; margin: 0; }
}

/* Navigation par pastilles violettes — cohérente dans toute l'application. */
.nav-barre { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: 1.2rem; }
/* Le « retour à la vue précédente » adopte la même pastille et se pousse à droite. */
.retour-prec { padding: .42rem .95rem; font-size: .85rem; }

/* ─────────── Header : roue admin + bascule de thème segmentée ─────────── */

.hdr-theme { display: inline-flex; flex: none; padding: 3px; gap: 3px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border); }
.hdr-theme__opt { width: 30px; height: 30px; border: 0; border-radius: 999px; background: transparent; cursor: pointer;
  font-size: .95rem; line-height: 1; display: grid; place-items: center; opacity: .5;
  transition: background .16s, opacity .16s, box-shadow .16s; }
.hdr-theme__opt:hover { opacity: .85; }
.hdr-theme__opt.on { opacity: 1; background: color-mix(in srgb, var(--brand) 20%, var(--surface));
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--brand) 60%, transparent); }

/* Badges & Attestations : cartes d'attestation obtenues */
.reco-atts { display: flex; flex-direction: column; gap: .7rem; margin-bottom: 1rem; }
.reco-att { display: flex; align-items: center; gap: 1rem; text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  padding: 1rem 1.2rem; color: var(--text); transition: border-color .15s, box-shadow .15s; }
.reco-att:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--border)); box-shadow: var(--shadow-sm); }
.reco-att__ico { font-size: 1.8rem; flex: none; }
.reco-att__txt { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.reco-att__txt span { font-size: .84rem; color: var(--text-soft); }
.reco-att__go { color: var(--brand); font-weight: 600; font-size: .85rem; white-space: nowrap; }

/* En-tête collant du détail d'un ticket : reste sous le topbar au défilement du
   fil. Le margin-top négatif annule le padding haut du <main> (2.2rem) : l'en-tête
   démarre déjà collé sous le topbar, sans espace visible ni à-coup au défilement. */
.tk-sticky { position: sticky; top: var(--topbar-h, 64px); z-index: 30; background: var(--bg);
  margin-top: -2.2rem; padding: 1rem 0 .8rem; margin-bottom: .8rem; border-bottom: 1px solid var(--border); }
/* Ligne du haut : bouton retour à gauche, pastille de statut à droite, alignés
   verticalement à la même hauteur. */
.tk-haut { display: flex; align-items: center; justify-content: space-between; gap: .6rem; margin-bottom: .6rem; }
.tk-haut .nav-barre { margin-bottom: 0; }
.tk-haut .pill { flex: none; }
/* Pastille de statut du détail : un cran plus grosse pour la distinguer des
   pastilles d'attributs (réf, dates). */
.tk-haut .pill-statut { font-size: .85rem; padding: .34em .9em; }
/* Côté admin, la pastille est un bouton qui ouvre le menu de changement de statut. */
.pill-statut--menu { appearance: none; border: 0; cursor: pointer; font-family: inherit; transition: filter .15s; }
.pill-statut--menu:hover { filter: brightness(1.12); }
.pill-chevron { width: 0; height: 0; border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor; margin-left: .15rem; opacity: .8; }
/* Menu déroulant de statut (aperçu ; appliqué à l'« Envoyer »). */
.statut-menu { position: fixed; z-index: 60; min-width: 172px; padding: .3rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); display: grid; gap: .1rem; }
.statut-opt { display: flex; align-items: center; gap: .55rem; width: 100%; text-align: left;
  background: transparent; border: 0; border-radius: calc(var(--radius) - 5px); padding: .5rem .65rem;
  font-family: inherit; font-size: .82rem; font-weight: 700; color: var(--text-soft); cursor: pointer;
  text-transform: uppercase; letter-spacing: .03em; transition: background .12s; }
.statut-opt:hover, .statut-opt.actif { background: var(--surface-2); }
.statut-opt.warn { color: var(--warn); }
.statut-opt.info { color: var(--info); }
.statut-opt.ok { color: var(--ok); }
.statut-opt.neutre { color: var(--text-soft); }
.statut-dot { width: .6rem; height: .6rem; border-radius: 50%; flex: none; background: currentColor; }
.tk-sticky .tk-detail-tete { margin: 0; }
/* Titre + pastille de statut sur une seule ligne ; le titre se tronque si besoin. */
.tk-titre-ligne { display: flex; align-items: center; gap: .6rem; }
.tk-titre-ligne h2 { font-size: 1.15rem; margin: 0; min-width: 0; flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-titre-ligne .pill { flex: none; }
/* Attributs du ticket, épinglés sous le titre dans l'en-tête collant. */
.tk-sticky .tk-detail-meta { margin-top: .6rem; }
/* En portrait mobile, la ligne d'attributs passe en grille 3 colonnes (33 %) :
   réf à gauche, création TOUJOURS au centre, clôture à droite (colonne réservée
   même si le ticket n'est pas clôturé). L'expéditeur (admin) occupe sa propre
   ligne en dessous. Paysage / tablette / desktop gardent l'affichage groupé. */
@media (max-width: 560px) and (orientation: portrait) {
  .tk-detail-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: .4rem .3rem; align-items: center; }
  .tk-detail-meta .tk-meta { padding: .24rem .55rem; gap: .26rem; font-size: .76rem; }
  .tk-meta--ref { justify-self: start; }
  .tk-meta--ouvert { grid-column: 2; justify-self: center; }
  .tk-meta--ferme { grid-column: 3; justify-self: end; }
  .tk-meta--exp { grid-column: 1 / -1; justify-self: start; }
}
/* Mention de clôture, centrée en bas du fil. */
.tk-clos { text-align: center; margin: 1.4rem 0 .4rem; padding-top: 1.1rem;
  border-top: 1px solid var(--border); font-size: .84rem; font-weight: 600; color: var(--text-mute); }

/* Drapeaux départ/arrivée dans les infos d'un ticket (remplacent « Ouvert le / Clôturé le »). */
.tk-meta em.tk-flag { font-size: 1.15em; margin-right: .18rem; line-height: 1; vertical-align: -.05em; }
/* Cadenas SVG (ouvert vert / fermé rouge), rendu constant sur tous les mobiles. */
.tk-lock { display: inline-flex; align-items: center; vertical-align: -.18em; margin-right: .24rem; }
.tk-lock svg { display: block; }
/* Double classe : bat le `.tk-meta em { color: var(--text-mute) }` existant. */
.tk-lock.tk-lock--open { color: var(--ok); }
.tk-lock.tk-lock--ferme { color: var(--danger); }
.tk-clos .tk-lock { vertical-align: -.2em; }

/* ═══════════════════════ Vos impressions ═══════════════════════ */
.imp-form { display: flex; flex-direction: column; gap: 1.4rem; margin-top: 1rem; max-width: 720px; }
.imp-bloc { display: flex; flex-direction: column; gap: .5rem; }
.imp-lbl { font-weight: 700; font-size: .95rem; }
.imp-opt { font-weight: 500; font-size: .82rem; color: var(--text-mute); }
/* Note : 5 étoiles, demi-étoile sélectionnable (étoile dorée clippée en largeur
   par-dessus une étoile grise). Valeur interne sur 10 ; affichage sur 5. */
.imp-note { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.imp-etoiles { display: inline-flex; gap: 3px; }
.imp-star { position: relative; appearance: none; border: 0; background: none; cursor: pointer; padding: 0;
  width: 2rem; height: 2rem; font-size: 2rem; line-height: 1; transition: transform .1s; }
.imp-star:hover { transform: scale(1.08); }
.imp-star__bg, .imp-star__fill {
  position: absolute; top: 0; left: 0; height: 100%;
  overflow: hidden; white-space: nowrap; pointer-events: none;
  /* Le glyphe vit dans une boîte de 1 em calée à GAUCHE, et il est centré DANS
     cette boîte — les deux couches le placent donc exactement au même endroit,
     et son milieu visuel tombe pile à 50 %.
     Deux erreurs se corrigent ici. Laisser l'alignement centré du bouton
     centrait l'étoile grise dans une boîte pleine et la dorée dans une boîte de
     moitié : elles ne se superposaient plus. Caler le glyphe à gauche sans le
     centrer dans sa boîte le décalait autrement, le dessin de l'étoile
     n'occupant pas toute sa chasse — la moitié dorée dépassait alors l'axe de
     symétrie. */
  text-align: left;
}
.imp-star__bg > i, .imp-star__fill > i {
  font-style: normal; display: block; width: 1em; text-align: center;
}
/* La barre dorée croît depuis la GAUCHE ; sa largeur (0/50/100 %) donne la
   demi-étoile. Pas de `right`/`inset` : sinon la largeur serait ignorée. */
.imp-star__bg { width: 100%; color: color-mix(in srgb, var(--text-mute) 40%, var(--border)); }
.imp-star__fill { width: 0; color: #f5b301; }
.imp-note-val { font-weight: 800; font-size: 1.05rem; color: var(--text-mute); min-width: 2.9rem; }
.imp-note-val.on { color: #f5b301; }
/* Version lecture seule (admin) : plus petite, non interactive. */
.imp-note-ro { display: inline-flex; align-items: center; gap: .5rem; }
.imp-etoiles--ro { gap: 1px; }
.imp-etoiles--ro .imp-star { width: 1.1rem; height: 1.1rem; font-size: 1.1rem; cursor: default; }
.imp-etoiles--ro .imp-star:hover { transform: none; }
/* `> span` et non `span` : ce sélecteur visait le seul libellé « 4,5/5 », mais
   il atteignait AUSSI le fond gris de chaque étoile, deux niveaux plus bas —
   `.imp-note-ro span` (une classe, un élément) pèse plus lourd que
   `.imp-star__bg` (une classe), et lui imposait la couleur dorée. Les cinq
   étoiles paraissaient donc pleines, quelle que soit la note. */
.imp-note-ro > span { font-weight: 700; font-size: .85rem; color: #f5b301; }
/* Chips (type + tags) */
.imp-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.imp-chip { appearance: none; cursor: pointer; font: inherit; font-size: .85rem; font-weight: 600;
  padding: .4em .85em; border-radius: 999px; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border); transition: border-color .15s, background .15s, color .15s; }
.imp-chip:hover { border-color: color-mix(in srgb, var(--brand) 40%, var(--border)); }
.imp-chip.on { background: color-mix(in srgb, var(--brand) 14%, var(--surface)); border-color: var(--brand); color: var(--brand); }
.imp-tags-grp { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.imp-grp-t { display: block; font-size: .78rem; font-weight: 700; margin-bottom: .45rem; }
.imp-grp-t.ok { color: var(--ok); }
.imp-grp-t.warn { color: var(--warn); }
@media (max-width: 560px) { .imp-tags-grp { grid-template-columns: 1fr; gap: .9rem; } }
/* Merci */
.imp-merci { text-align: center; max-width: 460px; margin: 2.5rem auto; display: flex; flex-direction: column; align-items: center; gap: .5rem; }
.imp-merci__em { font-size: 3rem; }
.imp-merci h2 { margin: 0; }
.imp-merci p { color: var(--text-soft); margin: 0 0 .8rem; }
/* Admin : stats + liste */
.imp-stats { display: flex; gap: 1rem; margin: .4rem 0 1.4rem; }
.imp-stat { flex: 1; text-align: center; padding: 1rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border); }
.imp-stat b { display: block; font-size: 1.6rem; font-weight: 800; color: var(--brand); }
.imp-stat span { font-size: .8rem; color: var(--text-mute); }
.imp-admin-liste { display: flex; flex-direction: column; gap: .7rem; }
.imp-carte { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: .9rem 1.1rem;
  display: flex; flex-direction: column; gap: .5rem; }
.imp-carte__tete { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.imp-nonote { color: var(--text-mute); font-size: .82rem; font-style: italic; }
.imp-type-pill { font-size: .78rem; font-weight: 600; padding: .15em .6em; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-soft); }
.imp-date { margin-left: auto; font-size: .8rem; color: var(--text-mute); }
.imp-carte__tags { display: flex; flex-wrap: wrap; gap: .35rem; }
.imp-tag-ro { font-size: .74rem; padding: .12em .55em; border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 9%, var(--surface-2)); border: 1px solid var(--border); color: var(--text-soft); }
.imp-carte__msg { font-size: .9rem; color: var(--text); line-height: 1.5; }
.imp-carte__qui { font-size: .8rem; color: var(--text-mute); }

/* ═══════════════ Formation : les mécaniques ajoutées (déroulé avril 2025) ═══════════════
 * Règle de survol commune : un halo et une bordure à la couleur de l'objet,
 * jamais de déplacement ni d'agrandissement. */

/* Illustration de module pas encore livrée : on n'affiche pas un cadre vide. */
.module-scene--absente { display: none; }

/* Positionnement : la réponse retenue, sans dire si elle est juste. */
.opt.choisi { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface-2)); }
.opt.choisi .let { background: var(--accent); color: #fff; border-color: var(--accent); }

.found-item--ko { background: var(--danger-soft) !important; color: color-mix(in srgb, var(--danger) 80%, var(--text)) !important; }

/* ── Les cartes du risque ── */
.cr-bouclier { text-align: center; font-weight: 700; color: var(--accent); margin-bottom: .8rem; min-height: 1.4em; }
.cr-manche {
  text-align: center; font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-mute); margin-bottom: .8rem;
}
.cr-pioche { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.cr-consigne { text-align: center; color: var(--text-soft); font-size: .92rem; margin: 0 0 .9rem; }
/* Quatre cartes en main : quatre de front sur grand écran, deux par deux sinon. */
.cr-main { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .8rem; }
@media (max-width: 820px) { .cr-main { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.cr-jouer {
  padding: 0; border: 0; background: none; cursor: pointer; border-radius: 14px; font-family: inherit;
  transition: box-shadow .18s; color: inherit;
}
.cr-jouer:hover:not(:disabled) { box-shadow: 0 0 0 3px color-mix(in srgb, var(--fam, #27ae60) 55%, transparent), 0 0 22px color-mix(in srgb, #27ae60 35%, transparent); }
.cr-jouer:disabled { cursor: default; }
.cr-jouer.ok { box-shadow: 0 0 0 3px var(--ok), 0 0 22px color-mix(in srgb, var(--ok) 45%, transparent); }
.cr-jouer.ko { box-shadow: 0 0 0 3px var(--danger); opacity: .85; }
.cr-jouer.bonne:not(.ok) { box-shadow: 0 0 0 3px var(--ok); }
.cr-jouer:disabled:not(.ok):not(.ko):not(.bonne) { opacity: .45; }
.cr-lecon { margin: .4rem 0 0; padding: .7rem 1rem; border-radius: var(--radius-sm); font-size: .9rem;
  background: color-mix(in srgb, #d4ac0d 14%, var(--surface-2)); }

/* La carte elle-même : 63 × 88, le format d'une carte à jouer. */
.cr-carte {
  --fam: #27ae60;
  position: relative; aspect-ratio: 63 / 88; width: 100%; max-width: 190px; margin: 0 auto;
  border-radius: 14px; overflow: hidden; text-align: center;
  background: linear-gradient(170deg, color-mix(in srgb, var(--fam) 26%, var(--surface)) 0%, var(--surface) 62%);
  border: 2px solid color-mix(in srgb, var(--fam) 70%, var(--border));
}
.cr-pioche .cr-carte { width: 170px; }
.cr-carte__dessin { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; padding: .7rem .65rem; gap: .35rem; }
.cr-carte__fam {
  font-size: .62rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--fam) 80%, var(--text)); align-self: stretch;
  border-bottom: 1px solid color-mix(in srgb, var(--fam) 40%, transparent); padding-bottom: .3rem;
}
.cr-carte__ico { font-size: 2.3rem; line-height: 1; margin: .5rem 0 .25rem; }
.cr-carte__titre { font-size: .86rem; line-height: 1.2; color: var(--text); }
.cr-carte__texte { font-size: .72rem; line-height: 1.35; color: var(--text-soft); margin: 0; }
/* Le visuel dessiné, texte compris, remplace le rendu en code. */
.cr-carte__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
.cr-carte--visuel { border: 0; background: none; }
.cr-carte--visuel .cr-carte__dessin { visibility: hidden; }
.cr-carte--dos { --fam: #34495e; background: repeating-linear-gradient(45deg, #1f2a44 0 10px, #24304d 10px 20px); }
.cr-carte--dos .cr-carte__titre, .cr-carte--dos .cr-carte__texte { color: #e8ecf5; }
.cr-carte--dos .cr-carte__dessin { justify-content: center; }

/* Planches imprimables : 9 cartes par page A4. */
.cr-feuilles { display: grid; gap: 1.6rem; }
.cr-feuille {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: .8rem; padding: 1rem;
  background: var(--surface-2); border: 1px dashed var(--border); border-radius: var(--radius);
}
.cr-feuille .cr-carte { max-width: none; }
@media print {
  .cr-feuilles { display: block; }
  .cr-feuille {
    display: grid; grid-template-columns: repeat(3, 63mm); grid-auto-rows: 88mm; gap: 0;
    justify-content: center; align-content: start; padding: 0; border: 0; background: none;
    break-after: page; -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .cr-feuille .cr-carte { width: 63mm; height: 88mm; max-width: none; border-radius: 3mm; outline: .2mm dashed #bbb; }
  .cr-carte__titre { color: #111; } .cr-carte__texte { color: #333; }
  .cr-carte--dos .cr-carte__titre, .cr-carte--dos .cr-carte__texte { color: #fff; }
}

/* ── Chasse aux risques ── */
.hs-grille { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .7rem; }
@media (max-width: 820px) { .hs-grille { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hs-objet {
  display: flex; flex-direction: column; align-items: center; gap: .4rem; padding: .9rem .7rem;
  border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2);
  font-family: inherit; font-size: .84rem; line-height: 1.35; color: var(--text); cursor: pointer; transition: .18s;
}
.hs-objet .em { font-size: 1.7rem; }
.hs-objet:hover { border-color: var(--warn); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent); }
.hs-objet.found { border-color: var(--ok); background: var(--ok-soft); cursor: default; }
.hs-objet.miss { border-color: var(--danger); background: var(--danger-soft); cursor: default; }
.hs-scene { position: relative; border-radius: var(--radius); overflow: hidden; }
.hs-scene img { display: block; width: 100%; }
.hs-zone { position: absolute; border: 0; background: transparent; cursor: pointer; border-radius: 10px; }
.hs-zone:hover { box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 60%, transparent); }
.hs-zone.found { box-shadow: 0 0 0 3px var(--ok); background: color-mix(in srgb, var(--ok) 18%, transparent); }
.hs-zone.miss { box-shadow: 0 0 0 3px var(--danger); background: color-mix(in srgb, var(--danger) 15%, transparent); }

/* ── Caviardage ── */
.cav-doc { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface-2); }
.cav-doc__entete { padding: .9rem 1.2rem; background: var(--surface); border-bottom: 1px solid var(--border); font-size: .88rem; display: grid; gap: .25rem; }
.cav-doc__entete .lbl { color: var(--text-mute); }
.cav-doc__corps { padding: 1.3rem; white-space: pre-wrap; line-height: 1.9; font-size: .95rem; }
.cav-seg {
  cursor: pointer; border-radius: 4px; padding: .1em .2em; transition: background .15s, color .15s;
  border-bottom: 2px dotted color-mix(in srgb, var(--text-mute) 60%, transparent);
}
.cav-seg:hover:not(.fige) { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.cav-seg.noirci { background: #000; color: #000; border-bottom-color: transparent; box-shadow: 0 0 0 1px #4a5470; }
.cav-seg.fige { cursor: default; }
.cav-seg.fige.noirci { color: #8a93a8; text-decoration: line-through; }
.cav-seg.juste { outline: 2px solid var(--ok); }
.cav-seg.faux { outline: 2px solid var(--danger); }

/* ── Appel à embranchements, et appel à Karine ── */
.appel, .tel { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface-2); }
.appel__tete, .tel__tete {
  display: flex; align-items: center; gap: .8rem; padding: .8rem 1.1rem;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.appel__tete small, .tel__tete small { display: block; color: var(--text-mute); font-size: .78rem; }
.appel__ico { font-size: 1.6rem; }
.appel__direct { margin-left: auto; font-size: .76rem; font-weight: 700; color: var(--ok); }
.appel__fil, .tel-fil { padding: 1rem; display: grid; gap: .6rem; max-height: 60vh; overflow-y: auto; }
.appel__bulle, .tel-bulle {
  max-width: 82%; padding: .7rem .95rem; border-radius: 14px; font-size: .93rem; line-height: 1.5;
  animation: stepIn .3s;
}
.appel__bulle p, .tel-bulle p { margin: 0; }
.appel__bulle--eux, .tel-bulle--eux { justify-self: start; background: var(--surface); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.appel__bulle--moi, .tel-bulle--moi {
  justify-self: end; background: color-mix(in srgb, var(--accent) 16%, var(--surface)); border-bottom-right-radius: 4px;
}
.tel-qui { display: block; font-size: .7rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-mute); margin-bottom: .2rem; }
.appel__fb { margin: 0 !important; font-size: .88rem; }
.appel__choix { padding: 0 1rem 1rem; display: grid; gap: .55rem; }
.appel__fin { padding: .9rem 1rem; border-radius: var(--radius-sm); display: grid; gap: .3rem; font-size: .92rem; }
.appel__fin--bonne { background: var(--ok-soft); }
.appel__fin--mauvaise { background: var(--danger-soft); }
.tel-portrait {
  width: 3rem; height: 3rem; border-radius: 50%; overflow: hidden; flex: 0 0 auto;
  display: grid; place-items: center; font-weight: 800; font-size: 1.2rem; color: #fff;
  background: linear-gradient(140deg, #2471a3, #6c3483); border: 2px solid #e67e22;
}
.tel-portrait img { width: 100%; height: 100%; object-fit: cover; }
.step-tel .cartes-plus { margin-top: .9rem; }

/* ── Boîte de réception ── */
.ib { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: .8rem; margin-bottom: 1rem; }
@media (max-width: 820px) { .ib { grid-template-columns: 1fr; } }
.ib-liste { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface); align-self: start; }
.ib-ligne {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: 'de heure' 'objet badge';
  gap: .1rem .5rem; padding: .6rem .8rem; border-bottom: 1px solid var(--border); font-size: .8rem;
}
.ib-ligne:last-child { border-bottom: 0; }
.ib-de { grid-area: de; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ib-objet { grid-area: objet; color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ib-heure { grid-area: heure; color: var(--text-mute); }
.ib-badge { grid-area: badge; }
.ib-ligne.on { background: color-mix(in srgb, var(--accent) 12%, var(--surface)); box-shadow: inset 3px 0 0 var(--accent); }
.ib-ligne.ok { opacity: .7; } .ib-ligne.ko { opacity: .7; background: var(--danger-soft); }
.ib-lecteur .mailbox__head, .ib-lecteur .mailbox__body { border: 1px solid var(--border); }
.ib-lecteur .mailbox__head { border-radius: var(--radius) var(--radius) 0 0; }
.ib-lecteur .mailbox__body { border-top: 0; border-radius: 0 0 var(--radius) var(--radius); background: var(--surface-2); }

/* ── Composer le bon envoi ── */
.cp { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem; align-items: start; }
@media (max-width: 820px) { .cp { grid-template-columns: 1fr; } }
.cp-rubriques { display: grid; gap: .8rem; }
.cp-rub { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .7rem .8rem .8rem; margin: 0; }
.cp-rub legend { font-weight: 700; font-size: .85rem; padding: 0 .3rem; color: var(--accent); }
.cp-opts { display: grid; gap: .45rem; }
.cp-opt {
  text-align: left; padding: .6rem .8rem; border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); font-family: inherit; font-size: .87rem; cursor: pointer; transition: .16s;
}
.cp-opt:hover:not(:disabled) { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent); }
.cp-opt.sel { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); }
.cp-opt.ok { border-color: var(--ok); background: var(--ok-soft); }
.cp-opt.ko { border-color: var(--danger); background: var(--danger-soft); }
.cp-opt.bonne:not(.ok) { border-color: var(--ok); }
.cp-opt:disabled { cursor: default; }
.cp-brouillon {
  position: sticky; top: 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: .9rem 1rem; display: grid; gap: .55rem; font-size: .86rem;
}
.cp-brouillon::before { content: '✉️ Brouillon'; font-weight: 800; font-size: .78rem; color: var(--text-mute); letter-spacing: .05em; text-transform: uppercase; }
.cp-ligne .lbl { display: block; color: var(--text-mute); font-size: .76rem; }
.cp-val.good { color: color-mix(in srgb, var(--ok) 80%, var(--text)); }
.cp-val.bad { color: color-mix(in srgb, var(--danger) 85%, var(--text)); text-decoration: line-through; }

/* ── Jauge de risque ── */
.jg-echelle { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .5rem; }
@media (max-width: 560px) { .jg-echelle { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .3rem; } .jg-cran span { font-size: .62rem; } }
.jg-cran {
  display: grid; gap: .2rem; justify-items: center; padding: .7rem .3rem; cursor: pointer; font-family: inherit;
  border: 2px solid color-mix(in srgb, var(--c) 55%, var(--border)); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--c) 10%, var(--surface-2)); color: var(--text); transition: .16s;
}
.jg-cran b { font-size: 1.3rem; color: var(--c); }
.jg-cran span { font-size: .74rem; color: var(--text-soft); }
.jg-cran:hover:not(:disabled) { box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 35%, transparent); border-color: var(--c); }
.jg-cran:disabled { cursor: default; opacity: .55; }
.jg-cran.sel { opacity: 1; box-shadow: 0 0 0 3px var(--accent); }
.jg-cran.cible { opacity: 1; border-color: var(--c); background: color-mix(in srgb, var(--c) 28%, var(--surface-2)); }
.jg-cran.cible::after { content: 'Karine'; font-size: .62rem; font-weight: 800; color: var(--c); }

/* ── Fil de crise ── */
.crise__fil { display: grid; gap: .6rem; margin-bottom: 1rem; }
.crise__notif {
  border: 1px solid var(--border); border-left: 4px solid var(--warn); border-radius: var(--radius-sm);
  background: var(--surface-2); padding: .75rem 1rem;
}
.crise__notif p { margin: .35rem 0 0; font-size: .93rem; }
.crise__notif.ok { border-left-color: var(--ok); opacity: .8; }
.crise__notif.ko { border-left-color: var(--danger); opacity: .8; }
.crise__meta { display: flex; gap: .55rem; align-items: center; font-size: .82rem; }
.crise__heure { font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; }
.crise__decision { margin-top: .45rem; font-size: .82rem; color: var(--text-soft); }

/* ── Vraie ou fausse page ── */
.pg-duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .9rem; margin-bottom: .6rem; }
@media (max-width: 820px) { .pg-duo { grid-template-columns: 1fr; } }
.pg-nav {
  text-align: left; padding: 0; border: 2px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font-family: inherit; cursor: pointer; overflow: hidden; transition: .16s;
}
.pg-nav:hover:not(:disabled) { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.pg-nav:disabled { cursor: default; }
.pg-nav.ok, .pg-nav.bonne { border-color: var(--ok); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 25%, transparent); }
.pg-nav.ko { border-color: var(--danger); }
.pg-lettre { display: block; padding: .4rem .8rem; font-size: .74rem; font-weight: 800; color: var(--text-mute); text-transform: uppercase; letter-spacing: .06em; }
.pg-barre {
  display: flex; gap: .4rem; align-items: center; margin: 0 .6rem; padding: .45rem .7rem; border-radius: 99px;
  background: var(--surface-2); border: 1px solid var(--border); font-size: .8rem;
}
.pg-url { font-family: ui-monospace, monospace; word-break: break-all; }
.pg-corps { display: grid; gap: .45rem; padding: 1rem 1.2rem 1.2rem; }
.pg-marque { font-size: 1rem; margin-bottom: .2rem; }
.pg-champ { padding: .5rem .7rem; border: 1px solid var(--border); border-radius: 8px; color: var(--text-mute); font-size: .8rem; }
.pg-bouton { padding: .5rem; border-radius: 8px; background: var(--accent); color: #fff; text-align: center; font-size: .82rem; font-weight: 700; }

/* ── Mon plan d'action ── */
.eng-compteur { text-align: right; font-size: .85rem; color: var(--text-mute); margin: .6rem 0; }
.eng-liste .item.eng-retenu { border-color: var(--ok); }
.eng-recap:not(:empty) { margin-top: 1rem; padding: 1rem 1.2rem; border-radius: var(--radius-sm); background: var(--ok-soft); }
.eng-recap ul { margin: .5rem 0 0; padding-left: 1.2rem; }

/* ── Le chemin parcouru (résultat de l'évaluation) ── */
.chemin h3 { margin-top: 0; }
.chemin__duo { display: flex; align-items: center; justify-content: center; gap: 1.4rem; flex-wrap: wrap; margin: .6rem 0; }
.chemin__pt { display: grid; justify-items: center; gap: .1rem; }
.chemin__pt small { color: var(--text-mute); font-weight: 700; text-transform: uppercase; font-size: .72rem; letter-spacing: .06em; }
.chemin__pt b { font-size: 2rem; color: var(--accent); }
.chemin__pt span { font-size: .8rem; color: var(--text-soft); }
.chemin__fleche { font-size: 1.8rem; color: var(--text-mute); }
.chemin .intro { text-align: center; }

/* La pastille d'écoute d'une réplique d'appel, sous son texte. */
.tel-bulle .voix-rv { margin-top: .45rem; }

/* ── Programme de la journée ── */
.prog-lien { display: flex; justify-content: flex-end; margin: -.4rem 0 .6rem; }
.prog-tete__haut { display: flex; gap: 1.2rem; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; }
.prog-tete h1 { font-size: 1.45rem; line-height: 1.25; margin: 0 0 .7rem; }
.prog-tete__infos { display: flex; gap: .45rem; flex-wrap: wrap; }
.prog-tete__intervenant {
  display: flex; align-items: center; gap: .75rem; padding: .55rem .9rem .55rem .55rem;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2);
}
.prog-tete__intervenant img { width: 3.2rem; height: 3.2rem; border-radius: 50%; object-fit: cover; object-position: top; }
.prog-tete__intervenant div { display: flex; flex-direction: column; line-height: 1.25; }
.prog-tete__intervenant small { color: var(--text-mute); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; }
.prog-tete__intervenant span { color: var(--text-soft); font-size: .85rem; }
.prog-tete__sous { font-size: 1rem; margin: 1.3rem 0 .5rem; }
.prog-tete__objectifs { margin: 0; padding-left: 1.2rem; color: var(--text-soft); line-height: 1.55; columns: 2 22rem; column-gap: 2rem; }
.prog-tete__objectifs li { break-inside: avoid; margin-bottom: .25rem; }

.prog { list-style: none; margin: 1.2rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .7rem; }
.prog > li { display: grid; grid-template-columns: 5.2rem 1fr; gap: 1rem; align-items: start; }
.prog__heure { display: flex; flex-direction: column; align-items: flex-end; padding-top: .95rem; line-height: 1.2; }
.prog__heure b { font-size: 1.05rem; }
.prog__heure span { color: var(--text-mute); font-size: .85rem; }
.prog__seq .prog__corps {
  background: var(--surface); border: 1px solid var(--border); border-left: 5px solid var(--brand);
  border-radius: var(--radius); padding: .9rem 1.1rem 1rem;
}
.prog__seq h3 { margin: 0 0 .3rem; font-size: 1.08rem; line-height: 1.3; }
.prog__obj { margin: 0 0 .55rem; color: var(--text-soft); }
.prog__contenus { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem; }
.prog__contenus li { font-size: .82rem; padding: .2rem .6rem; border-radius: 999px; background: var(--brand-soft); color: var(--text); }
.prog__meta { margin: .75rem 0 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .4rem 1.2rem; font-size: .85rem; }
.prog__meta dt { font-weight: 700; color: var(--text-mute); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; }
.prog__meta dd { margin: .1rem 0 0; color: var(--text-soft); }
.prog__modules { margin-top: .75rem; display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.prog__modules-tete { font-size: .74rem; font-weight: 700; color: var(--text-mute); text-transform: uppercase; letter-spacing: .05em; margin-right: .2rem; }
.prog__module { font-size: .82rem; padding: .2rem .6rem; border: 1px solid var(--border); border-radius: 999px; color: var(--text-soft); text-decoration: none; }
a.prog__module { color: var(--brand); border-color: color-mix(in srgb, var(--brand) 40%, var(--border)); }
a.prog__module:hover { box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent); }
.prog__pause .prog__heure { padding-top: .5rem; }
.prog__pause .prog__corps {
  padding: .5rem 1.1rem; border: 1px dashed var(--border); border-radius: var(--radius);
  color: var(--text-mute); font-size: .9rem;
}
/* La séquence en cours, pendant une séance en direct. */
.prog__seq--courante .prog__corps {
  background: var(--brand-soft);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
}
.prog__seq--courante .prog__heure b { color: var(--brand); }
.prog__maintenant {
  display: inline-block; margin-left: .5rem; vertical-align: middle; font-size: .72rem; font-weight: 700;
  padding: .12rem .5rem; border-radius: 999px; background: var(--brand); color: #fff;
}
.prog-actions { display: flex; gap: .6rem; flex-wrap: wrap; justify-content: center; margin: 1.6rem 0 1rem; }

/* Lecture en ligne : l'étape et sa durée à la place des horaires. */
.prog--en-ligne .prog__heure span { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; }
.prog--en-ligne .prog__heure b { white-space: nowrap; }
.prog-tete__note { margin: 1rem 0 0; font-size: .88rem; color: var(--text-mute); }

/* Version téléphone, en séance en direct. */
.prog--compact > li { grid-template-columns: 3.6rem 1fr; gap: .6rem; }
.prog--compact .prog__heure b { font-size: .95rem; }
.prog--compact .prog__seq h3 { font-size: .98rem; }
.prog--compact .prog__obj { font-size: .88rem; }
.live-prog { max-width: 44rem; width: calc(100% - 2rem); margin: 1.2rem auto 2rem; }
.live-prog summary { cursor: pointer; font-weight: 700; padding: .6rem 0; text-align: center; }

@media (max-width: 640px) {
  .prog > li { grid-template-columns: 3.6rem 1fr; gap: .6rem; }
  .prog-tete__intervenant { border-radius: var(--radius); }
}
@media print {
  .prog-lien, .prog__modules { display: none !important; }
  .prog-tete { box-shadow: none; }
  .prog > li { break-inside: avoid; }
  .prog { gap: .45rem; }
  .prog__seq .prog__corps, .prog__contenus li, .prog-tete__intervenant img {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}

/* ── Site formation : le logo d'Appui Santé France Formation ──
   Son texte bleu foncé se perdrait sur le fond sombre du site : il est posé
   sur une pastille blanche, comme le logo du site RGPD l'est déjà. */
.auth__logo--asff {
  display: inline-flex; background: #fff; border-radius: 16px; padding: .7rem 1.1rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
}
.auth__logo--asff img { display: block; height: 52px; width: auto; max-width: 100%; }
/* Dans l'en-tête, le symbole seul, sur la même pastille blanche. */
.topbar__logo .mark:has(.logo-img--asff), .live-join-marque .mark:has(.logo-img--asff),
.live-topbar__marque .mark:has(.logo-img--asff) {
  background: #fff; border-radius: 10px; padding: 3px;
}
/* Sur l'attestation, le logo complet : il porte le nom de l'organisme de formation. */
.cert-logo--asff { width: auto; height: 46px; max-width: 220px; }
/* La pastille du logo prend la largeur du logo, pas celle du panneau. */
.auth__logo--asff { align-self: flex-start; justify-self: start; width: fit-content; }
/* Justine occupe le coin bas-droit du panneau : la liste des points forts
   s'arrête avant elle, sans quoi ses dernières lignes passent sous son bras.
   Ses phrases sont plus longues que celles du site RGPD, d'où cette règle à part.
   Au-dessus de 900 px seulement : en dessous, l'encart change de disposition. */
@media (min-width: 900px) {
  html[data-site="formation"] .auth__features { max-width: calc(100% - 17rem); }
}
@media (min-width: 900px) and (max-width: 1180px) {
  html[data-site="formation"] .auth__features { max-width: calc(100% - 12rem); }
}
/* Le logo complet est plus haut que la pastille du site RGPD : on lui rend en
   marge ce qu'il prend en hauteur, pour que le panneau tienne sur l'écran. */
html[data-site="formation"] .auth__logo--asff { margin-bottom: 2rem; padding: .55rem 1rem; }
html[data-site="formation"] .auth__logo--asff img { height: 46px; }
html[data-site="formation"] .auth__features { margin-top: 2rem; }

/* ── Mentions légales : une colonne à la mesure d'un écran de PC ──
   62 caractères par ligne laissaient les deux tiers de l'écran vides. La page
   prend la largeur du contenu, jusqu'à une mesure qui reste lisible. */
.legal { max-width: min(100%, 68rem); }
.legal-page { max-width: min(96vw, 72rem); }
.legal-sec p, .legal-liste li { font-size: .97rem; }

/* L'éditeur des textes, dans la console. */
.mt-editeur { margin-top: 2rem; }
.mt-aide { margin: .4rem 0 1.2rem; font-size: .9rem; color: var(--text-soft); }
.mt-aide summary { cursor: pointer; font-weight: 600; }
.mt-aide ul { margin: .5rem 0 0; padding-left: 1.2rem; line-height: 1.6; }
.mt-sec { border: 1px solid var(--border); border-radius: var(--radius); padding: .8rem .9rem; margin-bottom: .8rem; background: var(--surface); }
.mt-sec__tete { display: flex; gap: .45rem; align-items: center; margin-bottom: .55rem; }
.mt-sec__tete input { flex: 1; font-weight: 700; }
.mt-sec__num { flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%; display: grid; place-items: center; font-size: .8rem; font-weight: 700; background: var(--brand-soft); }
.mt-sec__texte { width: 100%; font-size: .9rem; line-height: 1.55; resize: vertical; }
/* L'éditeur visuel : la section telle qu'elle s'affichera, dans un cadre. */
.mt-visuel { border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--bg); }
.mt-barre {
  display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; padding: .4rem .5rem;
  border-bottom: 1px solid var(--border); background: var(--surface-2);
}
.mt-outil {
  font: inherit; font-size: .82rem; padding: .25rem .6rem; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-soft);
}
.mt-outil:hover { box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 25%, transparent); color: var(--text); }
.mt-outil--jeton { margin-left: auto; }
.mt-wysiwyg { margin: 0; padding: .8rem 1rem; min-height: 4.5rem; outline: none; }
.mt-wysiwyg:focus { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--brand) 45%, transparent); }
.mt-wysiwyg ul { padding-left: 1.3rem; color: var(--text-soft); }
.mt-wysiwyg li { margin-bottom: .3rem; font-size: .97rem; }
.mt-champs { border-collapse: collapse; width: 100%; margin: .3rem 0 .9rem; font-size: .93rem; }
.mt-champs td { border: 1px dashed var(--border); padding: .4rem .6rem; color: var(--text-soft); vertical-align: top; }
.mt-champs td:first-child { width: 30%; font-weight: 600; color: var(--text-mute); }

/* Page de lancement : le panneau prend toute la largeur de la fenêtre (100vw),
   barre de défilement verticale comprise, et dépassait donc de quelques
   pixels : une barre horizontale apparaissait pour rien. La page qui le porte
   ne défile plus en largeur. */
body:has(.para-lancement) { overflow-x: clip; }
/* Le titre du parcours tient sur une ligne sur grand écran : son bloc n'est
   plus borné à 34 caractères, seul le paragraphe garde sa mesure de lecture. */
.para-lancement__mots { max-width: min(44rem, 100%); }
.para-lancement__mots p { max-width: 46ch; }
.para-lancement__mots h2 { text-wrap: balance; }
