/* Défi — feuille de style unique (§10.2).

   ── DEUX THÈMES (Lot 11, 07/08/2026) ────────────────────────────────────
   L'identité `Defi3.css` d'origine (noir pur, #00ff00, monospace système) a
   tenu du Lot 0 au Lot 10. Elle est REMPLACÉE : les bêta-testeurs ont
   exprimé une méfiance envers l'apparence « terminal / hack ». L'identité
   verte n'est pas abandonnée, elle est adoucie — et doublée d'une
   alternative claire pour ceux qu'elle intimide.

   - sombre « Terminal rond » : fond quasi noir, vert vif, monospace,
     capitales, angles arrondis, contrastes corrigés ;
   - clair « Clair & rond » : fond papier chaud, un seul vert, minuscules,
     sans-serif ronde.

   UN SEUL DOM, deux jeux de variables. RÈGLE ABSOLUE : toute couleur du
   fichier vient du bloc de tokens ci-dessous — aucune valeur en dur
   ailleurs, y compris dans les media queries (gardé par
   client/test/theme.test.mjs). Le thème est posé sur
   <html data-theme="dark|light"> par le script inline de index.html AVANT
   le premier paint ; `light` est le défaut produit (§10.2).

   ── MISE EN PAGE MOBILE-FIRST (sprints design 1 et 2, conservée) ────────
   Par défaut, plein écran mobile (100dvh + safe areas, typo fluide en
   clamp()) ; à partir de 700 px de large ET 500 px de haut, la colonne
   centrée redevient le layout (un téléphone en paysage reste en mobile).
   L'écran Jeu occupe 100dvh sans défilement : bandeau compteurs en haut,
   question au tiers supérieur, grille 2×2 dans la moitié basse (zone du
   pouce), PASSER puis FIN tout en bas au-dessus de la safe area.
   Accueil / Attente / Résultat occupent la hauteur sur mobile (héros
   centrés, actions dans la zone du pouce) — ces règles vivent dans une
   media query COMPLÉMENT EXACT du breakpoint desktop (rien à
   réinitialiser) ; fondu court entre écrans (jamais pendant le jeu),
   pull-to-refresh bloqué en partie. */

/* ── Polices AUTO-HÉBERGÉES (§10.3) ──────────────────────────────────────
   Une PWA hors ligne ne peut pas dépendre d'un CDN de polices, et un repli
   système changerait la casse et le rythme typographique des deux thèmes.
   Sous-ensembles latin + latin-ext seulement — les 5 langues (fr/en/es/de/it)
   n'ont besoin de rien d'autre ; 8 fichiers, 129 Ko au total, tous dans le
   SHELL du service worker.
   Outfit est VARIABLE (400..700 en un fichier), IBM Plex Mono est statique
   (3 graisses). font-display: swap — le texte s'affiche tout de suite dans la
   police système en attendant, jamais d'écran vide.
   Licences : SIL Open Font License 1.1 pour les deux familles (voir
   client/fonts/LICENCES.md). */

/* Outfit — latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/outfit-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Outfit — latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/outfit-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono — latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono — latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono — latin-ext */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/plex-mono-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — latin */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/plex-mono-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── Tokens indépendants du thème ────────────────────────────────────── */

:root {
  --r-chip: 999px;
}

/* Thème CLAIR — également le repli de `:root` nu : si le script inline ne
   s'exécute pas (JS coupé), l'application s'affiche en clair, qui est le
   défaut produit. Aucune couleur ne doit avoir sa seule définition dans le
   bloc sombre. */

:root,
:root[data-theme='light'] {
  --bg: #f7f6f2;
  --surface: #ffffff;
  /* Le handoff donnait #0a8a3c : blanc dessus = 4,46:1, sous le seuil AA de
     4,5 pour du texte de moins de 24 px — et l'accent lui-même ne faisait que
     4,12:1 sur le fond. Assombri d'un cran (5,05:1 sur blanc, 4,67:1 sur le
     fond), écart visuellement imperceptible. Mesuré, pas jugé à l'œil :
     serveur/scripts/verif-themes-lot11.js calcule chaque couple. */
  --accent: #098038;
  --on-accent: #ffffff;
  --text-strong: #21241f;
  --text: #21241f;
  --text-dim: #4c5049;
  /* Idem : le #8a8f88 du handoff ne donnait que 3,05:1 sur le fond clair. Il
     portait les liens discrets, les légendes et tout le HUD du jeu — soit
     exactement le défaut (T1) que cette refonte devait corriger, reproduit
     par la palette censée le corriger. Assombri à 4,68:1, toujours nettement
     en retrait de --text. */
  --muted: #6b706a;
  --border: #e3e1da;
  --border-strong: #d5d2c8;
  --danger: #b3261e;

  /* Fond des surfaces surélevées (boutons secondaires, tuiles de réponse) :
     une carte blanche en clair, RIEN en sombre (la bordure suffit, et un
     aplat gris sur fond noir salirait le contraste). */
  --raised: #ffffff;
  /* Sous-titre du logo : discret en clair, signature verte en sombre. */
  --subtitle: #6b706a;

  --r-btn: 14px;
  --r-tile: 16px;
  --r-row: 10px;

  --font: 'Outfit', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --btn-transform: none;
  --btn-tracking: 0;
  --titre-tracking: -0.01em;
}

