:root {
  /* Cartoon pop. Le monde est plat, sature et cerne d'encre : un bleu electrique
     tire de l'icone du jeu pour le sol, de la creme pour tout ce qui se lit de
     pres, du jaune soleil pour la seule chose a faire, du rose pour ce qui est
     choisi. Chaque surface porte un trait d'encre et une tranche pleine : c'est
     le trait qui fait le dessin anime, pas la couleur. */
  --bg: #4838f0;
  --bg-deep: #3527d1;
  --ink: #1b1240;
  --ink-rgb: 27, 18, 64;
  /* Les cartes sont en creme : ce qu'on lit de pres se lit sur du clair. La
     seconde surface, jaune pale, sert aux puits d'icone, au survol, au choix. */
  --surface: #fff6e4;
  --surface-2: #ffe7a6;
  --card-hover: #ffefbd;
  --line: var(--ink);
  --stroke: 3px;
  /* Sur le sol bleu, le texte est blanc ; sur les cartes, les composants
     redefinissent --text et --muted en encre (voir .carte plus bas). */
  --text: #ffffff;
  --muted: #dcd9ff;
  --card-ink: var(--ink);
  --card-muted: #5e5690;
  --canvas-line: rgba(255, 255, 255, 0.42);
  --accent: #ffd60a;
  --accent-2: #ff8a2c;
  --accent-grad: #ffd60a;
  --accent-ink: var(--ink);
  --accent-edge: var(--ink);
  --edge: var(--ink);
  --accent-rgb: 255, 214, 10;
  --accent-2-rgb: 255, 138, 44;
  /* Le rose : ce qui est choisi, ce qui chauffe. En texte sur creme il descend
     d'un ton pour rester lisible ; en aplat il reste vif. */
  --pink: #ff3d9c;
  --pink-rgb: 255, 61, 156;
  --pink-ink: #d10a75;
  --hot: var(--pink);
  --ok: #4ff08a;
  --ko: #ff6b7d;
  --card-ok: #0f8a3c;
  --card-ko: #d1264a;
  --ok-fill: #5df58f;
  --ko-fill: #ff5d73;
  --gold: #ffd60a;
  --gold-rgb: 255, 214, 10;
  /* Le contour d'encre des gros chiffres et du nom du jeu : huit ombres a
     plat font un trait ferme la ou text-stroke rongerait les lettres. */
  --contour: -2px -2px 0 var(--ink), 2px -2px 0 var(--ink), -2px 2px 0 var(--ink),
             2px 2px 0 var(--ink), -2px 0 0 var(--ink), 2px 0 0 var(--ink),
             0 -2px 0 var(--ink), 0 2px 0 var(--ink);
  --radius: 18px;
  /* Une seule famille, ronde et charnue : Baloo 2 tient les titres en 800 et
     le texte courant en 500-700. Deux polices arrondies se seraient ressemble
     sans se completer. */
  --font: "Baloo 2", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-title: var(--font);
  --font-pixel: var(--font);
}


* { box-sizing: border-box; margin: 0; padding: 0; }

/* Deux appuis rapproches zoomaient la page — mortel en quiz, ou l'on tape
   vite. `manipulation` retire le zoom au double-appui et le delai qui va avec,
   mais laisse le pincement a deux doigts : un joueur malvoyant garde le droit
   d'agrandir, seul le zoom accidentel disparait. (`user-scalable=no` aurait
   pris les deux, et iOS l'ignore en partie.) */
html { touch-action: manipulation; }

/* Un display en classe (.field, .board, .recap...) l'emporte sur la regle
   [hidden] du navigateur et rend visible ce qu'on masque en JS. On rattrape
   ici une fois pour toutes plutot qu'element par element. */
[hidden] { display: none !important; }

/* Le contour de focus etait celui du navigateur : un bleu systeme qui n'a
   jamais rien eu a voir avec la palette, et qui jure franchement sur le jaune
   des actions. Le decalage l'ecarte du bord pour qu'il reste lisible sur une
   surface de la meme famille. */
:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 3px;
  border-radius: 4px;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
  display: grid;
  /* La colonne est bornee a la largeur du body. Avec une piste implicite (auto),
     elle se calait sur le contenu le plus large — le carrousel pleine largeur —
     et devenait plus large que l'ecran : toute la colonne se decalait alors vers
     la droite, titre et bouton rognes. */
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: center;
  padding: 24px 20px calc(24px + env(safe-area-inset-bottom));
}

/* La remontee du contenu quand le clavier passerait sur le champ : posee par
   main.js d'apres la fenetre visuelle. Elle glisse plutot que de sauter — un
   ecran de saisie qui bondit se lit mal.
   Par `top` et non par une transformation : un ancetre transforme devient le
   repere de tout ce qui est en position fixe a l'interieur — la barre des
   etapes, le bouton de retour, le compteur de manche — et ces trois-la
   quittaient le haut de l'ecran pour se poser en haut de la colonne des que
   le clavier s'ouvrait sur le champ du prenom. */
