/* MathVaud — calcul réfléchi. Feuille unique, mobile d'abord.
 *
 * Applique `40_design_system_v2.md` : Inter (corps), Space Grotesk (titres),
 * grille 4pt, trois niveaux typographiques au maximum par écran, zones tactiles
 * de 44 px, largeur maximale 640 px, énoncé sticky pendant la résolution.
 *
 * LES TROIS NIVEAUX DE L'ÉCRAN D'UN ITEM, nommés une fois pour toutes :
 *
 *   1. ÉNONCÉ       Space Grotesk, 30 à 40 px, encre pleine, collant en haut.
 *                   Le calcul. Il ne bouge pas.
 *   2. CONSIGNE     Inter 16 px, poids 600, encre pleine. Ce qu'on demande —
 *                   et cela CHANGE à chaque cran d'assistance, d'où son poids.
 *                   Au même niveau de taille que le corps : elle s'en distingue
 *                   par le poids, pas par une taille de plus.
 *      corps        Inter 15 à 17 px, poids 400, encre forte. L'étayage, le
 *                   retour, tout ce qui se lit en phrases.
 *   3. MENTION      Inter 12 à 14 px, encre douce. Le repère de position, les
 *                   notes de pied. Rien qui porte une instruction.
 *
 * La réponse fausse montrée au cran « exemple erroné » reste au niveau du corps,
 * délibérément : en faire la plus grosse chose de l'écran graverait une
 * information fausse.
 *
 * ┌─ CE QUI EST PRIS DU DESIGN SYSTEM, ET CE QUI NE L'EST PAS ───────────────┐
 * │                                                                          │
 * │ Ce design system a été écrit pour un AUTRE produit MathVaud — les leçons, │
 * │ React, Supabase, trois voies. On en prend la langue visuelle,             │
 * │ pas le catalogue de composants. Trois choses en sont écartées, et par     │
 * │ règle, pas par goût :                                                     │
 * │                                                                          │
 * │  · `ScoreCard` (bronze/argent/or) et `StreakBadge` (« X jours            │
 * │    consécutifs ») — règle 2 : aucune récompense conditionnée ;            │
 * │  · la différenciation par voie VG1/VG2/VP — règle 1 ;                     │
 * │  · les emojis de félicitation — règle 6 : le retour est du texte.         │
 * │                                                                          │
 * │ Une divergence assumée : la couleur d'action reste le VERT MathVaud de    │
 * │ cette application, pas le bleu `primary-600` du design system. Le reste   │
 * │ des neutres, l'échelle d'espacement, les rayons et les ombres viennent du │
 * │ document. `#D97706` est repris tel quel comme accent de TEXTE, et ce     │
 * │ report a RECOPIÉ un chiffre au lieu de le mesurer — le plus coûteux des  │
 * │ recopiages du dépôt, parce qu'il vivait dans une décision figée.         │
 * │ Il disait « 5,1 : 1 sur blanc, AA » ; la mesure au                       │
 * │ navigateur donne 3,19 : 1 sur blanc et 3,04 sur le papier de l'app. Le   │
 * │ 5,1 venait du design system de l'autre produit, où l'ambre se pose sur   │
 * │ d'autres fonds, et il n'a jamais été re-mesuré ici. À 3,19 l'ambre n'est │
 * │ AA que pour du GRAND texte (≥ 24 px, ou ≥ 18,66 px gras) ; ses TROIS     │
 * │ emplois de texte sont à 12,75 px (`.tips-rubrique`), 15 px (l'entrée des │
 * │ TIPS) et 17 px (`.etayage .exemple`, qui porte l'énoncé d'un exemple     │
 * │ étayé, à 3,19 : 1 sur l'écran d'un item). Seul le deuxième est corrigé   │
 * │ localement, parce qu'il est le texte d'un CONTRÔLE ; le jeton général    │
 * │ est une décision 12, prise après les testeurs, et elle porte sur les     │
 * │ trois.                                                                   │
 * └──────────────────────────────────────────────────────────────────────────┘
 *
 * Ce qui est absent l'est par décision, pas par oubli : aucune barre de
 * progression qui se remplit, aucun score, aucune couleur de récompense, aucun
 * chronomètre. La ligne de partage est « récompense conditionnelle contre
 * information » (Deci, Koestner & Ryan 1999), et elle se tient jusque dans la
 * feuille de style — une barre qui se remplit est une récompense visuelle, un
 * repère de position ne l'est pas.
 */

/* Les polices sont servies d'ici — plus rien ne part chez Google (décision de
   Marc, 20.09.2026). Fichiers : ceux que Google servait aux téléphones ce jour-là
   (Inter 4.001, Space Grotesk 2.000, sous-ensemble latin, polices variables).
   Déclarations : les siennes, URL exceptée. Graisses DÉCLARÉES une à une, comme
   chez Google : une plage ferait rendre en vraie 500 les `font-weight: 500` qui
   héritent d'Inter — rendus en 400 aujourd'hui, faute de 500 déclarée.
   Licences : fontes/LICENCE-*.txt (SIL OFL 1.1). */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fontes/inter-latin.c9407645.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;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fontes/inter-latin.c9407645.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;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fontes/space-grotesk-latin.a0d054c4.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;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fontes/space-grotesk-latin.a0d054c4.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;
}