:root[data-theme='dark'] {
  --bg: #0b0e0c;
  --surface: #121a14;
  --accent: #00e050;
  --on-accent: #04140a;
  --text-strong: #ffffff;
  --text: #d9ffe4;
  --text-dim: #9fd8ae;
  --muted: #6fae7f;
  --border: #2c6b3f;
  --border-strong: #00e050;
  --danger: #ff5544;

  --raised: transparent;
  --subtitle: #00e050;

  --r-btn: 16px;
  --r-tile: 18px;
  --r-row: 12px;

  --font: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;
  --btn-transform: uppercase;
  --btn-tracking: 0.22em;
  --titre-tracking: 0.32em;
}

* {
  box-sizing: border-box;
}

/* Rien ne doit jamais dépasser le viewport. */
img,
svg,
video,
input,
select,
button,
textarea {
  max-width: 100%;
}

/* Un fieldset refuse par défaut de rétrécir sous la largeur de son contenu
   (min-inline-size: min-content) : cause du débordement de l'écran Options. */
fieldset {
  min-inline-size: 0;
}

/* L'attribut hidden doit gagner même sur les display: posés par le layout. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  min-height: 100vh; /* repli si dvh non supporté */
  min-height: 100dvh; /* hauteur réelle avec les barres de navigateur mobiles */
  display: flex;
  justify-content: center;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  /* Transition douce au changement de thème. Elle vit sur le BODY seulement :
     jamais sur .cellule ni sur les états de flash, dont la durée est de
     100 ms — une transition de 150 ms les avalerait (§10.2). */
  transition: background-color 150ms ease-out, color 150ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  body {
    transition: none;
  }
}

/* Pull-to-refresh Android bloqué pendant la partie et la salle d'attente :
   un rafraîchissement accidentel coûterait une reconnexion (§7) — la
   fenêtre de 30 s rattraperait, mais autant prévenir. Chrome lit la valeur
   sur l'élément racine, d'anciennes versions sur body : les deux sont
   couverts. */
