:root {
  /* Bleu de nuit. La palette est prise dans l'icone du jeu et dans la video de
     fond, qui sont l'une et l'autre d'un bleu pur (teinte 240, saturation 100)
     accentue de jaune. L'interface parlait jusqu'ici une autre langue : rose et
     orange, deux couleurs absentes des deux.

     Le sol descend et le texte secondaire remonte, ce qui degage assez de marge
     pour eclaircir vraiment les surfaces. C'est le point qui compte : sur
     l'ancienne palette une carte se detachait de son fond a 1,07:1 — autant dire
     pas du tout des que la video bougeait derriere. Ici elle est a 1,65:1 et son
     bord a 2,96:1 contre la pire image de la boucle, sans que le texte pose
     dessus y perde quoi que ce soit (6,61:1 contre 6,26:1 avant). */
  --bg: #07051f;
  /* Les surfaces laissent passer le decor a 36 %. Elles ne se detachent plus
     seulement par la clarte mais par la matiere : ce qu'on voit a travers reste
     du decor, en plus sombre et en plus calme. La contrepartie est qu'elles
     dependent desormais de ce qui passe derriere — le voile de .backdrop::after
     borne cette variation, sans quoi une image claire de la boucle les
     delaverait. */
  --surface: rgba(51, 44, 112, 0.64);
  --surface-2: rgba(65, 58, 138, 0.64);
  --line: #6660bd;
  --text: #f2f3ff;
  --muted: #b9bde4;
  --accent: #ffd60a;
  --accent-2: #ffa32c;
  /* Toutes les surfaces d'action portent le degrade ; le texte dessus utilise
     --accent-ink, assez sombre pour rester lisible sur le jaune comme sur l'ambre. */
  --accent-grad: linear-gradient(135deg, #ffd60a 0%, #ffa32c 100%);
  --accent-ink: #0b0726;
  /* Tranche des boutons : la meme famille que l'accent, en plus sombre. */
  --accent-edge: #b88a00;
  --edge: #413a8a;
  /* Les lueurs et les pulsations ont besoin de l'accent en composantes, pas en
     couleur : sans ce triplet elles restaient ecrites en dur et ne suivaient
     aucun changement de palette. */
  --accent-rgb: 255, 214, 10;
  --accent-2-rgb: 255, 163, 44;
  /* Le rose ne disparait pas, il change de metier : il ne porte plus les
     actions, seulement ce qui est chaud — le score qui monte, la serie en
     cours. Eclairci, il tient 4,99:1 sur une carte la ou l'ancien tombait
     a 3,59:1. */
  --hot: #ff7ab0;
  --ok: #3ee08f;
  --ko: #ff8095;
  --radius: 14px;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Bricolage Grotesque pour les titres : ses lettres serrees et son axe
     variable donnent du caractere la ou la police systeme reste neutre. */
  --font-title: "Bricolage Grotesque", var(--font);
  /* Pixelify Sans ne sert qu'au nom du jeu sur l'ecran d'accueil : c'est une
     signature, pas une police de texte. */
  --font-pixel: "Pixelify Sans", var(--font-title);
}


* { 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: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -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));
}

.app { width: 100%; max-width: 480px; position: relative; z-index: 1; }
.screen { display: flex; flex-direction: column; gap: 16px; }
.screen[hidden] { display: none; }