:root {
  /* neutres — design system §1.4 */
  --encre: #0F172A;
  --encre-forte: #334155;
  --encre-douce: #64748B;
  --papier: #F8FAFC;
  --carte: #FFFFFF;
  --trait: #E2E8F0;
  --trait-doux: #F1F5F9;
  /* Une surface EN CREUX : un bloc posé dans la page, plus sourd qu'elle. Il
     porte la même valeur que `--trait-doux` dans les deux thèmes, et c'est une
     coïncidence, pas une dépendance — l'un teinte un filet, l'autre remplit un
     panneau. `.etat-testeur pre` l'invoquait avec un repli en dur, `#f6f6f4`,
     et le jeton n'existait NULLE PART : le repli s'appliquait donc toujours, un
     quasi-blanc, pendant que le texte suivait le thème. En sombre, le résumé
     que le testeur doit copier mesurait 1,37 : 1. */
  --fond-doux: #F1F5F9;

  /* action — le vert MathVaud, divergence assumée (voir l'encadré) */
  --accent: #12615a;
  --accent-fort: #0d4a45;
  --accent-doux: #ECF5F3;
  /* Ce qui se POSE SUR l'accent, et qui n'est pas la même chose selon le thème.
     `#fff` écrit en dur dans `.principal` tenait en clair (7,28 : 1) et tombait
     à 1,48 : 1 en sombre, où l'accent devient un turquoise clair : le libellé du
     bouton le plus tapé de l'app — douze fois par séance — y était illisible.
     Un jeton et non une valeur en dur : la couleur d'un texte sur l'accent est
     une CHOSE qui change avec le thème, pas une constante. */
  --sur-accent: #FFFFFF;

  /* L'AMBRE, ET L'AMBRE QUI SE LIT — deux jetons, parce que ce ne sont pas les
     mêmes exigences.

     `--ambre` (#D97706) vient du design system d'un autre produit, qui le disait
     « AA ». Mesuré ici, sur les éléments rendus : 3,19 : 1 sur la carte, 3,04
     sur le papier — sous les 4,5 : 1 qu'un texte courant exige, et ses trois
     emplois de texte font 12,75 à 17 px. Il reste juste pour ce qui N'EST PAS du
     texte : un filet, une bordure, où WCAG ne demande que 3 : 1.

     `--ambre-texte` (#B45309, `amber-700`) est la teinte la plus claire qui
     passe sur les deux surfaces — 4,80 sur le papier, 5,02 sur la carte — donc
     la plus reconnaissable comme ambre parmi celles qui se lisent. Décision 12
     tranchée le 10 septembre 2026 : elle attendait « après les testeurs », un
     protocole abandonné, c'est-à-dire un événement qui n'arriverait jamais. */
  --ambre: #D97706;
  --ambre-texte: #B45309;
  --ambre-doux: #FEF3C7;

  /* sémantique — nommer une erreur, pas la punir */
  --faux: #B91C1C;
  --faux-doux: #FEF2F2;

  /* grille 4pt — design system §4.1 */
  --e1: 4px;  --e2: 8px;  --e3: 12px; --e4: 16px;
  --e6: 24px; --e8: 32px; --e12: 48px;

  --rayon-sm: 4px; --rayon: 8px; --rayon-lg: 12px; --rayon-xl: 16px;
  --ombre-sm: 0 1px 2px rgba(15, 23, 42, .04);
  --ombre-md: 0 2px 6px rgba(15, 23, 42, .07);

  --corps: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --titre: "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --encre: #F1F5F9; --encre-forte: #CBD5E1; --encre-douce: #94A3B8;
    --papier: #0F172A; --carte: #1E293B; --trait: #334155; --trait-doux: #1E293B;
    --fond-doux: #1E293B;
    --accent: #5EEAD4; --accent-fort: #99F6E4; --accent-doux: #134E4A;
    --sur-accent: #0F172A;   /* 12,07 : 1 — mesuré, pas estimé */
    --ambre: #FBBF24; --ambre-texte: #FBBF24; --ambre-doux: #422006;
    --faux: #FCA5A5; --faux-doux: #450A0A;
    --ombre-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --ombre-md: 0 2px 6px rgba(0, 0, 0, .35);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  background: var(--papier); color: var(--encre-forte);
  font-family: var(--corps);
  font-size: 17px; line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

/* L'écran n'est plus centré verticalement. Un contenu qui flotte au milieu du
 * vide n'a ni haut ni bas : c'est ce qui donnait à l'application son allure de
 * banc d'essai. On aligne en haut, et le rythme vient de l'espacement. */
/* LA BARRE D'ÉTAT DU TÉLÉPHONE, ET CE QU'ELLE MANGE.
 *
 * `viewport-fit=cover` laisse la page s'étendre SOUS la barre d'état et
 * l'encoche — c'est ce qui permet le plein écran. En échange, tout ce qui touche
 * le haut de la page doit s'en écarter de `env(safe-area-inset-top)`, sinon la
 * barre le recouvre. Le BAS le faisait depuis toujours ; le HAUT, non : les
 * douze marques étaient à 24 px du bord, la consigne à 50 px, sous une barre
 * qui en mesure une cinquantaine. Vu par Marc sur un 13 mini, sur un énoncé
 * long : « le haut du texte disparaît ». Mesuré à 375 × 812 avant correction.
 *
 * Une seule variable, lue partout : sur un écran sans encoche, elle vaut 0 et
 * rien ne bouge. On n'a rien rétréci, rien tassé — un petit écran défile, il
 * n'ampute pas. */
:root { --sat: env(safe-area-inset-top, 0px); }

main {
  max-width: 640px; margin: 0 auto;
  min-height: 100svh;
  display: flex; flex-direction: column;
  padding: var(--sat) var(--e4) calc(var(--e8) + env(safe-area-inset-bottom, 0px));
}

main > section {
  /* `flex: 1` donne à l'écran un HAUT et un BAS : le contenu s'aligne en haut,
   * et la signature de pied — `margin-top: auto` — descend au bas de la fenêtre
   * au lieu de flotter sous le dernier paragraphe. */
  flex: 1;
  display: flex; flex-direction: column;
  padding-top: var(--e6);
  animation: entree .28s cubic-bezier(.2, .7, .3, 1);
}

@keyframes entree {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  main > section { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* ── les trois niveaux typographiques ──────────────────────────────────── */
h1 {
  font-family: var(--titre); font-size: 1.75rem; font-weight: 600;
  line-height: 1.2; letter-spacing: -.02em;
  color: var(--encre); margin: 0 0 var(--e2);
}
h2 {
  font-family: var(--titre); font-size: 1.0625rem; font-weight: 600;
  line-height: 1.4; letter-spacing: -.01em;
  color: var(--encre); margin: 0 0 var(--e3);
}
p { margin: 0 0 var(--e4); max-width: 65ch; }
.mention { color: var(--encre-douce); font-size: .875rem; line-height: 1.45; }

/* ── boutons ───────────────────────────────────────────────────────────── */
button {
  font: inherit; color: inherit;
  min-height: 48px; padding: var(--e3) var(--e4);
  border: 1px solid var(--trait); border-radius: var(--rayon);
  background: var(--carte); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, box-shadow .12s ease,
              background-color .12s ease, border-color .12s ease;
}
button:active { transform: scale(.985); }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.principal {
  width: 100%; background: var(--accent); color: var(--sur-accent);
  border-color: var(--accent); font-weight: 600;
  box-shadow: var(--ombre-sm);
  min-height: 52px;
}
.principal:active { background: var(--accent-fort); border-color: var(--accent-fort); }
.secondaire {
  width: 100%; background: var(--carte); color: var(--encre-forte);
  font-weight: 500;
}
.secondaire:active { background: var(--trait-doux); }

/* ── l'arrivée ─────────────────────────────────────────────────────────── */
.accueil { padding-top: var(--e12); }
.accueil h1 { font-size: 2rem; }
.accueil .intro { color: var(--encre-douce); margin-bottom: var(--e8); }

.classes {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e2);
  margin-bottom: var(--e6);
}
.classes button {
  font-family: var(--titre); font-size: 1.375rem; font-weight: 600;
  color: var(--encre); min-height: 60px; padding: var(--e3) var(--e2);
  box-shadow: var(--ombre-sm);
}
.classes button:active {
  background: var(--accent-doux); border-color: var(--accent); color: var(--accent);
}

/* ── l'item : le seul écran vu douze fois par séance ───────────────────── */

/* Le repère de position. Ce n'est PAS un score : il dit combien de temps la
 * séance dure encore, il ne dit rien de ce qui est juste, et il disparaît avec
 * la séance. Il ne se remplit pas — c'est un repère, pas une jauge. */
/* ── LES DOUZE MARQUES ─────────────────────────────────────────────────
 *
 * L'identité de Douze vient de son NOM, pas d'une couleur ajoutée : douze
 * carrés, ceux déjà faits pleins, les autres en contour. Elle REMPLACE le
 * compteur « Calcul 5 sur 12 » — rien n'est ajouté à l'écran, une chose en
 * remplace une autre.
 *
 * CE QU'ELLE N'EST PAS, et c'est ce qui décide de sa forme :
 * · pas un anneau qui se ferme — douze marques SÉPARÉES, jamais une barre
 *   continue : une jauge qui se remplit appelle la récompense de l'achèvement,
 *   que la décision 2 interdit ;
 * · pas un compte de réussites — une marque dit « ce calcul a eu lieu », pas
 *   « il était juste ». Un élève qui se trompe douze fois voit exactement le
 *   même objet que celui qui réussit douze fois ;
 * · rien ne l'annonce, rien ne la commente, elle ne s'anime pas.
 *
 * Le texte « Calcul 5 sur 12 » reste dans le DOM pour les lecteurs d'écran :
 * douze carrés sans mots ne se lisent pas à voix haute. */
.douze {
  display: flex; gap: var(--e1); align-items: center;
  margin: 0 0 var(--e4);
}
.douze i {
  width: 10px; height: 10px; border-radius: 2px;
  /* `--encre-douce` et non `--trait` : mesuré au contraste, le trait donnait
     1,18 : 1 en clair et 1,72 : 1 en sombre — une marque qu'on ne voit pas ne
     dit pas combien il en reste, et WCAG demande 3 : 1 pour un composant.
     L'encre douce rend 4,55 : 1. Trouvé en mesurant, pas à l'œil : à l'œil,
     les douze paraissaient très bien. */
  border: 1.5px solid var(--encre-douce); background: transparent;
}
.douze i.faite { background: var(--accent); border-color: var(--accent); }
/* Sur la carte de fin, les douze sont pleines pour tout le monde : c'est le
   nombre du produit, pas un résultat. Un peu plus grandes, parce qu'elles y
   sont l'objet et non un repère de coin d'écran. */
.douze-fin { margin-bottom: var(--e6); }
.douze-fin i { width: 14px; height: 14px; border-radius: 3px; }

/* Le repère chiffré : présent pour la synthèse vocale, invisible à l'œil.
   `clip` plutôt que `display:none`, qui le retirerait aussi de la voix. */
.avancee {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; margin: -1px; padding: 0; border: 0;
}

/* LA CONSIGNE EST UN NIVEAU TYPOGRAPHIQUE, PAS UNE LÉGENDE.
 *
 * Elle était au niveau « mention » — petite et grise —, donc systématiquement
 * plus faible que l'énoncé. Or **elle change à chaque cran** : « donne le
 * résultat », « trouve l'erreur de cet élève », « complète la dernière étape ».
 * Une instruction qui varie et qu'on affiche toujours en gris finit par ne plus
 * être lue, et l'élève répond à la question qu'il croit reconnaître.
 *
 * Ce n'est donc pas un correctif au cran « exemple erroné », c'est la hiérarchie
 * de tous les écrans d'item. La consigne rejoint le niveau du CORPS — même
 * taille que le texte courant — et s'y distingue par le poids et par l'encre
 * pleine, pas par une taille de plus : les trois niveaux du design system
 * restent trois.
 *
 * CE QUI N'A PAS ÉTÉ FAIT, ET POURQUOI : grossir la réponse fausse. La plus
 * grosse chose à l'écran serait alors une information FAUSSE, et chez un élève
 * fragile c'est elle qui se grave. On renforce la consigne, on laisse l'énoncé
 * tel quel. */
.consigne {
  font-size: 1rem; font-weight: 600; line-height: 1.4;
  color: var(--encre);
  margin: 0 0 var(--e4);
}

/* L'ÉNONCÉ RESTE VISIBLE PENDANT QU'ON RÉPOND — design system §7.4, contre
 * l'effet d'attention partagée. Sur un téléphone, l'étayage peut être long et le
 * clavier fait remonter la page : sans cela, l'élève tape une réponse sans avoir
 * le calcul sous les yeux.
 *
 * C'est l'ÉLÉMENT LUI-MÊME qui colle, pas un bandeau qui le recopierait en
 * haut : l'ordre de lecture — exemple, puis énoncé, puis saisie — est une
 * décision pédagogique, et le déplacer dans le DOM l'aurait changée. */
.enonce {
  position: sticky; top: 0; z-index: 2;
  font-family: var(--titre); font-weight: 600;
  font-size: clamp(1.875rem, 8.5vw, 2.5rem);
  line-height: 1.15; letter-spacing: -.02em;
  color: var(--encre); margin: 0;
  padding: var(--e3) 0;
  background: var(--papier);
  overflow-wrap: break-word;
}

.etayage {
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-sm);
  padding: var(--e4);
  margin: 0 0 var(--e4);
}
.etayage p { margin: 0 0 var(--e2); font-size: .9375rem; line-height: 1.55; }
.etayage p:last-child { margin-bottom: 0; }

/* La ligne d'en-tête d'un exemple : le repère qui dit « ceci n'est pas ton
 * calcul ». Ambre, l'accent de texte du design system.
 *
 * SANS capitales : cette ligne PORTE L'ÉNONCÉ de l'exemple — « Exemple : 49 × 13
 * : à peu près combien ? ». Un `text-transform: uppercase` y criait les maths et
 * cassait la lecture des nombres. Vu sur la capture, corrigé avant d'aller plus
 * loin : la mise en forme ne doit jamais toucher au contenu mathématique. */
.etayage .exemple {
  font-family: var(--titre); font-weight: 600; font-size: 1rem;
  line-height: 1.35; color: var(--ambre-texte);
  margin-bottom: var(--e3);
}
.etayage .reponse-eleve {
  color: var(--faux); font-weight: 500;
  background: var(--faux-doux);
  border-radius: var(--rayon-sm);
  padding: var(--e2) var(--e3);
  margin-bottom: var(--e3);
}

/* La phrase qui nomme l'erreur. Ni reproche ni alerte : elle décrit un geste.
 * D'où le ton neutre — la couleur du faux est déjà portée par la réponse
 * montrée juste au-dessus. */
.erreur-nommee {
  color: var(--encre-forte);
  border-left: 2px solid var(--trait);
  padding-left: var(--e3);
  margin: var(--e3) 0 0;
}
.retour .erreur-nommee { margin: var(--e2) 0; border-left-color: currentColor; }

/* ── la saisie ─────────────────────────────────────────────────────────── */
.champs {
  display: flex; flex-wrap: wrap; gap: var(--e3); align-items: center;
  margin: var(--e6) 0 var(--e4);
}
.champ { display: flex; align-items: center; gap: var(--e2); }
input[type="text"] {
  font-family: var(--titre); font-size: 1.5rem; font-weight: 600;
  width: 100%; min-width: 0; min-height: 56px;
  padding: var(--e2) var(--e4);
  color: var(--encre); background: var(--carte);
  border: 1.5px solid var(--trait); border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-sm);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.champ input { width: 7ch; }

/* LES DEUX BOUTONS D'UN JUGEMENT — « oui / non », « acceptable / impossible ».
   Aucun clavier : un pavé numérique n'a pas de lettres, et « oui » ne se tape
   pas. Deux boutons de même poids, côte à côte, à la taille des boutons de
   classe — ce sont des RÉPONSES, pas des actions, d'où l'encre et non l'accent.
   Ils remplacent le champ ET le bouton « Valider » : toucher, c'est répondre. */
.choix {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--e3);
  margin: var(--e6) 0 var(--e4);
}
.choix button {
  font-family: var(--titre); font-size: 1.25rem; font-weight: 600;
  color: var(--encre); min-height: 60px; padding: var(--e3) var(--e2);
  box-shadow: var(--ombre-sm);
}
.choix button:active {
  background: var(--accent-doux); border-color: var(--accent); color: var(--accent);
}