html:has(#ecran-jeu.actif),
html:has(#ecran-attente.actif),
body:has(#ecran-jeu.actif),
body:has(#ecran-attente.actif) {
  overscroll-behavior-y: none;
}

.terminal {
  width: min(40rem, 100%);
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  /* Safe areas (encoches) : nécessite viewport-fit=cover dans le HTML. */
  padding: max(1.875rem, env(safe-area-inset-top))
    max(1.625rem, env(safe-area-inset-right))
    max(0.75rem, env(safe-area-inset-bottom))
    max(1.625rem, env(safe-area-inset-left));
}

h1 {
  margin: 0;
  color: var(--text-strong);
  font-size: clamp(1.875rem, 9vw, 2.5rem);
  font-weight: 700;
  letter-spacing: var(--titre-tracking);
  text-transform: var(--btn-transform);
}

h2 {
  margin: 0 0 1rem;
  color: var(--text-strong);
  font-size: clamp(1.4rem, 6vw, 1.75rem);
  font-weight: 600;
  letter-spacing: var(--btn-tracking);
  text-transform: var(--btn-transform);
}

.sous-titre {
  margin-top: 0.25rem;
  color: var(--subtitle);
}

/* Curseur clignotant : signature terminal, donc SOMBRE UNIQUEMENT — en
   thème clair il contredirait la promesse « ça ne ressemble pas à une
   console ». */
.curseur::after {
  content: '';
}

:root[data-theme='dark'] .curseur::after {
  content: '▮';
  animation: clignote 1s steps(1) infinite;
}

@keyframes clignote {
  50% {
    opacity: 0;
  }
}

/* ── Navigation par sections (§10.1) ─────────────────────────────────── */

.ecran {
  display: none;
}

.ecran.actif {
  display: block;
  flex: 1 0 auto; /* l'écran actif occupe toute la hauteur du terminal */
  /* Fondu court à l'arrivée sur un écran — sobre, et JAMAIS pendant le jeu
     lui-même (voir #ecran-jeu.actif) : la réactivité prime. */
  animation: ecran-fondu 150ms ease-out;
}

#ecran-jeu.actif {
  animation: none;
}

@keyframes ecran-fondu {
  from {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ecran.actif {
    animation: none;
  }
}

/* ── Boutons : système à TROIS niveaux (§10.2) ───────────────────────────
   1. .bouton-primaire — fond plein --accent, UNE SEULE par écran ;
   2. .bouton          — secondaire : bordure --border-strong ;
   3. .lien-discret    — sorties et actions destructives : texte seul.
   `.bouton` reste le niveau par défaut : c'est ce que portent les dix
   écrans non refondus par ce lot (11.2), qui doivent rester cohérents. */

.menu {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  margin: 1.5rem 0 0;
}

.bouton {
  display: block;
  width: 100%;
  min-width: 0; /* autorise le rétrécissement dans une grille */
  min-height: 3rem; /* >= 48 px */
  padding: 0.75rem 1rem;
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-btn);
  font: inherit;
  font-size: 1rem;
  font-weight: 500;
  text-transform: var(--btn-transform);
  letter-spacing: var(--btn-tracking);
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  transition: transform 100ms ease-out;
}

.bouton-primaire {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 600;
  padding: 1.1875rem 1rem;
}

/* Feedback pressé : un enfoncement bref, préféré à un changement de couleur
   qui entrerait en concurrence avec la sémantique juste/faux. */
.bouton:active:not(:disabled) {
  transform: scale(0.98);
}

.bouton:hover:not(:disabled),
.bouton:focus-visible {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  outline: none;
}

.bouton:disabled {
  background: var(--raised);
  color: var(--muted);
  border-color: var(--border);
  cursor: default;
}

/* Bouton secondaire ATTÉNUÉ (ex. PASSER en jeu) : présent mais en retrait
   d'une action de jeu fréquente mais non souhaitable. */
.bouton-secondaire {
  color: var(--text-dim);
  border-color: var(--border);
}

/* Niveau 3 — lien discret : sorties (FIN, Accueil, Quitter). Bouton réel
   pour l'accessibilité, apparence de lien. */
.lien-discret {
  display: block;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem;
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.9375rem;
  text-align: center;
  text-decoration: underline;
  text-underline-offset: 4px;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
}

.lien-discret:hover:not(:disabled),
.lien-discret:focus-visible {
  color: var(--text-strong);
  outline: none;
}

.lien-discret:disabled {
  color: var(--border);
  cursor: default;
}

/* Confirmation armée (FIN, deux temps §10.1) : la couleur d'alerte porte le
   changement d'état, quel que soit le niveau du bouton. */
.bouton-danger {
  color: var(--danger);
  border-color: var(--danger);
}

.bouton-danger:hover:not(:disabled),
.bouton-danger:focus-visible {
  background: var(--danger);
  color: var(--on-accent);
  border-color: var(--danger);
}

.lien-discret.bouton-danger {
  color: var(--danger);
  font-weight: 600;
}

.lien-discret.bouton-danger:hover:not(:disabled),
.lien-discret.bouton-danger:focus-visible {
  background: none;
  color: var(--danger);
}

small {
  color: var(--muted);
  text-transform: none;
  letter-spacing: normal;
}

/* ── Options ─────────────────────────────────────────────────────────── */

.bloc-options {
  border: 1px solid var(--border);
  border-radius: var(--r-tile);
  padding: 0.75rem 1rem;
  margin: 0 0 1rem;
}

.bloc-options legend {
  color: var(--muted);
  padding: 0 0.5rem;
  text-transform: var(--btn-transform);
  letter-spacing: var(--btn-tracking);
  font-size: 0.8125rem;
}

.choix {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 3rem; /* zone tactile >= 48 px */
  padding: 0.25rem 0;
  cursor: pointer;
}

.choix input {
  accent-color: var(--accent);
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
  flex: none;
}

.choix-desactive {
  color: var(--muted);
  cursor: default;
}

.selecteur {
  width: 100%;
  min-height: 3rem;
  padding: 0.5rem;
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-btn);
  font: inherit;
}

/* Focus visible au CLAVIER (desktop) sur les champs et radios ; les boutons
   et cellules ont déjà leur inversion / contour sur :focus-visible. Au
   tactile, aucun halo : le focus « simple » reste net. */
.selecteur:focus-visible,
.choix input:focus-visible,
.lien-discret:focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 2px;
}

.bouton:focus:not(:focus-visible),
.cellule:focus:not(:focus-visible),
.selecteur:focus:not(:focus-visible),
.lien-discret:focus:not(:focus-visible),
.choix input:focus:not(:focus-visible) {
  outline: none;
}

/* ── Compte (§9.1) ───────────────────────────────────────────────────── */

.champ {
  display: block;
  margin: 0 0 0.75rem;
}

.champ span {
  display: block;
  margin: 0 0 0.25rem;
  color: var(--muted);
}