.eyebrow {
  font-size: 12px; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--accent);
}
h1 {
  font-family: var(--font-title);
  font-size: 32px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05;
  /* 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 {
  font-family: var(--font-pixel);
  font-size: 24px; line-height: 32px;
  letter-spacing: 0.08em;
}
.lede { color: var(--muted); font-size: 15px; line-height: 1.55; }
/* 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: 1px 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.01em;
}
.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.03em;
  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: 1px solid var(--line);
  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 {
  position: absolute; z-index: 10; top: calc(100% + 6px); left: 0; right: 0;
  list-style: none; max-height: 340px; overflow-y: auto;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 5px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.65);
}
.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(--line); }
.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: 1px 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: 15px; font-weight: 700; padding: 14px 18px;
  border-radius: var(--radius); border: 1px solid transparent;
  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: transparent; color: var(--muted); border-color: var(--line); font-weight: 600; }
.btn-sm { font-size: 13px; padding: 7px 12px; }

/* --- revelation --- */
.reveal {
  display: grid; grid-template-columns: 64px 1fr auto; gap: 12px 14px; align-items: center;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px;
}
.reveal[hidden] { display: none; }
.verdict { grid-column: 1 / -1; font-size: 14px; font-weight: 700; }
.verdict[data-result="ok"] { color: var(--ok); }
.verdict[data-result="ko"] { color: var(--ko); }
.reveal-cover { width: 64px; height: 64px; border-radius: 8px; object-fit: cover; background: var(--surface-2); }
.reveal-title { font-size: 16px; font-weight: 700; line-height: 1.25; }
.reveal-artist { font-size: 14px; color: var(--muted); margin-top: 2px; }
.reveal .btn { grid-column: 1 / -1; }

/* --- 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: 1px 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(--line); color: var(--muted); 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: 1px 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: 1px 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(--accent); 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 : il n'y en a que deux et c'est la
   seule decision de l'ecran. Ailleurs — les fiches d'information des
   autorisations — elles restent un bloc serre. */
#step-mode .modes { gap: 24px; }
.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: 1px 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.01em; }
.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(--surface-2); border: 1px solid var(--line);
  color: var(--accent);
  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: 1px 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(--hot); }
@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 --- */
.points { font-family: var(--font-title);
  font-size: 40px; font-weight: 800; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums;
  background: var(--accent-grad);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: points-pop 420ms cubic-bezier(0.2, 1.5, 0.4, 1);
}
.points-bonus { font-size: 16px; font-weight: 700; margin-left: 8px; }
@keyframes points-pop {
  0%   { transform: scale(0.4); opacity: 0; }
  60%  { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* --- 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; }
.step-enter { animation: step-in 260ms cubic-bezier(0.22, 0.9, 0.3, 1); }
.step-enter-back { animation: step-in-back 260ms 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: 1px solid var(--line); color: var(--text);
}
.step-back:hover { border-color: var(--accent); }
.step-back[hidden] { display: none; }
.step-dots { list-style: none; display: flex; gap: 6px; }
.step-dot {
  width: 22px; height: 4px; border-radius: 2px; background: var(--line);
  transition: background 200ms ease, width 200ms ease;
}
.step-dot[data-state="done"] { background: var(--muted); }
.step-dot[data-state="current"] { background: var(--accent); width: 34px; }

/* Sur desktop, le chapelet devient un rail. Trois choses le rendent lisible de
   loin la ou cinq tirets de 22 px se perdaient en haut d'un grand ecran :

   - une largeur totale fixe, ou les segments s'etirent pour remplir. Le solo
     compte cinq etapes et le Battle six : a segments fixes, la barre changerait
     de longueur selon le mode, ce qui se voit. La, les deux parcours occupent
     le meme rail.
   - deux fois la hauteur, et des bouts ronds.
   - le jaune derriere soi. C'est ce qui change tout : le rail se remplit au
     lieu de promener un curseur, et la tete de progression garde son halo.

   Le tout tient dans la bande de 40 px qui partage son axe avec le bouton de
   retour — l'alignement des deux ne bouge pas. Au doigt, rien ne change : la
   largeur y manque, et le chapelet y est deja a l'echelle. */
@media (hover: hover) and (pointer: fine) {
  .step-dots { width: min(480px, 60vw); gap: 8px; }
  /* flex-basis a 0 : la largeur de 34 px de l'etape courante ne s'applique
     plus, tous les segments pesent pareil. */
  .step-dot {
    flex: 1; height: 8px; border-radius: 999px;
    transition: background 220ms ease, box-shadow 220ms ease;
  }
  /* Le jaune franchi est pose sur le fond, pas melange a lui : sous 0,7 il
     tourne a l'olive au lieu de rester de l'or eteint. */
  .step-dot[data-state="done"] { background: rgba(var(--accent-rgb), 0.75); }
  .step-dot[data-state="current"] {
    background: var(--accent);
    box-shadow: 0 0 12px rgba(var(--accent-rgb), 0.45);
  }
}

/* --- 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: 14px; overflow: hidden;
  box-shadow: 0 26px 52px rgba(0, 0, 0, 0.68);
  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(7, 5, 31, 0.45), 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.01em;
  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 --- */
/* --- curseur de duree (salle Battle) --- */
/* L'hote y regle la partie pour toute la table, sur une course continue : le
   pas a pas de l'onboarding ne s'y est pas encore installe. */
.slider { position: relative; padding: 6px 0 22px; }
.slider-input {
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 6px; border-radius: 3px; background: var(--surface-2);
  border: 1px solid var(--line); outline: none; cursor: pointer; display: block;
}
.slider-input::-webkit-slider-thumb {
  -webkit-appearance: none; width: 30px; height: 30px; border-radius: 50%;
  background: var(--accent-2); border: 3px solid var(--bg); cursor: grab;
  box-shadow: 0 3px 14px rgba(var(--accent-2-rgb), 0.45);
}
.slider-input::-moz-range-thumb {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent); border: 3px solid var(--bg); cursor: grab;
}
.slider-ticks {
  position: absolute; left: 0; right: 0; top: 26px;
  display: flex; justify-content: space-between; pointer-events: none;
}
.slider-tick { font-size: 11.5px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
.slider-tick[data-on] { color: var(--accent); }
.slider-value { font-family: var(--font-title);
  font-size: 34px; font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* --- 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: 1px 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.02em; 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: 22px;
  background: var(--accent-grad); color: var(--accent-ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  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.02em; }
.big-start-sub { font-size: 13px; font-weight: 600; opacity: 0.65; }

/* ===== 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: 7; stroke-linecap: round;
}
.hold-track { stroke: var(--surface-2); }
/* 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%, #16123c 0%, #0b0828 58%, #06041e 100%);
  border: 1px solid var(--line);
  transition: border-color 140ms ease;
}
/* Le disque tourne : sa tranche s'allume. */
.hold[data-spinning]::before { border-color: var(--accent); }

/* 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 1px var(--accent-edge);
  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(--accent);
  /* 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.03em; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.hold-points {
  font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums;
  background: var(--accent-grad); -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.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;
}
.step-back { 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: 1px 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. */
/* --- 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));
}
.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: 1px 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 1px rgba(255, 255, 255, 0.22);
}
.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-2); border: 1px solid var(--line);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
.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: rgba(255, 255, 255, 0.08); }
}

/* La pause reprend le fondu au noir du decompte : c'est deja ce que le jeu fait
   quand il demande a la table de tout arreter et de regarder l'ecran. */
.pause { z-index: 40; }
.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.02em; 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: 1px 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(--muted); min-width: 38px; text-align: right;
  transition: color 200ms ease;
}
.timer-track {
  flex: 1; height: 5px; border-radius: 3px;
  background: var(--surface-2); 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); }