/* La touche « − ». Le pavé décimal d'iOS ne la porte pas, et sans elle toute la
   famille des nombres relatifs est servie sans être répondable.
   Zone tactile de 48 px comme les autres boutons ; discrète, parce qu'elle sert
   sur un item sur cinq et ne doit pas concurrencer « Valider ». */
.touche-ajoutee {
  flex: 0 0 auto;
  min-width: 48px; min-height: 48px; padding: 0;
  margin-left: var(--e2);
  font-family: var(--titre); font-size: 1.5rem; font-weight: 600;
  color: var(--encre-forte);
  background: var(--carte); border: 1.5px solid var(--trait);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-sm);
}
.touche-ajoutee:active { background: var(--trait-doux); border-color: var(--encre-douce); }
.champs > .champ:only-child input { width: 100%; }
input:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-doux);
}
.etiquette { font-size: 1rem; color: var(--encre-douce); }

/* ── LA NOTATION SCIENTIFIQUE, À DEUX ÉTAGES ─────────────────────────────
   « 2,4 · 10² » : l'exposant se saisit EN HAUT, comme il s'écrit. Le livre de
   l'élève (Aide-mémoire 9-10-11, CIIP/LEP 2019, p. 35) écrit « a · 10ⁿ », et sa
   page 24 pose « · » comme LE signe de la multiplication, pour ne pas le
   confondre avec le x de l'algèbre : on garde donc le point médian, et on le
   rend lisible au lieu de le changer.

   Le bond ne sort PAS de la ligne : c'est la BASE qui descend. Une case
   surélevée par `position: relative` ne dirait rien à la mise en page, et un
   chevauchement ne se mesurerait plus.

   Les deux largeurs sont MESURÉES, pas posées : la case doit montrer ce qu'un
   DOIGT peut y taper — « 2,4 » dans la base, « −12 » dans l'exposant, réponse
   fausse comprise — et la ligne doit tenir d'un seul tenant à 320 px, où elle
   se repliait déjà avant ce changement. */