.champ input {
  width: 100%;
  min-height: 3rem;
  padding: 0.5rem 0.75rem;
  background: var(--raised);
  color: var(--text-strong);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-btn);
  font: inherit;
}

.champ input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Accueil : accès au compte — deux LIENS DISCRETS côte à côte (niveau 3),
   au-dessus du héros. Ils ne doivent jamais concurrencer le bouton de jeu
   principal ; l'allemand « Konto erstellen » doit tenir en demi-largeur à
   390 px. */
.accueil-compte {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.accueil-compte .lien-discret {
  font-size: 0.875rem;
  /* Discrets par la couleur et la taille, JAMAIS par la cible tactile : le
     plancher de 44 px reste le plancher (mesuré à 40 px avant correction). */
  min-height: 2.75rem;
}

/* Le pseudo du compte connecté (accueil), tappable : ouvre l'écran Compte
   (déconnexion) — remplace la rangée, jamais de bouton mort. */
.lien-pseudo {
  display: block;
  width: 100%;
  min-height: 3rem;
  margin-top: 0.25rem;
  padding: 0.5rem 0;
  background: none;
  border: none;
  color: var(--text-dim);
  font: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
}

.lien-pseudo strong {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 4px;
}

.lien-pseudo:hover strong {
  color: var(--text-strong);
}

.lien-pseudo:focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 2px;
}

.lien-pseudo:focus:not(:focus-visible) {
  outline: none;
}

/* ── Statistiques (§10.1) ────────────────────────────────────────────── */

/* Sujet et période : empilés pleine largeur sur mobile (aérés), côte à
   côte dès 500 px — le desktop retrouve la paire d'origine. */
.stats-selecteurs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.25rem;
  margin: 0 0 1rem;
}

/* Cadre de défilement des tableaux larges (statistiques, bilan Apprendre).
   La RÈGLE du projet est « rien ne dépasse jamais le viewport » : elle porte
   sur la PAGE. Un tableau à 5 colonnes en allemand, à 130 % de police et
   360 px de large, ne rentre pas (426 px mesurés) — il défile donc dans son
   propre cadre plutôt que d'entraîner l'écran entier. */
.table-defilante {
  max-width: 100%;
  overflow-x: auto;
  margin: 0 0 1rem;
  /* Le défilement horizontal du tableau ne doit pas se propager à la page. */
  overscroll-behavior-x: contain;
}

.table-defilante > table {
  margin-bottom: 0;
}

.stats-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 1rem;
}

/* Cinq colonnes sur un petit écran : compact par défaut (l'allemand
   « Trefferquote » doit tenir sur 390 px), aéré à partir de 500 px. */
.stats-table th,
.stats-table td {
  padding: 0.35rem 0.25rem;
  text-align: right;
  border-bottom: 1px solid var(--border);
}

.stats-table th {
  color: var(--muted);
  font-weight: normal;
  text-transform: var(--btn-transform);
  font-size: 0.7rem;
  letter-spacing: 0;
}

.stats-table td {
  font-size: 0.9rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.stats-table th:first-child,
.stats-table td:first-child {
  text-align: left;
  color: var(--text-strong);
}

/* ── Jeu ─────────────────────────────────────────────────────────────── */

/* L'écran Jeu est une colonne flex plein écran : bandeau en haut, la
   question absorbe l'espace libre (elle se retrouve au tiers supérieur),
   la grille des réponses est ancrée dans la moitié basse, les actions
   tout en bas. Sur desktop, retour au flux en colonne (media query). */
#ecran-jeu.actif {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.jeu-entete {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* Le chrono est une valeur qui change chaque seconde : chiffres à
   chasse fixe, sinon il danse (l'Outfit du thème clair est proportionnel). */
#jeu-chrono {
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

.jeu-compteurs {
  display: flex;
  justify-content: space-between;
  gap: 0.25rem 1rem;
  flex-wrap: wrap;
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.jeu-compteurs .ok {
  color: var(--accent);
}

.jeu-compteurs .ko {
  color: var(--danger);
}

.question {
  flex: 1 1 auto; /* absorbe l'espace libre entre bandeau et grille */
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.5rem 0;
  min-height: 3.5rem;
  color: var(--text-strong);
  /* Le facteur 14vw est CONSERVÉ tel quel des sprints design 1-2 : il est
     validé à 360×640 et à 130 % de police, y compris sur les questions les
     plus longues d'Apprendre (« ? − 15 = 120 »), qui passent alors sur deux
     lignes. Seul le plafond descend de 6 à 4,25 rem (68 px, valeur du
     handoff). Surtout PAS de white-space: nowrap ici — l'écran Jeu garantit
     l'absence de défilement horizontal, et une question à trou déborderait. */
  font-size: clamp(3rem, 14vw, 4.25rem);
  font-weight: 700;
  text-align: center;
}

.propositions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr; /* deux rangées égales */
  gap: 0.875rem;
  /* Moitié basse de l'écran (zone du pouce) : ~20vh minimum par cellule. */
  min-height: 40vh;
  min-height: 40dvh;
}

/* Cellules de réponse larges, états selected / right / wrong (§10.2).
   AUCUNE transition ici : le flash de correction dure 100 ms. */
.cellule {
  min-width: 0;
  min-height: 3rem; /* garde >= 48 px même si l'écran est très court */
  background: var(--raised);
  color: var(--text-strong);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-tile);
  font: inherit;
  font-size: clamp(1.8rem, 9vw, 2.625rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
}

.cellule:focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 2px;
}

.cellule.selected {
  background: var(--text-strong);
  color: var(--bg);
  border-color: var(--text-strong);
}

.cellule.right {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}

.cellule.wrong {
  color: var(--danger);
  border-color: var(--danger);
}

/* PASSER puis FIN, empilés et SÉPARÉS : FIN est une sortie (niveau 3), elle
   ne doit jamais se confondre avec l'action de jeu qui la précède. La
   confirmation en deux temps (§10.1) reste le vrai garde-fou. */
.jeu-actions {
  display: flex;
  flex-direction: column;
  margin-top: 1.375rem;
}

.jeu-actions .lien-discret {
  margin-top: 1.1875rem;
}

/* Sur mobile (flex), les actions passent SOUS la note d'état : PASSER et
   FIN restent tout en bas, au-dessus de la safe area. Sans effet sur le
   flux desktop (order est ignoré hors flex). */
#ecran-jeu #jeu-etat {
  order: 1;
  margin: 0.5rem 0 0;
}