/* --- 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 {
  box-shadow: 0 0 0 4px var(--ok), 0 26px 52px rgba(0, 0, 0, 0.68);
}
.cover-card[data-result="ko"] .cover-btn {
  box-shadow: 0 0 0 4px var(--ko), 0 26px 52px rgba(0, 0, 0, 0.68);
}
.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); }

/* --- lecture du morceau, dans la carte de revelation --- */
.reveal-text { min-width: 0; }
.play-mini {
  appearance: none; border: none; padding: 0; cursor: pointer; background: none;
  position: relative; width: 46px; height: 46px; flex: 0 0 auto;
  display: grid; place-items: center;
  color: var(--accent);
  transition: transform 90ms ease, color 160ms ease;
}
.play-mini:active { transform: scale(0.93); }
.play-mini[data-playing] { color: var(--accent-2); }

.play-mini-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.play-mini-track, .play-mini-arc { fill: none; stroke-width: 3; stroke-linecap: round; }
.play-mini-track { stroke: var(--line); }
.play-mini-arc {
  stroke: currentColor;
  /* 2 x pi x 19 : le trace complet du cercle. */
  stroke-dasharray: 119;
  stroke-dashoffset: 119;
}
.play-mini-glyph { width: 20px; height: 20px; }
.play-mini .icon-pause { display: none; }
.play-mini[data-playing] .icon-play { display: none; }
.play-mini[data-playing] .icon-pause { display: block; }