:root { --bond-exposant: 24px; --case-exposant: 68px; }
/* La base RÉTRÉCIT quand l'écran l'exige, entre deux bornes mesurées : à
   320 px la ligne manquait de 3,66 px et se repliait, l'exposant tombant
   sous le coefficient — le bond n'aurait plus rien voulu dire. 88 px est le
   plancher qui montre « 2,4 » en entier, la plus longue réponse servie. */
#champs.puissance > .champ:first-child > input { width: clamp(88px, 27vw, 100px); }
#champs.puissance { align-items: flex-start; }
#champs.puissance > .champ:first-child { margin-top: var(--bond-exposant); }
#champs.puissance > .champ:nth-child(2) > input {
  width: var(--case-exposant); padding-inline: var(--e2);
}
/* Le « · 10 » à la taille des nombres qu'il relie, et centré sur la hauteur de
   la case : à 1 rem sur la ligne de base, le point était presque invisible. */
#champs.puissance .etiquette {
  font-family: var(--titre); font-size: 1.5rem; font-weight: 600;
  color: var(--encre-forte);
}

/* ── le retour, jamais faux ────────────────────────────────────────────── */
.retour {
  border-radius: var(--rayon-lg); padding: var(--e4);
  margin: var(--e4) 0;
  border: 1px solid transparent;
}
.retour.juste { background: var(--accent-doux); border-color: var(--accent-doux); }
.retour.rate  { background: var(--faux-doux); border-color: var(--faux-doux); }
.retour p { margin: 0; max-width: none; }
.retour .titre {
  font-family: var(--titre); font-weight: 600; font-size: 1.0625rem;
  margin-bottom: var(--e1);
}
.retour.juste .titre { color: var(--accent); }
.retour.rate .titre { color: var(--faux); }
.retour.rate p { color: var(--encre-forte); }