#ecran-jeu .jeu-actions {
  order: 2;
}

/* ── Attente (concours) ──────────────────────────────────────────────── */

.attente-compte {
  margin: 1.5rem 0;
  text-align: center;
  color: var(--text-strong);
  font-size: clamp(2.5rem, 12vw, 4rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
}

.attente-compte span:last-child {
  color: var(--muted);
  font-size: 0.9375rem;
  font-weight: 400;
  letter-spacing: normal;
}

/* ── Entre amis (§2.3, §10.1) ────────────────────────────────────────── */

/* Le code de la session privée : héros absolu de l'écran, dans une carte —
   c'est le nombre qu'on dicte aux copains, il ne doit pas se confondre avec
   le compte à rebours voisin. */
.code-grand {
  margin: 1rem 0 0.25rem;
  padding: 1rem 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-tile);
  text-align: center;
  color: var(--text-strong);
  font-size: clamp(2.5rem, 12vw, 4rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.35em;
  text-indent: 0.35em; /* recentre malgré l'espacement du dernier chiffre */
}

.note.centre {
  text-align: center;
}

/* Saisie du code : grands chiffres espacés, centrés (clavier numérique via
   inputmode/pattern dans le HTML). */
.champ-code {
  font-size: 2rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.35em;
  text-align: center;
}

/* Liste des inscrits d'une session privée (écran Attente) : MÊME langage
   visuel que le classement final — lignes-cartes, ligne du joueur en plein
   accent. */
.inscrits {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  margin: 0 0 1rem;
  padding: 0;
}

.inscrits li {
  padding: 0.5rem 0.75rem;
  background: var(--surface);
  border-radius: var(--r-row);
  color: var(--text);
}

.inscrits li.moi {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

/* ── Classement (concours, §4.4) ─────────────────────────────────────── */

/* Classement live (écran Jeu, §10.1) : deux lignes de HAUTEUR FIXE — le rang
   personnel et la ligne de poursuite (leader ou poursuivant). Hauteur réservée
   constante même ligne vide, une seule ligne sans retour : les valeurs qui
   changent de longueur ne décalent jamais le reste de l'écran. */
.classement-live {
  margin: 0.5rem 0;
  border: 1px solid var(--border);
  border-radius: var(--r-row);
  padding: 0.5rem 0.75rem;
}

.classement-rang {
  margin: 0 0 0.25rem;
  height: 1.4rem;
  line-height: 1.4rem;
  white-space: nowrap;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.8125rem;
}

.classement-rang strong {
  color: var(--text-strong);
  font-weight: 600;
}

.classement-poursuite {
  margin: 0;
  height: 1.4rem;
  line-height: 1.4rem;
  white-space: nowrap;
  overflow: hidden;
  color: var(--text-dim);
  font-size: 0.8125rem;
}

.classement-bloc {
  margin: 0 0 1rem;
}

.classement-bloc h3 {
  margin: 0 0 0.5rem;
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 500;
  text-transform: var(--btn-transform);
  letter-spacing: var(--btn-tracking);
}

.classement {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  margin: 0;
  padding: 0;
}

.classement-ligne {
  display: grid;
  grid-template-columns: 1.75rem 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.625rem 0.75rem;
  background: var(--surface);
  border-radius: var(--r-row);
}

.classement-ligne .cl-rang {
  color: var(--muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.classement-ligne .cl-pseudo {
  color: var(--text);
  overflow-wrap: anywhere;
}

.classement-ligne .cl-points {
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

/* La ligne du joueur, mise en évidence (§10) : plein accent. L'ancienne
   version posait du texte vert sur un fond vert sombre — ~2,6:1, le seul
   vrai défaut de contraste de l'application, corrigé ici. */
.classement-ligne.moi {
  background: var(--accent);
}

.classement-ligne.moi .cl-pseudo,
.classement-ligne.moi .cl-rang,
.classement-ligne.moi .cl-points {
  color: var(--on-accent);
  font-weight: 700;
}

/* ── Résultat ────────────────────────────────────────────────────────── */

.recap {
  margin: 0 0 1rem;
}

.recap div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.recap dt {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.9375rem;
}

.recap dd {
  margin: 0;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

/* ── Apprendre (§2.4, §10.1) ─────────────────────────────────────────── */

/* Choix de l'opération : les indisponibles restent visibles, grisées par
   .bouton:disabled — la mention « bientôt » reste en minuscules, comme dans
   les sujets d'Options. */
#apprendre-operations .bouton small {
  margin-left: 0.25rem;
  font-size: 0.7em;
  text-transform: none;
  letter-spacing: normal;
}

/* Configuration : l'opération choisie s'affiche sous le titre, en compact —
   l'écran de l'addition doit tenir dans 360×640 sans scroll (budget
   vertical, Lot 8). */
#ecran-apprendre-config h2 {
  margin-bottom: 0.25rem;
}

#apprendre-config-operation {
  margin: 0 0 0.75rem;
}

/* 16 tuiles de tables : grille de boutons bascule (aria-pressed), cibles
   tactiles >= 48 px. */
.tuiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin: 0.25rem 0 0.75rem;
}

.tuile,
.segment {
  min-width: 0;
  min-height: 3rem;
  padding: 0.5rem;
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-row);
  font: inherit;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
}

.tuile[aria-pressed='true'],
.segment[aria-pressed='true'] {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 600;
}

.tuile:focus-visible,
.segment:focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 2px;
}