.app {
  width: 100%; max-width: 480px; position: relative; z-index: 1;
  top: 0;
  transition: top 180ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
.screen { display: flex; flex-direction: column; gap: 16px; }
.screen[hidden] { display: none; }

/* Le surtitre est le badge vert de la maquette : pose de travers au-dessus
   du titre, trait d'encre, tranche de 4 px, texte blanc cerne. Il porte le
   nom de l'etape, et parfois un chiffre qui bouge (la manche, le decompte). */
.eyebrow {
  box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center; align-self: center; width: auto;
  padding: 8px 20px; border-radius: 16px;
  font-family: var(--font-title); font-size: 20px; line-height: 32px; font-weight: 600;
  letter-spacing: 0; text-transform: none; white-space: nowrap;
  color: #fff; text-shadow: 2px 2px 0 #1e1144;
  -webkit-text-stroke: 4px #1e1144; paint-order: stroke fill;
  background: #86f601; border: 4px solid #1e1144;
  filter: drop-shadow(0 4px 0 #1e1144);
  transform: rotate(-5deg);
}
/* Un badge sans texte n'est pas un badge : le decompte en solo n'a rien a y
   mettre, et une pilule jaune vide flotterait au-dessus du chiffre. */
.eyebrow:empty { display: none; }
.step > .eyebrow, #screen-done > .eyebrow, #screen-board > .eyebrow,
#screen-spin > .eyebrow, #screen-standings > .eyebrow { width: auto; }
#spin-eyebrow[data-tie] { background: var(--accent-2); }
h1 {
  font-family: var(--font-title);
  font-size: 34px; font-weight: 800; letter-spacing: 0; line-height: 1.05;
  /* Le titre se pose sur le sol comme une lettre decoupee : une ombre pleine,
     pas un flou. */
  text-shadow: 0 3px 0 var(--ink);
  /* Deux lignes de longueur voisine plutot qu'une longue et un mot esseule. */
  text-wrap: balance;
}
/* Le nom du jeu, sur l'accueil seulement. Les autres surtitres restent en
   police systeme : ils annoncent une etape, ils ne signent rien. */
#step-accueil .eyebrow {
  display: block; padding: 0; border: 0; background: none; box-shadow: none; filter: none;
  transform: none; color: var(--accent);
  font-size: 30px; line-height: 36px;
  letter-spacing: 0.04em;
  /* Le nom du jeu est un logo : jaune cerne d'encre, avec sa tranche. */
  text-shadow: var(--contour), 0 5px 0 var(--ink);
}
.lede { color: var(--muted); font-size: 16px; line-height: 1.5; font-weight: 500; }
/* Sur l'accueil, la promesse du jeu porte plus gros que les textes d'etape :
   c'est la seule phrase de l'ecran, elle a la place. Et elle prend ses
   distances avec le bouton : 32 px au total, l'ecart commun double. */
#step-accueil .lede { font-size: 18px; margin-bottom: 16px; }
.status { color: var(--muted); font-size: 14px; font-variant-numeric: tabular-nums; }
.status[data-error] { color: var(--ko); }

/* --- barre du haut --- */
.bar { display: flex; align-items: center; gap: 12px; }
.counter, .score {
  font-size: 13px; font-weight: 700; letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
}
.counter { color: var(--muted); }
.score { color: var(--accent); margin-right: auto; }

/* --- paliers --- */
.tiers { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.tier {
  background: var(--surface); border: var(--stroke) solid var(--line);
  border-radius: 10px; padding: 10px 6px; text-align: center;
  transition: background 140ms ease, border-color 140ms ease, opacity 140ms ease;
}
.tier-dur {
  display: block; font-size: 15px; font-weight: 700;
  font-variant-numeric: tabular-nums; letter-spacing: 0;
}
.tier-pts { display: block; font-size: 11px; color: var(--muted); margin-top: 3px; font-weight: 600; }
.tier[data-state="past"] { opacity: 0.35; }
.tier[data-state="current"] {
  background: var(--accent-grad); border-color: var(--accent); color: var(--accent-ink);
}
.tier[data-state="current"] .tier-pts { color: var(--accent-ink); opacity: 0.7; }

/* --- bouton d'ecoute --- */
.listen {
  appearance: none; border: none; cursor: pointer; width: 100%;
  min-height: 130px; border-radius: 20px;
  background: var(--accent-grad); color: var(--accent-ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  font-family: inherit;
  transition: transform 120ms ease, filter 120ms ease;
}
.listen:active { transform: scale(0.985); }
.listen:disabled { filter: grayscale(1) brightness(0.5); cursor: default; }
.listen[data-playing] { animation: pulse 420ms ease-out; }
@keyframes pulse {
  from { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.5); }
  to { box-shadow: 0 0 0 22px rgba(var(--accent-rgb), 0); }
}
.listen-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; opacity: 0.6;
}
.listen-dur { font-family: var(--font-title);
  font-size: 40px; font-weight: 800; letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

/* --- saisie de la reponse --- */
.guess { position: relative; }
.guess-input {
  width: 100%; font-family: inherit; font-size: 16px; font-weight: 500;
  padding: 15px 16px; border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  border: var(--stroke) solid var(--line);
  /* La meme tranche que les boutons. Les deux boites font deja 51 px : c'est
     cette epaisseur peinte, absente du champ, qui les desaccordait a l'oeil —
     et sur l'ecran du prenom ils sont cote a cote, l'ecart s'y voyait. La
     teinte est celle des surfaces, pas de l'accent : le champ n'est pas une
     action. */
  box-shadow: 0 6px 0 var(--edge);
  transition: border-color 120ms ease;
}
.guess-input::placeholder { color: var(--muted); }
.guess-input:focus { outline: none; border-color: var(--accent); }
.guess-input[data-wrong] { animation: shake 320ms ease; border-color: var(--ko); }
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  30%, 70% { transform: translateX(4px); }
  50% { transform: translateX(-4px); }
}

/* La liste flotte au-dessus du contenu : sans cela, chaque frappe ferait
   sauter tout ce qui est en dessous. */
.guess-list {
  /* Six pixels plus bas qu'avant : la tranche du champ occupe desormais cet
     espace, et la liste se posait dessus. */
  position: absolute; z-index: 10; top: calc(100% + 12px); left: 0; right: 0;
  list-style: none; max-height: 340px; overflow-y: auto;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  border-radius: var(--radius); padding: 5px;
  box-shadow: 0 6px 0 var(--ink);
}
.guess-list[hidden] { display: none; }
.suggestion {
  display: grid; grid-template-columns: 38px 1fr; gap: 11px; align-items: center;
  padding: 8px; border-radius: 9px; cursor: pointer; text-align: left;
  width: 100%; background: none; border: none; font-family: inherit; color: inherit;
}
.suggestion:hover, .suggestion[aria-selected="true"] { background: var(--card-hover); }
.suggestion-cover {
  width: 38px; height: 38px; border-radius: 6px; object-fit: cover;
  background: var(--surface);
}
.suggestion-title, .suggestion-artist { display: block; }
.suggestion-title {
  font-size: 14px; font-weight: 600; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.suggestion-artist {
  font-size: 12.5px; color: var(--muted); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.suggestion-empty { padding: 12px 10px; font-size: 14px; color: var(--muted); }

/* --- tentatives ratees --- */
.attempts { list-style: none; display: flex; flex-direction: column; gap: 5px; }
.attempts:empty { display: none; }
.attempt {
  font-size: 13px; color: var(--muted);
  padding: 8px 12px; border-radius: 9px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  display: flex; gap: 8px; align-items: baseline;
}
.attempt-x { color: var(--ko); font-weight: 700; }
.attempt-text { text-decoration: line-through; }

/* --- boutons --- */
.actions { display: grid; grid-template-columns: 1fr auto; gap: 10px; }
.btn {
  appearance: none; cursor: pointer; font-family: inherit;
  font-size: 16px; font-weight: 800; padding: 14px 18px;
  border-radius: var(--radius); border: var(--stroke) solid var(--ink);
  transition: background 120ms ease, opacity 120ms ease;
}
.btn:disabled { opacity: 0.35; cursor: default; }
.btn-primary {
  background: var(--accent-grad); color: var(--accent-ink);
  box-shadow: 0 6px 0 var(--accent-edge);
}
.btn-secondary {
  background: var(--surface-2); color: var(--text); border-color: var(--line);
  box-shadow: 0 5px 0 var(--edge);
}
/* L'appui enfonce la face dans sa tranche : c'est le retour physique qui
   manquait aux boutons plats. */
.btn-primary, .btn-secondary {
  transition: transform 70ms ease, box-shadow 70ms ease, background 120ms ease;
}
.btn-primary:active:not(:disabled) { transform: translateY(5px); box-shadow: 0 1px 0 var(--accent-edge); }
.btn-secondary:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 var(--edge); }
.btn-primary:disabled, .btn-secondary:disabled { box-shadow: none; }
.btn-ghost { background: rgba(255, 255, 255, 0.08); color: var(--text); border-color: var(--canvas-line); font-weight: 700; }
.btn-sm { font-size: 13px; padding: 7px 12px; }

/* --- revelation --- */
/* Le morceau revele : la pochette en grand, son titre et son artiste dessous.
   Ce n'est plus une carte a manipuler — il n'y a rien a y appuyer — mais une
   image qu'on regarde le temps que l'extrait tourne. */
.reveal {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: 0; min-width: 0;
}
.reveal[hidden] { display: none; }
/* Le verdict est un autocollant : une couleur pleine, un trait d'encre, pose
   de travers. C'est le seul moment ou l'ecran a le droit de crier. */
.verdict {
  grid-column: 1 / -1; align-self: center; max-width: 100%;
  font-family: var(--font-title); font-size: 22px; font-weight: 800; line-height: 1.15;
  padding: 8px 18px; border-radius: 14px;
  border: var(--stroke) solid var(--ink); color: var(--ink);
  box-shadow: 0 5px 0 var(--ink);
  transform: rotate(-2deg);
  animation: sticker-in 380ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.verdict:empty { display: none; }
.verdict[data-result="ok"] { background: var(--ok-fill); }
.verdict[data-result="ko"] { background: var(--ko-fill); }
@keyframes sticker-in {
  from { opacity: 0; transform: rotate(-12deg) scale(0.5); }
  to   { opacity: 1; transform: rotate(-2deg) scale(1); }
}
/* La pochette prend la place qu'elle merite : un carre, borde d'encre comme
   les boutons, avec la meme tranche pleine dessous. */
.reveal-cover {
  width: min(58vw, 216px); aspect-ratio: 1; height: auto;
  border-radius: 16px; object-fit: cover; background: var(--surface-2);
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
}
.reveal-text { text-align: center; }
.reveal-title { font-size: 16px; font-weight: 700; line-height: 1.25; }
.reveal-artist { font-size: 14px; color: var(--muted); margin-top: 2px; }

/* --- pied --- */
.debug {
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  min-height: 1.2em;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ===== Mode soiree ======================================================== */

.accent { color: var(--accent); }

/* --- installation --- */
.player-form { display: grid; grid-template-columns: 1fr auto; gap: 10px; }
.player-list { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; }
.player-list:empty { display: none; }
.player-chip {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  border-radius: 999px; padding: 7px 8px 7px 14px;
  font-size: 14px; font-weight: 600;
}
.player-remove {
  appearance: none; border: none; cursor: pointer; font-family: inherit;
  width: 20px; height: 20px; border-radius: 50%; line-height: 1;
  background: var(--ink); color: #fff; font-size: 13px;
}
.player-remove:hover { background: var(--ko); color: var(--bg); }

.field { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.field-label { font-size: 14px; font-weight: 600; color: var(--muted); }
.segmented {
  display: flex; gap: 3px; padding: 3px;
  background: var(--surface); border: var(--stroke) solid var(--line); border-radius: 11px;
}
.segment {
  appearance: none; border: none; cursor: pointer; font-family: inherit;
  font-size: 14px; font-weight: 700; padding: 7px 16px; border-radius: 8px;
  background: none; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.segment[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }

/* --- qui joue --- */
.who {
  font-size: 14px; font-weight: 700; margin-right: auto;
  display: flex; align-items: center; gap: 8px;
}
.who[data-steal] { color: var(--accent); }
.who-tag {
  font-size: 10px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
  background: var(--accent); color: var(--accent-ink);
  padding: 3px 7px; border-radius: 5px;
}

/* --- tableau des scores --- */
.board { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.board-row {
  display: grid; grid-template-columns: 24px 1fr auto; gap: 10px; align-items: baseline;
  padding: 11px 14px; border-radius: 10px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  font-size: 15px;
}
.board-row[data-active] { border-color: var(--accent); }
.board-rank { color: var(--muted); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.board-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-score { font-weight: 800; font-variant-numeric: tabular-nums; }
.board-row[data-active] .board-score { color: var(--accent); }
.board-final .board-row:first-child {
  background: var(--accent-grad); border-color: var(--ink); color: var(--accent-ink);
}
.board-final .board-row:first-child .board-rank { color: var(--accent-ink); opacity: 0.6; }
.board-final .board-row:first-child .board-score { color: var(--accent-ink); }

/* --- vol --- */
.steal-players { display: grid; gap: 8px; }


/* --- choix du mode --- */
.modes { display: grid; gap: 10px; }
/* Au choix du mode, les cartes respirent un peu plus qu'ailleurs : il n'y en
   a que deux et c'est la seule decision de l'ecran. La tranche d'encre compte
   deja pour cinq pixels d'ecart visuel, l'espace reste donc mesure. */
#step-mode .modes { gap: 14px; }
.mode-card {
  appearance: none; cursor: pointer; font-family: inherit; text-align: left;
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; border-radius: var(--radius);
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
  /* La meme tranche que les boutons : ce qui se clique se pose sur une
     epaisseur et s'y enfonce a l'appui. */
  box-shadow: 0 5px 0 var(--edge);
  transition: border-color 120ms ease, background 120ms ease,
              transform 70ms ease, box-shadow 70ms ease;
}
.mode-card:active { transform: translateY(4px); box-shadow: 0 1px 0 var(--edge); }
.mode-card:hover { border-color: var(--accent); background: var(--surface-2); }
/* Les cartes d'information de l'ecran d'autorisations : meme habit que les
   cartes de mode, mais rien a cliquer — le survol ne doit pas le pretendre. */
.mode-card-static, .mode-card-static:hover {
  cursor: default;
  border-color: var(--line); background: var(--surface);
}
.mode-card-static, .mode-card-static:active { box-shadow: none; transform: none; }
.mode-title { font-family: var(--font-title); font-size: 19px; font-weight: 800; letter-spacing: 0; }
.mode-desc { font-size: 14px; color: var(--muted); line-height: 1.45; }
.mode-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mode-icon {
  flex: 0 0 auto; width: 54px; height: 54px; border-radius: 14px;
  display: grid; place-items: center;
  background: var(--bg); border: var(--stroke) solid var(--line);
  color: #fff;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.mode-card:hover .mode-icon {
  background: var(--accent-grad); border-color: transparent; color: var(--accent-ink);
}
.icon { width: 26px; height: 26px; display: block; }



/* ===== Reaction du jeu ==================================================== */


/* --- serie en cours --- */
.streak {
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
  padding: 3px 9px; border-radius: 999px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  animation: streak-pop 320ms cubic-bezier(0.2, 1.6, 0.4, 1);
}
.streak[data-hot] { background: var(--hot); color: var(--accent-ink); border-color: var(--ink); }
@keyframes streak-pop {
  from { transform: scale(0.5); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* --- jauge de duree sur le bouton d'ecoute --- */
.listen { position: relative; overflow: hidden; }
.listen .progress { background: rgba(11, 7, 38, 0.20); height: 4px; }
.listen .progress-bar { background: var(--accent-ink); opacity: 0.55; }

/* --- palier qui se consume --- */
.tier[data-burn] { animation: tier-burn 420ms ease-out; }
@keyframes tier-burn {
  0%   { transform: translateY(0) scale(1); background: var(--ko); border-color: var(--ko); }
  35%  { transform: translateY(-4px) scale(1.04); }
  100% { transform: translateY(0) scale(1); }
}

/* --- points gagnes --- */
/* Le chiffre est seul, centre, hors de toute carte : c'est lui qu'on lit en
   premier. Il tombe d'en haut et rebondit, puis le verdict se pose dessous. */
.points {
  font-family: var(--font-title);
  align-self: center; text-align: center;
  /* L'unite sous le chiffre, et non a cote : posee a droite, elle tirait le
     bloc de travers et le chiffre cessait d'etre au centre de l'ecran. Dessous,
     les deux s'empilent sur le meme axe. */
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0;
  font-size: 64px; font-weight: 800; letter-spacing: 0; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  text-shadow: var(--contour), 0 5px 0 var(--ink);
  animation: points-pop 520ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* Un morceau trouve se lit en vert — le meme que le badge « Trouve en » juste
   au-dessus —, un malus en rouge, avec le meme contour d'encre. */
.points[data-result="ok"] { color: var(--ok-fill); }
.points[data-result="ko"] { color: var(--ko-fill); }
/* Et le chiffre penche selon l'issue : vers la droite quand on marque, vers
   la gauche quand on perd. Cinq degres, comme les badges du jeu — assez pour
   que le corps du chiffre dise deja ce que sa couleur confirme.
   Par la propriete rotate et non par transform : l'entree du chiffre occupe
   deja transform, et la lui reprendre casserait le rebond. */
.points[data-result="ok"] { rotate: 5deg; }
.points[data-result="ko"] { rotate: -5deg; }
/* L'unite : nettement plus petite que le chiffre, mais du meme metal. */
.points-mot { font-size: 0.42em; font-weight: 800; letter-spacing: 0.01em; }
.points-bonus { font-size: 16px; font-weight: 800; color: var(--text); text-shadow: 0 2px 0 var(--ink); }
@keyframes points-pop {
  0%   { transform: translateY(-28px) scale(0.5); opacity: 0; }
  100% { transform: none; opacity: 1; }
}
/* Le verdict attend que le chiffre soit tombe. */
.points:not([hidden]) + .verdict { animation-delay: 140ms; }
@media (prefers-reduced-motion: reduce) {
  .points { animation: none; }
  .verdict { animation: none; transform: rotate(-2deg); }
}

/* --- record personnel --- */
.record { font-size: 14px; font-weight: 700; color: var(--accent); }

/* --- voile de celebration (palier 0,1 s) --- */
.flash {
  position: fixed; inset: 0; z-index: 900; pointer-events: none;
  background: var(--accent-grad);
}
.flash[hidden] { display: none; }
.flash.flash-run { animation: flash-out 420ms ease-out forwards; }
@keyframes flash-out {
  0%   { opacity: 0.75; }
  100% { opacity: 0; }
}

/* --- confettis --- */
/* Entre le decor et l'interface : le canvas vient apres le fond video dans le
   DOM donc se peint au-dessus de lui, et l'app (z-index 1) passe devant. La
   fete reste visible partout ou l'ecran est nu, mais ne recouvre jamais un
   bouton ni un texte — on celebre sans gener le geste suivant. */
.confetti { position: fixed; inset: 0; z-index: 0; pointer-events: none; }

/* ===== Onboarding pas a pas =============================================== */

/* Rien a copier ici : un appui long qui surligne un titre ou ouvre le menu
   contextuel casse l'elan sans rien offrir. Les champs de saisie gardent la
   selection — corriger un prenom ou coller un code de salle en ont besoin. */
#screen-onboard {
  user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none;
}
#screen-onboard input {
  user-select: text; -webkit-user-select: text;
}

.step { display: flex; flex-direction: column; gap: 16px; }
.step[hidden] { display: none; }
 /* Une entree sans depassement : la courbe qui rebondissait (1.3) faisait
   sursauter l'ecran a l'arrivee, surtout sur telephone. */
.step-enter { animation: step-in 280ms cubic-bezier(0.22, 0.9, 0.3, 1); }
.step-enter-back { animation: step-in-back 280ms cubic-bezier(0.22, 0.9, 0.3, 1); }
@keyframes step-in {
  from { opacity: 0; transform: translateX(26px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes step-in-back {
  from { opacity: 0; transform: translateX(-26px); }
  to   { opacity: 1; transform: translateX(0); }
}

.steps { display: flex; align-items: center; gap: 12px; min-height: 40px; }
.step-back {
  appearance: none; cursor: pointer; font-family: inherit; font-size: 18px;
  width: 40px; height: 40px; border-radius: 50%; line-height: 1;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
}
.step-back:hover { border-color: var(--accent); }
.step-back[hidden] { display: none; }
/* Le chapelet d'etapes : des pastilles numerotees, comme les cases d'un jeu
   de plateau. Le numero dit ou l'on va, le jaune dit ou l'on est, la coche
   verte dit ce qui est fait. Le solo compte cinq etapes et le Battle sept :
   sept pastilles de 26 px tiennent dans la bande de 40 px sur un telephone.
   Les etapes a venir sont en retrait sans etre invisibles : on voit combien
   il en reste. */
.step-dots { list-style: none; display: flex; gap: 8px; align-items: center; }
.step-dot {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
  background: var(--surface); color: var(--ink);
  border: 2px solid var(--ink); box-shadow: 0 2px 0 var(--ink);
  opacity: 0.55;
  transition: background 200ms ease, transform 200ms ease, opacity 200ms ease;
}
.step-dot[data-state="current"] {
  background: var(--gold); opacity: 1;
  transform: scale(1.15);
}
.step-dot[data-state="done"] { background: var(--ok-fill); opacity: 1; }
/* La coche est dessinee, pas tapee : deux cotes d'un carre tourne d'un
   huitieme de tour, dans le meme trait que le cercle. */
.step-dot[data-state="done"]::after {
  content: ''; width: 10px; height: 5px;
  border-left: 3px solid var(--ink); border-bottom: 3px solid var(--ink);
  transform: translateY(-1px) rotate(-45deg);
}
/* Sur desktop, un peu plus grandes : la bande a la place, et l'oeil est plus
   loin de l'ecran. */
@media (hover: hover) and (pointer: fine) {
  .step-dots { gap: 10px; }
  .step-dot { width: 30px; height: 30px; font-size: 13px; }
}

/* --- carrousel de pochettes --- */
.carousel {
  --cover: min(64vw, 300px);
  --card: calc(var(--cover) + 18px);
  position: relative;
  /* Le carrousel deborde la colonne de 480px pour laisser voir les pochettes
     voisines : sans cela on n'en apercoit que deux et l'effet tombe a plat. */
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  perspective: 900px;
  padding: 18px 0 22px;
  outline: none;
}
.carousel::-webkit-scrollbar { display: none; }
.carousel:focus-visible .cover-card[data-current] .cover-btn {
  outline: 2px solid var(--accent); outline-offset: 4px;
}
.carousel-track {
  list-style: none; display: flex; align-items: center;
  transform-style: preserve-3d;
}
/* Cales en elements reels : ni un padding de conteneur flex ni un pseudo-element
   ne comptent dans la zone defilable de Chrome — le carrousel restait fige et la
   derniere pochette ne pouvait jamais atteindre le centre. */
.carousel-spacer { flex: 0 0 calc(50vw - var(--cover) / 2 - 9px); height: 1px; }
/* La carte porte l'accrochage et reste sans transformation : le scroll-snap
   utilise la boite APRES transformation, donc faire pivoter la carte elle-meme
   deplacait son point d'accroche et le defilement sautait une pochette. */
.cover-card {
  flex: 0 0 var(--card); scroll-snap-align: center;
  display: flex; justify-content: center;
}
.cover-inner {
  display: flex; flex-direction: column; align-items: center; gap: 0;
  transform-style: preserve-3d;
}
.cover-btn {
  appearance: none; border: none; padding: 0; cursor: pointer; background: none;
  width: var(--cover); height: var(--cover); border-radius: 18px; overflow: hidden;
  border: var(--stroke) solid var(--ink);
  box-shadow: 0 8px 0 var(--ink);
  display: block;
}
.cover-art { width: 100%; height: 100%; object-fit: cover; display: block; background: var(--surface-2); }
/* Le reflet, signature visuelle du cover flow d'origine. Le fondu se fait par
   un voile de la couleur du fond plutot que par mask-image : un masque force un
   compositing hors GPU par element, ce qui divisait la fluidite par quatre. */
.cover-reflection {
  position: relative;
  width: var(--cover); height: calc(var(--cover) * 0.3); margin-top: 2px; border-radius: 0 0 14px 14px;
  overflow: hidden; opacity: 0.3;
}
.cover-reflection::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(72, 56, 240, 0.55), var(--bg) 85%);
}
.cover-reflection img { width: 100%; height: var(--cover); object-fit: cover; transform: scaleY(-1); display: block; }

.carousel-caption { text-align: center; min-height: 52px; }
.carousel-name { font-family: var(--font-title);
  font-size: 20px; font-weight: 800; letter-spacing: 0;
  animation: caption-in 220ms ease-out;
}
.carousel-desc { font-size: 13.5px; color: var(--muted); margin-top: 3px; line-height: 1.4; }
@keyframes caption-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* --- curseur crante --- */
/* --- pas a pas du nombre de manches --- */
.stepper {
  display: flex; align-items: center; justify-content: center; gap: 22px;
  padding: 4px 0;
}
.stepper-btn {
  flex: 0 0 auto; width: 56px; height: 56px; border-radius: 50%;
  appearance: none; cursor: pointer; font-family: inherit;
  font-size: 26px; font-weight: 700; line-height: 1;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
  /* La meme tranche que les autres surfaces qui se pressent. */
  box-shadow: 0 5px 0 var(--edge);
  transition: border-color 120ms ease, color 120ms ease,
              transform 70ms ease, box-shadow 70ms ease, opacity 120ms ease;
}
.stepper-btn:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 var(--edge); }
/* Au bout de la course, le bouton reste en place mais s'efface : le plafond se
   voit avant d'etre atteint. */
.stepper-btn:disabled { opacity: 0.3; cursor: default; box-shadow: none; }
@media (hover: hover) and (pointer: fine) {
  .stepper-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
}
.stepper-face {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 130px;
}
.stepper-value {
  font-family: var(--font-title);
  font-size: 52px; font-weight: 800; letter-spacing: 0; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stepper-unit { font-size: 14px; font-weight: 700; color: var(--muted); }

/* --- gros bouton de lancement --- */
.big-start {
  appearance: none; border: none; cursor: pointer; font-family: inherit;
  width: 100%; min-height: 96px; border-radius: 24px;
  border: var(--stroke) solid var(--ink);
  background: var(--accent-grad); color: var(--accent-ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  transition: transform 70ms ease, box-shadow 70ms ease, filter 120ms ease;
}
.big-start { box-shadow: 0 9px 0 var(--accent-edge); }
.big-start:active:not(:disabled) { transform: translateY(7px); box-shadow: 0 2px 0 var(--accent-edge); }
.big-start:disabled { filter: grayscale(1) brightness(0.5); cursor: default; box-shadow: none; }
/* Sur desktop le bouton n'a pas a etre une cible de pouce : 80 px suffisent la
   ou 96 servent la prise en main a une main. */
@media (hover: hover) and (pointer: fine) {
  .big-start { min-height: 80px; }
}
.big-start-label { font-family: var(--font-title); font-size: 30px; font-weight: 800; letter-spacing: 0; }
.big-start-sub { font-size: 14px; font-weight: 700; opacity: 0.7; }

/* ===== Ecoute maintenue ==================================================== */

.hold {
  --size: min(62vw, 260px);
  appearance: none; border: none; padding: 0; cursor: pointer; background: none;
  width: var(--size); height: var(--size); align-self: center;
  position: relative; display: grid; place-items: center;
  font-family: inherit; color: var(--text);
  touch-action: none;            /* sinon un appui long declenche le defilement */
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 90ms ease;
}
.hold:active { transform: scale(0.97); }
.hold:disabled { filter: grayscale(1) brightness(0.6); cursor: default; }

.hold-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.hold-track, .hold-arc {
  fill: none; stroke-width: 9; stroke-linecap: round;
}
.hold-track { stroke: var(--surface); }
/* Le bouton d'ecoute est un disque. Il ne l'est pas pour decorer : le geste
   central du jeu est de maintenir pour ecouter, et un disque qui tourne rend ce
   geste visible — c'est le seul element de l'ecran qui dit "ca joue" sans qu'on
   ait a lire un chiffre.
   Le corps porte les sillons. Ils ne tournent pas, et c'est deliberé : un
   anneau concentrique est identique a lui-meme sous n'importe quel angle,
   l'animer serait payer un repeint pour une image qui ne change pas. */
.hold::before {
  content: ''; position: absolute; inset: 12%;
  border-radius: 50%;
  background:
    /* Les sillons. Assez serres pour se lire comme une matiere, assez faibles
       pour ne pas devenir du bruit au-dessus de la video de fond. */
    repeating-radial-gradient(circle at 50% 50%,
      rgba(242, 243, 255, 0.06) 0 1px,
      transparent 1px 4px),
    radial-gradient(circle at 50% 50%, #2b1f66 0%, #1b1240 58%, #120b30 100%);
  border: var(--stroke) solid var(--line);
  transition: border-color 140ms ease;
}
/* Le disque tourne : sa tranche s'allume. */
.hold[data-spinning]::before { border-color: var(--gold); }

/* L'etiquette centrale, celle qu'on lit. Elle reprend la surface des cartes :
   les chiffres se detachent dessus comme partout ailleurs, et le jaune reste
   ou il veut dire quelque chose — l'anneau de progression et le triangle. */
.hold::after {
  content: ''; position: absolute; z-index: 0;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 60%; height: 60%; border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 0 0 3px var(--ink);
  transition: background 140ms ease;
}
.hold[data-spinning]::after { background: var(--surface-2); }

/* Le reflet, et c'est lui qui tourne : deux lueurs opposees, comme un disque
   sous une lampe. Une seule propriete animee, transform, donc rien a repeindre
   — le compositeur suffit, meme au-dessus d'une video qui joue. */
.hold-vinyl {
  position: absolute; inset: 12%;
  border-radius: 50%;
  pointer-events: none;
  background: conic-gradient(from 0deg,
    transparent 0deg,
    rgba(242, 243, 255, 0.11) 16deg,
    transparent 48deg,
    transparent 168deg,
    rgba(242, 243, 255, 0.08) 184deg,
    transparent 216deg,
    transparent 360deg);
  /* Le reflet s'arrete au bord de l'etiquette : sur du papier, la lumiere ne
     glisse pas comme sur du vinyle. */
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 0 30%, #000 31%);
          mask: radial-gradient(circle at 50% 50%, transparent 0 30%, #000 31%);
  /* 1,8 s par tour, soit trente-trois tours et demi par minute : la vitesse
     reelle d'un 33 tours. */
  animation: vinyl-spin 1.8s linear infinite;
  /* En pause plutot que retiree : le disque reprend ou il s'etait arrete au
     lieu de sauter a l'origine a chaque appui.
     Il ne tourne que sur data-spinning, pose quand la lecture a reellement
     demarre — pas sur l'appui. Le disque tournait des le pointerdown : quand le
     son ne partait pas, il annoncait une lecture qui n'existait pas, et rien ne
     disait au joueur que quelque chose avait echoue. */
  animation-play-state: paused;
}
.hold[data-spinning] .hold-vinyl { animation-play-state: running; }
@keyframes vinyl-spin { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) {
  .hold-vinyl { animation: none; }
}

/* Au-dessus de l'etiquette, qui est un pseudo-element donc peint apres. */
.hold-inner { position: relative; z-index: 1; }
.hold-arc {
  stroke: url(#holdGradient); stroke: var(--gold);
  /* 2 x pi x 53 : le trace complet du cercle. */
  stroke-dasharray: 333;
  stroke-dashoffset: 333;
  transition: stroke 200ms ease;
}
.hold[data-full] .hold-arc { stroke: var(--ko); }

.hold-inner {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  width: 52%; text-align: center;
}
/* Le triangle occupait un tiers du bouton. Sur un disque il n'a plus a porter
   l'affordance — la matiere et "maintiens" s'en chargent — et sa place revient
   aux chiffres, qui sont ce qu'on regarde vraiment. */
.hold-glyph {
  width: 19%; height: auto; margin-bottom: 4px;
  color: var(--accent);
  transition: transform 120ms ease, color 160ms ease;
}
.hold[data-playing] .hold-glyph { color: var(--accent-2); transform: scale(0.86); }
.hold[data-full] .hold-glyph { color: var(--muted); }
.hold-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted);
}
.hold-time { font-family: var(--font-title);
  font-size: 30px; font-weight: 800; letter-spacing: 0; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.hold-points {
  font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.hold[data-playing] .hold-label { color: var(--accent); }
/* Quand le son n'a pas demarre, le bouton le dit lui-meme : c'est la seule
   chose que le joueur regarde a ce moment-la. */
.hold[data-error] .hold-label { color: var(--ko); }

.actions-single { grid-template-columns: 1fr; }

/* --- centrage de l'onboarding --- */
.step { align-items: center; text-align: center; }
.step > * { width: 100%; }
/* Le titre annonce l'etape, ce qui suit y repond : un cran de plus que l'ecart
   commun le detache du bloc au lieu de le laisser coller a sa premiere ligne.
   En marge et non en gap — l'ecart n'est plus large qu'a cet endroit-la. */
.step > h1 { margin-bottom: 10px; }
/* Sauf sur l'accueil : le titre n'y annonce pas une etape, la phrase qui suit
   le prolonge — elle se tient a l'ecart commun. */
#step-accueil > h1 { margin-bottom: 0; }
/* Exception : le carrousel deborde volontairement la colonne (voir .carousel),
   la largeur uniforme ci-dessus le ramenerait a la largeur du texte. */
.step > .carousel { width: 100vw; }
/* Le bouton de retour sort du flux pour que les points restent centres. */
.steps { position: relative; justify-content: center; }
.step-back { position: absolute; left: 0; top: 0; }
.player-list { justify-content: center; }
.player-form { text-align: left; }

/* --- retour en haut a gauche de l'ecran --- */
.step-back {
  position: fixed;
  top: calc(14px + env(safe-area-inset-top));
  left: calc(14px + env(safe-area-inset-left));
  z-index: 5;
}
/* Les points de progression vivent en haut de l'ecran, centres, sur la meme
   ligne que le bouton de retour : la barre fait la hauteur du bouton (40 px)
   et centre son contenu, les deux partagent donc le meme axe. Sortis du flux,
   ils ne decalent pas le contenu centre. */
.steps {
  position: fixed; z-index: 5;
  left: 0; right: 0;
  top: calc(14px + env(safe-area-inset-top));
  height: 40px;
  min-height: 0;
  align-items: center;
  justify-content: center;
  /* Le conteneur couvre toute la largeur : sans cela il intercepterait les
     appuis sur le haut de l'ecran. Seul le bouton de retour reste cliquable. */
  pointer-events: none;
}
/* La fleche et la tete du joueur sont les deux seules choses cliquables de
   la barre : le reste laisse passer les appuis vers l'ecran. */
.step-back, .joueur { pointer-events: auto; }
/* De la place au-dessus du contenu pour que rien ne monte sous la barre. */
#screen-onboard { padding-top: 54px; }


/* --- sortie de partie --- */

/* Meme forme que le retour de l'onboarding, et la meme place : pendant une
   partie, le coin en haut a gauche veut dire "sortir d'ici". */
.quit {
  position: fixed; z-index: 31;
  top: calc(14px + env(safe-area-inset-top));
  left: calc(14px + env(safe-area-inset-left));
  appearance: none; cursor: pointer; font-family: inherit; font-size: 16px;
  width: 40px; height: 40px; border-radius: 50%; line-height: 1;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
}
.quit:hover { border-color: var(--accent); }
.quit[hidden] { display: none; }

/* Le compteur de manche — le nom de la regle en rush — quitte le bord droit
   pour le haut de l'ecran, centre sur l'axe du bouton qui quitte la partie.
   C'est la place qu'occupe la progression pendant l'onboarding : d'un ecran a
   l'autre, la meme bande de 40 px et le meme axe. La bande couvre toute la
   largeur pour rester centree sur la fenetre et non sur la colonne, d'ou le
   pointer-events qui laisse passer les appuis vers le bouton. */
#play-round {
  position: fixed; z-index: 5;
  top: calc(14px + env(safe-area-inset-top));
  left: 0; right: 0; height: 40px;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}

/* La bascule de langue : le pendant droit du bouton de retour. */
/* --- avis de la salle (depart, passation) --- */
/* Il se pose sous la barre du haut, la ou l'oeil revient entre deux manches,
   et jamais au centre : ce n'est pas une decision a prendre, c'est une
   nouvelle a lire. */
.avis {
  position: fixed; z-index: 32;
  top: calc(66px + env(safe-area-inset-top));
  left: 50%; transform: translateX(-50%);
  max-width: min(88vw, 420px);
  padding: 12px 18px; border-radius: 999px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  color: var(--text); font-size: 14px; font-weight: 700; text-align: center;
  box-shadow: 0 5px 0 var(--ink);
  animation: avis-entre 220ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
.avis[hidden] { display: none; }
/* La passation s'adresse a quelqu'un en particulier : elle se distingue. */
.avis[data-moi] { border-color: var(--accent); color: var(--accent); }
@keyframes avis-entre {
  from { opacity: 0; transform: translate(-50%, -10px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* --- choix de langue --- */
.langue {
  position: fixed; z-index: 31;
  top: calc(14px + env(safe-area-inset-top));
  right: calc(14px + env(safe-area-inset-right));
  display: flex; align-items: center; gap: 8px;
}
/* Le menu des langues se pose sous son drapeau, et non sous la rangee : d'ou
   cette pastille, qui lui sert de repere. */
.langue-pastille { position: relative; }

.langue[hidden] { display: none; }
/* Meme hauteur que la sortie de partie et le retour, en face d'eux. */
.langue-btn {
  display: flex; align-items: center; gap: 6px;
  height: 40px; padding: 0 10px 0 8px; border-radius: 999px;
  appearance: none; cursor: pointer;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--muted);
}
.langue-chevron { width: 12px; height: 8px; transition: transform 0.18s ease; }
.langue-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.langue-btn[aria-expanded="true"] .langue-chevron { transform: rotate(180deg); }

/* La pastille : le drapeau remplit le cercle, rogne sur ses cotes. */
.drapeau {
  display: block; width: 24px; height: 24px; flex: none;
  border-radius: 50%; overflow: hidden;
  box-shadow: inset 0 0 0 2px var(--ink);
}
.drapeau-svg { display: block; width: 100%; height: 100%; }

.langue-menu {
  position: absolute; top: calc(100% + 8px); right: 0;
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px; min-width: 168px;
  border-radius: 14px; background: var(--surface); border: var(--stroke) solid var(--line);
  box-shadow: 0 6px 0 var(--ink);
}
.langue-menu[hidden] { display: none; }
.langue-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 10px;
  appearance: none; cursor: pointer; text-align: left;
  background: transparent; border: 0; color: var(--text);
  font-family: inherit; font-size: 15px; font-weight: 600;
}
/* La langue active se marque par la coche, pas par un fond : le menu n'en
   compte que deux, et un fond plein ferait croire a un survol. */
.langue-item[aria-checked="true"] { color: var(--accent); }
.langue-item[aria-checked="true"]::after {
  content: '\2713'; margin-left: auto; font-weight: 800;
}
@media (hover: hover) and (pointer: fine) {
  .langue-btn:hover { border-color: var(--accent); color: var(--accent); }
  .langue-item:hover { background: var(--card-hover); }
}

/* La sortie de partie n'est plus un fondu mais un volet : il entre par la
   droite et recouvre le jeu. On le voit arriver, donc on sait par ou il
   repartira — et le jeu, derriere, n'a pas l'air d'avoir disparu.
   Le decompte, lui, garde son fondu au noir : ce n'est pas une porte, c'est
   une respiration. */
.pause { z-index: 40; }
/* Par l'identifiant : la classe seule perdait contre .screen-dark, declaree
   plus bas dans la feuille, et le volet gardait le fondu du decompte. */
#pause { animation: volet-entre 320ms cubic-bezier(0.22, 0.9, 0.3, 1) both; }
#pause[data-sort] { animation: volet-sort 260ms cubic-bezier(0.4, 0, 0.7, 0.2) both; }
@keyframes volet-entre { from { translate: 100% 0; } to { translate: 0 0; } }
@keyframes volet-sort { from { translate: 0 0; } to { translate: 100% 0; } }
/* Sans mouvement, le volet apparait et disparait, sans glisser. */
@media (prefers-reduced-motion: reduce) {
  #pause, #pause[data-sort] { animation: none; }
}
.pause[hidden] { display: none; }
.pause .lede { max-width: 34ch; text-align: center; }
.pause-title {
  font-family: var(--font-title);
  font-size: 30px; font-weight: 800; letter-spacing: 0; line-height: 1.1;
  text-align: center;
}
.pause-actions {
  display: flex; flex-direction: column; gap: 10px;
  width: 100%; max-width: 320px; margin-top: 6px;
}

/* --- rappels clavier (desktop) --- */
.hints {
  position: fixed; z-index: 20;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 16px;
  font-size: 12px; color: var(--muted);
  pointer-events: none;
}
.hints:empty { display: none; }
/* Sans clavier physique, ces rappels n'ont aucun sens. */
@media (pointer: coarse), (max-width: 640px) {
  .hints { display: none; }
}
.hint-item { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
kbd {
  background: var(--surface-2); border: var(--stroke) solid var(--line); border-radius: 5px;
  padding: 2px 6px; font-family: inherit; font-size: 11px; font-weight: 700;
  color: var(--text); line-height: 1.4;
}

/* --- flamme qui suit la progression --- */
.hold-orbit {
  position: absolute; inset: 0; pointer-events: none;
  opacity: 0; transition: opacity 200ms ease;
}
/* Elle n'apparait que pendant l'ecoute : c'est la progression qui l'allume. */
.hold[data-spinning] .hold-orbit { opacity: 1; }
.hold-flame {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -46%);
  font-size: 24px; line-height: 1;
}
/* L'animation et l'ombre ne vivent que pendant l'ecoute : une opacite nulle
   laisserait tourner le scintillement en permanence, pour rien. */
.hold[data-spinning] .hold-flame {
  filter: drop-shadow(0 0 8px rgba(var(--accent-2-rgb), 0.85));
  animation: flame-flicker 220ms ease-in-out infinite alternate;
}
@keyframes flame-flicker {
  from { transform: translate(-50%, -46%) scale(0.88) rotate(-6deg); }
  to   { transform: translate(-50%, -46%) scale(1.12) rotate(6deg); }
}

/* --- chronometre de manche --- */
.timer { display: flex; align-items: center; gap: 10px; }
.timer-value {
  font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text); min-width: 38px; text-align: right;
  transition: color 200ms ease;
}
.timer-track {
  flex: 1; height: 14px; border-radius: 8px;
  background: var(--surface); border: 2px solid var(--ink); overflow: hidden;
}
.timer-bar { display: block; height: 100%; width: 100%; background: var(--accent-grad); }
.timer[data-urgent] .timer-value { color: var(--ko); }
.timer[data-urgent] .timer-bar { background: var(--ko-fill); }

/* --- repere du meilleur point atteint --- */
.hold-mark { position: absolute; inset: 0; pointer-events: none; }
.hold-mark[hidden] { display: none; }
/* Le trait se pose exactement sur l'epaisseur de l'anneau : dans le viewBox de
   120, le trace occupe les rayons 49,5 a 56,5, soit 2,9 % a 8,8 % depuis le bord. */
.hold-mark-tick {
  position: absolute; top: 2.4%; left: 50%;
  width: 3px; height: 7%;
  transform: translateX(-50%);
  background: var(--text); border-radius: 2px;
  box-shadow: 0 0 0 2px var(--bg);
}

/* Le repere pousse par la lecture : on signale qu'un nouveau record s'ecrit. */
.hold-mark[data-pushed] .hold-mark-tick {
  background: var(--accent-2);
  box-shadow: 0 0 0 2px var(--bg), 0 0 10px rgba(var(--accent-2-rgb), 0.9);
}

/* --- recapitulatif en carrousel --- */
/* Le contour dit d'un coup d'oeil si le morceau a ete trouve. */
.cover-card[data-result="ok"] .cover-btn { border-color: var(--ok-fill); }
.cover-card[data-result="ko"] .cover-btn { border-color: var(--ko-fill); }
.cover-card[data-result="ko"] .cover-art { filter: grayscale(0.75) brightness(0.75); }

.recap-verdict {
  font-size: 13px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  margin-bottom: 4px;
}
.recap-verdict[data-result="ok"] { color: var(--ok); }
.recap-verdict[data-result="ko"] { color: var(--ko); }


/* --- ecran de fin : texte centre, comme l'onboarding --- */
#screen-done { align-items: center; text-align: center; }
#screen-done > * { width: 100%; }
/* Meme exception que sur l'onboarding : le carrousel deborde volontairement la
   colonne pour laisser voir les pochettes voisines — la largeur uniforme
   ci-dessus l'enfermait dans 480 px et coupait tout le reste du recap. */
#screen-done > .carousel { width: 100vw; }
/* Le classement garde son alignement : rang, nom et score sont en colonnes. */
#screen-done .board-row { text-align: left; }

/* La pochette du recap est bornee a la hauteur aussi : la taille du carrousel
   vient de l'ecran de styles, qui n'a que lui — ici elle cohabite avec le
   score, la legende et deux boutons, et poussait ces derniers sous le pli sur
   telephone. 32vh laisse l'ecran entier tenir dans le cas courant. La variable
   vit sur la section : la legende, hors du carrousel, en a besoin aussi. */
#screen-done { --cover: min(58vw, 32vh, 300px); }
#screen-done .carousel { --cover: inherit; padding-bottom: 0; }
/* La legende a 16 px de la pochette. Le reflet occupe 30 % de pochette sous
   elle et poussait le texte loin : la legende remonte de cette hauteur et se
   pose dessus — il s'efface vers le fond, le texte y reste lisible. Le gap de
   .screen fournit les 16 px restants. */
#screen-done .carousel-caption {
  position: relative; z-index: 1;
  margin-top: calc(var(--cover) * -0.3 - 2px);
}


/* ===== Etape des regles : le carrousel ==================================== */

/* Meme contrat que le carrousel de styles : il deborde la colonne pour laisser
   voir les regles voisines — c'est ce qui dit "fais glisser" sans le dire. */
.fmt-swiper {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  outline: none;
}
.fmt-swiper::-webkit-scrollbar { display: none; }
.fmt-track {
  list-style: none; display: flex; gap: 14px;
  padding: 6px 0 14px;
}
/* Les cales laissent la premiere et la derniere regle se centrer. Ce sont de
   vrais elements flex et non le padding de la piste : Chrome n'inclut pas le
   padding de fin d'un conteneur flex dans sa zone defilable, et la derniere
   carte restait alors hors d'atteinte — de 551 px sur un ecran large. Meme
   remede que les cales du carrousel des styles. */
.fmt-spacer { flex: 0 0 calc(50vw - min(39vw, 180px) - 14px); }
.fmt-slide {
  flex: 0 0 min(78vw, 360px);
  scroll-snap-align: center;
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px;
  border-radius: 16px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  /* La meme tranche que les boutons : la carte se clique, elle se pose sur
     une epaisseur. */
  box-shadow: 0 5px 0 var(--edge);
  transition: border-color 160ms ease, transform 200ms ease, opacity 200ms ease,
              box-shadow 70ms ease;
  transform: scale(0.94); opacity: 0.6;
  /* Le badge (Beta, Coming soon) s'ancre au coin haut-droit de la carte. */
  position: relative;
}
.fmt-slide > .mode-badge {
  position: absolute; top: 20px; right: 20px; z-index: 1;
  margin: 0;
}
/* La regle centree garde son trait d'encre et prend un anneau jaune : sur cet
   ecran le jaune dit deja « en cours » (le point dessous, la barre d'etapes).
   L'anneau est dessine au-dessus de la tranche, qui s'allonge d'autant pour
   depasser encore de cinq pixels. */
.fmt-slide[data-current] {
  transform: none; opacity: 1;
  box-shadow: 0 0 0 4px var(--gold), 0 9px 0 var(--edge);
}
.fmt-slide:active { transform: scale(0.94) translateY(4px); box-shadow: 0 1px 0 var(--edge); }
.fmt-slide[data-current]:active {
  transform: translateY(4px);
  box-shadow: 0 0 0 4px var(--gold), 0 5px 0 var(--edge);
}
/* Un mode en retrait ne se clique pas : pas d'epaisseur, pas d'enfoncement.
   Centre, il garde l'anneau — c'est bien lui qu'on regarde. */
.fmt-slide[data-off], .fmt-slide[data-off]:active { box-shadow: none; }
.fmt-slide[data-off][data-current], .fmt-slide[data-off][data-current]:active {
  box-shadow: 0 0 0 4px var(--gold);
}
.fmt-slide[data-off]:active { transform: scale(0.94); }
.fmt-slide[data-off][data-current]:active { transform: none; }
.fmt-slide[data-off] .fmt-media { filter: grayscale(0.6) brightness(0.7); }

/* La video de demonstration. En son absence, l'icone du mode tient le cadre. */
.fmt-media {
  position: relative; overflow: hidden;
  aspect-ratio: 16 / 10; border-radius: 12px;
  background: var(--bg); border: var(--stroke) solid var(--ink);
}
.fmt-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.fmt-media video[data-absente] { display: none; }
.fmt-fallback {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--gold);
}
.fmt-media video:not([data-absente]) + .fmt-fallback { display: none; }

/* --- scenes animees : la demo de chaque regle, en attendant les videos ------ */
/* Chaque cadre joue une vignette en CSS pur. Seule la diapositive centree
   anime : trois scenes en fond tourneraient pour personne. */
.fmt-slide:not([data-current]) .fmt-scene * { animation-play-state: paused !important; }
.fmt-scene {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 18px;
}

/* Les fausses propositions, communes au quizz et au rush. */
.fmt-ligne {
  width: 78%; height: 22px; border-radius: 8px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  display: flex; align-items: center; gap: 8px; padding: 0 8px;
}
.fmt-ligne::before {
  content: ''; width: 10px; height: 10px; border-radius: 4px;
  background: var(--line); flex: none;
}
.fmt-ligne::after {
  content: ''; height: 6px; border-radius: 3px; background: var(--line);
  width: 62%;
}
.fmt-ligne:nth-child(2)::after { width: 44%; }
.fmt-ligne:nth-child(3)::after { width: 54%; }

/* Quizz : les trois lignes, l'une s'allume a tour de role — on hesite, on
   choisit. Cycle de 3,6 s. */
.fmt-quiz .fmt-ligne { animation: fmt-quiz-allume 3.6s infinite; }
.fmt-quiz .fmt-ligne:nth-child(2) { animation-delay: 1.2s; }
.fmt-quiz .fmt-ligne:nth-child(3) { animation-delay: 2.4s; }
@keyframes fmt-quiz-allume {
  0%, 24%, 100% { border-color: var(--line); background: var(--surface-2); transform: none; }
  8%, 16% { border-color: var(--gold); background: var(--surface); transform: scale(1.04); }
}

/* Chrono, en quatre temps sur un cycle de 4,8 s : on maintient le disque, on
   relache, la reponse se tape toute seule dans le champ, et elle se valide en
   vert. La scene raconte la manche entiere. */
.fmt-disque {
  width: 74px; height: 74px; border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, var(--surface-2) 0 34%, var(--ink) 35% 100%);
  border: 3px solid var(--line);
  position: relative;
}
.fmt-chrono .fmt-disque { animation: fmt-disque-tient 4.8s infinite; }
@keyframes fmt-disque-tient {
  0%, 28%, 100% { border-color: var(--line); transform: none; }
  6%, 22% { border-color: var(--gold); transform: scale(0.96); }
}
.fmt-champ {
  width: 68%; height: 22px; border-radius: 7px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  display: flex; align-items: center; gap: 6px; padding: 0 8px;
  animation: fmt-champ-valide 4.8s infinite;
}
@keyframes fmt-champ-valide {
  0%, 66%, 100% { border-color: var(--line); transform: none; }
  72%, 88% { border-color: var(--ok); transform: scale(1.03); }
}
/* La reponse se tape lettre a lettre : largeur en escalier, caret au bout. */
.fmt-saisie {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; color: var(--text); line-height: 1;
  white-space: nowrap; overflow: hidden;
  border-right: 2px solid var(--accent);
  width: 0;
  animation: fmt-tape 4.8s steps(9, end) infinite;
}
@keyframes fmt-tape {
  0%, 30% { width: 0; border-right-color: var(--accent); }
  60% { width: 9ch; border-right-color: var(--accent); }
  70%, 100% { width: 9ch; border-right-color: transparent; }
}
.fmt-coche {
  font-size: 13px; font-weight: 800; color: var(--ok); line-height: 1;
  opacity: 0;
  animation: fmt-coche-pose 4.8s infinite;
}
@keyframes fmt-coche-pose {
  0%, 68% { opacity: 0; transform: scale(0.4); }
  74%, 88% { opacity: 1; transform: scale(1); }
  96%, 100% { opacity: 0; }
}

/* Rush : les lignes flashent vite, la serie grimpe. Cycle de 2,4 s. */
.fmt-rush .fmt-ligne { animation: fmt-rush-flash 2.4s infinite; }
.fmt-rush .fmt-ligne:nth-child(2) { animation-delay: 0.8s; }
.fmt-rush .fmt-ligne:nth-child(3) { animation-delay: 1.6s; }
@keyframes fmt-rush-flash {
  0%, 20%, 100% { border-color: var(--line); background: var(--surface-2); }
  6%, 12% { border-color: var(--ok); background: var(--surface); }
}
.fmt-combo {
  font-family: var(--font-title); font-weight: 800; font-size: 15px;
  color: var(--gold);
  animation: fmt-combo-pop 2.4s infinite;
}
@keyframes fmt-combo-pop {
  0%, 100% { transform: none; opacity: 0.65; }
  10% { transform: scale(1.3); opacity: 1; }
}

/* Voix : le micro pulse, l'egaliseur respire — la musique joue, on parle. */
.fmt-micro {
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent);
  animation: fmt-micro-pulse 2s infinite;
}
.fmt-micro .icon { width: 22px; height: 22px; }
@keyframes fmt-micro-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--gold-rgb), 0.5); }
  60% { box-shadow: 0 0 0 14px rgba(var(--gold-rgb), 0); }
}
.fmt-eq { display: flex; gap: 4px; align-items: flex-end; height: 22px; }
.fmt-eq span {
  width: 5px; border-radius: 2px; background: var(--accent-2);
  animation: fmt-eq-danse 0.9s ease-in-out infinite alternate;
}
.fmt-eq span:nth-child(1) { height: 40%; }
.fmt-eq span:nth-child(2) { height: 90%; animation-delay: 0.12s; }
.fmt-eq span:nth-child(3) { height: 60%; animation-delay: 0.24s; }
.fmt-eq span:nth-child(4) { height: 100%; animation-delay: 0.36s; }
.fmt-eq span:nth-child(5) { height: 50%; animation-delay: 0.48s; }
@keyframes fmt-eq-danse { from { transform: scaleY(0.4); } to { transform: scaleY(1); } }

/* L'icone rejoint le titre de la diapositive. */
.fmt-nom .icon { width: 20px; height: 20px; color: var(--accent); flex: none; }

.fmt-nom {
  font-family: var(--font-title); font-size: 19px; font-weight: 800;
  letter-spacing: 0; display: flex; align-items: center; gap: 8px;
  justify-content: center;
}
.fmt-desc { font-size: 13.5px; color: var(--muted); line-height: 1.45; text-align: center; }

.fmt-dots { display: flex; gap: 6px; justify-content: center; }
.fmt-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--canvas-line); transition: background 160ms ease, width 160ms ease;
}
.fmt-dot[data-on] { background: var(--accent); width: 20px; border-radius: 4px; }

/* ===== Classement ========================================================= */

#screen-board, #screen-profil, #screen-compte { align-items: center; text-align: center; }
#screen-board > *, #screen-profil > *, #screen-compte > *, #screen-board #board-corps > * { width: 100%; }

.board-teaser {
  font-size: 14.5px; font-weight: 700; color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.board-form { display: flex; flex-direction: column; gap: 12px; }
/* Les personnages du classement : une rangee qu'on fait defiler du pouce,
   sans carte autour. Celui du centre est celui qu'on choisit ; les autres
   reculent et s'effacent. */
.board-personnages {
  --perso: 148px;
  display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  /* La tranche des jetons tombe sous eux : la rangee lui garde ses 4 px,
     sinon le defilement horizontal la rognerait. */
  margin-inline: -20px; padding-block: 4px 8px;
  padding-inline: calc(50% - var(--perso) / 2);
}
.board-personnages::-webkit-scrollbar { display: none; }
/* Chaque tete dans son jeton rond, dans le dessin du bouton secondaire :
   fond blanc, trait d'encre de 4 px, tranche pleine dessous. Le retrait des
   voisines porte sur le jeton entier — le disque recule avec le personnage,
   et pas le personnage seul dans un disque qui resterait grand. */
.board-perso {
  /* Le personnage est dessine au carre et le jeton est rond : sans retrait, le
     cercle lui couperait les cheveux. 14 px de marge interieure le posent a
     112 px dans un jeton de 148. */
  appearance: none; cursor: pointer; padding: 14px;
  flex: 0 0 var(--perso); height: var(--perso); scroll-snap-align: center;
  display: grid; place-items: center; overflow: hidden;
  background: #fff; border: 4px solid var(--refonte-ink); border-radius: 50%;
  box-shadow: 0 4px 0 var(--refonte-ink);
  transform: scale(0.66);
  transition: transform 260ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
/* Le jeton garde son blanc plein — un disque a demi transparent virait au
   violet sale sur le sol bleu. C'est la tete, a l'interieur, qui s'efface. */
.board-perso img {
  display: block; width: 100%; height: auto; aspect-ratio: 1;
  opacity: 0.45; transition: opacity 260ms ease;
}
.board-perso[data-on] { transform: none; }
.board-perso[data-on] img { opacity: 1; }
.board-perso:focus-visible { outline: 3px solid var(--refonte-jaune); outline-offset: 4px; }
/* Les points, pour dire que ca defile. */
.board-perso-points { display: flex; gap: 8px; justify-content: center; }
.board-perso-point {
  width: 8px; height: 8px; border-radius: 999px; background: rgba(255, 255, 255, 0.35);
  transition: width 200ms ease, background 200ms ease;
}
.board-perso-point[data-on] { width: 20px; background: var(--refonte-jaune); }
@media (prefers-reduced-motion: reduce) { .board-perso img { transition: none; } }

.board-tabs { display: flex; gap: 8px; justify-content: center; }
.board-tab {
  appearance: none; cursor: pointer; font-family: inherit;
  font-size: 13px; font-weight: 700; letter-spacing: 0.04em;
  padding: 8px 14px; border-radius: 999px;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--muted);
}
.board-tab[data-on] { color: var(--accent-ink); background: var(--accent-grad); border-color: transparent; }

/* Les puces de playlist : une rangee qui defile, la selection en accent. */
.board-playlists {
  display: flex; gap: 6px; overflow-x: auto; padding: 10px 2px 2px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.board-playlists::-webkit-scrollbar { display: none; }
.board-puce {
  appearance: none; cursor: pointer; font-family: inherit; white-space: nowrap;
  font-size: 12px; font-weight: 700;
  padding: 6px 11px; border-radius: 999px;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--muted);
}
.board-puce[data-on] { color: var(--accent); border-color: var(--accent); }

/* --- le podium : 2e a gauche, 1er au centre et plus haut, 3e a droite ------- */
.podium {
  list-style: none; display: grid;
  grid-template-columns: 1fr 1fr 1fr; align-items: end; gap: 10px;
  margin-top: 18px; padding: 0;
}
.podium-place {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-width: 0;
}
.podium-place[data-rang='2'] { order: 1; }
.podium-place[data-rang='1'] { order: 2; }
.podium-place[data-rang='3'] { order: 3; }
.podium-avatar {
  width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center; font-size: 30px; line-height: 1;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  position: relative;
}
.podium-place[data-rang='1'] .podium-avatar {
  width: 82px; height: 82px; font-size: 40px;
  border-color: var(--gold);
}
/* La couronne du premier, posee sur le cercle. */
.podium-place[data-rang='1'] .podium-avatar::before {
  content: '\1F451';
  position: absolute; top: -22px; left: 50%; transform: translateX(-50%);
  font-size: 22px;
}
.podium-place[data-moi] .podium-avatar { box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.45); }
/* Le ruban de rang, aux couleurs de medaille, a cheval sur l'avatar. */
.podium-rang {
  margin-top: -14px; z-index: 1;
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.08em;
  padding: 3px 9px; border-radius: 999px;
  color: var(--accent-ink);
}
.podium-place[data-rang='1'] .podium-rang { background: var(--accent); }
.podium-place[data-rang='2'] .podium-rang { background: #c9cede; }
.podium-place[data-rang='3'] .podium-rang { background: #e0a35c; }
.podium-nom {
  max-width: 100%; font-weight: 700; font-size: 13.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.podium-place[data-moi] .podium-nom { color: var(--accent); }
.podium-score {
  font-weight: 800; font-size: 13px; font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.podium-place[data-rang='1'] .podium-score { font-size: 15px; color: var(--text); }

/* --- la liste, a partir du 4e : plate, a la Duolingo ------------------------ */
.lb { list-style: none; display: flex; flex-direction: column; margin-top: 14px; }
/* Les points de suspension entre le podium et ma zone. */
.lb-zone[data-loin]::before {
  content: '\2022 \2022 \2022';
  display: block; text-align: center; padding: 4px 0;
  color: var(--muted); letter-spacing: 6px; font-size: 11px;
}
.lb-row {
  display: grid; grid-template-columns: 30px 42px 1fr auto;
  align-items: center; gap: 10px; text-align: left;
  padding: 10px 14px; border-radius: 12px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.22);
}
.lb-row:last-child { border-bottom: 0; }
/* Ma ligne : le bandeau teinte pleine largeur, celui qu'on cherche des yeux. */
.lb-row[data-moi] {
  background: rgba(var(--accent-rgb), 0.14);
  border-bottom-color: transparent;
}
.lb-rank { font-weight: 800; font-variant-numeric: tabular-nums; color: var(--muted); font-size: 14px; }
.lb-row[data-moi] .lb-rank { color: var(--accent); }
.lb-emoji {
  width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center; font-size: 19px; line-height: 1;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
}
.lb-name {
  font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-row[data-moi] .lb-name { color: var(--accent); }
.lb-score { font-weight: 800; font-variant-numeric: tabular-nums; }
.lb-vide { color: var(--muted); font-size: 14.5px; padding: 14px 0; text-align: center; }

/* ===========================================================================
   Mode Battle
   ======================================================================== */

/* --- salle : code, QR, invitation --- */

.lobby-code {
  font-size: 48px; letter-spacing: 0.18em; text-indent: 0.18em;
  color: var(--accent);
  text-shadow: var(--contour), 0 5px 0 var(--ink);
}

.lobby-share {
  display: flex; gap: 16px; align-items: center; text-align: left;
  padding: 14px; border-radius: var(--radius);
  background: var(--surface); border: var(--stroke) solid var(--line);
}
/* Le QR reste sur fond clair : un code sombre sur fond sombre n'est pas lu. */
.lobby-qr {
  flex: 0 0 118px; width: 118px; height: 118px;
  background: #fff; border-radius: 12px; padding: 6px;
  border: var(--stroke) solid var(--ink);
  display: flex; align-items: center; justify-content: center;
}
.lobby-qr svg { width: 100%; height: 100%; display: block; }
.lobby-invite { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.lobby-hint { font-size: 13.5px; color: var(--muted); line-height: 1.45; }

/* --- salle : joueurs presents --- */

.lobby-players { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.lobby-player {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center;
  padding: 10px 14px; border-radius: 10px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  text-align: left;
}
.lobby-player[data-me] { border-color: var(--accent); }
/* Sans nouvelles depuis quinze secondes : la ligne s'efface sans disparaitre,
   le joueur garde sa place et son score. */
.lobby-player[data-off] { opacity: 0.42; }
.lobby-player-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobby-player-tag {
  font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent);
}
.lobby-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--line); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0);
}
.lobby-player[data-ready] .lobby-dot { background: var(--ok); }
.lobby-kick {
  appearance: none; border: none; background: none; cursor: pointer;
  color: var(--muted); font-size: 18px; line-height: 1; padding: 2px 6px;
}
.lobby-kick:hover { color: var(--ko); }

/* --- depouillement : la machine a sous --- */

/* Pendant le defilement, les pochettes voisines ne doivent plus attirer l'oeil :
   seule celle du centre compte. */
#screen-spin .carousel { pointer-events: none; }
#spin-carousel { scroll-snap-type: none; }
/* (la teinte d'egalite est reglee avec le badge, voir .eyebrow) */

/* --- decompte : fondu au noir et presentation des joueurs --- */

.screen-dark {
  position: fixed; inset: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; padding: 24px;
  background: var(--ink);
  animation: dark-in 420ms ease both;
}
.screen-dark[hidden] { display: none; }
@keyframes dark-in { from { opacity: 0; } to { opacity: 1; } }

/* Le decompte traverse l'ecran au lieu d'y apparaitre : il entre par la
   gauche, couvre le reglage, et ressort par la droite en decouvrant la
   manche. Un seul geste, de gauche a droite, qui dit qu'on passe d'un cote a
   l'autre — la ou un fondu disait seulement qu'on change d'image.
   Par l'identifiant : a poids egal, .screen-dark l'emporterait sur une
   classe, etant declaree ici meme. */
#screen-countdown { animation: volet-gauche 360ms cubic-bezier(0.22, 0.9, 0.3, 1) both; }
#screen-countdown[data-sort] { animation: volet-droite 320ms cubic-bezier(0.4, 0, 0.7, 0.2) both; }
/* Le volet part de travers, de cinq degres : le jeu penche deja ses badges et
   ses etiquettes, et un rectangle parfaitement droit qui glisse a l'horizontale
   y aurait l'air d'un element d'un autre jeu.
   Il grossit en meme temps qu'il penche, et pour la meme raison : un
   rectangle de la taille de l'ecran, incline, ne le couvre plus — ses coins
   rentrent et laisseraient voir une pointe de fond en haut et en bas. A cinq
   degres sur un ecran de telephone, il faut environ un cinquieme de plus pour
   que les bords restent dehors. Au repos, l'inclinaison est nulle : le
   grossissement l'est aussi, et rien ne deborde. */
@keyframes volet-gauche {
  from { translate: -104% 0; rotate: -5deg; scale: 1.2; }
  to   { translate: 0 0;     rotate: 0deg;  scale: 1; }
}
@keyframes volet-droite {
  from { translate: 0 0;    rotate: 0deg; scale: 1; }
  to   { translate: 104% 0; rotate: 5deg; scale: 1.2; }
}
/* Sans mouvement, le decompte apparait et disparait, sans glisser. */
@media (prefers-reduced-motion: reduce) {
  #screen-countdown, #screen-countdown[data-sort] { animation: none; }
}

.countdown-number {
  font-family: var(--font-title);
  font-size: 104px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  text-shadow: -3px -3px 0 var(--bg), 3px -3px 0 var(--bg), -3px 3px 0 var(--bg), 3px 3px 0 var(--bg),
               0 8px 0 var(--bg);
}
/* Chaque chiffre repart d'un battement : le decompte se voit sans etre lu. */
.countdown-number[data-beat] { animation: beat 460ms cubic-bezier(0.22, 0.9, 0.3, 1); }
@keyframes beat {
  from { transform: scale(1.35); opacity: 0.2; }
  to { transform: scale(1); opacity: 1; }
}

.countdown-players {
  list-style: none; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
}
.countdown-player {
  padding: 8px 14px; border-radius: 999px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  font-weight: 600; font-size: 14.5px;
  animation: chip-in 380ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
.countdown-player[data-me] { border-color: var(--accent); }
@keyframes chip-in {
  from { transform: translateY(8px) scale(0.9); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* --- manche : l'attente des autres --- */

/* Reponse rendue : plus rien a jouer, on masque les commandes plutot que de
   les desactiver — un bouton grise invite quand meme a insister. */
#screen-play[data-waiting] .hold,
#screen-play[data-waiting] .guess,
#screen-play[data-waiting] .attempts,
#screen-play[data-waiting] .actions,
#screen-play[data-waiting] .debug { display: none; }

.waiting {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 30px 0; text-align: center;
  animation: step-in 300ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
.waiting[hidden] { display: none; }
/* Le verdict de l'attente porte le meme autocollant que la revelation en solo :
   une couleur pleine, un trait d'encre, pose de travers. On doit savoir si l'on
   a eu bon avant d'avoir lu quoi que ce soit. */
.waiting-verdict {
  align-self: center; max-width: 100%;
  font-family: var(--font-title); font-size: 22px; font-weight: 800; line-height: 1.15;
  padding: 8px 18px; border-radius: 14px;
  border: var(--stroke) solid var(--ink); color: var(--ink);
  box-shadow: 0 5px 0 var(--ink);
  transform: rotate(-2deg);
  animation: sticker-in 380ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.waiting-verdict[data-result="ok"] { background: var(--ok-fill); }
.waiting-verdict[data-result="ko"] { background: var(--ko-fill); }
@media (prefers-reduced-motion: reduce) {
  .waiting-verdict { animation: none; transform: rotate(-2deg); }
}
.waiting-detail { color: var(--muted); font-size: 14.5px; }
.waiting-players { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.waiting-player {
  padding: 7px 13px; border-radius: 999px; font-size: 14px; font-weight: 600;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--muted);
}
/* On montre qui a rendu sa copie, jamais si elle est juste : ce serait
   souffler la reponse a ceux qui cherchent encore. */
.lobby-player[data-replied], .waiting-player[data-replied] {
  border-color: var(--accent); color: var(--text);
}

/* --- revelation : le detail par joueur --- */

.results { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.result-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: baseline;
  padding: 10px 14px; border-radius: 10px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  font-size: 15px; text-align: left;
}
.result-row[data-me] { border-color: var(--accent); }
.result-row[data-result="ko"] { opacity: 0.6; }
.result-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.result-time { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.result-points { font-weight: 800; font-variant-numeric: tabular-nums; }
.result-row[data-result="ok"] .result-points { color: var(--ok); }
.result-row[data-result="ko"] .result-points { color: var(--muted); }

/* --- classement anime entre deux manches --- */

.standings { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.standings-row {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: 26px 1fr auto auto; gap: 10px; align-items: baseline;
  padding: 14px 16px; border-radius: 12px;
  background: var(--surface); border: var(--stroke) solid var(--line);
  text-align: left;
  /* Les lignes se posent l'une apres l'autre, du dernier au premier. */
  animation: standing-in 420ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
@keyframes standing-in {
  from { transform: translateX(-14px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.standings-row:first-child { border-color: var(--gold); }
/* La barre dit la part du score du meneur : le classement se lit sans lire
   les chiffres. */
.standings-bar {
  position: absolute; inset: 0 auto 0 0; z-index: 0;
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.22), rgba(var(--accent-2-rgb), 0.1));
  transition: width 700ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
.standings-row > *:not(.standings-bar) { position: relative; z-index: 1; }
.standings-rank { color: var(--muted); font-weight: 800; font-variant-numeric: tabular-nums; }
.standings-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.standings-gain { font-size: 13.5px; font-weight: 700; color: var(--ok); }
.standings-gain[data-zero] { color: var(--muted); opacity: 0.55; }
.standings-score { font-weight: 800; font-variant-numeric: tabular-nums; font-size: 17px; }
.standings-row[data-me] .standings-score { color: var(--accent); }

/* --- ecrans battle centres, comme l'onboarding --- */

#screen-spin, #screen-standings { align-items: center; text-align: center; }
#screen-spin > *, #screen-standings > * { width: 100%; }
#screen-spin > .carousel { width: 100vw; }

/* Un malus se distingue d'une manche a zero : l'un coute, l'autre pas. */
.standings-gain[data-malus] { color: var(--ko); opacity: 1; }
.result-row[data-result="ko"] .result-points[data-malus] { color: var(--ko); }

/* ===========================================================================
   Mode quiz
   ======================================================================== */

/* En quiz, rien a maintenir ni a taper : l'extrait tourne tout seul et il n'y a
   qu'a choisir. On masque plutot que de desactiver — un bouton grise invite
   quand meme a insister. */
#screen-play[data-format="quiz"] .hold,
#screen-play[data-format="quiz"] .guess,
#screen-play[data-format="quiz"] .attempts,
#screen-play[data-format="quiz"] .actions { display: none; }

.quiz-choices { list-style: none; display: flex; flex-direction: column; gap: 14px; }
.quiz-choices[hidden] { display: none; }

.quiz-choice {
  width: 100%; text-align: left; cursor: pointer;
  display: grid; grid-template-columns: 26px 1fr; gap: 12px; align-items: center;
  padding: 16px 18px; border-radius: 12px;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
  font: inherit; font-size: 16px; font-weight: 700;
  /* La meme tranche que les boutons : une proposition se presse, elle a une
     epaisseur, et elle s'enfonce sous le doigt. */
  box-shadow: 0 5px 0 var(--edge);
  transition: transform 70ms ease, box-shadow 70ms ease, background 120ms ease;
  /* Les propositions entrent l'une apres l'autre : on lit la premiere pendant
     que la troisieme arrive, au lieu d'affronter un mur de texte. */
  animation: choice-in 320ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
@keyframes choice-in {
  from { transform: translateY(10px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
/* Le survol n'existe que la ou il y a une souris : sur iOS, l'etat :hover
   reste colle au point du dernier tap, et le rush reconstruit ses boutons au
   meme endroit a chaque morceau — la proposition qui naissait sous le doigt
   arrivait deja bordee d'accent, comme selectionnee. */
@media (hover: hover) and (pointer: fine) {
  .quiz-choice:hover:not(:disabled) { border-color: var(--accent); background: var(--surface-2); }
}
.quiz-choice:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 1px 0 var(--edge); }
.quiz-choice:disabled { cursor: default; }
/* Une fois la reponse rendue, les propositions ecartees s'aplatissent : il
   n'y a plus rien a presser. La bonne et la mauvaise gardent leur epaisseur,
   ce sont elles qu'on regarde. */
.quiz-choice[data-state="off"] { box-shadow: none; }

/* Le rang sert de raccourci : on peut repondre au clavier, et le crier a voix
   haute veut dire quelque chose puisque l'ordre est le meme partout. */
.quiz-rank {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  font-size: 13px; font-weight: 800; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
  .quiz-choice:hover:not(:disabled) .quiz-rank { color: var(--accent); border-color: var(--accent); }
}

/* Apres coup : ce qui etait juste, et ce qu'on avait choisi. */
.quiz-choice[data-state="ok"] { border-color: var(--ink); background: var(--ok-fill); }
.quiz-choice[data-state="ok"] .quiz-rank { color: var(--ok); border-color: var(--ok); }
.quiz-choice[data-state="ko"] { border-color: var(--ink); background: var(--ko-fill); opacity: 0.9; }
.quiz-choice[data-state="ko"] .quiz-rank { color: var(--ko); border-color: var(--ko); }
.quiz-choice[data-state="off"] { opacity: 0.4; }
/* Reponse rendue : on attend les autres, les propositions n'ont plus rien a
   dire tant que la revelation n'est pas la. */
#screen-play[data-waiting] .quiz-choices { display: none; }
/* La regle choisie se voit sur la carte, comme un reglage retenu. */
.mode-card[data-on] { border-color: var(--accent); background: var(--surface-2); }

/* ===========================================================================
   Mode voix
   ======================================================================== */

/* Rien a taper : on parle. Le bouton d'ecoute reste, lui — c'est toujours le
   temps ecoute qui paie. */
#screen-play[data-format="voice"] .guess { display: none; }

/* --- mode rush --- */
/* Le disque devient le tableau de bord : temps restant, score, serie. Pas de
   champ, pas de chronometre bas — le temps EST le disque. */
#screen-play[data-format="rush"] .guess,
#screen-play[data-format="rush"] .attempts,
#screen-play[data-format="rush"] .voice,
#screen-play[data-format="rush"] .actions,
#screen-play[data-format="rush"] .timer { display: none; }
#screen-play[data-format="rush"] .hold { pointer-events: none; }
/* Le compteur de trouves passe sous le disque. En haut a gauche il flottait
   seul au-dessus, loin du chiffre qu'il commente ; dessous et centre, il se
   lit dans le meme regard que le temps et le score que porte l'etiquette. */
#screen-play[data-format="rush"] .bar { order: 1; justify-content: center; }
#screen-play[data-format="rush"] .who { margin-right: 0; }
#screen-play[data-format="rush"] .quiz-choices { order: 2; }
#screen-play[data-format="rush"] .debug { order: 3; }
/* Pas de flamme en rush : elle marque la progression d'une ecoute, or ici le
   disque compte un temps qui descend — la fleche de l'arc suffit. */
#screen-play[data-format="rush"] .hold-orbit { display: none; }
/* Ni triangle de lecture : le disque n'est pas un bouton en rush, l'inviter a
   presser serait mentir. Le temps, le score et la serie occupent l'etiquette. */
#screen-play[data-format="rush"] .hold-glyph { display: none; }
#screen-play[data-waiting] .voice { display: none; }

.voice { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.voice[hidden] { display: none; }

/* Plus un bouton : un indicateur d'etat. Le micro vit tout seul. */
.voice-btn {
  width: 100%; cursor: default; margin: 0;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 16px 18px; border-radius: 12px;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--text);
  font: inherit; font-size: 16px; font-weight: 700;
  /* Le doigt reste sur le bouton pendant qu'on parle : la selection de texte
     et le menu contextuel n'ont rien a faire ici. */
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  touch-action: none;
}
.voice-btn:hover { border-color: var(--accent); }
.voice-glyph { width: 22px; height: 22px; flex: none; }

/* En voix le disque n'est qu'un indicateur : il tourne, il compte, mais il ne
   se presse pas — c'est l'ecran entier qui repond. */
#screen-play[data-format="voice"] .hold { pointer-events: none; }

/* Micro ouvert : la carte respire, pour qu'on voie sans lire que ca enregistre. */
.voice-btn[data-on] {
  border-color: var(--accent); background: var(--surface-2); color: var(--accent);
  animation: voice-pulse 1.4s ease-in-out infinite;
}
@keyframes voice-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--gold), 0 0 0 3px rgba(var(--gold-rgb), 0.5); }
  50% { box-shadow: 0 0 0 3px var(--gold), 0 0 0 13px rgba(var(--gold-rgb), 0); }
}

/* Ce qui a ete compris, et a quel point ca collait. Sans ce retour, un refus
   est incomprehensible pour le joueur. */
.voice-heard { min-height: 22px; font-size: 14.5px; color: var(--muted); text-align: center; }
.voice-heard[data-result="ok"] { color: var(--ok); font-weight: 600; }
.voice-heard[data-result="ko"] { color: var(--ko); }

/* Une carte de regle indisponible dans ce navigateur. */
.mode-card[data-off] { opacity: 0.45; cursor: default; }
.mode-card[data-off], .mode-card[data-off]:active { box-shadow: none; transform: none; }
/* Le badge d'un mode annonce mais pas encore rendu. */
.mode-badge {
  display: inline-block; vertical-align: middle; margin-left: 8px;
  font-size: 11px; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; line-height: 1;
  padding: 4px 8px; border-radius: 999px;
  background: var(--surface-2); border: var(--stroke) solid var(--line);
  color: var(--accent);
}
.mode-card[data-off]:hover { border-color: var(--line); background: var(--surface); }

/* --- l'icone du jeu, sur l'ecran d'accueil --- */

/* Le selecteur porte le parent : `.step > *` impose une largeur pleine a tous
   les enfants de l'etape, et l'icone y serait etiree sur toute la colonne. */
.step > .brand-icon {
  width: 108px; height: 108px;
  border-radius: 30px;
  display: block;
  /* Un trait d'encre, une tranche, et un leger penche : l'icone est un
     autocollant colle sur le sol, pas une vignette. */
  border: 4px solid var(--ink);
  box-shadow: 0 7px 0 var(--ink);
  transform: rotate(-5deg);
}

/* --- fond video de l'onboarding --- */

/* Fixe et derriere tout : c'est un decor, il ne participe ni au flux ni au
   defilement. Le mur de pochettes occupait cette place ; la video la reprend. */
.backdrop {
  display: none;
  position: fixed; inset: 0; z-index: 0; overflow: hidden;
  /* contain: strict isole le repeint de la video du reste de la page — sans
     lui, chaque image relancait la mise en page de tout l'ecran. */
  contain: strict;
  pointer-events: none;
}
.backdrop[data-on] { display: none; }
@keyframes backdrop-fade { from { opacity: 0; } to { opacity: 1; } }

.backdrop-video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Meme voile que le mur de pochettes : plus dense au centre, pour que le decor
   reste vivant sur les bords pendant que le texte se detache sur du quasi-noir. */
.backdrop::after {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(68% 46% at 50% 50%, rgba(4, 3, 20, 0.94) 35%, rgba(4, 3, 20, 0.6) 100%),
    rgba(4, 3, 20, 0.55);
}

/* Mouvement reduit : on garde le decor, on lui retire son mouvement. La video
   est mise en pause par le script ; l'affiche reste affichee. */
@media (prefers-reduced-motion: reduce) {
  .backdrop[data-on] { animation: none; }
}

/* ===== Avant-gout ========================================================= */

/* Trois manches jouees avant tout reglage, sur le vrai ecran de jeu. Ce qui
   suit n'habille que ce qui s'ajoute : les etiquettes, la sortie, la bascule.
   Le jeu lui-meme n'est pas redecore pour l'occasion — c'est le principe. */

/* --- les etiquettes --- */

/* Trois couches, et chacune porte une seule transformation. La position sur
   l'exterieur, le flottement au milieu, l'entree et le survol sur la pilule :
   empilees sur un seul element, elles se remplaceraient l'une l'autre au lieu
   de se composer. */
.coach {
  position: fixed; top: 0; left: 0; z-index: 33;
  pointer-events: none;
  will-change: transform;
}
.coach[hidden] { display: none; }

.coach-flot { animation: coach-flot 3.6s ease-in-out infinite; }

.coach-pill {
  position: relative;
  pointer-events: auto; cursor: pointer;
  max-width: min(78vw, 320px);
  padding: 9px 15px; border-radius: 999px;
  appearance: none; font: inherit; font-size: 13px; font-weight: 600;
  text-align: center; line-height: 1.35;
  background: var(--surface-2); color: var(--text);
  border: var(--stroke) solid var(--line);
  box-shadow: 0 5px 0 var(--ink);
  transition: transform 140ms ease, opacity 160ms ease, border-color 200ms ease;
}
/* L'etiquette designe : sans cette pointe elle flotte a cote de ce qu'elle
   explique, et il faut deviner de quoi elle parle. */
.coach-pill::after {
  content: ''; position: absolute; left: 50%; margin-left: -5px;
  border: 5px solid transparent;
}
.coach[data-cote="dessous"] .coach-pill::after {
  bottom: 100%; border-bottom-color: var(--line);
}
.coach[data-cote="dessus"] .coach-pill::after {
  top: 100%; border-top-color: var(--line);
}

.coach-pill:hover { transform: translateY(-2px); border-color: var(--accent); }
.coach-pill:active { transform: translateY(0); }

/* Legere surcourse a l'entree : une etiquette qui arrive tout droit se lit
   comme un message d'erreur. */
.coach-entre { animation: coach-entre 260ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.coach[data-parti] .coach-pill { opacity: 0; transform: scale(0.96); }

/* Le rappel sans mot : l'etiquette se signale au moment ou ce qu'elle annonce
   se produit — le chronometre a mi-course. C'est ce qui la rend vivante
   plutot que decorative. */
.coach-pill[data-pulse] { animation: coach-pulse 560ms ease-out; }

@keyframes coach-flot {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
@keyframes coach-entre {
  from { opacity: 0; transform: scale(0.9); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes coach-pulse {
  0% { transform: scale(1); border-color: var(--line); }
  40% { transform: scale(1.06); border-color: var(--gold); }
  100% { transform: scale(1); border-color: var(--line); }
}

/* --- la sortie --- */

/* A la place du choix de langue, absent pendant une partie, et en face du
   bouton qui quitte : meme hauteur, meme bande. */
.demo-skip {
  position: fixed; z-index: 31;
  top: calc(14px + env(safe-area-inset-top));
  right: calc(14px + env(safe-area-inset-right));
  height: 40px; padding: 0 14px; border-radius: 999px;
  appearance: none; cursor: pointer; font: inherit; font-size: 14px; font-weight: 600;
  background: var(--surface); border: var(--stroke) solid var(--line); color: var(--muted);
}
.demo-skip:hover { border-color: var(--accent); color: var(--text); }
.demo-skip[hidden] { display: none; }

/* --- la bascule --- */

/* Les trois pochettes de ce qu'on vient d'entendre. Elles ne decorent pas :
   elles rappellent que la musique etait vraie, et que le catalogue l'est. */
.apercu-pochettes {
  list-style: none; display: flex; justify-content: center; gap: 10px;
  margin: 4px 0 8px;
}
.apercu-pochettes img {
  width: 64px; height: 64px; border-radius: 12px; display: block;
  border: var(--stroke) solid var(--line);
  box-shadow: 0 4px 0 var(--ink);
  animation: apercu-pochette 420ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
/* La manche ratee se reconnait : on ne maquille pas le resultat. */
.apercu-pochettes li[data-rate] img { opacity: 0.42; filter: grayscale(1); }

@keyframes apercu-pochette {
  from { opacity: 0; transform: translateY(12px) scale(0.94); }
  to { opacity: 1; transform: none; }
}


/* ===========================================================================
   Monde cartoon : le contexte des cartes et le decor
   ======================================================================== */

/* Le sol est bleu, les cartes sont creme. Tout ce qui porte une surface claire
   redefinit ici les jetons de texte et d'accent : l'encre remplace le blanc,
   le rose remplace le jaune (illisible sur creme), les verts et rouges passent
   en version foncee. Les enfants heritent des jetons, il n'y a donc rien a
   reprendre composant par composant. */
.mode-card, .guess-input, .guess-list, .attempt, .btn-secondary, .board-row,
.player-chip, .lobby-share, .lobby-player, .countdown-player, .waiting-player, .result-row,
.standings-row, .quiz-choice, .voice-btn, .fmt-slide, .board-tab, .board-puce,
.podium-avatar, .lb-emoji, .stepper-btn, .step-back, .quit, .langue-btn, .langue-menu, .avis,
.coach-pill, .demo-skip, kbd, .streak, .hold, .suggestion {
  --text: var(--card-ink);
  --muted: var(--card-muted);
  --accent: var(--pink-ink);
  --accent-rgb: var(--pink-rgb);
  --ok: var(--card-ok);
  --ko: var(--card-ko);
  color: var(--text);
}
/* Les petites pieces gardent un trait plus fin : trois pixels sur une puce de
   douze pixels de haut, c'est le trait qui mange la puce. */
.attempt, .streak, .mode-badge, .board-puce, .countdown-player, .waiting-player, .quiz-rank,
.lb-emoji, .player-chip, .who-tag, kbd, .lobby-dot, .fmt-ligne, .fmt-champ, .fmt-micro {
  --stroke: 2px;
}

/* Les boutons ronds du haut d'ecran ont leur tranche, comme tout ce qui se
   presse. */
.step-back, .quit, .langue-btn, .demo-skip { box-shadow: 0 4px 0 var(--ink); }
.step-back:active, .quit:active, .langue-btn:active, .demo-skip:active {
  transform: translateY(3px); box-shadow: 0 1px 0 var(--ink);
}
.step-back, .quit { font-weight: 800; }

/* Le decor : une trame de points, comme un aplat de bande dessinee, une
   lumiere qui tombe du haut, et par-dessus des notes de musique et des
   etincelles dessinees au trait, en blanc tres pale. C'est le sujet du jeu
   pose dans le decor : on sait qu'on est dans une app de musique avant de
   lire le titre. Le motif est un SVG de 220 px embarque dans la feuille — rien
   a charger — et se repete ; les notes sont assez pales pour qu'un texte qui
   passe dessus reste lisible. Fixe, derriere tout. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(70% 45% at 50% -5%, rgba(255, 255, 255, 0.22), transparent 70%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220' fill='%23fff'%3E%3Cg opacity='.16'%3E%3Ccircle cx='34' cy='52' r='6'/%3E%3Crect x='38.5' y='24' width='3' height='28' rx='1.5'/%3E%3Cpath d='M41.5 24c8 2 12 6 12 12' stroke='%23fff' stroke-width='3' fill='none' stroke-linecap='round'/%3E%3Ccircle cx='150' cy='150' r='6'/%3E%3Ccircle cx='172' cy='146' r='6'/%3E%3Crect x='154.5' y='118' width='3' height='32' rx='1.5'/%3E%3Crect x='176.5' y='114' width='3' height='32' rx='1.5'/%3E%3Cpath d='M154.5 118l25-6' stroke='%23fff' stroke-width='4' stroke-linecap='round'/%3E%3C/g%3E%3Cg opacity='.22'%3E%3Cpath d='M180 40l3 8 8 3-8 3-3 8-3-8-8-3 8-3z'/%3E%3Cpath d='M60 170l2 5 5 2-5 2-2 5-2-5-5-2 5-2z'/%3E%3Cpath d='M110 96l2 5 5 2-5 2-2 5-2-5-5-2 5-2z'/%3E%3C/g%3E%3C/svg%3E"),
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.10) 0 1.5px, transparent 1.8px);
  background-size: auto, 220px 220px, 18px 18px;
}
/* Les cartes qui se cliquent : la surface se rechauffe au survol, le trait
   reste d'encre. */
.mode-card:hover { border-color: var(--ink); background: var(--card-hover); }
.mode-card:hover .mode-icon { background: var(--accent-grad); color: var(--ink); }

/* La saisie : le curseur et la selection prennent le rose. */
.guess-input { caret-color: var(--pink-ink); }
::selection { background: var(--pink); color: var(--ink); }

/* Le badge (Beta, Coming soon) : un autocollant jaune. */
.mode-badge { background: var(--accent-grad); color: var(--ink); border-color: var(--ink); }

/* Le compteur de manche : blanc sur bleu, sans trainer. */
.counter { color: var(--text); font-weight: 800; }

/* Sur un bouton jaune, le contour de focus jaune disparait : il passe au blanc. */
.btn-primary:focus-visible, .big-start:focus-visible, .board-tab[data-on]:focus-visible { outline-color: #fff; }

/* ===== Mode En ligne ====================================================== */

/* Le titre de la salle publique attend : trois points qui battent. */
#lobby-code[data-recherche]::after {
  content: '…';
  display: inline-block; width: 1.2em; text-align: left;
  animation: recherche-points 1.2s steps(4, end) infinite;
}
@keyframes recherche-points {
  0% { clip-path: inset(0 100% 0 0); }
  100% { clip-path: inset(0 -10% 0 0); }
}
@media (prefers-reduced-motion: reduce) {
  #lobby-code[data-recherche]::after { animation: none; }
}

/* ===== Le jaune dit « choisi » ============================================= */

/* Sur une carte creme, le rose est la couleur des textes d'accent — pas celle
   des bordures. Ce qui est choisi, a moi, ou en cours garde son trait d'encre
   et prend un anneau jaune autour, comme la carte de regle : une seule
   couleur pour « c'est celui-la », partout. La tranche s'allonge de la
   largeur de l'anneau pour depasser encore dessous. */
.board-row[data-active], .lobby-player[data-me], .lobby-player[data-replied],
.waiting-player[data-replied], .countdown-player[data-me], .result-row[data-me] {
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--gold);
}
.mode-card[data-on] {
  border-color: var(--ink); background: var(--surface-2);
  box-shadow: 0 0 0 3px var(--gold), 0 8px 0 var(--edge);
}
.guess-input:focus {
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--gold), 0 9px 0 var(--edge);
}
.board-puce[data-on] { border-color: var(--ink); box-shadow: 0 0 0 2px var(--gold); }
.voice-btn[data-on] { border-color: var(--ink); }
.langue-btn[aria-expanded="true"] {
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--gold), 0 7px 0 var(--ink);
}
.avis[data-moi] { border-color: var(--ink); box-shadow: 0 0 0 3px var(--gold), 0 8px 0 var(--ink); }
.podium-place[data-moi] .podium-avatar { box-shadow: 0 0 0 3px var(--gold); }

/* Le survol ne colore pas le trait : la surface se rechauffe, l'encre reste. */
.step-back:hover, .quit:hover, .demo-skip:hover, .voice-btn:hover, .coach-pill:hover {
  border-color: var(--ink); background: var(--card-hover);
}
@media (hover: hover) and (pointer: fine) {
  .stepper-btn:hover:not(:disabled) { border-color: var(--ink); color: var(--text); background: var(--card-hover); }
  .langue-btn:hover { border-color: var(--ink); color: var(--text); background: var(--card-hover); }
  .quiz-choice:hover:not(:disabled) { border-color: var(--ink); background: var(--card-hover); }
  .quiz-choice:hover:not(:disabled) .quiz-rank { color: var(--ink); border-color: var(--ink); background: var(--gold); }
}

/* ===== Le score pendant la manche ======================================== */

/* En solo, le score n'est pas une mention en coin : c'est le gros chiffre de
   l'ecran, dans le meme dessin qu'a la revelation — jaune, cerne d'encre,
   centre au-dessus du chronometre et des propositions. */
.bar:has(.who[data-score]) { justify-content: center; }
.who[data-score] {
  margin-right: 0;
  font-family: var(--font-title);
  font-size: 44px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--gold);
  text-shadow: var(--contour), 0 5px 0 var(--ink);
}

/* ===== La flamme de la serie ============================================== */

/* Une flamme qui reste immobile est un pictogramme ; une flamme qui bouge est
   un feu. Deux mouvements se superposent, de durees qui ne se divisent pas
   l'une par l'autre : la flamme s'etire et s'ecrase sur un rythme, oscille
   sur un autre, et le cycle apparent ne se repete jamais tout a fait. La
   lueur respire avec elle. Le chiffre, lui, ne bouge pas. */
.streak-flamme {
  display: inline-block;
  transform-origin: 50% 90%;
  animation:
    flamme-danse 760ms ease-in-out infinite alternate,
    flamme-souffle 1180ms ease-in-out infinite alternate;
  filter: drop-shadow(0 0 3px rgba(var(--accent-2-rgb), 0.7));
}
/* Serie chaude : plus vive, et plus grande. */
.streak[data-hot] .streak-flamme {
  animation-duration: 520ms, 830ms;
  font-size: 1.15em;
  filter: drop-shadow(0 0 5px rgba(var(--accent-2-rgb), 0.95));
}
@keyframes flamme-danse {
  0%   { transform: scale(0.94, 1.02) rotate(-5deg); }
  35%  { transform: scale(1.06, 0.95) rotate(2deg); }
  70%  { transform: scale(0.98, 1.08) rotate(-2deg); }
  100% { transform: scale(1.04, 0.97) rotate(5deg); }
}
@keyframes flamme-souffle {
  0%   { translate: 0 0; filter: drop-shadow(0 0 2px rgba(var(--accent-2-rgb), 0.5)); }
  50%  { translate: 0 -1px; filter: drop-shadow(0 0 5px rgba(var(--accent-2-rgb), 0.95)); }
  100% { translate: 0.5px 0; filter: drop-shadow(0 0 3px rgba(var(--accent-2-rgb), 0.7)); }
}
@media (prefers-reduced-motion: reduce) {
  .streak-flamme { animation: none; }
}

/* ===== Les manches en pastilles ========================================== */

/* Le compteur de manche reprend les pastilles de l'onboarding, en plus petit :
   dix pastilles doivent tenir entre le bouton qui quitte et le bord droit,
   dans la bande de 40 px. */
.step-dots-manches { gap: 6px; }
.step-dots-manches .step-dot {
  width: 24px; height: 24px; font-size: 12px;
  box-shadow: 0 2px 0 var(--ink);
  /* La pastille qui arrive se pose d'un petit rebond : la manche en cours
     entre dans la rangee, les autres ne bougent pas. */
  animation: pastille-entre 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes pastille-entre {
  from { transform: scale(0.4); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.step-dots-manches .step-dot[data-state="current"] { animation-name: pastille-entre-courante; }
@keyframes pastille-entre-courante {
  from { transform: scale(0.4); opacity: 0; }
  to   { transform: scale(1.15); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .step-dots-manches .step-dot { animation: none; }
}
.step-dots-manches .step-dot[data-state="done"]::after { width: 9px; height: 4.5px; border-width: 0 0 2.5px 2.5px; }
/* La manche ratee : rouge, et une croix au meme trait que la coche. */
.step-dot[data-state="rate"] { background: var(--ko-fill); opacity: 1; position: relative; }
.step-dot[data-state="rate"]::before, .step-dot[data-state="rate"]::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 11px; height: 3px; border-radius: 2px; background: var(--ink);
}
.step-dot[data-state="rate"]::before { transform: rotate(45deg); }
.step-dot[data-state="rate"]::after { transform: rotate(-45deg); }
.step-dots-manches .step-dot[data-state="rate"]::before,
.step-dots-manches .step-dot[data-state="rate"]::after { width: 9px; height: 2.5px; }
.step-gap {
  color: var(--text); font-weight: 800; font-size: 14px; line-height: 1;
  letter-spacing: -1px; opacity: 0.7;
}
@media (hover: hover) and (pointer: fine) {
  .step-dots-manches { gap: 8px; }
  .step-dots-manches .step-dot { width: 28px; height: 28px; font-size: 13px; }
}

/* ===== L'anneau du decompte ============================================== */

/* Le chiffre est pose dans un anneau qui se vide a chaque battement : le
   temps se voit sans se lire. Le trait est jaune sur l'encre du fond, la piste
   a peine visible ; l'animation repart a chaque chiffre, sur la duree du
   battement (800 ms en solo, la seconde du serveur en Battle). */
.countdown-anneau {
  position: relative; width: 176px; height: 176px;
  display: grid; place-items: center;
}
.countdown-ring {
  position: absolute; inset: 0; width: 100%; height: 100%;
  transform: rotate(-90deg);
}
.countdown-track, .countdown-arc { fill: none; stroke-width: 9; stroke-linecap: round; }
.countdown-track { stroke: rgba(255, 255, 255, 0.14); }
.countdown-arc {
  stroke: var(--gold);
  stroke-dasharray: 333; stroke-dashoffset: 0;
}
.countdown-anneau[data-beat] .countdown-arc {
  animation: anneau-vide var(--beat, 800ms) linear forwards;
}
@keyframes anneau-vide {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: 333; }
}
.countdown-number { position: relative; z-index: 1; }

/* ===== Le bouton pause ================================================== */

/* Sous la croix, meme forme, meme colonne : le coin en haut a gauche est
   celui des sorties, la pause en est une douce. */
/* La pause se pose a droite de la croix, sur la meme ligne. */
.pause-btn {
  left: calc(14px + 40px + 10px + env(safe-area-inset-left));
  display: grid; place-items: center;
}
.quit { display: grid; place-items: center; }
.quit svg { display: block; }

/* Les gros chiffres ne se selectionnent pas : un appui un peu long sur le
   score le surlignait en rose, comme un texte a copier. */
.who[data-score], .points { user-select: none; -webkit-user-select: none; }

/* La serie se pose au-dessus du score, pas a cote : le badge dit « tu es
   lance », le chiffre dit combien. La barre passe en colonne, la flamme en
   premier. */
.bar:has(.who[data-score]) { flex-direction: column; gap: 8px; }
.bar:has(.who[data-score]) .streak { order: -1; }

/* ===========================================================================
   Refonte : l'ecran de chargement et l'accueil, d'apres la maquette Figma
   ======================================================================== */

/* Les couleurs de la maquette. Le reste du jeu garde encore les siennes ; ces
   trois-la ne vivent que sur le chargement et l'accueil, le temps de la
   refonte. */
:root {
  --refonte-bg: #6651e0;
  --refonte-ink: #1e1144;
  --refonte-jaune: #ffd400;
}

/* --- le buzzer ------------------------------------------------------------- */

.buzzer-cadre { display: block; width: 192px; height: 192px; }
.buzzer {
  position: relative; display: block;
  /* Le buzzer remplit son cadre : c'est le cadre qui donne la taille, et lui
     seul — un reglage en ligne sur le buzzer l'emportait sur la feuille et
     laissait un dessin de 192 px dans un cadre plus petit. */
  width: 100%; height: 100%;
  overflow: visible;
}
.buzzer-corps { position: absolute; inset: 0; display: block; }
.buzzer-calque { position: absolute; display: block; }
.buzzer-calque img { position: absolute; display: block; width: 100%; height: 100%; max-width: none; }

/* Le mouvement, transcrit de la piste Figma : une boucle de six secondes. Le
   corps respire et saute ; les eclairs tremblent ; le bouton s'enfonce et
   rebondit avec son reflet ; le visage suit le corps ; les yeux clignent puis
   petillent. Tout est en `translate`, `scale`, `rotate` et opacity, donc sur le
   compositeur. */
.buzzer-corps { animation: buzzer-corps-translate 6s linear infinite, buzzer-corps-scale 6s linear infinite; }
.buzzer-left-lightning { animation: buzzer-eclair-gauche 6s linear infinite; }
.buzzer-right-lightning { animation: buzzer-eclair-droit 6s linear infinite; }
.buzzer-button { animation: buzzer-bouton-translate 6s linear infinite, buzzer-bouton-scale 6s linear infinite; }
.buzzer-highlight { animation: buzzer-reflet-translate 6s linear infinite, buzzer-bouton-scale 6s linear infinite; }
.buzzer-face { animation: buzzer-corps-translate 6s linear infinite; }
.buzzer-eye-spark-left { animation: buzzer-clin-gauche 6s linear infinite, buzzer-etincelle-gauche 6s linear infinite, buzzer-etincelle-scale 6s linear infinite; }
.buzzer-eye-spark-right { animation: buzzer-clin-droit 6s linear infinite, buzzer-etincelle-droite 6s linear infinite, buzzer-etincelle-scale 6s linear infinite; }

@keyframes buzzer-corps-translate {
  0% { animation-timing-function: ease-in-out; translate: 0 0; }
  6.667% { animation-timing-function: ease-in-out; translate: 0 -2.297px; }
  13.333% { animation-timing-function: ease-in-out; translate: 0 0; }
  20% { animation-timing-function: ease-in-out; translate: 0 -1.531px; }
  25% { translate: 0 0; }
  38.333% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); translate: 0 0; }
  43.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); translate: 0 -7.656px; }
  53.333% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  59.167% { animation-timing-function: ease-in; translate: 0 -2.297px; }
  64.167% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  69.167% { animation-timing-function: ease-in-out; translate: 0 -0.919px; }
  74.167% { translate: 0 0; }
  100% { translate: 0 0; }
}
@keyframes buzzer-corps-scale {
  0% { scale: 1 1; }
  25% { animation-timing-function: ease-in-out; scale: 1 1; }
  31.667% { scale: 1.012 0.988; }
  36.667% { animation-timing-function: ease-out; scale: 1.012 0.988; }
  38.333% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); scale: 1 1; }
  41.667% { animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1); scale: 0.975 1.025; }
  43.333% { animation-timing-function: ease-in; scale: 0.98 1.02; }
  52.5% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); scale: 1 1; }
  53.333% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); scale: 1.035 0.965; }
  58.333% { animation-timing-function: ease-out; scale: 0.99 1.01; }
  62.5% { animation-timing-function: ease-in-out; scale: 1.01 0.99; }
  68.333% { scale: 1 1; }
  100% { scale: 1 1; }
}
@keyframes buzzer-eclair-gauche {
  0% { animation-timing-function: ease-in-out; rotate: 0rad; }
  8.333% { animation-timing-function: ease-in-out; rotate: 0.087rad; }
  16.667% { animation-timing-function: ease-in-out; rotate: -0.052rad; }
  25% { animation-timing-function: ease-in-out; rotate: 0.14rad; }
  38.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: 0.087rad; }
  40.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: -0.314rad; }
  42.5% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: 0.244rad; }
  44.667% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: -0.175rad; }
  47% { animation-timing-function: ease-out; rotate: 0.087rad; }
  50% { animation-timing-function: ease-in-out; rotate: -0.035rad; }
  53.333% { rotate: 0rad; }
  100% { rotate: 0rad; }
}
@keyframes buzzer-eclair-droit {
  0% { animation-timing-function: ease-in-out; rotate: 0rad; }
  8.333% { animation-timing-function: ease-in-out; rotate: -0.087rad; }
  16.667% { animation-timing-function: ease-in-out; rotate: 0.052rad; }
  25% { animation-timing-function: ease-in-out; rotate: -0.14rad; }
  38.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: -0.087rad; }
  40.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: 0.314rad; }
  42.5% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: -0.244rad; }
  44.667% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); rotate: 0.175rad; }
  47% { animation-timing-function: ease-out; rotate: -0.087rad; }
  50% { animation-timing-function: ease-in-out; rotate: 0.035rad; }
  53.333% { rotate: 0rad; }
  100% { rotate: 0rad; }
}
/* Le ressort du rebond, tel que Figma l'echantillonne. */
@keyframes buzzer-bouton-translate {
  0% { translate: 0 0; }
  25% { animation-timing-function: ease-in-out; translate: 0 0; }
  31.667% { translate: 0 4.287px; }
  36.667% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); translate: 0 4.287px; }
  41.667% { animation-timing-function: linear(0, 0.0188, 0.0679, 0.1374, 0.2195, 0.308, 0.3978, 0.4856, 0.5686, 0.6452, 0.7142, 0.7753, 0.8283, 0.8735, 0.9113, 0.9423, 0.9671, 0.9866, 1.0014, 1.0123, 1.0198, 1.0247, 1.0274, 1.0283, 1.0281, 1.0268, 1.025, 1.0227, 1.0202, 1.0177, 1.0152, 1.0128, 1.0106, 1.0085, 1.0068, 1.0052, 1.0039, 1.0028, 1.0018, 1.0011, 1.0005, 1, 0.9997, 0.9995, 0.9993, 0.9992, 0.9992, 0.9992, 0.9992, 0.9993, 0.9993); translate: 0 -1.531px; }
  46.667% { translate: 0 0; }
  100% { translate: 0 0; }
}
@keyframes buzzer-bouton-scale {
  0% { scale: 1 1; }
  25% { animation-timing-function: ease-in-out; scale: 1 1; }
  31.667% { scale: 1 0.82; }
  36.667% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); scale: 1 0.82; }
  41.667% { animation-timing-function: linear(0, 0.0188, 0.0679, 0.1374, 0.2195, 0.308, 0.3978, 0.4856, 0.5686, 0.6452, 0.7142, 0.7753, 0.8283, 0.8735, 0.9113, 0.9423, 0.9671, 0.9866, 1.0014, 1.0123, 1.0198, 1.0247, 1.0274, 1.0283, 1.0281, 1.0268, 1.025, 1.0227, 1.0202, 1.0177, 1.0152, 1.0128, 1.0106, 1.0085, 1.0068, 1.0052, 1.0039, 1.0028, 1.0018, 1.0011, 1.0005, 1, 0.9997, 0.9995, 0.9993, 0.9992, 0.9992, 0.9992, 0.9992, 0.9993, 0.9993); scale: 1 1.08; }
  46.667% { scale: 1 1; }
  100% { scale: 1 1; }
}
@keyframes buzzer-reflet-translate {
  0% { animation-timing-function: ease-in-out; translate: 0 0; }
  6.667% { animation-timing-function: ease-in-out; translate: 0 -2.297px; }
  13.333% { animation-timing-function: ease-in-out; translate: 0 0; }
  20% { animation-timing-function: ease-in-out; translate: 0 -1.531px; }
  25% { animation-timing-function: ease-in-out; translate: 0 0; }
  31.667% { translate: 0 3.062px; }
  36.667% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); translate: 0 3.062px; }
  39.167% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); translate: 0 0; }
  43.333% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); translate: 0 -7.656px; }
  53.333% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  59.167% { animation-timing-function: ease-in; translate: 0 -2.297px; }
  64.167% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  69.167% { animation-timing-function: ease-in-out; translate: 0 -0.919px; }
  74.167% { translate: 0 0; }
  100% { translate: 0 0; }
}
@keyframes buzzer-clin-gauche {
  0% { animation-timing-function: ease-in-out; opacity: 1; }
  3.333% { animation-timing-function: ease-in-out; opacity: 0.3; }
  7.5% { animation-timing-function: ease-in-out; opacity: 1; }
  11.667% { animation-timing-function: ease-in-out; opacity: 0.3; }
  15.833% { animation-timing-function: ease-in-out; opacity: 1; }
  20% { animation-timing-function: ease-in-out; opacity: 0.3; }
  25% { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes buzzer-clin-droit {
  0% { opacity: 1; }
  5% { animation-timing-function: ease-in-out; opacity: 1; }
  7.5% { animation-timing-function: ease-in-out; opacity: 0.3; }
  11.667% { animation-timing-function: ease-in-out; opacity: 1; }
  15.833% { animation-timing-function: ease-in-out; opacity: 0.3; }
  20% { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes buzzer-etincelle-gauche {
  0% { animation-timing-function: ease-in-out; translate: 0 0; }
  6.667% { animation-timing-function: ease-in-out; translate: 0 -2.297px; }
  13.333% { animation-timing-function: ease-in-out; translate: 0 0; }
  20% { animation-timing-function: ease-in-out; translate: 0 -1.531px; }
  25% { translate: 0 0; }
  38.333% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); translate: 0 0; }
  41.667% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: -1.837px -3.828px; }
  44.167% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); translate: 0 -7.656px; }
  53.333% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  59.167% { animation-timing-function: ease-in; translate: 0 -2.297px; }
  64.167% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  69.167% { animation-timing-function: ease-in-out; translate: 0 -0.919px; }
  74.167% { translate: 0 0; }
  100% { translate: 0 0; }
}
@keyframes buzzer-etincelle-droite {
  0% { animation-timing-function: ease-in-out; translate: 0 0; }
  6.667% { animation-timing-function: ease-in-out; translate: 0 -2.297px; }
  13.333% { animation-timing-function: ease-in-out; translate: 0 0; }
  20% { animation-timing-function: ease-in-out; translate: 0 -1.531px; }
  25% { translate: 0 0; }
  38.333% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); translate: 0 0; }
  41.667% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: -1.837px -4.746px; }
  44.167% { animation-timing-function: cubic-bezier(0.2, 0, 0, 1); translate: 0 -7.656px; }
  53.333% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  59.167% { animation-timing-function: ease-in; translate: 0 -2.297px; }
  64.167% { animation-timing-function: cubic-bezier(0, 0, 0.15, 1); translate: 0 0; }
  69.167% { animation-timing-function: ease-in-out; translate: 0 -0.919px; }
  74.167% { translate: 0 0; }
  100% { translate: 0 0; }
}
@keyframes buzzer-etincelle-scale {
  0% { scale: 1 1; }
  38.333% { animation-timing-function: cubic-bezier(0.45, 1.45, 0.8, 1); scale: 1 1; }
  41.667% { animation-timing-function: ease-in-out; scale: 1.35 1.35; }
  50% { scale: 1 1; }
  100% { scale: 1 1; }
}
@media (prefers-reduced-motion: reduce) {
  .buzzer *, .buzzer-corps { animation: none !important; }
}