/* ── la postdiction ────────────────────────────────────────────────────── */
.strategies {
  display: flex; flex-direction: column; gap: var(--e2);
  /* LES DEUX SORTIES SE TOUCHENT : l'écart sous la boîte vaut celui qui sépare
   * deux boutons, pour que « J'ai fait autrement » — dernier de la boîte — et
   * « Je ne sais pas » — juste dessous — forment une paire et non deux blocs. */
  margin-bottom: var(--e2);
}
.strategies button {
  text-align: left; font-size: 1rem; box-shadow: var(--ombre-sm);
  padding: var(--e3) var(--e4);
}
.strategies button:active {
  background: var(--accent-doux); border-color: var(--accent);
}
/* … et elles se ressemblent : « J'ai fait autrement » prend l'allure de
 * « Je ne sais pas », pas celle d'une démarche. Le texte CENTRÉ est ce qui les
 * distingue des propositions, toutes alignées à gauche : sans lui, la première
 * capture montrait deux boutons voisins qui ne se ressemblaient pas. */
.strategies .sortie {
  background: var(--carte); color: var(--encre-forte); font-weight: 500;
  box-shadow: none; text-align: center;
}
.strategies .sortie:active { background: var(--trait-doux); border-color: var(--trait); }

/* ── la fin de séance ──────────────────────────────────────────────────── */
/* Deux listes, et la seconde peut manquer. La première dit ce que l'élève a
 * FAIT — elle est toujours là. La seconde dit ce qui a bougé dans le graphe, et
 * elle disparaît entièrement, titre compris, quand rien n'a bougé : un intitulé
 * seul au-dessus du vide dirait l'absence. */