.segments {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.segment {
  flex: 1 1 4rem;
}

.apprendre-raccourcis {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.apprendre-raccourcis .bouton {
  /* Discrets par la taille de texte, JAMAIS par la cible tactile : le
     plancher de 44 px reste le plancher (mesurés à 40 px avant correction). */
  min-height: 2.75rem;
  padding: 0.4rem;
  font-size: 0.85rem;
}

.apprendre-sous-titre {
  margin: 1rem 0 0.5rem;
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: var(--btn-transform);
  letter-spacing: var(--btn-tracking);
}

.apprendre-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 1rem;
}

.apprendre-table th,
.apprendre-table td {
  padding: 0.35rem 0.5rem;
  text-align: right;
  border-bottom: 1px solid var(--border);
}

.apprendre-table th {
  color: var(--muted);
  font-weight: normal;
  text-transform: var(--btn-transform);
  font-size: 0.75rem;
}

.apprendre-table td {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.apprendre-table th:first-child,
.apprendre-table td:first-child {
  text-align: left;
  color: var(--text-strong);
}

#apprendre-bilan-reussite {
  font-size: 1.25rem;
}

/* ── Divers ──────────────────────────────────────────────────────────── */

.ligne-etat {
  color: var(--text-strong);
}

.ligne-etat strong {
  color: var(--accent);
  font-weight: 600;
}

/* Sujet et niveau sur l'Accueil : une PILULE, pour se distinguer du corps de
   texte. Elle n'est PAS tapable (le choix du sujet vit dans Options) : pas
   de chevron, qui promettrait une affordance inexistante. */
#ecran-accueil .ligne-etat {
  display: inline-block;
  /* L'écran est une colonne flex sur mobile : sans align-self, la pilule
     s'étirerait sur toute la largeur (un item flex est « blockifié »). */
  align-self: flex-start;
  margin: 1rem 0 0;
  padding: 0.6875rem 1.125rem;
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  color: var(--text-dim);
  font-size: 0.875rem;
}

/* Statistiques et Options : liens discrets côte à côte, sous les boutons de
   mode — présents sans peser (constat « cinq boutons identiques »). */
.accueil-liens {
  display: flex;
  justify-content: center;
  gap: 2.125rem;
  margin-top: 0.25rem;
}

.accueil-liens .lien-discret {
  width: auto;
}