/* --- l'ecran de chargement ------------------------------------------------- */

.splash {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  transition: opacity 320ms ease;
}
.splash-centre { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.splash-nom { position: relative; z-index: 1; }
.splash-nom {
  font-family: var(--font-title); font-size: 32px; font-weight: 600; line-height: 36px;
  color: #fff; text-shadow: 2px 2px 0 var(--refonte-ink);
  transition: opacity 260ms ease;
}
/* La glissade : le buzzer part rejoindre l'accueil, le nom et le fond
   s'effacent derriere lui. */
.splash[data-glisse] { background: transparent; transition: background 380ms ease 300ms; }
.splash[data-glisse] .splash-nom { opacity: 0; }
.splash[data-glisse] #splash-buzzer { transition: transform 700ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.splash[data-fondu] { opacity: 0; }
/* Sous le chargement, le buzzer de l'accueil attend son double, et le reste
   de l'accueil attend la fin de la glissade pour se montrer. */
html[data-splash] #accueil-buzzer { visibility: hidden; }
html[data-splash] .accueil-haut > :not(#accueil-buzzer),
html[data-splash] .accueil-bas,
html[data-splash] .langue { opacity: 0; }
.accueil-haut > *, .accueil-bas { transition: opacity 420ms ease; }

/* --- l'accueil, d'apres la maquette ---------------------------------------- */

/* Le fond : le meme decor que les autres ecrans (notes et eclats), pour
   que l'accueil, le mode et les regles se suivent sans rupture. */

/* L'accueil prend toute la hauteur : le haut centre, les portes en bas. */
/* L'ecran entier remplit la fenetre ; l'etape prend ce qui reste sous la
   barre des etapes. Compter la barre a la main l'avait fait deborder. */
/* Jamais de defilement de page sur les ecrans refaits : la fenetre est la
   colonne, decoupee en trois zones. En haut la barre (fixe), au milieu le
   contenu qui prend ce qui reste et se centre, en bas les boutons colles
   au-dessus de la zone de securite — la zone du pouce, toujours au meme
   endroit. Les tailles et les espaces du milieu suivent la hauteur de
   l'ecran, entre un minimum et un maximum ; si ca ne tient toujours pas,
   seul le milieu defile, jamais les boutons. */
html[data-refonte] body { height: 100dvh; min-height: 0; overflow: hidden; }
/* Le body est une grille centree : un pourcentage n'y prend pas ; la
   hauteur se dit en clair, fenetre moins les marges du body. */
html[data-refonte] .app { height: calc(100dvh - 48px - env(safe-area-inset-bottom)); min-height: 0; }
html[data-refonte] #screen-onboard { height: 100%; min-height: 0; }
.accueil, .mode-refonte { flex: 1 1 auto; min-height: 0; justify-content: space-between; gap: clamp(12px, 2dvh, 24px); }
.accueil-haut, .mode-haut {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* La barre du haut est posee par-dessus : on garde sa hauteur libre. */
  padding-top: calc(30px + env(safe-area-inset-top));
}
.accueil-haut > * , .mode-haut > * { flex: none; }
.accueil-haut { gap: clamp(16px, 3.6dvh, 32px); }
/* Le buzzer garde ses 192 px de la maquette ; ce sont les espaces autour
   qui se resserrent sur les petits ecrans. */
.accueil-haut > .buzzer-cadre { width: 192px; height: 192px; }
.accueil-haut > h1 { margin: 0; }
/* Le nom, sous le buzzer : blanc, avec l'ombre pleine de la maquette. */
#step-accueil .accueil-nom {
  display: block; padding: 0; border: 0; background: none; box-shadow: none; transform: none;
  font-size: 28px; line-height: 36px; font-weight: 600; letter-spacing: 0; text-transform: none;
  color: #fff; text-shadow: 2px 2px 0 var(--refonte-ink);
  /* Le dessin du buzzer deborde de son cadre de quelques pixels et bouge :
     le nom garde 12 px sous le cadre, et passe devant s'ils se touchent. */
  /* Le nom mord de 8 px sur le cadre du buzzer. */
  margin-top: calc(-8px - clamp(16px, 3.6dvh, 32px));
  position: relative; z-index: 1;
}
/* Une phrase par ligne : le retour est dans le texte, la feuille le respecte. */
#step-accueil .lede { font-size: 16px; line-height: 24px; font-weight: 600; color: rgba(255, 255, 255, 0.87); margin: 0; white-space: pre-line; }
/* 20 px entre le titre et le paragraphe, quel que soit l'ecart de la colonne. */
#step-accueil .lede { margin-top: calc(20px - clamp(16px, 3.6dvh, 32px)); }
.accueil-bas { flex: none; display: flex; flex-direction: column; gap: 12px; padding-bottom: env(safe-area-inset-bottom); }

/* Les boutons de la maquette : 64 px, trait de 4, tranche de 4. */
.btn-cta {
  appearance: none; cursor: pointer; font-family: inherit;
  width: 100%; height: 64px; padding: 8px 32px;
  font-size: 20px; font-weight: 600; color: #000;
  border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
  transition: transform 70ms ease, box-shadow 70ms ease;
}
.btn-cta:active { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
.btn-cta-jaune { background: var(--refonte-jaune); }
.btn-cta-blanc { background: #fff; }
.btn-cta:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

/* La bascule de langue retourne en haut a droite, ou la regle de base la
   pose : elle ne s'affiche que sur l'accueil, et c'est le coin libre — la
   gauche accueille le retour sur tous les autres ecrans. Le menu s'ouvre du
   meme cote, sans quoi il sortirait de l'ecran. */

/* --- l'etape du mode, d'apres la maquette --------------------------------- */


.mode-haut { gap: clamp(14px, 2.9dvh, 25px); }
.mode-haut > .mode-vinyle { --vinyle: clamp(88px, 15dvh, 128px); width: var(--vinyle); height: var(--vinyle); display: block; }
.mode-refonte h1 { margin: 0; }
#step-mode .modes { flex: none; gap: clamp(8px, 1.4dvh, 12px); padding-bottom: calc(8px + env(safe-area-inset-bottom)); }

/* Les cartes : blanches, 80 px, trait de 4 et tranche de 4, l'avatar carre
   colore a gauche. */
#step-mode .mode-card {
  --text: #000; --muted: rgba(0, 0, 0, 0.64);
  height: 80px; padding: 8px 32px 8px 6px; gap: 20px; border-radius: 16px;
  background: #fff; color: #000;
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
}
#step-mode .mode-card:hover { background: #fff; border-color: var(--refonte-ink); }
#step-mode .mode-card:active { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
#step-mode .mode-icon {
  box-sizing: border-box; flex: none;
  width: 64px; height: 64px; border-radius: 12px; padding: 0; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  border: 3.2px solid var(--refonte-ink); background: #6737fd;
}
#step-mode .mode-card:hover .mode-icon { background: #6737fd; }
#step-mode .mode-icon img { width: 56px; height: 56px; display: block; object-fit: cover; }
#step-mode .mode-icon-solo, #step-mode .mode-card:hover .mode-icon-solo { background: #6737fd; }
#step-mode .mode-icon-battle, #step-mode .mode-card:hover .mode-icon-battle { background: #fccc07; }
#step-mode .mode-icon-online, #step-mode .mode-card:hover .mode-icon-online { background: #01ddfb; }
#step-mode .mode-title { font-size: 24px; line-height: 28px; font-weight: 600; color: #000; }
#step-mode .mode-desc { font-size: 20px; line-height: 24px; font-weight: 600; color: rgba(0, 0, 0, 0.64); }
#step-mode .mode-text { gap: 4px; min-width: 0; }
#step-mode .mode-desc { white-space: nowrap; }
/* Sur les ecrans etroits, la ligne « Contre le monde » et la pastille ne
   tiennent plus cote a cote a 20 px : on descend d'un cran. */
@media (max-width: 380px) { #step-mode .mode-desc { font-size: 18px; } }

/* La pastille verte dans la carte En ligne : combien sont en ligne. */
/* La ligne du titre : le nom a gauche, la pastille a droite. */
/* Le titre puis la pastille, colles a gauche. */
.mode-titre-ligne { display: flex; align-items: center; justify-content: flex-start; gap: 10px; min-width: 0; }
.mode-titre-ligne .mode-title { min-width: 0; }
.monde-pill {
  flex: none; margin-left: 0;
  display: inline-flex; align-items: center; gap: 0; height: 28px; padding: 0 10px 0 0;
  background: #86f601; border: 3px solid var(--refonte-ink); border-radius: 8px;
  box-shadow: 3px 3px 0 var(--refonte-ink);
  font-size: 16px; line-height: 24px; font-weight: 700; color: #fff; text-shadow: 1px 1px 0 var(--refonte-ink);
  font-variant-numeric: tabular-nums;
}
.monde-pill img { width: 20px; height: 20px; display: block; }
.monde-pill[hidden] { display: none; }
#btn-mode-online { padding-right: 14px; }
/* « Battle Multijoueurs » est le plus long titre des trois : il tient sur une
   ligne, quitte a se serrer sur les petits ecrans plutot que de deborder de
   sa carte. */
#btn-mode-battle .mode-title { white-space: nowrap; }
@media (max-width: 380px) { #btn-mode-battle .mode-title { font-size: 20px; } }
@media (max-width: 340px) { #btn-mode-battle .mode-title { font-size: 18px; } }
#btn-mode-online .mode-text { flex: 1 1 auto; }

/* L'accueil : moins d'air en haut et en bas pour tenir dans l'ecran. */

/* Tous les titres, d'apres la maquette : Baloo 2 demi-gras, 36/40, blanc,
   cerne d'encre de 2 px et une ombre pleine de 2 px. Le cerne est un vrai
   trait de texte peint sous les lettres, pas une ombre en croix. */
h1, .accueil h1, .mode-refonte h1 {
  font-family: 'Baloo 2', var(--font-title);
  font-weight: 600; font-size: 36px; line-height: 40px; letter-spacing: 0;
  text-align: center; color: #fff;
  /* Figma pose le trait a l'exterieur des lettres ; le navigateur le centre
     sur leur bord. Un trait de 4 px centre laisse donc les 2 px voulus
     dehors, et paint-order garde le blanc entier par-dessus. */
  -webkit-text-stroke: 4px var(--refonte-ink); paint-order: stroke fill;
  text-shadow: 2px 2px 0 var(--refonte-ink);
}

/* Le nom du jeu, sur le chargement et l'accueil, prend le meme cerne que les
   titres : dans la maquette, il est trace comme eux. */
.splash-nom, #step-accueil .accueil-nom {
  -webkit-text-stroke: 4px var(--refonte-ink); paint-order: stroke fill;
  text-shadow: 2px 2px 0 var(--refonte-ink);
}

/* --- le quiz, d'apres la maquette ------------------------------------------ */

/* Le fond uni de la maquette, sans trame, le temps de la manche. */
html[data-quiz-refonte] body { background: var(--refonte-bg); height: 100dvh; min-height: 0; overflow: hidden; }
html[data-quiz-refonte] body::before { display: none; }
html[data-quiz-refonte] .app { height: calc(100dvh - 48px - env(safe-area-inset-bottom)); min-height: 0; }

/* Trois zones, sans defilement : le score et la barre de temps au-dessus,
   les trois propositions collees en bas, sous le pouce. */
#screen-play[data-format="quiz"] { height: 100%; min-height: 0; gap: 0; justify-content: flex-end; }
#screen-play[data-format="quiz"] .bar {
  flex: 1 1 auto; min-height: 0;
  flex-direction: column; justify-content: flex-end; align-items: center; gap: 8px;
  /* Les pastilles de manche occupent les 54 premiers pixels : le score ne
     monte jamais jusqu'a elles, meme quand l'ecran manque de hauteur. */
  padding-top: calc(60px + env(safe-area-inset-top));
}
/* La serie ouvre l'empilement, au-dessus du disque : c'est une flamme qu'on
   voit du coin de l'oeil pendant qu'on cherche, et sous le disque elle se
   confondait avec le score. */
#screen-play[data-format="quiz"] .bar .streak { order: -3; }
#screen-play[data-format="quiz"] .who { margin: 0; }
/* Le score, dans le dessin des titres : blanc, cerne, ombre pleine. */
#screen-play[data-format="quiz"] .who[data-score] {
  font-size: 36px; line-height: 40px; font-weight: 600; color: #fff;
  -webkit-text-stroke: 4px var(--refonte-ink); paint-order: stroke fill;
  text-shadow: 2px 2px 0 var(--refonte-ink);
}
/* La barre de temps : blanche, cernee, avec sa tranche ; le temps qui reste
   est peint en arc-en-ciel du rouge au vert, fixe sur la piste — quand la
   barre fond par la droite, le vert s'en va le premier et il ne reste que
   le rouge. */
#screen-play[data-format="quiz"] .timer { flex: none; margin-top: 16px; margin-bottom: 4dvh; }
#screen-play[data-format="quiz"] .timer-value { display: none; }
#screen-play[data-format="quiz"] .timer-track {
  height: 20px; border-radius: 16px; background: #fff;
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  container-type: inline-size;
  /* Safari ne rogne pas un enfant positionne aux coins arrondis d'un
     overflow: hidden ; on l'y force avec un masque, et la barre porte ses
     propres coins a gauche pour ne laisser aucun blanc dans l'angle. */
  isolation: isolate;
  -webkit-mask-image: -webkit-radial-gradient(#fff, #000);
}
#screen-play[data-format="quiz"] .timer-bar {
  position: relative; overflow: hidden; background: none;
  border-radius: 12px 0 0 12px; transform: translateZ(0);
}
#screen-play[data-format="quiz"] .timer-bar::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 100cqw;
  background: linear-gradient(90deg, #f60000 0%, #f67b00 25%, #f6f600 50%, #7bf600 75%, #52f600 100%);
}
#screen-play[data-format="quiz"] .timer[data-urgent] .timer-bar { background: none; }