#titre-deplacement { margin: var(--e6) 0 var(--e3); }
/* Les deux listes portent le MÊME trait ambre. La seconde a longtemps eu un
   trait vert, et il était décoratif : rien ne s'y clique. Or l'écran de fin
   porte désormais les douze marques, elles aussi vertes — deux verts, dont un
   seul veut dire quelque chose. Le vert reste ce qu'il est ailleurs, la couleur
   d'action (décision 18) ; il cesse simplement d'être aussi un ornement. Ce qui
   sépare les deux listes est leur TITRE, pas leur couleur. */

/* Ce qui a bougé, et rien d'autre. Chaque ligne est un FAIT, pas une note :
 * pas de coche verte, pas de pourcentage, pas de barre. Le trait ambre à gauche
 * dit « regarde ici » sans dire « bravo ». */
.bilan { list-style: none; padding: 0; margin: 0 0 var(--e6); }
.bilan li {
  background: var(--carte);
  border: 1px solid var(--trait);
  border-left: 3px solid var(--ambre);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-sm);
  padding: var(--e3) var(--e4);
  margin-bottom: var(--e2);
  font-size: .9375rem; line-height: 1.5;
}
.bilan li:last-child { margin-bottom: 0; }

/* ── le pont vers MathVaud — règle 13 ──────────────────────────────────── */
/* Une signature, en pied de l'accueil et de la fin de séance. JAMAIS sur
 * l'écran d'un item : là, elle serait la seule chose cliquable à côté du champ
 * de réponse. */
.signature {
  margin-top: auto; padding-top: var(--e8);
  text-align: center;
}
.signature .lien { font-size: .8125rem; }