.note {
  min-height: 1.25rem;
  color: var(--muted);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

/* ── >= 500 px : paires de boutons côte à côte, tableau stats aéré ───── */

@media (min-width: 500px) {
  .stats-selecteurs {
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
  }

  .stats-table th,
  .stats-table td {
    padding: 0.4rem 0.35rem;
  }

  .stats-table th {
    font-size: 0.8rem;
    letter-spacing: 0.05em;
  }

  .stats-table td {
    font-size: 1rem;
  }
}

/* ── MOBILE UNIQUEMENT (sprint design 2) : complément EXACT du breakpoint
   desktop (700 px ET 500 px) — ces règles ne touchent jamais le desktop,
   aucune réinitialisation nécessaire. Accueil / Attente / Résultat
   occupent la hauteur : héros centrés, actions dans la zone du pouce. ── */

@media (max-width: 699.98px), (max-height: 499.98px) {
  /* Accueil : logo et pilule en haut, le menu dans la moitié basse (zone
     du pouce). */
  #ecran-accueil.actif {
    display: flex;
    flex-direction: column;
  }

  #ecran-accueil .menu {
    margin-top: auto; /* pousse le bloc dans la moitié basse */
    justify-content: flex-end;
  }

  /* Les boutons de mode grossissent dans la zone du pouce. Ils tiennent
     plus large qu'avant le Lot 11 : Statistiques et Options sont passées en
     liens discrets, l'Accueil ne porte plus que 4 boutons au lieu de 6. */
  #ecran-accueil .menu .bouton {
    min-height: 3.5rem;
  }

  /* Attente : le héros — code amis, sinon compte à rebours — est centré
     verticalement par paires de marges auto ; liste des inscrits sous le
     héros ; LANCER, notes et QUITTER descendent dans la zone du pouce. */
  #ecran-attente.actif {
    display: flex;
    flex-direction: column;
  }

  /* Premier élément du groupe héros : le bloc code (amis), sinon le
     compte à rebours (public) — il encaisse la marge haute flexible. */
  #attente-amis:not([hidden]) {
    margin-top: auto;
  }

  #attente-amis[hidden] + .attente-compte {
    margin-top: auto;
  }

  /* Public : la ligne participants clôt le groupe centré. */
  #attente-participants {
    margin-bottom: auto;
  }

  /* Le CODE, héros de l'écran amis. */
  .code-grand {
    font-size: clamp(3.5rem, 17vw, 5rem);
  }

  /* Le compte à rebours (départ ou validité du code), lisible de loin mais
     NETTEMENT plus petit que le code : son libellé passe SOUS le nombre
     (l'allemand « Gültigkeit des Codes » ferait un retour à la ligne
     maladroit à côté du grand chiffre). */
  .attente-compte {
    font-size: clamp(2.25rem, 11vw, 3rem);
  }

  .attente-compte span:last-child {
    display: block;
    margin-top: 0.25rem;
  }

  /* Amis : le conteneur éclate (display: contents), ses enfants deviennent
     des items flex de l'écran — la liste reste sous le compte à rebours,
     la marge auto sous la liste envoie LANCER en bas. */
  #attente-amis-liste:not([hidden]) {
    display: contents;
  }

  #attente-amis-liste .inscrits {
    margin-bottom: auto;
  }

  /* Résultat : le rang et les points gagnés EN GRAND d'abord (c'est ce que
     le joueur veut savoir), classement final puis récapitulatif ensuite,
     REJOUER / ACCUEIL en bas. Ordres flex explicites, DOM inchangé. */
  #ecran-resultat.actif {
    display: flex;
    flex-direction: column;
  }

  #resultat-classement:not([hidden]) {
    display: contents; /* libère rang/points, titre et top 5 dans le flex */
  }

  /* Héros : rang + points en concours, niveau en entraînement. */
  #resultat-points,
  #resultat-niveau {
    order: 1;
    margin: 1.25rem 0;
    text-align: center;
  }

  #resultat-points {
    font-size: clamp(1.6875rem, 7vw, 1.8125rem);
    font-weight: 700;
  }

  #resultat-niveau {
    font-size: clamp(1.4rem, 6vw, 2rem);
  }

  #resultat-classement h3,
  #resultat-top {
    order: 2;
  }

  #ecran-resultat .recap {
    order: 3;
  }

  #ecran-resultat .note {
    order: 4;
  }

  #ecran-resultat .menu {
    order: 5;
    margin-top: auto; /* zone du pouce */
    padding-top: 0.75rem;
  }
}