/* --- 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: 1px 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;
}
.fmt-slide[data-current] { transform: none; opacity: 1; border-color: var(--accent); }
.fmt-slide:active { transform: scale(0.94) translateY(4px); box-shadow: 0 1px 0 var(--edge); }
.fmt-slide[data-current]:active { transform: translateY(4px); }
.fmt-slide[data-off][data-current] { border-color: var(--line); }
/* Un mode en retrait ne se clique pas : pas d'epaisseur, pas d'enfoncement. */
.fmt-slide[data-off], .fmt-slide[data-off]:active { box-shadow: none; }
.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: 10px;
  background: radial-gradient(80% 80% at 50% 30%, var(--surface-2), rgba(7, 5, 31, 0.9));
}
.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(--accent);
}
.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: 1px 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(--accent); 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%, #16123c 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(--accent); transform: scale(0.96); }
}
.fmt-champ {
  width: 68%; height: 22px; border-radius: 7px;
  background: var(--surface-2); border: 1px 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(--accent);
  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: 1px 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(--accent-rgb), 0.35); }
  60% { box-shadow: 0 0 0 14px rgba(var(--accent-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.01em; 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(--line); transition: background 160ms ease, width 160ms ease;
}
.fmt-dot[data-on] { background: var(--accent); width: 20px; border-radius: 4px; }

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

#screen-board { align-items: center; text-align: center; }
#screen-board > *, #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; }
.board-emojis { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.board-emoji {
  appearance: none; cursor: pointer; font-size: 22px; line-height: 1;
  width: 44px; height: 44px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line);
}
.board-emoji[data-on] { border-color: var(--accent); background: var(--surface-2); }

.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: 1px 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: 1px 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: 2px solid var(--line);
  position: relative;
}
.podium-place[data-rang='1'] .podium-avatar {
  width: 82px; height: 82px; font-size: 40px;
  border-color: var(--accent);
}
/* 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 var(--line);
}
.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: 1px 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: 44px; letter-spacing: 0.22em; text-indent: 0.22em;
  background: var(--accent-grad); -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

.lobby-share {
  display: flex; gap: 16px; align-items: center; text-align: left;
  padding: 14px; border-radius: var(--radius);
  background: var(--surface); border: 1px 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: 10px; padding: 6px;
  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: 1px 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); }
.lobby-settings[hidden] { display: none; }

/* --- 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; }
#spin-eyebrow[data-tie] { color: var(--accent-2); }

/* --- 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: #040318;
  animation: dark-in 420ms ease both;
}
.screen-dark[hidden] { display: none; }
@keyframes dark-in { from { opacity: 0; } to { opacity: 1; } }

.countdown-number {
  font-family: var(--font-title);
  font-size: 92px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
  background: var(--accent-grad); -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
/* 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: 1px 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; }
.waiting-verdict { font-family: var(--font-title); font-size: 26px; font-weight: 800; }
.waiting-verdict[data-result="ok"] { color: var(--ok); }
.waiting-verdict[data-result="ko"] { color: var(--ko); }
.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: 1px 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: 1px 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: 1px 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(--accent); }
/* 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: 10px; }
.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: 1px solid var(--line); color: var(--text);
  font: inherit; font-size: 16px; font-weight: 600;
  /* 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(2px); }
.quiz-choice:disabled { cursor: default; }

/* 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: 1px 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(--ok); }
.quiz-choice[data-state="ok"] .quiz-rank { color: var(--ok); border-color: var(--ok); }
.quiz-choice[data-state="ko"] { border-color: var(--ko); opacity: 0.75; }
.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: 1px 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 0 rgba(var(--accent-rgb), 0.34); }
  50% { box-shadow: 0 0 0 10px rgba(var(--accent-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: 10.5px; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; line-height: 1;
  padding: 4px 8px; border-radius: 999px;
  background: var(--surface-2); border: 1px 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: 96px; height: 96px;
  border-radius: 32px;
  display: block;
  /* Elle se pose sur le mur de pochettes : sans ombre ni liseré, elle s'y
     fondrait au lieu de s'en detacher. */
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  outline: 1px solid rgba(255, 255, 255, 0.08);
  outline-offset: -1px;
}

/* --- 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: block; animation: backdrop-fade 600ms ease both; }
@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; }
}