.apropos h2 { margin-top: var(--e6); }
.apropos .lien-externe {
  display: inline-block; min-height: 44px; padding: var(--e3) 0;
  color: var(--accent); font-weight: 600; text-decoration: none;
  border-bottom: 2px solid var(--accent-doux);
}

/* Un lien, pas un bouton : ce qui sert une fois par trimestre ne doit pas
 * ressembler à ce qui sert douze fois par séance. Pas de cadre, pas de fond —
 * mais une zone tactile de 44 px quand même. */
/* LA VERSION, en pied d'« à propos ». La DATE parle au parent ; l'EMPREINTE
 * sert au signalement, et elle se lit à voix haute au téléphone — donc plus
 * discrète que la date, jamais illisible : même jeton de couleur, déjà mesuré
 * au-dessus de AA, une taille en dessous, et des chiffres espacés. */
.version {
  text-align: center; margin: var(--e4) 0 0;
  font-size: .8125rem; color: var(--encre-douce);
}
.version .empreinte {
  font-size: .75rem; letter-spacing: .06em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.discret { text-align: center; margin: var(--e6) 0 0; }
.lien {
  min-height: 44px; padding: var(--e3);
  background: none; border: 0; color: var(--encre-douce);
  font-size: .875rem; text-decoration: underline;
  text-underline-offset: 3px; text-decoration-color: var(--trait);
  box-shadow: none;
}
.lien:active { color: var(--encre); background: none; transform: none; }

/* Le message de reprise perdue. Il informe, il ne s'excuse pas et il n'alarme
 * pas : ni rouge d'erreur, ni icône d'avertissement — l'élève n'a rien fait de
 * mal. L'ambre est la couleur de ce qui s'adresse à lui ; le rouge reste réservé
 * à une réponse fausse, et confondre les deux serait un faux retour. */
.reprise-perdue {
  margin: 0 0 var(--e4); padding: var(--e3) var(--e4);
  background: var(--ambre-doux); border-radius: var(--rayon);
  color: var(--encre); font-size: .9375rem; line-height: 1.5;
}
#ecran-reprise .reprise-perdue { margin-top: var(--e2); }
#ecran-reprise .principal { margin-top: auto; }

/* ── TIPS de Pros ───────────────────────────────────────────────────────────
 *
 * Une page qu'on LIT, pas un exercice qu'on fait : le texte y prend le pas sur
 * la mécanique. D'où la seule différence de traitement avec le reste de
 * l'application — l'interligne s'ouvre, la mesure se resserre à 60 signes, et
 * rien n'est cliquable au milieu.
 *
 * L'accent est l'ambre, celui qui sert déjà à la consigne : c'est la couleur de
 * ce qui s'adresse à l'élève plutôt que de ce qui l'évalue. Le vert reste au
 * moteur.
 *
 * AUCUN compteur, AUCUNE progression, AUCUNE collection : il n'y a donc ici ni
 * pastille, ni « 3 / 14 », ni bouton « la suivante ». Ce n'est pas un oubli de
 * mise en forme, c'est la règle 16 et la décision 2.
 */
/* L'entrée, en fin de séance. Trouvable sans faire concurrence à
 * l'entraînement : un élève qui a cinq minutes doit s'entraîner, pas
 * feuilleter. Elle reste donc un lien, plus petite que « Nouvelle séance », et
 * séparée d'elle par du blanc — l'ambre suffit à la faire voir. */
.tips { text-align: center; margin: var(--e8) 0 0; }
/* Ce lien est la PORTE d'une rubrique entière : illisible, c'est la rubrique qui
 * disparaît. Il portait une valeur LOCALE en dur, #B45309, en attendant que « l'ambre
 * quand c'est du texte » soit tranché — c'est fait, et la valeur est devenue le jeton
 * `--ambre-texte`. Le repli sombre, qui la remplaçait par `--ambre`, n'a plus lieu
 * d'être : le jeton porte déjà les deux thèmes. */
.tips .lien { color: var(--ambre-texte); text-decoration-color: var(--ambre-doux); }
.tips .lien:active { color: var(--ambre-texte); }

.tips-rubrique {
  margin: 0 0 var(--e2);
  font-family: var(--titre); font-size: .75rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ambre-texte);
}
.tips-carte h1 { font-size: 1.5rem; margin-bottom: var(--e6); }
.tips-carte p { max-width: 60ch; line-height: 1.65; overflow-wrap: break-word; }
/* Le trait d'un pas de calcul : les lignes que l'auteur a coupées restent
 * coupées, et l'œil suit la démarche au lieu de relire une phrase. */
.tips-carte .ligne { display: block; }
/* Les formules. Le fond les détache de la phrase — utile sur un téléphone, où
 * « 97 − 4 = 93 » et le texte qui l'entoure se confondent vite. Le rembourrage
 * horizontal reste à deux pixels : à quatre, la ponctuation qui suit décollait
 * du calcul et « 12 ». se lisait « 12 » puis un point orphelin. */