/* ── Portrait COURT (petit Android ~360×640, grande police système) : avec
   Apprendre, l'Accueil porte 6 boutons de mode (Lot 7) — dont deux passés en
   LIENS DISCRETS au Lot 11, ce qui rend de la hauteur. La gouttière se
   resserre malgré tout ; les écrans hauts gardent leur respiration.
   Placé AVANT le bloc paysage (max-height:480px) : sur un écran vraiment
   court, la règle de ce dernier l'emporte. */

@media (max-width: 699.98px) and (max-height: 720px) {
  #ecran-accueil .menu {
    gap: 0.625rem;
  }

  #ecran-accueil .menu .bouton {
    min-height: 3.125rem; /* 50 px, marge conservée sur le plancher de 48 */
  }

  .bouton-primaire {
    padding: 0.875rem 1rem;
  }

  #ecran-accueil .ligne-etat {
    margin-top: 0.75rem;
  }

  /* ── Écran Jeu : le budget vertical y est le plus serré de l'application,
     parce que c'est le seul écran qui GARANTIT 100dvh sans défilement. Les
     respirations posées au Lot 11 (padding d'écran de 30 px, FIN écarté de
     PASSER) doivent se resserrer ici. Mesuré AVANT ce palier, en allemand
     à 360×640 et 130 % de police : 703 px de contenu pour 640 px de
     viewport — c'est-à-dire la limite connue depuis le Lot 7 (le palier se
     déclenche sur la hauteur du viewport, alors que le débordement vient
     des rem), rendue atteignable par la refonte. Le plancher tactile de
     44 px et l'écart FIN/PASSER de 16 px sont TENUS. */
  .terminal:has(#ecran-jeu.actif) {
    padding-top: max(0.5rem, env(safe-area-inset-top));
  }

  .jeu-compteurs {
    margin-top: 0.25rem;
  }

  .question {
    margin: 0.25rem 0;
    min-height: 2.5rem;
  }

  .propositions {
    min-height: 36dvh;
  }

  .jeu-actions {
    margin-top: 0.75rem;
  }

  .jeu-actions .lien-discret {
    margin-top: 1rem; /* >= 16 px : la séparation FIN/PASSER reste garantie */
  }
}

/* ── Écran court (téléphone en PAYSAGE) : jeu compacté, toujours 100dvh ── */

@media (max-height: 480px) {
  .terminal {
    padding-top: max(0.5rem, env(safe-area-inset-top));
    padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
  }

  .jeu-compteurs {
    margin-top: 0.25rem;
  }

  /* Les deux lignes de hauteur fixe sont conservées ; seuls marges et
     padding se compressent. */
  .classement-live {
    margin: 0.25rem 0;
    padding: 0.25rem 0.75rem;
  }

  #ecran-jeu #jeu-etat {
    margin-top: 0.25rem;
  }

  .question {
    margin: 0.25rem 0;
    min-height: 2.5rem;
    font-size: clamp(1.75rem, 9vh, 3rem);
  }

  .propositions {
    min-height: 0;
  }

  .cellule {
    font-size: clamp(1.4rem, 7vh, 2rem);
  }

  .jeu-actions {
    margin-top: 0.5rem;
  }

  .jeu-actions .lien-discret {
    margin-top: 0.5rem;
    min-height: 2.25rem;
  }

  /* Accueil et héros (sprint design 2) compactés en hauteur : les tailles
     repassent en vh pour tenir dans ~390 px de haut. */
  #ecran-accueil .menu {
    margin-top: 1rem;
  }

  #ecran-accueil .bouton {
    min-height: 3rem;
  }

  .bouton-primaire {
    padding: 0.75rem 1rem;
  }

  .code-grand {
    padding: 0.5rem;
    font-size: clamp(2.5rem, 13vh, 4rem);
  }

  .attente-compte {
    margin: 0.75rem 0;
    font-size: clamp(1.75rem, 9vh, 2.5rem);
  }

  #resultat-points {
    font-size: clamp(1.4rem, 8vh, 2rem);
  }

  #resultat-niveau {
    font-size: clamp(1.25rem, 7vh, 1.75rem);
  }
}

/* ── DESKTOP (>= 700 px de large ET >= 500 px de haut) : la colonne
   centrée d'origine. Un téléphone en paysage (~400 px de haut) ne rentre
   pas ici : il garde le plein écran. ── */

@media (min-width: 700px) and (min-height: 500px) {
  .terminal {
    display: block;
    min-height: 0;
    padding: 1.5rem 1rem 2rem;
  }

  #ecran-jeu.actif {
    display: block;
  }

  .question {
    display: block;
    flex: none;
    margin: 1.5rem 0;
    min-height: 3.5rem;
    font-size: 3rem;
  }

  .propositions {
    min-height: 0;
    grid-auto-rows: auto;
  }

  .cellule {
    min-height: 5rem;
    font-size: 2rem;
  }

  .choix {
    min-height: 0;
    padding: 0.65rem 0;
  }
}