/* Les propositions : des cartes blanches de 80 px, le rang dans un carre
   jaune, le titre et l'artiste sur deux lignes. */
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choices { flex: none; gap: 12px; padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice {
  display: flex; align-items: center; gap: 20px;
  height: 80px; padding: 8px 24px 8px 4px; border-radius: 16px;
  background: #fff; color: #000;
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  font-family: var(--font-title); font-size: 24px; line-height: 28px; font-weight: 600;
}
@media (hover: hover) and (pointer: fine) {
  #screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice:hover:not(:disabled) { border-color: var(--refonte-ink); background: #fff; }
  #screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice:hover:not(:disabled) .quiz-rank { color: #000; border-color: var(--refonte-ink); }
}
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice:active:not(:disabled) { transform: translateY(4px); box-shadow: none; }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-rank {
  flex: none; width: 64px; height: 64px; border-radius: 12px;
  background: #fccc07; border: 3.2px solid var(--refonte-ink); color: #000;
  font-size: 24px; font-weight: 600;
}
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-texte { display: flex; flex-direction: column; gap: 4px; min-width: 0; text-align: left; }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-titre,
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-artiste { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-artiste { font-size: 20px; line-height: 24px; color: rgba(0, 0, 0, 0.64); }
/* Apres coup : la bonne en vert, la mauvaise en rose, les autres s'effacent. */
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice[data-state="ok"] { background: var(--ok-fill); border-color: var(--refonte-ink); }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice[data-state="ok"] .quiz-rank { background: #86f601; color: #000; border-color: var(--refonte-ink); }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice[data-state="ko"] { background: var(--ko-fill); border-color: var(--refonte-ink); opacity: 1; }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice[data-state="ko"] .quiz-rank { background: var(--pink); color: #fff; border-color: var(--refonte-ink); }
#screen-play:is([data-format="quiz"], [data-format="rush"]) .quiz-choice[data-state="off"] { opacity: 0.45; box-shadow: none; }

/* Petits ecrans (iPhone SE) : le buzzer garde ses 192 px, ce sont les
   espaces et le texte de presentation qui se serrent pour que tout tienne
   sans defiler. */
@media (max-height: 700px) {
  .accueil { gap: 8px; }
  .accueil-haut { gap: 10px; padding-top: calc(16px + env(safe-area-inset-top)); }
  #step-accueil .accueil-nom { margin-top: -18px; }
  #step-accueil .lede { margin-top: 10px; }
  #step-accueil .lede { font-size: 14px; line-height: 20px; }
}

/* Le decor de fond (notes, eclats, trame) est range pour le moment, partout :
   le violet reste nu. Le dessin est conserve plus haut pour le jour ou il
   reviendra. */
body::before { display: none; }

/* Sur le choix du mode, pas de pastilles d'etapes en haut pour le moment ;
   le bouton de retour reste. */
#step-dots { transition: opacity 200ms ease; }
html[data-etape="mode"] #step-dots { visibility: hidden; opacity: 0; }

/* Style et Duree : la meme colonne pleine hauteur. Le contenu se centre
   entre la barre et le bouton, le bouton reste colle en bas sous le pouce.
   Les deux marges automatiques se partagent l'espace libre a parts egales,
   c'est ce qui centre le milieu sans conteneur de plus. */
/* La regle aussi : sans la colonne fixe, le carrousel qui deborde des bords
   faisait osciller la page a l'entree de l'etape. */
#step-style, #step-rounds, #step-format { flex: 1 1 auto; min-height: 0; }
#step-style > .eyebrow, #step-rounds > .eyebrow, #step-format > .eyebrow { margin-top: auto; }
#btn-style-next, #btn-start, #btn-format-go { margin-top: auto; }
#btn-style-next, #btn-format-go { margin-bottom: calc(8px + env(safe-area-inset-bottom)); }
/* Sur Duree, le record se lit au-dessus du bouton Jouer, et c'est lui qui
   ouvre le bloc du bas ; sans record, le bouton prend sa place. */
#step-rounds > .status { order: 1; margin-top: auto; }
/* Sans record, la ligne reste dans le flux a hauteur nulle : c'est elle qui
   porte la marge automatique, et le bouton la suit sans decaler le milieu.
   (Un :has() sur le bouton ne passait pas partout sur iPhone.) */
#step-rounds > .status:empty { display: block; height: 0; margin-bottom: -16px; overflow: hidden; }
#btn-start { order: 2; margin-top: 0; margin-bottom: calc(8px + env(safe-area-inset-bottom)); }

/* Le titre du choix du mode est plus grand que les autres : 48/52, comme la
   maquette. Pose apres la regle commune des titres pour l'emporter. */
#step-mode h1 { font-size: 48px; line-height: 52px; }

/* Petits ecrans : le choix du mode serre ses espaces, le disque et le titre
   descendent d'un cran pour tenir sans defiler. */
@media (max-height: 700px) {
  .mode-haut { gap: 12px; padding-top: calc(16px + env(safe-area-inset-top)); }
  .mode-haut > .mode-vinyle { --vinyle: 96px; }
  #step-mode h1 { font-size: 40px; line-height: 44px; }
}

/* Le bouton secondaire, partout : celui de « J'ai deja un compte ». Blanc,
   trait d'encre de 4 px, coins de 16, tranche de 4, 64 px de haut. Changer
   de style, Quitter la salle, Je seche… tous le meme. */
.btn.btn-ghost {
  min-height: 64px; padding: 8px 32px;
  font-family: var(--font-title); font-size: 20px; font-weight: 600; line-height: 24px;
  background: #fff; color: #000;
  border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
  transition: transform 70ms ease, box-shadow 70ms ease, opacity 120ms ease;
}
.btn.btn-ghost:hover { background: #fff; border-color: var(--refonte-ink); }
.btn.btn-ghost:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
.btn.btn-ghost:disabled { box-shadow: none; }
.btn.btn-ghost:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

/* Le bouton principal, partout : celui de « Creer un compte ». Jaune, trait
   d'encre de 4 px, coins de 16, tranche de 4, 64 px de haut. Continuer,
   Rejouer, Manche suivante, Reprendre la partie… tous le meme. */
.btn.btn-primary {
  min-height: 64px; padding: 8px 32px;
  font-family: var(--font-title); font-size: 20px; font-weight: 600; line-height: 24px;
  background: var(--refonte-jaune); color: #000;
  border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
  transition: transform 70ms ease, box-shadow 70ms ease, opacity 120ms ease;
}
.btn.btn-primary:hover { background: var(--refonte-jaune); border-color: var(--refonte-ink); }
.btn.btn-primary:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
.btn.btn-primary:disabled { box-shadow: none; }
.btn.btn-primary:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

/* Le grand « Jouer » garde ses deux lignes, dans le meme dessin. */
.big-start {
  background: var(--refonte-jaune); color: #000;
  border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
}
.big-start:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
/* Quand il ne lance pas — la duree n'est plus la derniere etape en solo — le
   grand bouton n'a plus de raison d'etre grand : il reprend la taille des
   « Continuer » des autres ecrans. */
.big-start[data-suite] { min-height: 0; padding: 14px 18px; border-radius: var(--radius); }
.big-start[data-suite] .big-start-label { font-size: 16px; font-weight: 800; }
/* Les deux lignes collent a leurs lettres : les 8 px d'ecart sont les 8 px
   qu'on voit, sans l'air que la hauteur de ligne ajouterait. */
.big-start-label { font-size: 28px; font-weight: 600; line-height: 1; }
.big-start-sub { color: rgba(0, 0, 0, 0.64); font-weight: 600; opacity: 1; line-height: 1; }

/* La pause se pose sur le meme violet que le reste du jeu : les boutons y
   gardent leur trait et leur tranche. */
.pause.screen-dark { background: var(--bg); }

/* --- l'avant-gout guide ----------------------------------------------------- */

/* Manche guidee : ce qui n'est pas en lumiere s'efface. Les propositions
   restent pleines, le score et le chrono se retirent. */
html[data-demo-spot="choix"] #screen-play .bar,
html[data-demo-spot="choix"] #screen-play .timer { opacity: 0.3; }
#screen-play .bar, #screen-play .timer { transition: opacity 300ms ease; }

/* La bulle du guide, dans le dessin des cartes : blanche, cernee d'encre,
   avec sa tranche, et sa pointe de la meme encre. */
.coach-pill {
  padding: 10px 16px; border-radius: 12px;
  font-family: var(--font-title); font-size: 16px; font-weight: 600; line-height: 20px;
  background: #fff; color: #000;
  border: 3px solid var(--refonte-ink);
  box-shadow: 0 4px 0 var(--refonte-ink);
}
.coach-pill:hover { border-color: var(--refonte-ink); }
.coach-pill::after { border-width: 7px; margin-left: -7px; }
.coach[data-cote="dessous"] .coach-pill::after { border-bottom-color: var(--refonte-ink); }
.coach[data-cote="dessus"] .coach-pill::after { border-top-color: var(--refonte-ink); }

/* --- le classement, d'apres la maquette ------------------------------------ */

/* Trois zones sans defilement de page : l'en-tete bleu nuit avec les onglets
   et le podium, la liste qui defile, le bouton colle en bas. */
html[data-classement] body { height: 100dvh; min-height: 0; overflow: hidden; }
html[data-classement] .app { height: calc(100dvh - 48px - env(safe-area-inset-bottom)); min-height: 0; }
#screen-board, #screen-profil, #screen-compte, #screen-email { height: 100%; min-height: 0; gap: 0; align-items: stretch; }
#screen-board > *, #screen-profil > *, #screen-compte > *,
/* La barre du retour est en position fixe et couvre deja l'ecran d'un bord a
   l'autre : lui imposer la largeur de la colonne la retrecirait. */
#screen-email > :not(.steps) { width: 100%; }
/* L'en-tete et la liste debordent des marges : leur largeur se deduit des
   bords, pas d'un pourcentage — l'ancienne regle du tableau les retrecissait. */
#screen-board #board-corps > .classement-haut,
#screen-board #board-corps > .classement-defile { width: auto; align-self: stretch; }
.classement-corps { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* L'en-tete deborde des marges du body pour toucher les bords. Il ne porte
   plus ni fond propre ni trait de separation : le violet de la page passe
   dessous d'un bout a l'autre, et les onglets se detachent seuls. Transparent
   plutot que la couleur recopiee — le jour ou le violet change, l'en-tete
   suit sans qu'on y pense. */
.classement-haut {
  flex: none; margin: -24px -20px 0; padding: 20px 20px 0;
  background: transparent;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
/* Le podium se replie au fil du defilement de la liste : --repli va de 0
   (deplie) a 1 (disparu), pose par le script sur l'en-tete. Le cadre perd sa
   hauteur, le dessin remonte et s'efface. */
.classement-haut { --repli: 0; }
.classement-podium-cadre {
  width: 310px; max-width: 100%; overflow: hidden;
  /* Le volet se ferme plus vite que le dessin ne s'efface : il est ferme a
     un peu plus de la moitie du geste, et pousse le dessin hors du cadre. */
  height: calc(180px * max(0, 1 - var(--repli) * 1.8)); margin-top: -2px; margin-bottom: -4px;
}
.classement-podium {
  display: block; width: 100%; height: auto;
  transform: translateY(calc(var(--repli) * -16px)) scale(calc(1 - var(--repli) * 0.08));
  transform-origin: 50% 100%;
  opacity: calc(1 - var(--repli));
}
/* Les onglets : une carte blanche, la periode en cours en jaune. */
.classement-onglets {
  width: 100%; height: 64px; padding: 8px; border-radius: 16px;
  background: #fff; border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  display: flex; gap: 8px;
}
.classement-onglets { background: #fff; }
.classement-onglet {
  flex: 1 1 0; min-width: 0; height: 100%; border-radius: 12px;
  appearance: none; cursor: pointer; font-family: var(--font-title);
  font-size: 16px; line-height: 20px; font-weight: 600; letter-spacing: -0.06px;
  background: #f7f7f7; color: #000; border: 3.2px solid transparent;
}
.classement-onglet[data-on] { background: #fccc07; border-color: var(--refonte-ink); }
.classement-onglet:focus-visible { outline: 3px solid var(--refonte-jaune); outline-offset: 2px; }

.classement-defile {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 24px 0 16px; margin: 0 -20px; padding-inline: 20px;
  scrollbar-width: none;
}
.classement-defile::-webkit-scrollbar { display: none; }
.classement-statut { margin: 0 0 12px; text-align: center; font-weight: 600; }
.classement-statut:empty { display: none; }
.classement-liste { list-style: none; margin: 0; padding: 4px 0 0; display: flex; flex-direction: column; gap: 19px; }

/* Une ligne : carte blanche de 80 px, le badge de rang a cheval sur le coin. */
.classement-ligne {
  position: relative;
  display: flex; align-items: center; gap: 20px;
  height: 80px; padding: 8px 32px 8px 8px; border-radius: 16px;
  background: #fff; color: #000;
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  text-align: left;
}
.classement-avatar {
  flex: none; width: 64px; height: 64px; border-radius: 12px;
  display: grid; place-items: center;
  background: #fccc07; border: 3.2px solid var(--refonte-ink);
}
.classement-avatar img { width: 54px; height: 54px; border-radius: 9px; display: block; }
.classement-texte { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.classement-nom {
  font-family: var(--font-title); font-size: 16px; line-height: 20px; font-weight: 600; letter-spacing: -0.06px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.classement-pays { font-family: var(--font-title); font-size: 16px; line-height: 24px; font-weight: 600; white-space: nowrap; }
.classement-gain {
  flex: none; width: 56px; height: 36px; border-radius: 8px;
  display: grid; place-items: center;
  background: #40bf4d; color: #fcfcfc;
  font-family: var(--font-title); font-size: 24px; line-height: 1; font-weight: 600;
}
.classement-score {
  flex: none; font-family: var(--font-title); font-size: 24px; line-height: 1; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* Le rang : une etoile a cheval sur le coin pour les trois premiers, une
   pastille d'encre pour les autres. */
.classement-rang {
  position: absolute; left: -12px; top: -15px; z-index: 1;
  width: 40px; height: 40px; display: grid; place-items: center;
  font-family: var(--font-title); font-size: 24px; line-height: 1; font-weight: 600; color: var(--refonte-ink);
  background: url('/assets/classement/etoile.svg') center / 40px 40px no-repeat;
}
.classement-ligne[data-rang="2"] .classement-rang,
.classement-ligne[data-rang="3"] .classement-rang { filter: saturate(0) brightness(1.15); }
.classement-ligne:not([data-rang="1"]):not([data-rang="2"]):not([data-rang="3"]) .classement-rang {
  left: -8px; top: -10px; width: 28px; height: 28px; border-radius: 50%;
  background: var(--refonte-ink); color: #fff; font-size: 14px;
}
/* Ma ligne garde le meme trait que les autres : c'est la pastille des points
   qui la signale, pas un liseret. */
.classement-vide { color: #fff; font-weight: 600; text-align: center; padding: 16px 0; }
#btn-board-suite { flex: none; margin-top: 12px; }
#btn-board-explorer, #btn-board-compte {
  flex: none; margin-top: 12px; margin-bottom: calc(8px + env(safe-area-inset-bottom));
}

/* --- l'ecran du compte ------------------------------------------------------ */
/* Trois zones comme ailleurs : la fleche en haut, le compte au centre, la
   sortie sous le pouce. */
.compte-corps {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px;
}
.compte-avatar {
  width: 112px; height: 112px; border-radius: 50%; object-fit: cover; scale: 1.18;
  background: var(--refonte-jaune);
  border: 4px solid var(--refonte-ink); box-shadow: 2px 5px 0 var(--refonte-ink);
  /* Le badge vert passe au-dessus : l'air se met donc de ce cote-la. */
  overflow: clip; margin-top: 12px;
}
#screen-compte h1 { margin: 0; }
#screen-compte .lede { margin: 0; }
/* Les deux chiffres cote a cote, chacun dans sa carte blanche. */
.compte-chiffres { display: flex; gap: 12px; margin-top: 14px; width: min(100%, 360px); }
.compte-chiffre {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 14px 8px; background: #fff; color: var(--refonte-ink);
  border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
}
.compte-nombre { font-family: var(--font-title); font-size: 26px; font-weight: 800; line-height: 1; }
.compte-legende { font-size: 12.5px; font-weight: 700; opacity: 0.66; }
/* Les deux sorties du compte : meme largeur, meme ecart qu'ailleurs, et le
   meme empilement que « Se deconnecter » — l'icone a cote du mot, l'ensemble
   centre. Sans inline-flex, l'icone du bouton plein retombait sur la ligne de
   base du texte et se deformait. Le dernier porte la marge du bas, comme sur
   l'ecran du classement. */
#btn-compte-classement, #btn-compte-sortir {
  flex: none; width: min(100%, 360px); align-self: center;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 12px;
}
#btn-compte-sortir {
  margin-bottom: calc(8px + env(safe-area-inset-bottom));
}
.btn-icone { flex: none; width: 22px; height: 22px; display: block; }
/* Les deux sorties de l'ecran de fin : le mot, puis son icone, l'ensemble
   centre — comme « Se deconnecter », l'icone de l'autre cote. */
#btn-see-board, #btn-again {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
}

/* Le formulaire d'entree, centre dans l'ecran. */
.board-form { flex: 1 1 auto; min-height: 0; justify-content: center; }
/* Le mot du hors-ligne, sous le pays : il s'insere sans pousser le reste. */
.profil-statut { margin: 0; font-size: 14px; }
.profil-statut[hidden] { display: none; }
/* Le pays est un menu deroulant, et la fleche du systeme a ete retiree avec
   l'apparence native : plus rien ne disait qu'on pouvait l'ouvrir. On repose
   donc le chevron du bouton de langue — meme trait, memes bouts ronds — a
   droite du champ, et on lui laisse sa place. */
.board-pays {
  appearance: none; -webkit-appearance: none;
  padding-right: 46px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%231b1240' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 2l5 4 5-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
  background-size: 14px 10px;
}

/* Sur iPhone, le body garde une marge basse de securite en plus des 48 px :
   la colonne la compte, sinon la page depasse d'autant et se met a defiler,
   l'en-tete part au-dessus et le bouton passe sous le pouce. Et c'est la
   racine qu'on empeche de defiler : sur Safari, un body en overflow hidden
   defile quand meme. */
html[data-refonte], html[data-quiz-refonte], html[data-classement] { height: 100dvh; overflow: hidden; }
.classement-defile { position: relative; }

/* --- le rush, dans le meme dessin que le quiz -------------------------------- */

/* Le disque du chrono et le compte de morceaux se centrent entre la barre du
   haut et les propositions ; les propositions restent collees en bas. */
#screen-play[data-format="rush"] { height: 100%; min-height: 0; gap: 0; justify-content: flex-end; padding-top: calc(30px + env(safe-area-inset-top)); }
#screen-play[data-format="rush"] .hold { order: 0; margin-top: auto; align-self: center; }
#screen-play[data-format="rush"] .bar { order: 1; margin: 16px 0 auto; flex-direction: column; align-items: center; justify-content: center; }
#screen-play[data-format="rush"] .who {
  margin: 0; font-family: var(--font-title); font-size: 24px; line-height: 28px; font-weight: 600; color: #fff;
  -webkit-text-stroke: 4px var(--refonte-ink); paint-order: stroke fill; text-shadow: 2px 2px 0 var(--refonte-ink);
}
#screen-play[data-format="rush"] .quiz-choices { order: 2; flex: none; gap: 12px; padding-bottom: calc(8px + env(safe-area-inset-bottom)); }

/* Partie terminee : le bilan en etiquettes — morceaux trouves, serie. */
.fin-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.fin-tag {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 16px;
  border-radius: 12px; background: #fff; color: #000;
  border: 3px solid var(--refonte-ink); box-shadow: 0 3px 0 var(--refonte-ink);
  font-family: var(--font-title); font-size: 18px; line-height: 1; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.fin-tag-icone { font-size: 20px; line-height: 1; }

/* Revelation : le verdict, le score, puis la pochette. On apprend d'abord si
   on l'a eu, ensuite ce que ca rapporte, et l'on regarde le morceau pendant
   que l'extrait tourne. */
#screen-reveal > #reveal-verdict { order: -3; }
#screen-reveal > #reveal-points { order: -2; }
/* L'entree suit cet ordre et non celui du document : le badge se pose, le
   chiffre tombe apres. Sans ces deux lignes, l'element du bas arriverait le
   premier — la regle generale echelonne d'apres l'ordre du document, ou le
   chiffre precede le verdict. */
#screen-reveal > #reveal-verdict { animation-delay: 0ms; }
#screen-reveal > #reveal-points:not([hidden]) { animation-delay: 140ms; }
/* Le score de la manche. Une manche gagnee s'affiche en grand — c'est la
   recompense ; une manche ratee garde une taille plus sobre, il n'y a pas de
   quoi fanfaronner. */
#screen-reveal .points { font-size: 64px; }
/* L'unite ne suit plus le chiffre a l'echelle : vingt pixels, quelle que soit
   la taille du nombre. Elle nomme, elle ne crie pas. */
#screen-reveal .points-mot { font-size: 20px; }
/* Et il se tient droit. L'inclinaison de cinq degres va aux etiquettes — le
   chiffre, lui, est l'axe de l'ecran : penche, il cessait d'etre d'aplomb avec
   son unite juste dessous. */
#screen-reveal .points { rotate: 0deg; }

/* Partie terminee : le badge, le score, le record, puis les etiquettes. */
#screen-done > .eyebrow { order: -3; }
#screen-done > #winner-line { order: -2; }
#screen-done > #record-line { order: -1; }

/* Duree : le nombre de manches dans le dessin du titre de l'accueil. */
/* Le nombre de manches est ecrit comme le titre de l'accueil : la meme regle
   au mot pres — Baloo 2 demi-gras, blanc, cerne d'encre de 4 px, ombre pleine
   de 2 px, centre — avec la taille choisie pour lui. Les chiffres y reprennent
   leur chasse naturelle : la tabulaire du pas-a-pas, heritee du theme d'avant,
   ne dessinait pas les memes lettres que le titre. */
.stepper-value {
  font-family: 'Baloo 2', var(--font-title);
  font-weight: 600; font-size: 56px; line-height: 60px; letter-spacing: 0;
  text-align: center; color: #fff;
  font-variant-numeric: normal;
  -webkit-text-stroke: 4px var(--refonte-ink); paint-order: stroke fill;
  text-shadow: 2px 2px 0 var(--refonte-ink);
}

/* --- barre de navigation de developpement (staging seulement) ---------------- */
#dev-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90; height: 28px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--refonte-ink); color: #fff;
  font: 700 12px/1 var(--font); letter-spacing: 0.08em;
}
#dev-nav select {
  font: 600 13px/1 var(--font); color: #000; background: #fff;
  border: 0; border-radius: 6px; padding: 3px 8px;
}
/* Tout ce qui est colle en haut descend d'autant, et les ecrans pleine
   hauteur perdent la hauteur de la barre. */
html[data-dev] body { padding-top: 52px; }
html[data-dev] :is(.quit, .steps, .langue, .demo-skip, #play-round) { translate: 0 28px; }
html[data-dev][data-refonte] .app,
html[data-dev][data-quiz-refonte] .app,
html[data-dev][data-classement] .app { height: calc(100dvh - 76px - env(safe-area-inset-bottom)); }
html[data-dev] .classement-haut { margin-top: -24px; }

/* --- le decompte : Ready, Set, Go! en badges ---------------------------------- */
.countdown-ring { display: none; }
.countdown-anneau { width: auto; height: 176px; }
/* Trois badges de 30 % de la largeur de l'ecran en hauteur : Ready en rouge
   (le rouge de Rate), Set en orange, Go en vert. */
.countdown-number.eyebrow {
  min-height: 30vw; padding: 0 8vw; border-radius: 6vw;
  font-size: clamp(36px, 13vw, 80px); line-height: 1.1;
  color: #fff; text-shadow: 3px 3px 0 var(--refonte-ink);
  -webkit-text-stroke: 5px var(--refonte-ink); paint-order: stroke fill;
  font-variant-numeric: normal;
  box-shadow: none; filter: drop-shadow(0 6px 0 var(--refonte-ink));
}
.countdown-number.eyebrow[data-pas="3"] { background: var(--ko-fill); }
.countdown-number.eyebrow[data-pas="2"] { background: #ff9f1a; }
.countdown-number.eyebrow[data-pas="1"] { background: #86f601; }
@media (min-width: 600px) {
  .countdown-number.eyebrow { min-height: 180px; padding: 0 48px; border-radius: 36px; }
}
/* L'entree en coup de poing : de tres grand a sa taille en 220 ms, avec un
   leger depassement. Sur Go, le badge continue de pousser jusqu'au bout. */
.countdown-number.eyebrow[data-beat] { animation: badge-punch 220ms cubic-bezier(0.2, 0.9, 0.3, 1.3) both; }
.countdown-number.eyebrow[data-beat][data-pas="1"] {
  animation: badge-punch 220ms cubic-bezier(0.2, 0.9, 0.3, 1.3) both, badge-pousse 1100ms 220ms ease-out forwards;
}
@keyframes badge-punch {
  from { transform: scale(2.5) rotate(-5deg); opacity: 0; }
  to { transform: scale(1) rotate(-5deg); opacity: 1; }
}
@keyframes badge-pousse {
  from { transform: scale(1) rotate(-5deg); }
  to { transform: scale(1.12) rotate(-5deg); }
}
/* Le fond prend la couleur du mot. */
#screen-countdown { transition: background 250ms ease; }
#screen-countdown[data-pas="3"] { background: #5a1024; }
#screen-countdown[data-pas="2"] { background: #7a3d05; }
#screen-countdown[data-pas="1"] { background: #2fb600; animation: secousse 300ms ease-out; }
@keyframes secousse {
  0%, 100% { translate: 0 0; }
  20% { translate: -4px 2px; } 40% { translate: 4px -2px; } 60% { translate: -3px -1px; } 80% { translate: 3px 1px; }
}
/* Les ondes de choc, depuis le badge. */
.countdown-anneau { position: relative; }
.onde {
  position: absolute; left: 50%; top: 50%; width: 120px; height: 120px; margin: -60px 0 0 -60px;
  border-radius: 50%; border: 6px solid rgba(255, 255, 255, 0.7); opacity: 0; pointer-events: none;
}
.countdown-anneau[data-beat] .onde-1 { animation: onde 600ms ease-out; }
#screen-countdown[data-pas="1"] .countdown-anneau[data-beat] .onde-1 { animation-duration: 450ms; }
#screen-countdown[data-pas="1"] .countdown-anneau[data-beat] .onde-2 { animation: onde 450ms 120ms ease-out; }
@keyframes onde {
  from { transform: scale(0.4); opacity: 0.7; }
  to { transform: scale(3.2); opacity: 0; }
}
/* La fin : Go ne tombe plus, il enfle une derniere fois sur place. C'est le
   voile vert qui l'emporte, une fraction de seconde plus tard — le faire
   partir deux fois, chacun de son cote, cassait le geste en deux. */
#screen-countdown[data-fin] #countdown-number.eyebrow { animation: go-enfle 420ms cubic-bezier(0.3, 0, 0.2, 1) forwards; }
@keyframes go-enfle {
  from { transform: scale(1.12) rotate(-5deg); }
  to { transform: scale(1.3) rotate(-5deg); }
}

/* Le voile vert s'en va par la gauche en emportant le Go, et decouvre le
   violet de la manche derriere lui. De travers, comme le volet du decompte,
   avec le grossissement qu'il faut pour qu'un rectangle incline couvre encore
   l'ecran. */
.voile-go {
  position: fixed; inset: 0; z-index: 900; background: #2fb600; pointer-events: none;
  transition: translate 480ms cubic-bezier(0.5, 0, 1, 0.6),
              rotate 480ms cubic-bezier(0.5, 0, 1, 0.6),
              scale 480ms cubic-bezier(0.5, 0, 1, 0.6);
}
.voile-go[data-tombe] { translate: -104% 0; rotate: 5deg; scale: 1.2; }
/* La copie du Go, posee a la place exacte de l'original : le voile la porte,
   donc elle part avec lui sans avoir a s'animer elle-meme. Son dessin vient
   des regles du badge plus haut, ecrites sur .countdown-number.eyebrow : la
   copie garde ses classes, donc elle garde son fond vert et son blanc cerne.
   Le script ne lui pose que sa place et son elan. */
.voile-go-mot { position: absolute; }
@media (prefers-reduced-motion: reduce) {
  .voile-go { transition: none; }
  #screen-countdown[data-fin] #countdown-number.eyebrow { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  #screen-countdown[data-pas="1"] { animation: none; }
  .onde { display: none; }
}

/* Les cartes des regles : le nom et le texte alignes a gauche, a 20 px du
   bord (12 px de marge de la carte, 8 px de plus). */
.fmt-nom { justify-content: flex-start; text-align: left; padding-left: 8px; }
.fmt-desc { text-align: left; padding-left: 8px; padding-right: 8px; }

/* --- le vinyle sous les points, en quiz ------------------------------------- */
#screen-play .platine { display: none; }
#screen-play[data-format="quiz"] .platine {
  display: block; flex: none; align-self: center;
  /* Au milieu de l'empilement : la serie au-dessus, le disque, puis les
     points. */
  order: -2;
  /* 192 au plus, la taille du dessin. En dessous, il suit la hauteur de
     l'ecran : sur un petit telephone, il laisse la place au score et aux
     cartes plutot que de les pousser hors du cadre. */
  width: clamp(88px, 20dvh, 192px); height: auto; margin-top: 8px; margin-bottom: 10px;
  /* Trente-trois tours et un tiers, la vitesse d'un vrai 33 : un tour toutes
     les 1,8 seconde. Plus lent, il aurait l'air en panne ; plus vite, il
     tirerait l'oeil au moment ou l'on ecoute. */
  animation: platine-tourne 1.8s linear infinite;
  will-change: transform;
}
@keyframes platine-tourne { to { rotate: 360deg; } }
/* Qui ne veut pas de mouvement garde le disque, immobile. */
@media (prefers-reduced-motion: reduce) {
  #screen-play[data-format="quiz"] .platine { animation: none; }
}
/* En manche guidee, le vinyle s'efface avec le score. */
html[data-demo-spot="choix"] #screen-play .platine { opacity: 0.3; transition: opacity 300ms ease; }

/* --- la fleche de retour dans la barre des etapes ---------------------------- */
/* Le retour et les pastilles vivent dans le meme conteneur, sur une ligne :
   la fleche a gauche, les pastilles centrees. Un espace de la largeur de la
   fleche a droite garde les pastilles au centre de l'ecran, et la fleche
   cachee garde sa place plutot que de disparaitre du flux. */
.steps {
  display: flex; gap: 12px; justify-content: space-between;
  padding: 0 calc(14px + env(safe-area-inset-right)) 0 calc(14px + env(safe-area-inset-left));
}
.steps > .step-back { position: static; top: auto; left: auto; flex: none; }
.steps > .step-back[hidden] { display: grid; visibility: hidden; pointer-events: none; }
.steps > .step-dots { flex: 1 1 auto; justify-content: center; }
.steps::after { content: ''; flex: none; width: 40px; height: 40px; }
/* La pastille du joueur prend la place du vide de droite : quand elle est la,
   c'est elle qui equilibre la fleche. */
.steps:has(> .joueur:not([hidden]))::after { display: none; }

/* Qui joue : la tete choisie, seule. Meme cercle, meme trait d'encre et meme
   tranche que la fleche de retour, en face d'elle. La tete deborde du cercle
   et s'y fait couper : c'est ce debord qui la fait remplir la pastille. */
.joueur {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; padding: 0;
  background: var(--refonte-jaune);
  border: 3px solid var(--refonte-ink); border-radius: 50%;
  box-shadow: 0 4px 0 var(--refonte-ink);
  overflow: clip;
  appearance: none; cursor: pointer;
  transition: transform 70ms ease, box-shadow 70ms ease;
}
.joueur:active { transform: translateY(4px); box-shadow: 0 0 0 var(--refonte-ink); }
.joueur:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.joueur[hidden] { display: none; }

.joueur-avatar {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* La tete est dessinee avec de l'air autour : on l'agrandit un peu pour
     qu'elle touche les bords plutot que de flotter au centre. */
  scale: 1.18;
}

/* Petits ecrans : la platine, la barre de temps et les cartes se serrent pour
   que la flamme et le score restent sous les pastilles de manche. */
@media (max-height: 700px) {
  #screen-play[data-format="quiz"] .platine { width: clamp(72px, 15dvh, 120px); margin-top: 4px; }
  #screen-play[data-format="quiz"] .timer { margin-top: 10px; }
  #screen-play[data-format="quiz"] .quiz-choices { gap: 8px; }
  #screen-play[data-format="quiz"] .bar { gap: 4px; }
}

/* --- exploration : la future etape Style (staging) --------------------------- */
#screen-explo { height: 100%; min-height: 0; gap: 0; align-items: stretch; text-align: center; }
#screen-explo > * { width: 100%; }
/* La barre du haut : un seul conteneur fixe, le retour a gauche, les points
   et les parametres a droite. */
#screen-explo > .explo-barre { width: auto; }
.explo-barre {
  position: fixed; z-index: 6;
  top: calc(14px + env(safe-area-inset-top));
  left: calc(14px + env(safe-area-inset-left)); right: calc(14px + env(safe-area-inset-right));
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.explo-barre > .explo-retour { position: static; top: auto; left: auto; width: 40px; flex: none; }
/* 32 px entre le bas de la barre du haut (54 px) et le titre : la colonne
   commence a 24 px, d'ou 62. */
.explo-haut { flex: none; display: flex; flex-direction: column; align-items: center; gap: 12px; padding: calc(62px + env(safe-area-inset-top)) 0 16px; }
.explo-haut h1 { margin: 0; }
/* Le milieu defile seul ; l'en-tete et le bas restent en place. */
.explo-defile {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 24px; padding: 4px 0 16px;
  margin: 0 -20px; padding-inline: 20px; scrollbar-width: none; text-align: left;
}
.explo-defile::-webkit-scrollbar { display: none; }
#screen-explo #explo-defile { width: auto; }
/* Les blocs gardent leur hauteur : sans cela la colonne qui defile les
   ecrasait pour tenir, et le champ perdait vingt pixels. */
.explo-defile > * { flex: none; }
/* La recherche : un champ blanc cerne d'encre, la loupe a gauche. */
.explo-recherche {
  display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px;
  background: #fff; color: #000; border: 4px solid var(--refonte-ink); border-radius: 16px;
  box-shadow: 0 4px 0 var(--refonte-ink);
}
.explo-recherche svg { flex: none; color: var(--refonte-ink); }
.explo-recherche input {
  flex: 1 1 auto; min-width: 0; height: 40px; border: 0; background: none; outline: none;
  -webkit-appearance: none; appearance: none;
  font-family: var(--font-title); font-size: 18px; font-weight: 600; color: #000;
}
.explo-recherche input::-webkit-search-decoration { -webkit-appearance: none; }
.explo-recherche input::placeholder { color: rgba(0, 0, 0, 0.45); }
.explo-recherche:focus-within { border-color: var(--refonte-jaune); }
.explo-resultats { display: flex; flex-direction: column; gap: 20px; }
.explo-bloc { display: flex; flex-direction: column; gap: 10px; }
.explo-titre {
  margin: 0; font-family: var(--font-title); font-size: 22px; line-height: 26px; font-weight: 600; color: #fff;
  text-shadow: 2px 2px 0 var(--refonte-ink);
}
.explo-texte { margin: 0; font-size: 15px; line-height: 22px; font-weight: 600; color: rgba(255, 255, 255, 0.85); }
.explo-texte[data-erreur] { color: var(--refonte-jaune); }
/* Une rangee qui defile de cote, cartes de 140 px. */
.explo-rangee {
  display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x proximity; scroll-padding-inline: 20px;
  margin: 0 -20px; padding: 6px 20px 8px; scrollbar-width: none;
}
.explo-rangee::-webkit-scrollbar { display: none; }
.explo-carte {
  appearance: none; cursor: pointer; font-family: var(--font-title); text-align: left;
  flex: none; width: 140px; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 6px; padding: 8px; border-radius: 16px;
  background: #fff; color: #000; border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  transition: transform 70ms ease, box-shadow 70ms ease;
}
.explo-carte:active { transform: translateY(4px); box-shadow: none; }
.explo-carte[data-on] { border-color: var(--refonte-jaune); box-shadow: 0 4px 0 var(--refonte-ink), 0 0 0 3px var(--refonte-jaune); }
/* Le bouton Jouer dans la carte : petit, jaune, pleine largeur de la carte. */
#screen-explo .btn.explo-jouer { width: 100%; min-height: 40px; padding: 0 12px; margin-top: 4px; font-size: 16px; border-width: 3px; border-radius: 12px; box-shadow: 0 3px 0 var(--refonte-ink); }
#screen-explo .btn.explo-jouer:active { transform: translateY(3px); box-shadow: none; }
.explo-carte-large { width: 100%; flex-direction: row; align-items: center; gap: 14px; }
.explo-carte-large .explo-vignette { width: 64px; height: 64px; font-size: 28px; }
.explo-vignette {
  display: grid; place-items: center; width: 120px; height: 120px; border-radius: 10px; overflow: hidden;
  border: 3px solid var(--refonte-ink); background: #6737fd; color: #fff;
  font-size: 44px; font-weight: 600; text-shadow: 2px 2px 0 var(--refonte-ink);
}
.explo-vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
.explo-carte-nom { font-size: 16px; line-height: 20px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.explo-carte-detail { font-size: 13px; line-height: 16px; font-weight: 600; color: rgba(0, 0, 0, 0.6); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* L'import : un champ et son bouton sur une ligne, les trois services dessous. */
/* Le bloc d'import prend le trait et la tranche des cartes. */
.explo-import { gap: 12px; padding: 16px; border-radius: 16px; background: #fff; border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink); }
/* Sur le blanc, le titre passe en encre sans ombre, le texte en noir attenue. */
.explo-import .explo-titre { color: var(--refonte-ink); text-shadow: none; }
.explo-import .explo-texte { color: rgba(0, 0, 0, 0.72); }
.explo-import .explo-texte[data-erreur] { color: #d10a75; }
.explo-import .explo-lien { background: #f7f7f7; }
/* Sur mobile, le bouton Importer passe sous le champ, pleine largeur. */
.explo-import-ligne { display: flex; flex-direction: column; gap: 10px; }
@media (min-width: 600px) { .explo-import-ligne { flex-direction: row; } }
.explo-lien {
  flex: 1 1 auto; min-width: 0; height: 52px; padding: 0 14px;
  background: #fff; color: #000; border: 4px solid var(--refonte-ink); border-radius: 14px;
  font-family: var(--font-title); font-size: 15px; font-weight: 600; outline: none;
}
.explo-lien:focus { border-color: var(--refonte-jaune); }
#screen-explo .btn.explo-importer { width: 100%; min-height: 52px; padding: 0 18px; font-size: 17px; }
@media (min-width: 600px) { #screen-explo .btn.explo-importer { width: auto; } }
.explo-import-apercu { display: flex; }
.explo-genres { display: flex; flex-direction: column; gap: 20px; }
/* Le mot en bas, le temps de le lire : ce qui n'est pas encore branche. */
#screen-explo > .explo-toast { width: auto; }
.explo-toast {
  position: fixed; left: 20px; right: 20px; bottom: calc(20px + env(safe-area-inset-bottom)); z-index: 7;
  margin: 0; padding: 12px 16px; border-radius: 12px; text-align: center;
  background: #fff; color: #000; border: 3px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
  font-family: var(--font-title); font-size: 15px; line-height: 20px; font-weight: 600;
}
.explo-toast[hidden] { display: none; }
.explo-defile { padding-bottom: calc(16px + env(safe-area-inset-bottom)); }

/* Les points du joueur, en haut a droite de l'exploration. */
/* Le coin haut droit : les points, puis les parametres, sur une ligne. */
.explo-coin { display: flex; align-items: center; gap: 10px; }
.explo-coin > .explo-parametres { position: static; top: auto; left: auto; display: grid; place-items: center; }
.explo-parametres svg { display: block; }
.explo-points {
  display: inline-flex; align-items: center; gap: 6px; height: 40px; padding: 0 14px 0 10px;
  background: #fff; color: #000; border: 4px solid var(--refonte-ink); border-radius: 999px;
  box-shadow: 0 4px 0 var(--refonte-ink);
  font-family: var(--font-title); font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.explo-points-icone { font-size: 16px; line-height: 1; }
html[data-dev] .explo-barre { translate: 0 28px; }

/* --- la page de recherche, par-dessus l'exploration ------------------------- */
/* Le champ de l'ecran est un bouton : meme dessin, le texte en gris. */
.explo-recherche-porte { width: 100%; appearance: none; cursor: text; font-family: var(--font-title); }
.explo-recherche-texte { flex: 1 1 auto; text-align: left; font-size: 18px; font-weight: 600; color: rgba(0, 0, 0, 0.45); }
#screen-explo > .explo-page-recherche { width: auto; }
.explo-page-recherche {
  position: fixed; inset: 0; z-index: 40; background: var(--bg);
  display: flex; flex-direction: column; gap: 16px;
  padding: calc(14px + env(safe-area-inset-top)) 20px calc(16px + env(safe-area-inset-bottom));
  animation: page-recherche 220ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
.explo-page-recherche[hidden] { display: none; }
@keyframes page-recherche {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}
.explo-page-barre { display: flex; align-items: center; gap: 10px; flex: none; }
.explo-page-barre > .step-back { position: static; top: auto; left: auto; flex: none; }
.explo-page-barre > .explo-recherche { flex: 1 1 auto; min-width: 0; }
.explo-page-resultats {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 20px; text-align: left;
  margin: 0 -20px; padding: 4px 20px 16px; scrollbar-width: none;
}
.explo-page-resultats::-webkit-scrollbar { display: none; }
.explo-puces { display: flex; flex-wrap: wrap; gap: 8px; }
.explo-puce {
  appearance: none; cursor: pointer; font-family: var(--font-title); font-size: 15px; font-weight: 600;
  padding: 8px 14px; border-radius: 999px; background: #fff; color: #000;
  border: 3px solid var(--refonte-ink); box-shadow: 0 3px 0 var(--refonte-ink);
}
.explo-puce:active { transform: translateY(3px); box-shadow: none; }
html[data-dev] .explo-page-recherche { padding-top: calc(42px + env(safe-area-inset-top)); }
@media (prefers-reduced-motion: reduce) { .explo-page-recherche { animation: none; } }

/* --- l'arrivee des pochettes sur Choisis ton terrain ------------------------ */
/* Chaque pochette vient de son cote, en fondu, la plus proche du centre en
   premier ; celle du centre grandit sur place. */
.cover-card[data-intro] {
  animation: pochette-arrive 520ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
  animation-delay: calc(var(--d, 0) * 90ms);
}
@keyframes pochette-arrive {
  from { opacity: 0; transform: translateX(calc(var(--sens, 0) * 140px)) scale(0.7); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .cover-card[data-intro] { animation: none; } }

/* Le challenge : une carte blanche, la pochette, deux lignes, et le compte a
   rebours a droite. */
/* --- le challenge (maquette 384:10) ----------------------------------------- */
/* Une carte blanche a trait d'encre : la playlist en haut, un trait, le temps
   qui reste en plaques de chiffres, et le bouton qui prend toute la largeur. */
.explo-challenge {
  font-family: var(--font-title); text-align: left;
  width: 100%; display: flex; flex-direction: column; gap: 20px;
  padding: 16px 16px 20px; border-radius: 16px;
  background: #fff; color: #000;
  border: 4px solid var(--refonte-ink); box-shadow: 0 4px 0 var(--refonte-ink);
}
.explo-challenge-ligne { display: flex; align-items: center; gap: 20px; }
.explo-challenge-cover { flex: none; }
.explo-challenge .explo-vignette {
  width: 64px; height: 64px; font-size: 28px; border-radius: 12px;
  /* La pochette se pose sur le jaune et non sur l'encre : c'est lui qui borde.
     La maquette dit #fccc07 ; on garde le jaune du jeu, la nuance ne vaut pas
     un second or dans la palette. */
  border: 3.2px solid var(--refonte-jaune); background: var(--refonte-jaune);
}
.explo-challenge-texte { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.explo-challenge-nom { font-size: 16px; line-height: 20px; font-weight: 600; letter-spacing: -0.06px; }
.explo-challenge-desc { font-size: 16px; line-height: 24px; font-weight: 600; color: rgba(0, 0, 0, 0.64); }
/* Le trait qui separe la playlist du compte a rebours. */
.explo-challenge-trait { display: block; height: 2px; background: var(--refonte-ink); }
.explo-challenge-bas { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.explo-challenge-restant { font-size: 16px; line-height: 24px; font-weight: 600; }
/* L'horloge : chaque chiffre sur sa plaque, les deux points entre les paires. */
.explo-challenge-compte { flex: none; display: flex; align-items: center; gap: 6px; }
.explo-chiffre {
  display: grid; place-items: center;
  width: 24px; height: 28px; border-radius: 4px;
  background: #fff; color: #000;
  border: 2px solid var(--refonte-ink); box-shadow: 1px 1px 0 var(--refonte-ink);
  font-size: 18px; line-height: 24px; font-weight: 600; font-variant-numeric: tabular-nums;
}
/* Les deux points battent la seconde, la ou les chiffres ne bougent qu'a la
   minute : c'est lui qui dit que le temps court. */
.explo-chiffre-points {
  font-size: 18px; line-height: 24px; font-weight: 600; color: var(--refonte-ink);
  animation: horloge-bat 1s steps(1, end) infinite;
}
@keyframes horloge-bat { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .explo-chiffre-points { animation: none; } }
/* Le bouton de la carte suit la maquette : quarante de haut, coins de huit. */
#screen-explo .explo-challenge .btn.explo-jouer {
  margin-top: 0; min-height: 40px; height: 40px;
  border-width: 4px; border-radius: 8px; box-shadow: 0 4px 0 var(--refonte-ink);
}
#screen-explo .explo-challenge .btn.explo-jouer:active { transform: translateY(4px); box-shadow: none; }

/* --- la pochette d'import, en bout de carrousel ------------------------------ */
/* Pas d'album derriere : un aplat d'encre, un plus, un mot. Elle prend la
   meme place et les memes transformations que les vraies pochettes. */
.cover-btn.cover-import {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  background: var(--refonte-ink); color: #fff;
  font-family: var(--font-title); text-align: center; padding: 10px;
}
.cover-import-plus { font-size: 52px; line-height: 1; font-weight: 600; color: var(--refonte-jaune); }
.cover-import-mot { font-size: 17px; line-height: 20px; font-weight: 600; }
/* Le badge « Bientot », dans le dessin des etiquettes du jeu : vert, cerne
   d'encre, legerement penche. Pose en bas de la pochette, il annonce sans
   masquer le plus ni le mot. */
.cover-import-bientot {
  position: absolute; bottom: 12px; left: 50%;
  translate: -50% 0; rotate: -4deg;
  padding: 4px 12px; border-radius: 999px;
  background: var(--refonte-vert, #7ef29d); color: var(--refonte-ink);
  border: 3px solid var(--refonte-ink);
  box-shadow: 0 3px 0 var(--refonte-ink);
  font-family: var(--font-title); font-size: 13px; line-height: 16px; font-weight: 700;
}
/* Le plus et le mot remontent pour lui laisser la place. */
.cover-btn.cover-import[data-bientot] { position: relative; padding-bottom: 46px; }

/* --- le compte par courriel --------------------------------------------------- */
/* Le formulaire vit dans la feuille de connexion, sous les portes des
   fournisseurs. Il est replie tant qu'on ne le demande pas : deploye d'emblee,
   il donnerait a la feuille l'air d'un questionnaire la ou deux boutons
   suffisent a la plupart. */
/* Le courriel est une porte parmi les autres, et se dessine comme elles :
   meme fond, meme trait, logo a gauche et mot au centre. L'enveloppe la nomme
   sans qu'on ait a lire. Aucune n'est mise en avant — le joueur choisit celle
   dont il a la cle, pas celle qui crie le plus fort. */
.connexion-email { width: 100%; margin-top: -4px; }
.connexion-email[hidden] { display: none; }
/* L'ecran du compte par courriel. Chaque champ porte son nom au-dessus :
   un texte gris dans la case s'efface des qu'on tape, et il ne reste plus rien
   pour dire ce qu'on est en train de remplir. */
.champ { display: flex; flex-direction: column; align-items: stretch; gap: 6px; width: 100%; }
.champ[hidden] { display: none; }
.champ-nom {
  font-size: 16px; font-weight: 700; line-height: 20px;
  text-align: left; padding-left: 2px; color: var(--text);
}
/* La promesse se lit sous le titre, centree comme lui : alignee a gauche, elle
   faisait un bloc de texte la ou il n'y a qu'une phrase. */
#email-lede { text-align: center; }
/* Le formulaire part du haut, et le bouton descend sous le pouce — la meme
   place que les portes de l'accueil, au-dessus de la zone de securite. Le mot
   du statut le suit : ce qu'il dit concerne le geste qu'on vient de faire. */
#screen-email .board-form { justify-content: flex-start; }
#btn-email-envoyer { margin-top: auto; }
/* Clavier ouvert, le bouton se pose juste au-dessus de lui et l'y suit : on
   garde ainsi le haut de l'ecran — le titre, le champ — et l'action sous le
   pouce, sans avoir a choisir entre les deux. Position fixe : la colonne se
   deplace par `top` et non par une transformation, rien ne fausse le repere. */
html[data-keyboard] #btn-email-envoyer {
  position: fixed; z-index: 20;
  left: 50%; translate: -50% 0;
  width: min(360px, calc(100vw - 40px));
  bottom: calc(var(--clavier, 0px) + 12px);
  margin: 0;
}
#email-erreur { margin: 0; }
/* La feuille du code : le champ, le bouton, et le mot qui dit ce qui se passe.
   Meme colonne que le reste de la feuille. */
.code-form { display: flex; flex-direction: column; gap: 12px; width: min(100%, 360px); }
.code-form .btn { width: 100%; }
/* Le renvoi n'est pas l'action principale : il attend en retrait, et son
   decompte se lit sans qu'on ait a le chercher. */
.code-renvoyer { font-size: 15px; padding: 11px 16px; }
.code-renvoyer:disabled { opacity: 0.5; }
/* Le code se lit chiffre par chiffre : espace les signes et centre-les, comme
   sur les ecrans qui en demandent ailleurs. */
.email-code {
  text-align: center; letter-spacing: 0.32em; font-variant-numeric: tabular-nums;
  font-family: var(--font-title); font-size: 22px;
}
.email-code[hidden] { display: none; }
.email-statut { margin: 0; text-align: center; font-size: 14px; max-width: 34ch; margin-inline: auto; }
.email-statut[hidden] { display: none; }
.email-statut[data-error] { color: var(--ko-fill); }

/* --- la feuille d'import ---------------------------------------------------- */
.import-lien { width: min(100%, 360px); }
.feuille > .feuille-panneau > .import-lien { text-align: left; }
.import-statut { margin: 0; max-width: 34ch; text-align: center; font-size: 14px; }
.import-statut[hidden] { display: none; }
/* Reconnu : le meme vert que les badges, pour dire que ca a pris. */
.import-statut[data-ok] { color: var(--refonte-vert, #7ef29d); }

/* --- la feuille du bas ------------------------------------------------------ */
/* Elle monte du bas et s'arrete a mi-hauteur : le jeu reste visible derriere,
   le compte est une commodite et non une porte fermee. */
.feuille {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.feuille[hidden] { display: none; }
.feuille-voile {
  position: absolute; inset: 0; background: rgba(30, 17, 68, 0.62);
  animation: voile-entre 220ms ease both;
  transition: opacity 280ms ease;
}
.feuille-panneau {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 12px 20px calc(20px + env(safe-area-inset-bottom));
  background: var(--bg);
  border-top: 4px solid var(--refonte-ink);
  border-radius: 28px 28px 0 0;
  animation: feuille-monte 320ms cubic-bezier(0.22, 0.9, 0.3, 1) both;
  /* Le doigt prend la main sur le panneau : sans cela le navigateur croit a
     un defilement et ne nous envoie plus rien. */
  touch-action: none;
  /* La montee se joue sur transform, le glissement sur translate : les deux
     proprietes cohabitent, l'animation d'entree ne mange pas le geste. */
  transition: translate 280ms cubic-bezier(0.32, 0, 0.67, 0);
}
/* Pendant le geste, rien ne transitionne : le panneau colle au doigt. */
.feuille[data-glisse] .feuille-panneau, .feuille[data-glisse] .feuille-voile {
  transition: none;
}
/* Fixe, la feuille ne se tire pas : la poignee n'a plus rien a promettre. */
.feuille[data-fixe] .feuille-poignee { visibility: hidden; }
/* La poignee ne sert a rien d'autre qu'a dire que ca vient du bas. */
.feuille-poignee {
  width: 56px; height: 6px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.4); margin-bottom: 2px;
}
@keyframes voile-entre { from { opacity: 0; } to { opacity: 1; } }
@keyframes feuille-monte { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .feuille-panneau, .feuille-voile { animation: none; transition: none; }
}
.feuille .lede { max-width: 34ch; text-align: center; margin: 0; }
.feuille > .feuille-panneau > .btn { width: min(100%, 360px); }
/* Les portes se touchent presque : ce sont des variantes d'un meme geste, pas
   des choix sans rapport. Huit pixels de moins qu'avant, ici comme sous la
   derniere porte. */
.connexion-boutons { display: flex; flex-direction: column; gap: 4px; width: min(100%, 360px); }
/* Le bouton d'un fournisseur : trois colonnes. Le logo tient dans la
   premiere, le mot au centre de la seconde, et la troisieme — vide, de la
   largeur du logo — rend au mot sa symetrie. Rien n'est en absolu : le
   logo ne peut donc pas sortir du bouton. */
.connexion-porte.btn {
  display: grid; grid-template-columns: 26px 1fr 26px; align-items: center;
  gap: 14px; padding-inline: 20px; text-align: center;
}
.connexion-logo { width: 26px; height: 26px; display: block; }
/* Le panneau prend le focus a l'ouverture, sans le montrer : c'est un point
   d'entree pour le clavier, pas un bouton. */
.feuille-panneau:focus { outline: none; }
.connexion-boutons:empty { display: none; }
/* La ligne sous les deux portes de l'accueil : qui est connecte. */
.accueil-compte {
  margin: 0; text-align: center;
  font-size: 14px; line-height: 20px; font-weight: 600; color: rgba(255, 255, 255, 0.85);
}
.accueil-compte[hidden] { display: none; }
/* Jouer sans compte : un lien sous les portes, pas une porte de plus. */
.accueil-sans-compte {
  appearance: none; background: none; border: 0; cursor: pointer; font-family: inherit;
  align-self: center; padding: 8px 12px;
  font-size: 14px; line-height: 20px; font-weight: 600; color: rgba(255, 255, 255, 0.85);
  text-decoration: underline; text-underline-offset: 3px;
}
.accueil-sans-compte[hidden] { display: none; }
.accueil-sans-compte:focus-visible { outline: 3px solid #fff; outline-offset: 3px; border-radius: 8px; }

/* La pastille « Bientot » d'un bouton eteint : le vert des badges du jeu,
   penchee comme eux. Le bouton s'eteint, elle reste nette pour etre lue. */
.bientot {
  display: inline-block; margin-left: 4px; padding: 2px 8px;
  font-size: 13px; line-height: 18px; font-weight: 700; color: #000;
  background: #86f601; border: 2px solid var(--refonte-ink); border-radius: 8px;
  rotate: -4deg;
}
#btn-compte-classement:disabled { opacity: 1; cursor: default; }
#btn-compte-classement:disabled > :not(.bientot) { opacity: 0.45; }

/* --- Essai : fond « Neon » ------------------------------------------------- */
/* Gradient 01 de la page Figma « Test gradient » : un violet electrique franc,
   en diagonale, vers un bleu-violet profond. Pose en dernier pour passer devant
   les fonds unis du corps (--bg, --refonte-bg) ; retirer ce bloc rend l'ancien
   violet. */
:root { --fond-degrade: linear-gradient(165deg, #8B3DFF 0%, #5B21FF 45%, #3310D6 100%); }
html { background: #3310D6; }
html body,
html[data-refonte] body,
html[data-quiz-refonte] body { background: var(--fond-degrade); background-attachment: fixed; }
/* Le reflet des pochettes s'effacait vers un violet uni, qui dessinait un
   rectangle sur le degrade : il s'efface par un masque. Plus couteux a
   l'affichage (voir .cover-reflection) — a mesurer si l'essai est garde. */
.cover-reflection { -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), transparent 85%); mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), transparent 85%); }
.cover-reflection::after { display: none; }
/* La pause reprend le meme fond que le jeu. */
.pause.screen-dark { background: var(--fond-degrade); }
/* Le chargement s'ouvre deja sur le degrade : pas de violet uni avant le jeu. */
.splash { background: var(--fond-degrade); }