.tips-carte code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .9375em; padding: 1px 2px;
  background: var(--trait-doux); border-radius: 3px;
}
/* Le pourquoi. Il vient APRÈS le tour, jamais avant — l'élève doit être surpris
 * avant d'être instruit — et il se distingue sans crier : un fond, un filet. */
.tips-carte .pourquoi {
  margin: var(--e6) 0; padding: var(--e4);
  background: var(--carte); border: 1px solid var(--trait);
  border-left: 3px solid var(--ambre); border-radius: var(--rayon);
  box-shadow: var(--ombre-sm);
}
/* Dans l'encadré, les formules changent de fond : `--trait-doux` et `--carte`
 * sont la MÊME valeur en thème sombre (#1E293B), et les calculs y devenaient
 * invisibles. `--trait` se détache des deux, dans les deux thèmes. */
.tips-carte .pourquoi code { background: var(--trait); }
/* La mise en garde. Elle porte déjà son signe dans le texte : on ne lui en
 * ajoute pas un second, on lui donne seulement de l'air. */
.tips-carte .attention {
  padding-left: var(--e3); border-left: 2px solid var(--trait);
  color: var(--encre-forte); font-size: .9375rem;
}

/* La ligne « prête hors-ligne » : en haut, discrète, et elle s'efface seule.
 * Ce n'est pas une notification — c'est une information, une seule fois. */
.hors-ligne {
  position: fixed; top: calc(var(--e2) + env(safe-area-inset-top, 0px)); left: 0; right: 0;
  z-index: 3; text-align: center; font-size: .75rem;
  color: var(--encre-douce);
  transition: opacity .6s;
}
.hors-ligne.efface { opacity: 0; }

[hidden] { display: none !important; }

/* Le résumé d'état — écran « à propos » seulement, jamais dans une séance.
   Le `pre` défile horizontalement plutôt que de déborder : l'état brut tient
   sur une seule très longue ligne. */
.etat-testeur { margin-top: 32px; border-top: 1px solid var(--trait, #ddd); padding-top: 16px; }
/* « Copier » et l'adresse côte à côte : on copie, puis on touche l'adresse. Le
 * bouton perd sa pleine largeur ici, et l'adresse passe dessous si l'écran est
 * trop étroit pour les deux. */
.etat-testeur .envoi {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--e2) var(--e4); margin-top: var(--e3);
}
.etat-testeur .envoi .secondaire { width: auto; padding-inline: var(--e6); }
.etat-testeur pre {
  margin-top: 12px; padding: 12px; max-height: 40vh;
  overflow: auto; white-space: pre-wrap; word-break: break-word;
  font-size: 12px; line-height: 1.5;
  background: var(--fond-doux); border-radius: 8px;
}

/* ═══════════════════════════════════════════════════════════════════════
   LA FRACTION À DEUX ÉTAGES — décision 21, et sans KaTeX
   ═══════════════════════════════════════════════════════════════════════
   Douze n'a pas le droit de charger un CDN : il doit fonctionner hors ligne,
   c'est tout son intérêt. La fraction se dessine donc en CSS, avec une bordure
   pour barre. Voir la décision 21 du CLAUDE.md.

   `align-items: center` sur un `inline-flex` en colonne : les deux étages se
   centrent l'un sous l'autre, et `vertical-align: middle` pose l'ensemble sur
   la ligne de base du texte qui l'entoure — c'est ce qui met l'opérateur en
   face de la barre, et non au-dessus. */
.frac {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
  /* L'OPÉRATEUR SE CENTRE SUR LES BARRES, et « middle » n'y suffit pas :
     il aligne le milieu de la boîte sur `ligne de base + demi-hauteur d'x`,
     ce qui laissait la barre 4 px SOUS le centre du « + » à 31,9 px de corps —
     mesuré au navigateur. Le décalage est exprimé en `em` parce que l'écart,
     lui, est proportionnel au corps : 4 / 31,875 = 0,125 em. */
  position: relative;
  top: -0.125em;
  margin: 0 .14em;
  line-height: 1.15;
}
.frac .num { border-bottom: .09em solid currentColor; padding: 0 .22em; }
.frac .den { padding: 0 .22em; }

/* LES DEUX CASES DE LA RÉPONSE, EMPILÉES — nature `fraction` seule.
   D4 relie deux entiers par « et » : ses cases restent côte à côte. */
#champs.empile {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e2);
}
#champs.empile .champ { width: 100%; max-width: 190px; }
#champs.empile .champ input { width: 100%; }
#champs.empile::after {
  content: "";
  display: none;
}
/* La barre entre les deux cases : posée sur la première, elle ne demande
   aucun élément de plus et ne peut donc pas se désynchroniser des champs. */
#champs.empile .champ:first-child {
  border-bottom: 3px solid var(--encre-forte);
  padding-bottom: var(--e2);
  border-radius: 0;
}
