/* LA FAMILLE D'HABILLAGE, ET LA FEUILLE DE L'APPLICATION — SPEC §26.1,
 * ARCHITECTURE §12.1, règles de surface au SPEC §26 bis.
 *
 * CE QUE CE FICHIER EST. Les deux fonds normatifs du §26.1, tout ce qui change
 * avec le thème, et les règles de composition (structure, tableaux, rendu
 * mathématique, impression) héritées d'`essai.css`, dont ce fichier et
 * `semantique.css` sont la scission — passe P10, lot interface. `essai.css`
 * annonçait sa propre disparition pour ce lot, et elle a eu lieu.
 *
 * COMMENT LE THÈME S'APPLIQUE (ARCHITECTURE §12.1). Un attribut sur la racine
 * du document, écrit dans la page servie : `data-theme="clair"` ou
 * `"sombre"` — DEUX valeurs depuis P21 (26/08) : « comme mon appareil »
 * (`systeme`) a disparu de l'écran et des libellés, et les comptes qui le
 * portaient sont migrés (`0008`). CHAQUE VALEUR DE
 * COULEUR VIT UNE FOIS, en tête, en paire `-clair`/`-sombre` ; les blocs de
 * thème ne font que REMAPPER les variables effectives vers l'une des deux.
 * Un bloc de thème qui porterait une valeur littérale serait une seconde
 * source, qui divergerait au premier oubli — le garde
 * `tests/gardes/test_variables_css.py` le refuse.
 *
 * « Comme mon téléphone » (`systeme`) suivait le réglage du système par
 * `prefers-color-scheme` ; il a VÉCU de P10 à P21, et son bloc de remappage
 * est retiré — voir la note à son ancien emplacement.
 *
 * CHANGER L'UN DES DEUX FONDS INVALIDE TOUTE LA PALETTE (§26.1) :
 * `tests/gardes/test_contraste.py` recalcule les dix-huit contrastes depuis ce
 * fichier et `semantique.css`, et refuse un fond sombre éclairci — le cas
 * qu'une liste figée manque (§26.5).
 *
 * AUCUNE COULEUR SÉMANTIQUE ICI : juste/faux, signes, courbes et statuts
 * vivent dans `semantique.css`, invariants au thème (`T-02`).
 *
 * LES FACES SONT EMBARQUÉES, JAMAIS TÉLÉCHARGÉES D'UN TIERS (ARCHITECTURE
 * §6.1). Tranché par Kenny le 23/08 (`DU-19`) — critère : la couverture des
 * glyphes avant l'esthétique — et servi le 24/08, passe P11a. Les fichiers
 * vivent sous `polices/`, avec leurs licences (SIL OFL 1.1) et leur
 * provenance (`polices/LISEZ_MOI.md`) ; le garde
 * `tests/gardes/test_polices_embarquees.py` mesure leur table `cmap` et
 * refuse toute face qui ne couvrirait pas l'inventaire des glyphes servis.
 *
 * UNE SEULE FAMILLE — SPEC §26 bis.1.1, décision de Kenny du 26/08 (passe
 * P22) : TOUT le texte servi est en IBM Plex Serif — l'énoncé et la notation
 * comme les titres, les boutons, les libellés de formulaire et la console.
 *
 * La séparation serif/sans de P19 (« ce que l'élève lit pour réfléchir » en
 * serif, « ce sur quoi il agit » en Atkinson) était une décision de session,
 * présentée dans un récapitulatif non contesté — un silence n'est pas un
 * choix. Ce que Kenny a choisi, c'est IBM Plex Serif, sur comparaison de
 * quatre rendus, et il l'attend partout où elle peut servir. La règle des
 * deux familles était GÊNANTE, pas fausse (CLAUDE.md §8.14) : une famille
 * unique compose des écrans corrects.
 *
 * LA LIMITE DE LA RÈGLE, ET IL FAUT LA CONNAÎTRE : l'énoncé est rendu PAR LE
 * SERVEUR (macro `expression` de `gabarits/mathematiques.html`), donc Plex
 * Serif gouverne bien la notation AFFICHÉE. Elle ne gouverne PAS ce que
 * MathLive compose dans le champ de saisie : ses fontes KaTeX sont chargées à
 * part (`statique/mathlive/fontes.css`), seulement sur saisie libre, et
 * aucune règle de cette feuille ne les atteint.
 *
 * ATKINSON ET INTER RESTENT EMBARQUÉES ET NE SONT SERVIES PAR RIEN (`DU-133`).
 * Ne pas les retirer : Atkinson ne couvre pas U+2192 quand Inter le couvre,
 * et c'est Inter qui fait passer `test_polices_embarquees` — retirer une face
 * ferait rougir le garde pour un glyphe que rien ne sert. Leurs `@font-face`
 * restent déclarées (le précédent d'Inter depuis P19) : une famille qu'aucune
 * règle ne nomme n'est jamais téléchargée par le navigateur.
 *
 * L'ATTRIBUTION DES GLYPHES EST DÉCIDÉE PAR `unicode-range`, PAS ÉMERGENTE.
 * Une chaîne de repli laisse le navigateur décider ; ici, la mesure a décidé
 * (24/08 pour les deux faces sans empattement, 25/08 pour Plex Serif — table
 * `cmap` contre l'inventaire du garde) :
 *   · six points de code manquent à Atkinson, à Inter ET à IBM Plex Serif —
 *     U+0305 (barre du complémentaire), U+211D (ℝ), U+2229 (∩), U+222A (∪),
 *     U+2A7D (⩽), U+2A7E (⩾) — et viennent de Noto Sans Math, sous-ensemblée
 *     à ces six. **Plex Serif manque EXACTEMENT les six mêmes** (mesuré le
 *     25/08 par le garde sur sa table `cmap` : 930 points de code) : le repli
 *     la sert donc sans qu'une ligne de ce mécanisme change ;
 *   · tout le reste de l'inventaire est mesuré couvert par les trois faces de
 *     prose, et leur `unicode-range` est « tout sauf ces six ».
 *     *DEPUIS LE 30/09/2026 (P117-UX2, réponses Q3 et Q3 bis de Kenny ;
 *     `DU-133`), HUIT : ∅ (U+2205, l'écriture d'un ensemble de solutions) et
 *     ⋯ (U+22EF, le menu du rail), qu'aucune face servie ne portait (le
 *     navigateur les prenait à la pile système, Cambria Math au poste),
 *     viennent aussi de Noto, sous-ensemblée à huit ; les faces de prose les
 *     excluent, et `test_le_repli_sert_seul_les_glyphes_du_sous_ensemble`
 *     tient les deux plages contre la table `cmap` du sous-ensemble.*
 * Un glyphe HORS inventaire (une saisie libre de l'élève) suit la même liste
 * et retombe sur la pile système en dernier ressort — comme avant ce lot.
 *
 * BUDGET D'ANIMATION : ZÉRO (§26 bis). Aucune propriété `animation` ni
 * `transition` dans cette feuille ; la préférence de réduction des animations
 * est donc respectée par vacuité, et le jour où une animation entrerait, la
 * règle du §26 bis exige qu'elle vive sous
 * `@media (prefers-reduced-motion: no-preference)` et dure 200 ms au plus. */

/* ══════════════════════════════════════════════════════════════════════════
 * LES FACES EMBARQUÉES — SPEC §26 bis.1.1, décision de Kenny du 23/08.
 * Deux graisses par face de prose, 400 et 600, et pas une de plus (§26
 * bis.1.3). Le repli mathématique n'a qu'une graisse : Noto Sans Math
 * n'existe qu'en Regular, et les huit glyphes qu'elle sert ne sont jamais
 * composés en gras dans un énoncé — si un `font-weight: 600` les atteint, le
 * navigateur les épaissit lui-même, et c'est le seul cas de gras synthétique
 * admis dans cette feuille.
 *
 * `unicode-range` des faces de prose : TOUT SAUF les huit points de code du
 * repli — voir l'en-tête. Les intervalles s'écrivent par exclusion pour que
 * l'attribution de chaque glyphe de l'inventaire soit décidée une fois.
 * ══════════════════════════════════════════════════════════════════════════ */

/* Atkinson et Inter : embarquées, DÉCLARÉES, servies par AUCUNE règle depuis
 * P22 (une seule famille — en-tête). Une `@font-face` que rien ne nomme n'est
 * pas téléchargée ; les fichiers restent au dépôt pour la couverture du garde
 * (`DU-133` : U+2192 n'est couvert que par Inter). */

@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("polices/AtkinsonHyperlegibleNext-Regular.88ed5c31a715.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("polices/AtkinsonHyperlegibleNext-SemiBold.8e2fff779f35.ttf") format("truetype");
  font-weight: 600;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

@font-face {
  font-family: "Inter";
  src: url("polices/Inter-Regular.40d692fce188.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

@font-face {
  font-family: "Inter";
  src: url("polices/Inter-SemiBold.78a843fade9d.ttf") format("truetype");
  font-weight: 600;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

/* LA FAMILLE SERVIE — IBM Plex Serif, choisie par Kenny le 25/08 (P19, alors
 * pour le seul contenu) et étendue à tout le texte le 26/08 (P22, en-tête).
 * Le rapport hauteur d'x sur capitale a tranché entre deux serifs libres,
 * parce que c'est lui qui décide si un mélange avec le repli mathématique
 * paraît voulu ou subi :
 *
 *     Atkinson Hyperlegible Next   x/cap = 0,7425
 *     IBM Plex Serif Regular       x/cap = 0,7393   écart 0,003  ← retenue
 *     IBM Plex Serif SemiBold      x/cap = 0,7479
 *     Source Serif 4               x/cap = 0,709    écart 0,034  ← écartée
 *
 * Source Serif a été écartée sur un second motif, qui n'est pas l'esthétique :
 * son écart au REPLI Noto Sans Math (x/cap = 0,7507) atteint 0,042 — et Noto
 * sert six glyphes qui paraissent AU MILIEU d'une expression, entourés de
 * serif. L'écart s'y serait vu dans le mot même.
 *
 * SERVIE EN `.woff`, ET C'EST LE FICHIER OFFICIEL D'IBM, PAS UNE CONVERSION.
 * Le `.woff` de la publication `@ibm/plex-serif@2.0.0` pèse 90 404 et 96 072
 * octets, contre 217 332 et 222 876 pour les `.ttf` de la même publication :
 * 253 732 octets de moins servis à un téléphone, pour la même table de glyphes.
 * Le garde sait le mesurer (`FORMATS_MESURABLES`, décodeur WOFF 1.0 maison) —
 * `.woff2` reste refusé, faute de savoir en lire la `cmap`. */

@font-face {
  font-family: "IBM Plex Serif";
  src: url("polices/IBMPlexSerif-Regular.4c09eb709664.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

@font-face {
  font-family: "IBM Plex Serif";
  src: url("polices/IBMPlexSerif-SemiBold.12614abbd2f0.woff") format("woff");
  font-weight: 600;
  font-style: normal;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

/* L'ITALIQUE — P22 (point 2 ter), 26/08 : la note de la restitution et la
 * phrase du manque de « Mes données » se composent en italique, qui est une
 * FACE de la même famille, jamais une troisième graisse (§26 bis.1.3). Elle
 * est EMBARQUÉE — une italique synthétisée (oblique) varierait d'un appareil
 * à l'autre, le motif exact du refus du gras synthétique. Même publication
 * officielle IBM que les deux faces droites, couverture MESURÉE : 930 points
 * de code, les mêmes, et les six manques sont ceux du repli. Elle n'est
 * téléchargée que par les pages qui composent de l'italique. */
@font-face {
  font-family: "IBM Plex Serif";
  src: url("polices/IBMPlexSerif-Italic.ad355966a258.woff") format("woff");
  font-weight: 400;
  font-style: italic;
  unicode-range: U+0000-0304, U+0306-211C, U+211E-2204, U+2206-2228, U+222B-22EE, U+22F0-2A7C, U+2A7F-10FFFF;
}

@font-face {
  /* LE REPLI MATHÉMATIQUE — sous-ensemble de Noto Sans Math (9 560 octets
   * contre 1 015 684 pour la face entière) : embarquer 2 655 glyphes pour en
   * servir six serait du poids servi à un téléphone pour rien. Le garde
   * recalcule l'inventaire à chaque exécution : un sous-ensemble devenu
   * insuffisant fait ROUGIR `test_polices_embarquees`, jamais silence. */
  font-family: "Noto Sans Math";
  src: url("polices/NotoSansMath-SousEnsemble.f3f6c96cba85.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0305, U+211D, U+2205, U+2229-222A, U+22EF, U+2A7D-2A7E;
}

:root {
  /* ── LA PALETTE, CHAQUE VALEUR UNE FOIS ─────────────────────────────────
   *
   * REVUE ENTIÈREMENT LE 25/08 (P19), décision de Kenny : LE MARINE N'EST PAS
   * SEULEMENT UN ACCENT, C'EST L'ENCRE — le texte lui-même tire vers le bleu.
   * La conséquence commande tout le reste de la colonne claire : une encre
   * marine sur des gris CHAUDS jure, et les gris ont donc été refaits par
   * mélange de l'encre et du blanc, donc froids comme elle. Les surfaces,
   * elles, restent le crème : c'est le papier sous l'encre, pas le trait.
   *
   * LE FOND CLAIR EST PASSÉ DE `#FFFFFF` AU CRÈME `#F3F1EC` LE 26/08
   * (P19 bis), décision de Kenny — c'est l'identité qu'il a validée sur
   * maquette, et l'encre y rend **13,91:1**, très exactement le chiffre qu'il
   * avait mesuré. `DU-134` se clôt ici.
   *
   * CE QUI L'EMPÊCHAIT, ET COMMENT C'EST TOMBÉ. La veille, ce fond faisait
   * tomber `--semantique-partiel` (`#B8860B`) à **2,88:1** — le garde a été
   * mis devant ce fond exprès et l'a refusé en nommant `partiel`. La borne
   * mesurée alors reste vraie et vaut d'être gardée : **le fond clair le plus
   * sombre qui laisse les neuf couleurs au-dessus du seuil avait L ⩾ 0,9179**,
   * soit environ `#F8F6F0`, un crème qui ne se voit pas. Ce n'est pas le fond
   * qui a cédé, c'est l'ambre : `partiel` est descendu à `#9C7207`, DANS LES
   * DEUX THÈMES À LA FOIS — voir `semantique.css`, où le motif est écrit.
   * *Ce que le §26.3 interdit est de recolorer une couleur sémantique SELON
   * LE THÈME ; corriger une valeur qui manque son seuil ne l'entame pas.*
   *
   * LA COLONNE CLAIRE ENTIÈRE A ÉTÉ REFAITE AVEC LUI, jamais cette seule
   * valeur : un fond neuf change ce que chaque couleur rend dessus. La
   * méthode est celle de P19, appliquée au papier neuf — **les gris clairs
   * sont l'encre diluée par LA PAGE, à fraction d'encre INCHANGÉE** (0,199 ·
   * 0,239 · 0,579 · 0,720) ; seul le diluant passe du blanc au crème. Les
   * surfaces, elles, sont le crème lui-même ASSOMBRI d'un facteur (0,964 ·
   * 0,944 · 0,902) : une ombre garde la teinte du papier, et le pas de
   * chacune vers le fond est celui qu'elle tenait la veille contre le blanc.
   *
   * LE CHAMP RESTE BLANC, ET IL Y GAGNE : `#FFFFFF` sur un fond blanc ne se
   * distinguait que par sa bordure ; sur le crème il rend 1,13:1 et se voit.
   * C'est aussi le seul de la colonne qui aille vers le CLAIR quand les
   * surfaces vont vers l'encre — parce qu'on écrit SUR du papier blanc.
   *
   * CONTRASTES MESURÉS (WCAG 2.1), clair / sombre :
   *   texte 13,91 / 14,99 · texte fort 15,92 / 16,82 ·
   *   texte atténué 5,91 / 6,95 · accent 12,40 / 8,84. */
  --habillage-fond-clair: #F3F1EC;
  --habillage-fond-sombre: #16161A;
  --habillage-texte-clair: #12233F;
  --habillage-texte-sombre: #E8EAEF;
  /* L'ENCRE RENFORCÉE — l'énoncé, §26 bis.1.7. À corps réduit, une encre
   * marine paraît MOINS foncée : moins de matière pour la même couleur. Ce
   * n'est pas une seconde encre, c'est la même densifiée. */
  --habillage-texte-fort-clair: #0B1729;
  --habillage-texte-fort-sombre: #F5F7FB;
  --habillage-texte-attenue-clair: #515D70;
  --habillage-texte-attenue-sombre: #9BA1AC;
  /* LA BORDURE DE BLOC — c'est elle qui S'EFFACE en sombre (§26 bis.5 bis) :
   * 1,49 sur le fond clair, 1,23 sur le fond sombre. */
  --habillage-bordure-clair: #C6C8CA;
  --habillage-bordure-sombre: #26282F;
  /* LE TRAIT FONCTIONNEL — la ligne d'un tableau, entrée le 25/08, et il
   * existe pour que l'asymétrie des thèmes ne coûte rien à la JUSTESSE : une
   * ligne de relevé qu'on lit de travers donne une information fausse (voir
   * `.releve` plus bas). Il garde donc sa force dans les DEUX thèmes —
   * 1,62 sur le fond clair, 1,64 sur le fond sombre — là où la bordure de
   * bloc s'efface. */
  --habillage-trait-clair: #BDC0C3;
  --habillage-trait-sombre: #3A3C42;
  --habillage-bordure-forte-clair: #717A88;
  --habillage-bordure-forte-sombre: #4A4F59;
  /* LES TROIS SURFACES CLAIRES SONT LE CRÈME DU FOND, ASSOMBRI — 26/08
   * (P19 bis). Elles valaient le crème lui-même jusqu'à ce jour ; le crème
   * étant devenu le FOND, une surface qui l'aurait gardé rendrait 1,000:1 et
   * ne serait plus une surface. Chacune reprend le pas qu'elle tenait contre
   * le blanc la veille : légère 1,08 · surface 1,13 · active 1,25. */
  --habillage-surface-clair: #E5E4DF;
  --habillage-surface-sombre: #1E1F25;
  --habillage-surface-legere-clair: #EAE8E4;
  --habillage-surface-legere-sombre: #1C1D23;
  /* LA SURFACE ACTIVE — le troisième niveau, entré le 25/08. En sombre, c'est
   * lui qui SÉPARE : fond #16161A · surface #1E1F25 · active #24262D. */
  --habillage-surface-active-clair: #DBD9D5;
  --habillage-surface-active-sombre: #24262D;
  --habillage-champ-clair: #FFFFFF;
  --habillage-champ-sombre: #1B1C21;
  /* LE FOND D'INFORMATION — P44 (01/09), décision de Kenny : le bleu clair de
   * sa maquette du 31/08 entre à la palette, sur SPECIMEN VALIDÉ le 1er
   * septembre (les deux thèmes, valeurs et contrastes). P43 avait posé
   * l'encadré du changement de type sur la surface crème en publiant l'écart
   * — « une couleur neuve est une décision de charte, pas un geste de
   * correction » — ; la décision de charte est prise, et c'est celle-ci.
   *
   * POURQUOI CE N'EST PAS L'ACCENT SOMBRE, et la question a été posée :
   * `#93B9E3` est le bleu clair des boutons, mais c'est l'ACCENT — une encre
   * et un filet, jamais un fond. En fond de thème sombre il rendrait le
   * texte clair illisible — mesuré : `--habillage-texte-sombre` y rend
   * 1,70:1, le texte fort 1,90:1. Le seul texte qui passe sur l'accent
   * sombre est l'encre du thème clair (7,69:1), et c'est exactement ce que
   * `--habillage-sur-accent-sombre` dit déjà. Le fond d'information sombre
   * est donc un bleu SOMBRE de la famille de l'encre, pas l'accent.
   *
   * CONTRASTES MESURÉS (01/09, formules du garde), sur le fond neuf,
   * clair/sombre : texte 13,07/13,10 · fort 14,97/14,71 · atténué 5,55/6,07
   * · filet d'accent 11,66/7,73 — les trois textes au-dessus du 4,5:1, le
   * filet au-dessus du 3:1 graphique. Le garde du contraste RECALCULE ces
   * huit paires à chaque exécution et en assère le décompte
   * (`test_contraste.py`) : ces nombres-ci sont un témoignage daté, jamais
   * la source.
   *
   * LES EMPLOIS, ÉLARGIS PAR LES MAQUETTES DU 01/09 — P45 (V0.2). « Un seul
   * emploi » (P44, 1.5) valait jusqu'à ce que les DEUX MAQUETTES VALIDÉES de
   * la séance quotidienne posent le rôle en pavé d'information (« En classe
   * cette semaine », « Demain ») : les emplois sont désormais l'encadré du
   * changement de type (`[data-type-avertissement]`) et le pavé
   * `.pave-info` des écrans de séance — DEUX, décidés sur maquette, et
   * toujours pas cinq : l'interdit de l'extension « pendant qu'on y est »
   * reste entier, et les huit paires du garde couvrent tout texte posé sur
   * ce fond. */
  --habillage-info-clair: #E3EBF7;
  --habillage-info-sombre: #1B2331;
  /* L'ACCENT D'INTERFACE — `ARCHITECTURE.md` §12.1 le nommait depuis le
   * 24/08 (« accent d'interface, anneau de focus », deux rôles) et AUCUNE
   * variable ne le portait : l'anneau tenait les deux. Il entre le 25/08.
   * Il sert QUATRE choses, recomptées le 26/08 (P19 bis) : le filet de
   * l'objectif, la bordure des contrôles, le filet de l'étape COURANTE de la
   * frise, et la marque — cette dernière SEULEMENT là où l'élève ne travaille
   * pas (§26 bis.1.8) : sur l'item, la revue et l'interruption, elle est en
   * texte atténué. La ligne disait « trois » et nommait trois emplois : le
   * filet de la frise, entré le 25/08 avec elle, n'y avait jamais été inscrit.
   * UN ACCENT NE TRAVERSE PAS LES DEUX THÈMES, et c'est mesuré : `#052962`
   * sur le fond sombre rend **1,29:1** — invisible. */
  --habillage-accent-clair: #052962;
  --habillage-accent-sombre: #93B9E3;

  /* L'ENCRE SUR L'APLAT D'ACCENT — P21 (point 10), décision de Kenny : le
   * bouton de l'ACTION PRINCIPALE porte l'aplat, comme les maquettes
   * validées. Remesuré le 26/08 (jamais recopié) : texte sur aplat 14,00:1
   * en clair (#FFFFFF sur #052962) et 7,69:1 en sombre (#12233F sur
   * #93B9E3) ; l'aplat sur son fond 12,40:1 en clair et 8,84:1 en sombre.
   * La valeur sombre EST l'encre du thème clair — une référence, pas une
   * seconde source. */
  --habillage-sur-accent-clair: #FFFFFF;
  --habillage-sur-accent-sombre: var(--habillage-texte-clair);
  /* L'ANNEAU DE FOCUS — le rôle reste distinct (ARCHITECTURE §12.1), la
   * VALEUR est celle de l'accent depuis le 25/08 : c'est ce que la décision
   * de Kenny emporte (« l'accent remplace `#2F5FA8` »), et une seconde
   * valeur littérale identique serait une seconde source. Le focus se
   * distingue de la bordure du contrôle par l'ÉPAISSEUR et le DÉCALAGE
   * (2 px, `outline-offset: 2px`), jamais par la seule couleur. */
  --habillage-anneau-clair: var(--habillage-accent-clair);
  --habillage-anneau-sombre: var(--habillage-accent-sombre);

  /* ── LES VARIABLES EFFECTIVES — le thème clair est la base ──────────────── */
  --habillage-fond: var(--habillage-fond-clair);
  --habillage-texte: var(--habillage-texte-clair);
  --habillage-texte-fort: var(--habillage-texte-fort-clair);
  --habillage-texte-attenue: var(--habillage-texte-attenue-clair);
  --habillage-bordure: var(--habillage-bordure-clair);
  --habillage-trait: var(--habillage-trait-clair);
  --habillage-bordure-forte: var(--habillage-bordure-forte-clair);
  --habillage-surface: var(--habillage-surface-clair);
  --habillage-surface-legere: var(--habillage-surface-legere-clair);
  --habillage-surface-active: var(--habillage-surface-active-clair);
  --habillage-champ: var(--habillage-champ-clair);
  --habillage-info: var(--habillage-info-clair);
  --habillage-accent: var(--habillage-accent-clair);
  --habillage-sur-accent: var(--habillage-sur-accent-clair);
  --habillage-anneau: var(--habillage-anneau-clair);

  /* LE RAYON — un seul au dépôt (§26 bis) : tout arrondi est celui-ci ou 0. */
  --habillage-rayon: 6px;

  /* LA FACE SERVIE — SPEC §26 bis.1.1, décision de Kenny du 26/08 (P22) :
   * une seule famille partout. La variable unique `--habillage-police` avait
   * été scindée à P19 en `-interface`/`-contenu` pour nommer laquelle des
   * deux familles une règle sert ; à une seule famille, chacun des deux noms
   * mentirait — le nom d'avant la scission revient. Le jour où une seconde
   * famille reviendrait, ce serait une scission de nouveau, avec son motif.
   * Les guillemets font partie de la valeur : `"IBM Plex Serif"`. */
  --habillage-police: "IBM Plex Serif";
}

/* Le thème choisi « sombre » — remappage pur, aucune valeur littérale.
 *
 * `.theme-force[data-theme="sombre"]` — P48 (02/09) : le spécimen des figures
 * montre chaque figure dans les DEUX thèmes sur une même page. Un bloc qui
 * porte cet attribut remappe les variables pour lui seul (il pose son fond
 * et son encre, voir `.theme-force` plus bas). Le bloc jumeau
 * `.theme-force[data-theme="clair"]` remappe vers la colonne claire — sans
 * lui, un bloc « clair » sur une page sombre hériterait du sombre. Deux
 * blocs de thème de plus, aucune valeur littérale : le garde
 * `test_variables_css` les lit comme les autres. */
.theme-force[data-theme="clair"] {
  --habillage-fond: var(--habillage-fond-clair);
  --habillage-texte: var(--habillage-texte-clair);
  --habillage-texte-fort: var(--habillage-texte-fort-clair);
  --habillage-texte-attenue: var(--habillage-texte-attenue-clair);
  --habillage-bordure: var(--habillage-bordure-clair);
  --habillage-trait: var(--habillage-trait-clair);
  --habillage-bordure-forte: var(--habillage-bordure-forte-clair);
  --habillage-surface: var(--habillage-surface-clair);
  --habillage-surface-legere: var(--habillage-surface-legere-clair);
  --habillage-surface-active: var(--habillage-surface-active-clair);
  --habillage-champ: var(--habillage-champ-clair);
  --habillage-info: var(--habillage-info-clair);
  --habillage-accent: var(--habillage-accent-clair);
  --habillage-sur-accent: var(--habillage-sur-accent-clair);
  --habillage-anneau: var(--habillage-anneau-clair);
}

:root[data-theme="sombre"],
.theme-force[data-theme="sombre"] {
  --habillage-fond: var(--habillage-fond-sombre);
  --habillage-texte: var(--habillage-texte-sombre);
  --habillage-texte-fort: var(--habillage-texte-fort-sombre);
  --habillage-texte-attenue: var(--habillage-texte-attenue-sombre);
  --habillage-bordure: var(--habillage-bordure-sombre);
  --habillage-trait: var(--habillage-trait-sombre);
  --habillage-bordure-forte: var(--habillage-bordure-forte-sombre);
  --habillage-surface: var(--habillage-surface-sombre);
  --habillage-surface-legere: var(--habillage-surface-legere-sombre);
  --habillage-surface-active: var(--habillage-surface-active-sombre);
  --habillage-champ: var(--habillage-champ-sombre);
  --habillage-info: var(--habillage-info-sombre);
  --habillage-accent: var(--habillage-accent-sombre);
  --habillage-sur-accent: var(--habillage-sur-accent-sombre);
  --habillage-anneau: var(--habillage-anneau-sombre);
}

/* LE TROISIÈME THÈME « systeme » A DISPARU LE 26/08 (P21, décision de
 * Kenny) : son bloc `@media (prefers-color-scheme: dark)` vivait ici. Deux
 * thèmes nommés, le clair par défaut — un `data-theme="systeme"` résiduel
 * tomberait sur la colonne claire du `:root`, jamais sur un état non
 * stylé. */

/* ══════════════════════════════════════════════════════════════════════════
 * LA COMPOSITION — héritée d'`essai.css`, couleurs passées aux variables.
 * L'échelle typographique et le rythme sont ceux du SPEC §26 bis.
 * ══════════════════════════════════════════════════════════════════════════ */

/* ── L'ÉCHELLE DES CORPS, EN VARIABLES — P76 (08/09/2026), décision F de Kenny
 * du 7 septembre : « la taille des caractères doit être choisie de manière à
 * avoir un contenu harmonieux — ce n'est pas partout le cas ». Les SEPT valeurs
 * sont celles, closes, du SPEC §26 bis.1.2 — le garde
 * `tests/gardes/test_echelle_des_corps.py` les y LIT et exige que ce bloc les
 * porte toutes, une fois chacune. Ce bloc les NOMME PAR L'USAGE, et c'est le
 * SEUL endroit de cette feuille où un corps s'écrit en rem : toute autre
 * déclaration `font-size` est `var(--corps-…)`, dans cette feuille, dans les
 * gabarits et dans les scripts — le garde refuse le reste (un `0.8rem` posé
 * « parce que la touche est petite », un `em` hors du rendu mathématique).
 * MESURÉ AVANT D'ÉCRIRE (P76) : 53 corps en rem sur six valeurs de l'échelle
 * (0,9 ×29 · 0,95 ×16 · 1,1 ×3 · 1,25 ×3 · 1,35 ×1 · 1,5 ×1), un `0,9em` hors
 * du rendu mathématique (`.cadre .date`), et cinq `em` de géométrie de glyphe
 * (`.m-…`, `.maths sup`) que le §26 bis.1.2 exempte ; les 54 premiers sont
 * ramenés ici. Les textes des FIGURES ont leurs deux corps en unités du
 * `viewBox` (`app/services/figures/rendu.py`, `CORPS` et `CORPS_PETIT`) : ils
 * suivent la largeur de la figure, pas celle du texte, et le garde les tient
 * à part. ── */
:root {
  /* ce qui SITUE sans informer : mentions, frises, dates, légendes, la marque atténuée */
  --corps-legende: 0.9rem;
  /* un cran sous le courant : l'objectif, les cases, les compléments, le relevé */
  --corps-petit: 0.95rem;
  /* le texte que l'élève lit — la base (html : 17 px) */
  --corps-courant: 1rem;
  /* les titres de section (h2), l'énoncé, la valeur d'une réponse */
  --corps-titre-de-bloc: 1.1rem;
  /* la marque, le champ de composition et ses touches, le signe d'un bouton d'insertion */
  --corps-grand: 1.25rem;
  /* le titre de chaque écran (h1) */
  --corps-titre-de-page: 1.35rem;
  /* le titre de la restitution — le seul document qui s'imprime */
  --corps-titre-de-document: 1.5rem;
}

html {
  /* LA SEULE DÉCLARATION DE FAMILLE DE CETTE FEUILLE — P22 (26/08) : une
   * famille partout, servie à la racine et héritée par tout ce qui s'affiche.
   * Puis le repli mathématique (huit glyphes, `unicode-range` ci-dessus), puis
   * la pile serif du système en dernier ressort — pour les seuls glyphes hors
   * inventaire qu'une saisie libre pourrait porter. */
  font-family: var(--habillage-police), "Noto Sans Math", Georgia, "Times New Roman", serif;
  font-size: 17px;
  line-height: 1.55;
  background: var(--habillage-fond);
  color: var(--habillage-texte);
}

body {
  margin: 0;
  font-size: var(--corps-courant);
}

/* La règle groupée de « la famille du contenu » (P19 : `.item`, `.options`,
 * `.touche`, `.restitution`, `.cadre`, `.releve`, `.reponse-donnee`,
 * `.maths`, `.notation`) vivait ici. Retirée à P22 (26/08) : une seule
 * famille est servie, ces sélecteurs héritent de la racine. Le seul cas que
 * `habillage.css` n'atteint toujours pas est le champ MathLive, composé par
 * ses fontes KaTeX (`statique/mathlive/fontes.css`). */

main {
  max-width: 40rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

p {
  margin: 0 0 1rem;
}

/* LA HIÉRARCHIE DES TITRES — demande de Kenny du 18/08 (`DU-19`), règle au
 * §26 bis : titre de page > titre de section > corps, par la taille ET la
 * graisse. Générale et non par écran : un `<h2>` écrit dans six mois la
 * reçoit sans qu'on y pense. Le BALISAGE que ces règles attendaient est servi
 * depuis le 24/08 (P11a, `DU-45`) : chaque écran porte son `<h1>`, et les
 * amorces de la transparence leur `<h2>`. */
h1 {
  font-size: var(--corps-titre-de-page);
  font-weight: 600;
  margin: 0 0 1.25rem;
}

h2 {
  font-size: var(--corps-titre-de-bloc);
  font-weight: 600;
  margin: 0 0 0.75rem;
}

/* LE TITRE DE LA RESTITUTION — P21 (point 6), décision de Kenny : le <h1>
 * existait sans se lire comme un titre. 1,5 rem, la plus grande valeur de
 * l'échelle close (§26 bis.1.2) — jamais un corps inventé — et la graisse
 * reste 600, la plus forte des deux embarquées (§26 bis.1.3). Il demeure un
 * titre de DOCUMENT : Plex Serif par héritage (§26 bis.1.1), et la
 * hiérarchie du §26 bis.1.5 est renforcée, jamais entamée (h1 > h2). */
.restitution h1 {
  font-size: var(--corps-titre-de-document);
}

/* L'OBJECTIF DE L'ANNÉE — demande de Kenny du 18/08 (`DU-19`), règle au
 * §26 bis. Servi en permanence (`socle.html`, CDC §9.1 « en évidence ») : il
 * se distingue du corps par la graisse et par son filet, en couleur de texte
 * PLEINE — un objectif atténué serait le contraire d'une mise en évidence —,
 * et reste sous le titre de page par la taille : il situe, il ne titre pas. */
.objectif {
  font-size: var(--corps-petit);
  font-weight: 600;
  color: var(--habillage-texte);
  /* LE FILET est en couleur d'ACCENT depuis le 25/08 (P19) : c'est le premier
   * des QUATRE emplois de l'accent, énumérés une seule fois au :root — y
   * renvoyer plutôt que les recopier, une liste en double périmant à
   * l'emploi suivant (elle avait déjà périmé, voir là-bas). */
  border-left: 3px solid var(--habillage-accent);
  padding-left: 0.75rem;
  margin-bottom: 1.5rem;
}

/* ── LA MARQUE — 25/08 (P19), décision de Kenny ───────────────────────────
 *
 * « PY » suivi d'un CARRÉ, jamais d'un point. Le motif est mathématique et il
 * est écrit dans `gabarits/socle.html` : le carré est la marque de l'angle
 * droit, et le théorème de Pythagore ne vaut que là.
 *
 * COMPOSÉE, JAMAIS DESSINÉE. Les deux lettres sont du TEXTE, en IBM Plex Serif
 * SemiBold — la famille servie, héritée de la racine depuis P22 (elle était
 * nommée ici du temps des deux familles, la marque étant le nom de la chose,
 * pas un contrôle). Le carré est
 * une BOÎTE, pas un caractère : « ■ » (U+25A0) dépendrait de la fonte servie
 * et n'est pas à l'inventaire du garde des polices, donc il tomberait sur la
 * pile système. Une boîte se rend partout, identique.
 *
 * LE CARRÉ EST POSÉ SUR LA LIGNE DE BASE, et c'est là qu'on le trace dans le
 * coin d'un triangle rectangle. `vertical-align: baseline` est le défaut, mais
 * il est écrit ici parce qu'il est le SENS de cette forme, non une valeur par
 * défaut qu'on subit.
 *
 * DEUX TRAITEMENTS DEPUIS LE 26/08 (P19 bis), SPEC §26 bis.1.8, décision de
 * Kenny — et c'est une PROPRIÉTÉ, jamais une liste d'écrans :
 *
 *     la marque paraît EN ACCENT là où l'élève n'est pas en train de
 *     travailler, et EN ATTÉNUÉ là où il travaille. Elle ne disparaît jamais.
 *
 * Le motif est celui que l'en-tête d'`item.html` écrit de son écran : il
 * énumère ce qu'il ne porte PAS, et c'est « la moitié de sa définition ». Une
 * marque en accent, quarante fois de suite, est du bruit pendant que l'élève
 * calcule. Sur les écrans qui portent une frise, elle passe en outre sur SA
 * ligne (`.entete-parcours`) : une ligne au lieu de deux, présente sans rien
 * prendre.
 *
 * UNE VARIANTE, JAMAIS UN SECOND BLOC. `.marque-attenuee` ne redéclare que ce
 * qui diffère — le corps et la couleur — et hérite du reste. Deux blocs
 * complets divergeraient à la première retouche : la face changée d'un côté,
 * l'approche des capitales de l'autre.
 *
 * CONTRASTES MESURÉS, ET LE CARRÉ EN A BESOIN PARCE QU'IL EST UN ÉLÉMENT
 * GRAPHIQUE — les QUATRE cas, pour 3:1 requis :
 *
 *     accent  / fond clair  12,40:1      accent  / fond sombre  8,84:1
 *     atténué / fond clair   5,91:1      atténué / fond sombre  6,95:1
 *
 * Les quatre passent AUSSI le seuil de 4,5:1 du TEXTE, et ce n'est pas un
 * luxe : les deux lettres sont du texte, et à `0,9 rem` elles ne sont pas du
 * « grand texte » au sens de WCAG. La marge est structurelle plutôt que
 * heureuse — `--habillage-texte-attenue` est tenu à 4,5:1 sur son fond par
 * `tests/gardes/test_contraste.py`, qui porte des phrases entières ailleurs.
 * Sur une SURFACE (bandeau de test, bloc de message) : 11,00 / 5,24 en clair,
 * 8,05 / 6,33 en sombre. */

.marque {
  /* La famille est celle de la racine depuis P22 — la ligne `font-family`
   * qui la nommait ici (P19 bis) est devenue une redite. */
  font-size: var(--corps-grand);
  font-weight: 600;
  color: var(--habillage-accent);
  line-height: 1;
  margin: 0 0 1.5rem;
}

.marque-attenuee {
  /* Le plus petit corps de l'échelle close (§26 bis.1.2) — celui de la frise,
   * dont elle partage la ligne : deux corps différents sur une même ligne se
   * liraient comme deux niveaux d'information, et il n'y en a qu'un. */
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  /* La marge du bas appartient à la LIGNE, pas à la marque : c'est
   * `.entete-parcours` qui la porte quand la frise l'accompagne, et le bloc
   * qui suit quand elle est seule (écran d'interruption). */
  margin: 0 0 1.5rem;
}

/* ── LA LIGNE DE PARCOURS — la marque et la frise ensemble, 26/08 (P19 bis) ──
 *
 * `align-items: baseline` ET NON `center` : le carré de la marque est posé sur
 * SA ligne de base — c'est le sens de cette forme —, et l'aligner sur autre
 * chose que la ligne d'écriture de la frise le décollerait de la seule
 * référence qu'il ait. Les deux textes partagent donc une baseline, comme deux
 * mots d'une même phrase.
 *
 * LA FRISE PREND CE QUI RESTE (`flex: 1`) et la marque ne prend que sa
 * largeur : sur un téléphone, c'est la frise qui a besoin de place — quatre
 * libellés dont « Statistiques et probabilités » —, et son
 * `overflow-wrap: anywhere` s'en charge.
 *
 * `min-width: 0` sur la frise : sans lui, un élément de flex refuse de
 * rétrécir sous la largeur de son contenu, et la grille des quatre colonnes
 * pousserait la ligne hors de l'écran au lieu de couper ses libellés. */

.entete-parcours {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0 0 1.5rem;
}

.entete-parcours .marque,
.entete-parcours .frise {
  margin: 0;
}

.entete-parcours .frise {
  flex: 1;
  min-width: 0;
}

.marque-lettres {
  /* L'approche des deux capitales, resserrée d'un cheveu : `P` et `Y` laissent
   * un blanc à la jonction que la fonte ne corrige pas à ce corps. */
  letter-spacing: -0.01em;
}

.marque-carre {
  display: inline-block;
  /* Le côté est la hauteur d'x de la face qui porte les lettres : le carré
   * s'accorde alors au corps, quel qu'il soit, sans valeur à retoucher. */
  width: 0.52em;
  height: 0.52em;
  margin-left: 0.18em;
  background: currentColor;
  vertical-align: baseline;
}

/* ── LA FRISE DES SEGMENTS — SPEC §17.4 bis, 25/08 (P19) ──────────────────
 *
 * AUCUNE COULEUR NE PORTE L'ÉTAT, ET C'EST LA PROPRIÉTÉ CENTRALE DE CE BLOC.
 * « terminé », « en cours », « à venir » sont des MOTS, servis par le gabarit
 * depuis `seance.yaml` ; un élève qui ne verrait aucune couleur lirait la même
 * frise (CDC §11.1 bis, SPEC §26.4). Ce que la couleur fait ici est ce qu'elle
 * a le droit de faire : APPUYER une information que le texte porte déjà.
 *
 * L'ÉTAPE COURANTE SE DISTINGUE PAR TROIS MOYENS DONT AUCUN N'EST UNE COULEUR
 * PORTEUSE DE SENS — la graisse (600), la couleur de texte PLEINE au lieu de
 * l'atténuée, et le filet en couleur d'ACCENT (habillage, jamais sémantique).
 * Chacun tient sans les deux autres, et le sélecteur porte sur
 * `aria-current="step"` plutôt que sur une classe : ce qui est vrai à l'œil
 * est alors exactement ce qui est vrai à la voix, par construction.
 *
 * `grid-auto-flow: column` PLUTÔT QUE `repeat(4, 1fr)`, et ce n'est pas un
 * détail de style : le nombre de segments est celui des domaines PEUPLÉS
 * (`nombre_de_segments`), et il peut valoir moins de quatre. Une grille à
 * quatre colonnes fixes laisserait une colonne vide ce jour-là.
 *
 * AUCUNE ANIMATION, AUCUNE TRANSITION : le budget du §26 bis.4 est zéro, et
 * une frise qui glisserait d'une étape à l'autre porterait de l'information
 * par le mouvement — le défaut que « jamais par la couleur seule » interdit à
 * la couleur. */

.frise {
  margin: 0 0 1.5rem;
}

.frise ol {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.frise-etape {
  /* LE FILET EST AU-DESSUS, et il tient lieu de séparation : quatre colonnes
   * sans trait se liraient comme un paragraphe en morceaux. */
  border-top: 2px solid var(--habillage-trait);
  padding-top: 0.4rem;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  /* Un libellé long — « Statistiques et probabilités » — se coupe plutôt que
   * de pousser les trois autres colonnes hors de l'écran d'un téléphone. */
  overflow-wrap: anywhere;
}

.frise-etape[aria-current="step"] {
  border-top-color: var(--habillage-accent);
  color: var(--habillage-texte);
  font-weight: 600;
}

.frise-domaine,
.frise-etat {
  /* Deux lignes : le domaine, puis son état. Sur une seule, « Statistiques et
   * probabilités à venir » se lit comme une phrase et non comme un état. */
  display: block;
}

.message,
.erreur {
  border: 1px solid var(--habillage-bordure);
  border-radius: var(--habillage-rayon);
  padding: 0.75rem 1rem;
  background: var(--habillage-surface);
}

/* LE LIEN ET SON BOUTON COPIER — P24 (point 5 sexies) : le champ prend la
 * place, le bouton reste à sa taille. */
.lien-et-copie {
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
}

.lien-et-copie input {
  flex: 1 1 auto;
  min-width: 0;
}

/* L'ŒIL DU MOT DE PASSE — P24 (27/08). Le bouton vit DANS le champ, à
 * droite : un enveloppeur en position relative, l'œil en absolu. L'icône
 * est en `currentColor` atténué — un contrôle d'appoint, pas une action
 * principale — et la cible reste confortable (2,75 rem de haut, la règle
 * des cibles). Le champ garde un rembourrage à droite pour que la saisie ne
 * passe pas sous l'icône. */
.champ-mot-de-passe {
  position: relative;
  display: block;
}

.champ-mot-de-passe input {
  width: 100%;
  box-sizing: border-box;
  padding-right: 2.75rem;
}

.oeil {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  min-height: 2.75rem;
  width: 2.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  padding: 0;
  color: var(--habillage-texte-attenue);
  cursor: pointer;
}

.oeil:hover,
.oeil:focus-visible {
  color: var(--habillage-texte);
}

/* LE CODE DU REFUS — P24 (27/08) : à côté de la cause, en petit et atténué.
 * Kenny est le seul lecteur de la console, et le code lui sert ; il reste une
 * donnée d'appoint, jamais le message. 0,9 rem est le plus petit corps de
 * l'échelle close du §26 bis.1.2 — le garde refuse tout autre. */
.code-de-refus {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

/* L'ÉNONCÉ — 25/08, P19, décision de Kenny sur mesure.
 *
 * LE CORPS DESCEND À `1,1 rem`, l'une des sept valeurs de l'échelle close.
 * Le motif est une mesure de hauteur d'x : Kenny a comparé IBM Plex Serif à
 * Georgia et jugé Georgia plus juste. Georgia porte **0,4814** de hauteur d'x
 * relative, Plex Serif **0,516** — **+7,2 %** —, si bien qu'à corps égal Plex
 * paraît plus grande. Réduire le corps lui rend la discrétion cherchée.
 *
 * CE QUE LE MANDAT DE P19 CROYAIT, ET CE QUE LE FICHIER DISAIT — écrit ici
 * parce que l'écart change le résultat et non le geste. Le mandat annonçait
 * « de 1,25 rem à 1,1 rem », soit **un** cran. Le corps servi était **1,5 rem**
 * (`1,25 rem` est celui de `math-field`, plus bas) : la valeur nommée fait
 * donc descendre de **deux** crans, et l'énoncé passe à la taille d'un `h2`.
 * **La valeur nommée l'emporte** — elle est écrite trois fois au mandat, et
 * Kenny a vérifié qu'elle appartient à l'échelle — mais remonter à `1,35 rem`
 * est un changement d'UNE valeur ici, et c'est sa décision.
 *
 * L'ENCRE EST RENFORCÉE, ET C'EST LA CONSÉQUENCE DU CORPS RÉDUIT : à corps
 * plus petit, une encre marine paraît moins foncée — moins de matière pour la
 * même couleur. `--habillage-texte-fort` rend **17,97:1** sur le fond clair
 * et **16,82:1** sur le fond sombre, pour 4,5 requis. */
.enonce {
  font-size: var(--corps-titre-de-bloc);
  color: var(--habillage-texte-fort);
  font-variant-numeric: tabular-nums;
  margin: 1.5rem 0 0.5rem;
}

.consigne {
  margin-bottom: 1.5rem;
}

.apres {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

ul {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
}

.priorites {
  list-style: disc;
  padding-left: 1.25rem;
}

.priorites li {
  margin-bottom: 0.75rem;
}

/* LE TITRE D'UNE PRIORITÉ PASSE EN GRAS — P21 (point 8), décision de Kenny.
 * 600 et rien d'autre : le §26 bis.1.3 n'admet que 400 et 600, et
 * `test_echelle_des_corps` lit la règle. Le texte d'explication (`pourquoi`,
 * `premiere_action`) ne bouge pas. Aucune taille : ce n'est toujours pas un
 * titre de section (`DU-132` porte la question du balisage), c'est le nom de
 * la priorité, mis en relief dans son bloc. */
.priorites .titre {
  font-weight: 600;
}

.options li {
  margin-bottom: 0.5rem;
}

.reserves {
  margin-top: 2rem;
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
  /* La note du diagnostic en ITALIQUE — P22 (point 2 ter), décision de Kenny
   * du 26/08 : une face de la même famille, embarquée (voir la @font-face),
   * jamais une troisième graisse. */
  font-style: italic;
}

/* LES LIENS — P22 (point 2 bis), 26/08. Aucune règle `a` n'existait : le
 * navigateur décidait, et rendait VIOLETS les liens visités — Kenny l'a vu
 * sur le rail, dans les deux thèmes. L'accent porte le lien, et l'état
 * VISITÉ PREND LA MÊME COULEUR — décision écrite : les liens du produit sont
 * de la navigation (le rail, une sortie d'écran), pas un corpus à explorer ;
 * « déjà suivi » n'y apporte aucune information, et le violet du navigateur
 * est hors palette. `a` seul suffirait (l'origine auteur l'emporte sur le
 * `a:visited` du navigateur), mais l'état est nommé pour que la décision se
 * lise ici. CONTRASTES MESURÉS (26/08) : accent sur fond 12,40:1 en clair,
 * 8,84:1 en sombre ; sur SURFACE (celle du rail) 11,00:1 et 8,05:1 — tous
 * au-dessus du 4,5:1 du texte. Le soulignement du navigateur reste : c'est
 * lui qui dit « lien », jamais la couleur seule (CLAUDE.md §7). */
a,
a:visited {
  color: var(--habillage-accent);
}

/* LES CONTRÔLES SONT CERCLÉS D'ACCENT — 25/08, P19. La bordure d'un contrôle
 * n'est pas une séparation de bloc : c'est la LIMITE DE LA CIBLE, et elle doit
 * se voir dans les deux thèmes (14,00:1 en clair, 8,84:1 en sombre). C'est ce
 * qui rend tenable l'effacement des bordures de bloc en sombre (§26 bis.5 bis). */

/* Un `select` n'hérite pas non plus — les sélecteurs de la console (P21)
 * se rendaient dans la fonte par défaut du navigateur, vu à l'écran à P22. */
select {
  font: inherit;
}

input[type="text"],
input[type="email"],
input[type="number"],
input[type="password"] {
  /* `email` et `number` ajoutés à P22 : l'épreuve à l'écran a montré le champ
   * d'adresse de la connexion (P21) rendu dans la fonte par défaut du
   * navigateur — les contrôles n'héritent pas d'eux-mêmes, chaque type se
   * nomme. */
  font: inherit;
  width: 100%;
  box-sizing: border-box;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  color: inherit;
  margin-bottom: 1rem;
}

/* LE CHAMP DE COMPOSITION — P11b, le clavier mathématique (CDC §11.3).
 * L'élément `<math-field>` est créé par le script de l'écran d'item quand
 * MathLive est chargé ; il prend la place du champ de texte, et il en prend
 * l'habillage : mêmes bordure, fond, rayon — un élève ne doit pas voir deux
 * familles de champs. Le corps est 1,25 rem, de l'échelle close (§26 bis.1.2) :
 * une fraction composée a un numérateur et un dénominateur plus petits que le
 * corps, et un corps de lecture ordinaire les rendrait illisibles sur un
 * téléphone — c'est la géométrie relative du §12.3, portée par un corps plus
 * grand. La cible fait 2,75 rem au moins (§26 bis.2.2). */
math-field {
  font-size: var(--corps-grand);
  width: 100%;
  box-sizing: border-box;
  min-height: 2.75rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  color: inherit;
  margin-bottom: 1rem;
}

button {
  font: inherit;
  padding: 0.55rem 1.1rem;
  /* LA CIBLE TACTILE — §26 bis.2 : 2,75 rem au moins pour tout contrôle
   * actionnable. La valeur vient des touches d'insertion (passe W1), que la
   * spécification de surface a reprise pour tout bouton. */
  min-height: 2.75rem;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  color: inherit;
  cursor: pointer;
  margin: 0 0.5rem 0.5rem 0;
}

/* L'ACTION PRINCIPALE PORTE L'APLAT — P21 (point 10), décision de Kenny,
 * comme les maquettes validées. RÉSERVÉ à elle : un écran à quatre boutons
 * pleins devient un mur d'accent, et plus rien ne dit où aller — les gestes
 * secondaires gardent le contour. Le relevé écran par écran est au rapport
 * de P21 ; en deux mots : « Répondre », « Continuer », « Reprendre
 * l'essai », et le bouton de clé d'accès des deux écrans d'accès ; le
 * dialogue d'accueil n'en a AUCUN (ses boutons sont des CHOIX égaux —
 * marquer l'un biaiserait un choix qui appartient à l'élève), la
 * restitution non plus (écran terminal, l'impression est un confort —
 * §18.1 bis), la console non plus (pas UNE action). */
button.principal {
  background: var(--habillage-accent);
  color: var(--habillage-sur-accent);
  border-color: var(--habillage-accent);
}

button:hover,
button:focus-visible {
  /* LA SURFACE ACTIVE, ET NON LA SURFACE — 25/08, P19. En sombre, c'est le
   * troisième niveau de clarté qui dit « ce bouton répond » ; l'ancienne
   * valeur était celle des blocs et des en-têtes de tableau, si bien qu'un
   * bouton survolé prenait la couleur d'un en-tête. */
  background: var(--habillage-surface-active);
}

/* Le principal GARDE son aplat au survol — la surface active des boutons de
 * contour le déshabillerait ; sa réponse visible est l'anneau de focus. */
button.principal:hover,
button.principal:focus-visible {
  background: var(--habillage-accent);
}

/* LE FORMULAIRE FIGÉ — P25 (28/08), décision de Kenny : après un geste
 * réussi qui modifie, champs ET bouton sont grisés (attribut `disabled`,
 * posé par le script de la console) — un formulaire qui garde ses valeurs
 * et son bouton actif invite à recliquer, et des champs restés modifiables
 * laissent croire qu'on peut encore changer un geste qui est fait. Le
 * principal PERD son aplat en se figeant : un aplat grisé garderait l'air
 * d'une action. Le survol ne répond plus — la surface active dirait « ce
 * bouton répond », et il ne répond pas. */
button:disabled,
button.principal:disabled,
input:disabled,
select:disabled {
  background: var(--habillage-surface);
  border-color: var(--habillage-trait);
  color: var(--habillage-texte-attenue);
  cursor: default;
}

button:disabled:hover,
button:disabled:focus-visible,
button.principal:disabled:hover,
button.principal:disabled:focus-visible {
  background: var(--habillage-surface);
}

/* LE BOUTON DE REPRISE — P25 : séparé par un FILET, en bas du bloc — il ne
 * fait plus partie du formulaire, il le remplace. En contour, jamais en
 * aplat : l'aplat reste à l'action principale du volet (§26 bis), et
 * recommencer ne l'est pas. */
.reprise {
  border-top: 1px solid var(--habillage-trait);
  margin-top: 1.5rem;
  padding-top: 1rem;
}

/* LE LIEN HABILLÉ EN BOUTON — P23 (point 2), la page introuvable. L'inverse
 * exact de « Reprendre plus tard » (bouton habillé en lien parce que son
 * geste POSTE) : ici le geste NAVIGUE, donc un `<a>`, et la maquette validée
 * le montre en bouton d'aplat — l'action principale du seul écran qui n'en a
 * qu'une (§26 bis). Mêmes cotes que `button` (cible 2,75 rem, §26 bis.2.2),
 * même aplat que `button.principal`, texte sur accent — et le soulignement
 * disparaît ICI SEULEMENT : la forme de bouton dit la cible, le soulignement
 * des liens ordinaires ne perd rien. `:visited` suit, comme pour tout lien
 * du produit (la règle `a` ci-dessus). */
a.principal,
a.principal:visited {
  display: inline-block;
  box-sizing: border-box;
  min-height: 2.75rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-accent);
  color: var(--habillage-sur-accent);
  text-decoration: none;
}

/* LA LIGNE DE GESTES — P22 (point 2 quinquies), maquette validée par Kenny le
 * 26/08 : les gestes de RÉPONSE à gauche, côte à côte, et la SORTIE
 * (« Reprendre plus tard ») poussée à DROITE, en lien souligné et texte
 * atténué. Le motif qui tient la forme : la sortie n'est pas une réponse —
 * trois gestes à poids égal laisseraient croire qu'elle en est une troisième.
 * Si la ligne ne tient pas (téléphone), les gestes passent à la ligne et la
 * sortie reste à droite, toujours en lien : distincte quelle que soit la
 * disposition. Contraste du lien : l'atténué, 5,91:1 en clair et 6,95:1 en
 * sombre sur le fond — au-dessus du 4,5:1 du texte (et du 3:1 graphique). */
.gestes {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 0.25rem;
}

.gestes .secondaire {
  margin-left: auto;
}

/* Un BOUTON HABILLÉ EN LIEN — le geste poste, donc la balise reste un bouton ;
 * la forme dit qu'il n'est pas une réponse. La cible garde 2,75 rem
 * (§26 bis.2.2) : c'est le remplissage vertical qui la tient, pas une boîte. */
.gestes .secondaire button {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0.55rem 0;
  margin: 0;
  text-decoration: underline;
  color: var(--habillage-texte-attenue);
}

.gestes .secondaire button:hover,
.gestes .secondaire button:focus-visible {
  background: none;
  color: var(--habillage-texte);
}

/* L'ANNEAU DE FOCUS — la famille du §12.1 le nomme, et il change avec le
 * thème : un anneau unique serait invisible sur l'un des deux fonds. */
:focus-visible {
  outline: 2px solid var(--habillage-anneau);
  outline-offset: 2px;
}

.choix {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* ── LES TOUCHES D'INSERTION ──────────────────────────────────────────────
 *
 * 21/08, passe W1, `DU-75`. Un bouton qui insère « √ » dans le champ de
 * réponse. Ce n'est PAS le clavier mathématique du CDC §11.3 : voir le
 * commentaire de `gabarits/item.html`, qui porte la distinction.
 *
 * LA CIBLE TACTILE EST DEVENUE UNE RÈGLE LE 23/08 (§26 bis) : 2,75 rem de
 * côté au moins pour tout contrôle pressé au pouce. La valeur est celle que
 * la passe W1 avait posée en jugement d'ingénierie déclaré ; la
 * spécification de surface la reprend, et c'est désormais elle qui tranche —
 * exactement ce que le commentaire d'`essai.css` annonçait.
 *
 * `display: none` SOUS `[hidden]`, ET CE N'EST PAS UNE PRÉCAUTION DE STYLE.
 * Le conteneur est servi `hidden` et révélé par le script (`item.html`). Or
 * une règle `display: flex` ÉCRASE le `display: none` que le navigateur
 * applique à `[hidden]` : sans la ligne ci-dessous, les touches resteraient
 * visibles chez un élève sans JavaScript, et chaque clic ne ferait rien. Un
 * test le refuse. */

.touches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.6rem;
}

/* LE BLOC SANS SCRIPT — P12, `DU-75` (a). Rendu dans <noscript>, donc visible
 * seulement quand le script n'est pas là ; il nomme l'écriture « racine »
 * (SPEC §2 ter). Corps 0,95 rem — l'échelle close du §26 bis.1.2 —, graisse du
 * corps, couleur atténuée comme `.reserves` : il renseigne, il ne titre pas.
 * Aucun rayon, aucune animation. */
.sans-script {
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
  margin: 0 0 0.6rem;
}

.touches[hidden] {
  display: none;
}

.touche {
  /* Le bouton ordinaire de cette feuille, à trois écarts près : une cible
   * carrée à la règle du §26 bis, le signe agrandi, et l'espacement laissé au
   * `gap` du conteneur plutôt qu'à une marge. */
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0.35rem 0.6rem;
  margin: 0;
  font-size: var(--corps-grand);
  line-height: 1;
}

/* ── LA BASCULE DE THÈME ──────────────────────────────────────────────────
 *
 * Le pied de page de `socle.html` — `T-01`, CDC §11.1 bis : « le choix reste
 * modifiable à tout moment ». Servie `hidden` et révélée par le script, sur
 * le précédent exact des touches d'insertion : sans JavaScript, la bascule
 * immédiate n'existe pas, et un contrôle qui ne fait rien se lit comme une
 * panne (`DU-19`, le sélecteur inerte du 19/08).
 *
 * AUCUNE COULEUR NE PORTE LE CHOIX : trois boutons à mots, et l'état courant
 * est dit par `aria-pressed` — que ce sélecteur rend AUSSI visible par la
 * bordure, un moyen qui n'est pas une couleur porteuse de sens. */

/* LE BANDEAU DU COMPTE DE TEST — CDC §2.4 « Signalisation permanente »,
 * `S-08`, P9c. Collé au haut de l'écran (`sticky` : visible en tout temps,
 * même en cours de défilement — sans animation, le budget du §26 bis.4 est
 * zéro). Il se distingue par la GRAISSE (600) et le filet, jamais par une
 * couleur porteuse de sens (CLAUDE.md §7) : ses couleurs sont l'habillage,
 * et le texte dit tout — « Compte de test », et le décalage en cours. */
.bandeau-test {
  position: sticky;
  top: 0;
  margin: 0;
  padding: 0.5rem 1rem;
  font-size: var(--corps-legende);
  font-weight: 600;
  text-align: center;
  background: var(--habillage-surface);
  color: var(--habillage-texte);
  border-bottom: 2px solid var(--habillage-bordure-forte);
}

.bascule-theme {
  margin-top: 3rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--habillage-trait);
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.bascule-theme[hidden] {
  display: none;
}

.bascule-theme button {
  min-height: 2.75rem;
  font-size: var(--corps-legende);
}

.bascule-theme button[aria-pressed="true"] {
  border: 2px solid var(--habillage-texte);
  font-weight: 600;
}

/* LE RAIL — P21 (deux volets), refait à P22 (points 3 à 6, maquette validée
 * par Kenny le 26/08) : une COLONNE PLEINE HAUTEUR sur une SURFACE distincte
 * du contenu, séparée par un filet — c'est ce qui en fait un rail plutôt
 * qu'une liste de liens. La marque en tête, les liens, et le COMPTE au pied
 * avec son menu qui se déplie VERS LE HAUT, en flottant — il ne pousse rien,
 * sinon le contenu sauterait à l'ouverture. L'entrée courante est dite par
 * `aria-current` et rendue par la GRAISSE — jamais une couleur seule.
 *
 * `main:has(> .volets)` : les écrans à rail élargissent la page — le
 * sélecteur vit ici pour qu'AUCUN gabarit ne porte de classe en dur (le
 * garde des textes refuse tout nœud de texte dans un gabarit, attributs
 * compris quand ils sortent d'une balise). Un navigateur sans `:has()`
 * (antérieur à 2023) garde la colonne de 40 rem : le rail y est borné mais
 * entier — une dégradation, jamais une impasse. La colonne de lecture de
 * 40 rem se reporte sur le volet de contenu.
 *
 * SUR TÉLÉPHONE (sous 700 px), LE TIROIR EST LE RAIL — P22 (point 5) : une
 * barre en haut (le bouton et la marque, rien d'autre), le rail devient un
 * panneau fixe qui RECOUVRE le contenu. Le compte reste au PIED DU TIROIR —
 * pas en barre fixe au bas de l'écran : une barre permanente coûterait une
 * quarantaine de pixels sur CHAQUE écran, diagnostic compris, et occuperait
 * la zone du pouce, pour une action qu'on fait deux fois par séance. SANS
 * JavaScript, ni barre ni tiroir : les volets s'empilent — « liste puis
 * contenu », le repli de P21 (le script pose `data-tiroir`).
 *
 * CONTRASTES DU MENU, mesurés sur SA surface (`--habillage-champ`), pas sur
 * celle de la page : texte 15,70:1 clair / 14,13:1 sombre ; atténué 6,67 /
 * 6,55 ; accent (liens) 14,00 / 8,33 — tous au-dessus du 4,5:1 du texte. */
main:has(> .volets) {
  max-width: none;
  margin: 0;
  padding: 0;
}

.volets {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

/* Les deux cents pixels de la maquette validée du 27/08 (P23, point 4) sur
 * la base de 17 px du dépôt : 11,75 rem ≈ 200 px — le rail ouvert ; réduit,
 * 3,3 rem ≈ 56 px. En rem et non en px : le rail suit le corps si l'élève
 * agrandit le texte. */
.volet-gauche {
  flex: 0 0 11.75rem;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background: var(--habillage-surface);
  border-right: 1px solid var(--habillage-trait);
  padding: 1.25rem 1rem;
}

/* LE REDIMENSIONNEMENT NATIF DU RAIL — P117-UX2, la lettre de Kenny du 30/09
 * (« la poignée d'angle, une ligne de style, sans JavaScript ») : sur un
 * écran large, le rail ouvert se redimensionne à la poignée de son angle ;
 * sa largeur ne se retient pas d'une page à l'autre (aucun script, aucun
 * stockage). Le rail replié et le tiroir du téléphone n'en ont pas. */
@media (min-width: 700.02px) {
  .volets:not(.rail-reduit) > .volet-gauche {
    flex: 0 0 auto;
    width: 11.75rem;
    min-width: 11.75rem;
    max-width: 24rem;
    overflow: auto;
    resize: horizontal;
  }
}

/* La tête du rail : la marque à gauche, le bouton de réduction à sa droite
 * (P23, maquette validée). */
.rail-marque {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.25rem;
}

.rail-marque .marque {
  margin-bottom: 1.5rem;
}

/* LE BOUTON DE RÉDUCTION — un geste discret, pas une action de l'écran :
 * pas de contour, l'atténué, la surface active en réponse. La cible garde
 * 2,75 rem (§26 bis.2.2). Révélé par le script ; le `hidden` d'origine le
 * tait sans JavaScript. */
#rail-reduire {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  min-width: 2.75rem;
  padding: 0;
  margin: -0.5rem -0.25rem 0 0;
  background: none;
  border: none;
  color: var(--habillage-texte-attenue);
}

#rail-reduire[hidden] {
  display: none;
}

#rail-reduire:hover,
#rail-reduire:focus-visible {
  background: var(--habillage-surface-active);
}

/* L'ICÔNE ET LE MOT COEXISTENT, RAIL DÉPLOYÉ — P89 (12/09/2026), décision de
 * Kenny du 12 septembre sur la maquette validée
 * `docs/maquettes/maquette_rail_pictogrammes.html`. Jusque-là l'icône était
 * masquée par défaut et ne paraissait qu'à l'état réduit : les deux ne se
 * voyaient JAMAIS ensemble.
 *
 * C'EST UNE COLONNE FIXE DE LA LARGEUR DU TRACÉ — `flex: 0 0 16px`, la taille
 * des cinq SVG du rail. Fixe et non rétractable : c'est ce qui aligne tous les
 * libellés au même x, quelle que soit l'icône. `display: flex` pour que le SVG
 * se cale verticalement dans sa boîte. */
.rail-icone {
  flex: 0 0 16px;
  display: flex;
}

.volet-gauche ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.volet-gauche li {
  margin-bottom: 0.35rem;
}

/* LE LIEN PORTE DEUX BOÎTES DEPUIS P89 — la colonne d'icône et le mot — donc
 * il devient un flex. L'ÉCART EST DE 0,35 REM ET NON DE 0,6 COMME LA MAQUETTE,
 * et c'est le premier barreau de l'échelle de réparation que Kenny a posée le
 * 12/09 : à 0,6 rem, « Ma progression » SE REPLIE SUR DEUX LIGNES. Mesuré au
 * navigateur, à la largeur réelle du rail : l'intérieur du rail fait 166 px,
 * le lien en laisse 149, la colonne d'icône en prend 16 ; à 0,6 rem d'écart
 * (10,2 px) le mot dispose de 121 px et en demande 124 — il manque trois
 * pixels. À 0,35 rem (5,95 px) il dispose de 127 px. C'est le PLANCHER que la
 * décision fixe, et le second barreau — réduire le tracé de 16 à 14 px pour
 * les cinq — n'a pas eu à être employé.
 *
 * `min-width: 0` SUR LE MOT, et ce n'est pas un ornement : un élément de flex
 * ne rétrécit pas sous son `min-content` par défaut, et un libellé long
 * pousserait la colonne au lieu de s'ajuster — c'est le remède que le dépôt
 * emploie déjà sur les contrôles de `form.fiche`. */
.volet-gauche a {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--habillage-rayon);
  text-decoration: none;
}

.rail-libelle {
  min-width: 0;
}

.volet-gauche a:hover,
.volet-gauche a:focus-visible {
  background: var(--habillage-surface-active);
}

.volet-gauche button[data-fonction] {
  display: block;
  width: 100%;
  text-align: left;
  margin: 0;
}

.volet-gauche [aria-current="page"] {
  font-weight: 600;
  background: var(--habillage-surface-active);
}

/* LE CONTENU EST CENTRÉ DANS SON VOLET, À MARGES ÉGALES — P24 (27/08),
 * décision de Kenny, mesurée sur l'écran servi : le contenu commençait près
 * du rail et s'arrêtait loin du bord droit. Les marges `auto` d'un enfant
 * flex répartissent l'espace libre également des deux côtés — autant entre
 * le rail et le bloc qu'entre le bloc et le bord droit. Sur un écran SANS
 * rail, le volet est la page entière : `main { margin: 0 auto }` fait déjà le
 * même centrage. À 375 px le tiroir remplace le rail, le volet prend toute la
 * largeur moins ses marges — le centrage n'a plus d'objet et ne casse rien
 * (les marges auto d'un bloc pleine largeur sont nulles).
 *
 * LA LARGEUR N'EST PLUS FIGÉE — P75 (07/09/2026), décision C de Kenny du
 * 7 septembre, maquette `docs/maquettes/maquette_largeur_et_tiroir.html` §§1-2 :
 * *« le contenu doit être centré dans le volet à droite du rail, mais la
 * largeur qu'il occupe doit s'adapter et pas être figée »*. Le volet plafonnait
 * à 40 rem quelle que soit la place disponible ; sur un écran large, le relevé
 * défilait dans 640 px pendant que 700 px restaient vides à droite, et
 * « Thématique » se brisait en « Thématiqu / e ».
 *
 * LE PLAFOND RESTE, ET IL EST DE CONFORT, NON DE STRUCTURE : 72 rem. La
 * maquette le dit en toutes lettres — *« il garde un plafond de confort pour la
 * lecture, mais très au-delà de l'actuel »* —, et 72 rem est au-delà des 64 rem
 * que le tableau des comptes réclamait déjà, tout en bornant une ligne de prose.
 *
 * L'EXCEPTION `:has(table.comptes)` EST RETIRÉE, et ce n'est pas un nettoyage
 * de passage : à 64 rem sous un plafond général de 72, elle RÉTRÉCIRAIT le seul
 * écran pour lequel elle avait été écrite. Une exception qui devient plus
 * étroite que la règle n'est plus une exception, c'est un bogue en attente. */
.volet-droit {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 72rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

/* LE PIED DU RAIL ET LE MENU DE COMPTE — P22 (point 4). Le bouton : pastille
 * à l'initiale, prénom, chevron. Le chevron est une BOÎTE CSS, pas un
 * caractère — le motif du carré de la marque : un « ▴ » dépendrait de la
 * fonte servie et n'est pas à l'inventaire du garde des polices. */
.volet-pied {
  margin-top: auto;
  border-top: 1px solid var(--habillage-trait);
  padding-top: 0.75rem;
  font-size: var(--corps-legende);
  position: relative;
}

.volet-pied .compte {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: none;
  border-radius: var(--habillage-rayon);
  padding: 0.4rem 0.5rem;
  margin: 0;
  text-align: left;
  font-size: var(--corps-legende);
}

.volet-pied .compte:hover,
.volet-pied .compte:focus-visible {
  background: var(--habillage-surface-active);
}

.pastille {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--habillage-accent);
  color: var(--habillage-sur-accent);
  font-weight: 600;
  flex: none;
}

.compte-nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chevron {
  margin-left: auto;
  flex: none;
  width: 0;
  height: 0;
  border-left: 0.3rem solid transparent;
  border-right: 0.3rem solid transparent;
  border-bottom: 0.35rem solid var(--habillage-texte-attenue);
}

/* Le menu FLOTTE au-dessus du rail — position absolue ancrée au pied, jamais
 * dans le flux : il ne pousse rien. Sans JavaScript il reste dans le flux
 * (le script le replie et arme le bouton) : un sommaire, pas une impasse. */
.compte-menu:not([hidden]) {
  background: var(--habillage-champ);
  border: 1px solid var(--habillage-bordure-forte);
  border-radius: var(--habillage-rayon);
  padding: 0.5rem;
}

#compte-bouton:not([hidden]) ~ .compte-menu:not([hidden]) {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 0;
  right: 0;
}

.compte-adresse {
  margin: 0 0 0.5rem;
  padding: 0.25rem 0.5rem 0.5rem;
  color: var(--habillage-texte-attenue);
  border-bottom: 1px solid var(--habillage-trait);
  overflow: hidden;
  text-overflow: ellipsis;
}

.compte-ligne {
  display: block;
  padding: 0.4rem 0.5rem;
  border-radius: var(--habillage-rayon);
  text-decoration: none;
}

.compte-ligne:hover,
.compte-ligne:focus-visible {
  background: var(--habillage-surface-active);
}

/* La bascule de thème, dans le menu : une ligne — la légende puis les deux
 * boutons à mots, l'état dit par `aria-pressed` (bordure et graisse). */
.compte-menu .bascule-theme {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin: 0;
  padding: 0.4rem 0.5rem 0.5rem;
  border-bottom: 1px solid var(--habillage-trait);
}

.compte-menu .bascule-theme button {
  margin: 0;
  min-height: 2.75rem;
}

/* « Se déconnecter » est une LIGNE DE MENU, jamais un bouton en aplat ni à
 * contour : le §26 bis réserve l'aplat à l'action principale d'un écran, et
 * se déconnecter ne l'est jamais — un aplat sur la sortie attirerait l'œil. */
.compte-menu .deconnexion {
  margin: 0.25rem 0 0;
  color: inherit;
  font-size: inherit;
}

.compte-menu .deconnexion button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--habillage-rayon);
  padding: 0.4rem 0.5rem;
  margin: 0;
}

.compte-menu .deconnexion button:hover,
.compte-menu .deconnexion button:focus-visible {
  background: var(--habillage-surface-active);
}

/* LA BARRE MOBILE — cachée au-dessus de 700 px quoi qu'en dise le script. */
.barre-mobile {
  display: none;
}

/* ── LE RAIL RÉDUIT — P23 (point 4), décision de Kenny du 27/08 sur maquette
 * validée. RIEN NE DISPARAÎT : TOUT SE RESSERRE — la marque abrégée (« PY »
 * sans le carré, le nom accessible intact), la flèche retournée, les trois
 * entrées par leur icône, la pastille seule au pied — le compte RESTE
 * cliquable et son menu se déplie toujours vers le haut, élargi hors du
 * rail (56 px ne portent pas un menu). CES RÈGLES NE VIVENT QU'AU-DESSUS DE
 * 700 px : sur téléphone le tiroir est le rail (P22) et le rail réduit n'y a
 * pas de sens — décision de Kenny, le bouton n'y est pas affiché. ────────── */
@media (min-width: 700.02px) {
  .volets.rail-reduit .volet-gauche {
    flex-basis: 3.3rem;
    /* 0,25 rem de côté : la cible de 2,75 rem du bouton plus deux fois cette
     * marge tiennent dans les 3,3 rem — un rembourrage plus large pousserait
     * le `min-content` du rail au-delà de la base, et la colonne s'élargirait
     * en silence (mesuré : 61 px au lieu de 56 à 0,4 rem). */
    padding: 1.25rem 0.25rem;
  }

  .volets.rail-reduit .rail-marque {
    flex-direction: column;
    align-items: center;
  }

  .volets.rail-reduit .rail-marque .marque {
    margin-bottom: 0.25rem;
  }

  .volets.rail-reduit .marque .marque-carre {
    display: none;
  }

  .volets.rail-reduit #rail-reduire {
    margin: 0 0 0.75rem;
  }

  .volets.rail-reduit #rail-reduire svg {
    transform: scaleX(-1);
  }

  /* LE MOT RESTE DANS LA PAGE, MIS À L'ÉCART VISUELLEMENT — P89 (12/09/2026),
   * décision de Kenny. C'EST UN DÉFAUT QUI SE RÉPARE, PAS UN CONFORT : le
   * `display: none` d'avant retirait le libellé de l'ARBRE D'ACCESSIBILITÉ, et
   * l'icône porte `aria-hidden="true"` — le lien réduit n'avait donc AUCUN nom
   * accessible, et il n'était annonçable ni au lecteur d'écran ni à la
   * navigation au clavier.
   *
   * L'IDIOME EST CELUI DU DÉPÔT, REPRIS ET NON RÉINVENTÉ : le même que le nom
   * d'une case posée sur une figure (`.figure-case-posee .figure-case-nom`) et
   * que `.lecture-seule`, qui le cite déjà comme sa source. Ce n'est PAS la
   * forme de `.m-lu` (`clip-path: inset(50%)`), distincte et documentée pour un
   * autre motif. */
  .volets.rail-reduit .rail-libelle {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .volets.rail-reduit .volet-gauche a {
    justify-content: center;
    padding: 0.65rem 0;
    /* `gap: 0` : la gouttière du rail déployé décentrerait l'icône, le mot
     * étant hors du flux. Et `position: relative` borne la mise à l'écart au
     * lien plutôt qu'au premier ancêtre positionné, quel qu'il soit. */
    gap: 0;
    position: relative;
  }

  .volets.rail-reduit .compte-nom,
  .volets.rail-reduit .chevron {
    display: none;
  }

  .volets.rail-reduit .volet-pied .compte {
    justify-content: center;
    padding: 0.4rem 0;
  }

  .volets.rail-reduit #compte-bouton:not([hidden]) ~ .compte-menu:not([hidden]) {
    right: auto;
    min-width: 13rem;
  }
}

/* LA CONSOLE — P22 (commit 10), décisions de Kenny du 26/08 validées sur
 * maquette dans les DEUX thèmes. Le titre « Console d'administration » reste
 * AU-DESSUS des deux volets ; sur un <main> élargi par le rail, il garde le
 * retrait de la colonne. Le compte au pied du rail porte le même fond que la
 * fonction courante — la maquette validée le montre ainsi. */
main:has(> .volets) > h1,
main:has(> .volets) > noscript p {
  margin: 1rem 1.25rem 0;
}

.volet-pied .compte {
  background: var(--habillage-surface-active);
}

/* LA LISTE DES COMPTES, UN VRAI TABLEAU — P22 (point 2 septies) : des filets
 * entre les colonnes ET entre les lignes, une ligne d'en-tête sur une
 * surface distincte, en gras (600, la seule graisse forte embarquée). Une
 * valeur absente se DIT — « (aucun) », « (aucune) » — en texte atténué,
 * jamais une case vide. Sous 700 px, six colonnes ne tiennent pas : la table
 * DÉFILE horizontalement dans son propre cadre — l'empiler en fiches
 * referait l'écran, et les colonnes cesseraient d'être comparables ;
 * l'administration se fait d'abord au bureau, le défilement reste lisible. */
table.comptes {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--corps-petit);
}

table.comptes th,
table.comptes td {
  border: 1px solid var(--habillage-trait);
  padding: 0.4rem 0.6rem;
  text-align: left;
}

table.comptes thead th {
  background: var(--habillage-surface);
  font-weight: 600;
}

table.comptes td.absente {
  color: var(--habillage-texte-attenue);
}

@media (max-width: 700px) {
  table.comptes {
    display: block;
    overflow-x: auto;
  }

  /* `display: block` ÉCRASE le `display: none` que le navigateur applique à
   * `[hidden]` — le piège exact que `.touches` documente plus haut. Sans la
   * règle ci-dessous, les trois tables servies cachées (comptes, clés,
   * liens) restaient VISIBLES sous 700 px — squelettes d'en-têtes vides
   * avant tout chargement — et un `hidden` posé par le script n'y cachait
   * plus rien. Vu par le premier contrôle de rendu réel (P44, 01/09) avant
   * même son entrée au dépôt : la table sortait à 499 px d'un cadre de 375. */
  table.comptes[hidden] {
    display: none;
  }
}

/* LE FORMULAIRE DE CRÉATION — P22 (point 2 nonies) : étiquettes en GRAS,
 * alignées entre elles, champs à la même largeur, chaque couple sur sa
 * ligne. « (facultatif) » et la mention des destinataires restent en graisse
 * normale et en texte atténué : des précisions, pas des étiquettes. */
form.fiche {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.75rem 1rem;
  align-items: center;
}

form.fiche > label {
  font-weight: 600;
}

form.fiche > fieldset,
form.fiche > button[type="submit"] {
  grid-column: 1 / -1;
  justify-self: start;
}

/* TOUT ENFANT DIRECT DE `form.fiche` QUI N'EST NI UNE ÉTIQUETTE NI UN CONTRÔLE
 * DE SAISIE S'ÉTEND SUR LES DEUX COLONNES DE LA GRILLE ET OCCUPE LEUR LARGEUR.
 * P42, vu en production le 01/09 : le bloc d'avertissement du changement de
 * type — un <div> enfant direct du formulaire « modifier », né caché — n'était
 * couvert par aucune règle d'étendue et tombait dans la PREMIÈRE colonne, en
 * `max-content` : son paragraphe, mesuré d'un seul tenant, la gonflait à
 * 1 345 px (mesuré au navigateur, fenêtre de 1 280) et poussait tout ce qui se
 * calait sur la seconde colonne hors de l'écran. La règle vise la PROPRIÉTÉ et
 * non le bloc par son nom : le prochain enfant ajouté à une fiche naît couvert
 * au lieu de rejouer le défaut. PAS de `justify-self: start` ici, et la
 * différence avec la règle ci-dessus n'est pas de style : `fieldset` et le
 * bouton d'envoi se dimensionnent à leur contenu par choix ; un avertissement
 * rétréci à son contenu le plus court cesserait d'occuper la largeur des deux
 * colonnes, qui est la sienne. */
form.fiche > :not(label):not(input):not(select):not(textarea) {
  grid-column: 1 / -1;
}

/* L'ENCADRÉ DU CHANGEMENT DE TYPE — P43 (01/09), décision de Kenny : le pavé
 * de la maquette validée le 31/08 — un fond, un filet vertical sur le bord
 * gauche, les décomptes alignés à droite. Le bloc s'affichait NU : le script
 * faisait son travail, aucune règle de style ne le visait (le troisième point
 * vu en production le 01/09) — P42 lui avait donné son ÉTENDUE, pas son
 * habillage.
 *
 * LES COULEURS VIENNENT DES VARIABLES. À P43, l'écart avec la maquette était
 * ASSUMÉ ET PUBLIÉ : elle montrait un pavé BLEU CLAIR, la palette ne portait
 * aucun bleu de FOND, et une couleur neuve est une décision de charte, pas un
 * geste de correction (P43, 3.1) — le pavé prenait la SURFACE. LA DÉCISION DE
 * CHARTE A ÉTÉ PRISE LE 1er SEPTEMBRE (P44) : le rôle `--habillage-info`
 * existe, validé sur specimen, et le pavé le prend — c'est le bleu de la
 * maquette. Le filet ne change pas : l'ACCENT, l'idiome exact du filet de
 * `.objectif`, 3 px sur le bord gauche.
 *
 * CONTRASTES MESURÉS (01/09, formules du garde), sur le fond d'information,
 * clair/sombre : texte 13,07 / 13,10 · fort 14,97 / 14,71 · atténué (la
 * mention) 5,55 / 6,07 — tous au-dessus du 4,5:1 du texte — · filet d'accent
 * 11,66 / 7,73, au-dessus du 3:1 graphique. Depuis P44, le garde du contraste
 * RECALCULE ces huit paires (`test_contraste.py`) : elles ont cessé d'être des
 * nombres que seul un rapport portait.
 *
 * AUCUNE PROPRIÉTÉ `display` : le bloc naît `hidden`, et l'attribut ne
 * l'emporte que sur un élément dont aucune règle ne fixe le `display` — une
 * ligne de plus ici l'afficherait en permanence.
 *
 * L'ÉTENDUE DE P42 EST INTACTE : rien ici n'écrit de grille — la règle
 * ci-dessus continue d'étendre le bloc sur les deux colonnes, et le garde de
 * la disposition le tient. */
[data-type-avertissement] {
  background: var(--habillage-info);
  border-left: 3px solid var(--habillage-accent);
  padding: 0.75rem 1rem;
  box-sizing: border-box;
}

/* Les décomptes de l'historique : l'intitulé à GAUCHE, le nombre à DROITE,
 * comme sur la maquette. La table prend la largeur du pavé — sans elle,
 * l'alignement à droite ne se verrait pas — et l'intitulé reste en graisse
 * normale : le pavé ouvre déjà sur un `<strong>`, deux graisses fortes se
 * disputeraient la lecture. */
[data-type-decomptes] {
  width: 100%;
  border-collapse: collapse;
  margin: 0.5rem 0;
}

[data-type-decomptes] th {
  text-align: left;
  font-weight: 400;
  padding: 0.15rem 0;
}

[data-type-decomptes] td {
  text-align: right;
  padding: 0.15rem 0;
}

/* LES CONTRÔLES DE LA FICHE CÈDENT — P25, mesuré à 375 px : la largeur
 * intrinsèque d'un <select> (la plus longue option — « Doura ·
 * testeur2@exemple.fr · eleve · compte de test ») gonflait la colonne de la
 * grille à 438 px dans un volet de 333, et tout ce qui se calait sur la
 * colonne débordait de l'écran. `min-width: 0` retire ce plancher : la
 * colonne redevient la largeur du volet, et le contenu du contrôle est
 * tronqué par lui plutôt que l'écran par le contrôle. */
form.fiche select,
form.fiche input {
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}

/* LE DÉPLIANT DU SEUIL (P60) — même mesure, même remède : le `<select>` des
 * seuils recevables débordait du cadre à 375 px (bord droit à 392, attrapé
 * par `test_rien_ne_depasse_du_cadre`), parce que sa plus longue option
 * fixait sa largeur intrinsèque. Il cède comme ceux de la fiche. */
.vivier-seuil select {
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}

form.fiche fieldset legend {
  font-weight: 600;
}

/* UNE RANGÉE DE DESTINATAIRE — P25 : l'adresse et son « Retirer » vivent SUR
 * LA MÊME LIGNE, et c'est la boîte qui le tient — le champ CÈDE (flex,
 * min-width 0) pour que la rangée tienne dans le volet à 375 px : mesuré
 * avant cette règle, la rangée débordait de 11 px (la largeur intrinsèque
 * d'un champ courriel plus le bouton). Le `min-inline-size: min-content`
 * PAR DÉFAUT d'un fieldset l'empêche de céder, et le `justify-self: start`
 * de `form.fiche > fieldset` le fait dimensionner à son CONTENU — mesuré :
 * sans les deux lignes ci-dessous, le bloc débordait de 22 px à 375 px, la
 * mention lui donnant sa largeur au lieu de la colonne. */
[data-destinataires] {
  min-inline-size: 0;
  max-inline-size: 100%;
  box-sizing: border-box;
}

[data-destinataires] [data-liste] p {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
}

[data-destinataires] [data-destinataire] {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
}

[data-destinataires] [data-liste] button {
  margin: 0;
  flex-shrink: 0;
}

.facultatif {
  font-weight: 400;
  color: var(--habillage-texte-attenue);
}

.mention {
  margin: 0.25rem 0 0.75rem;
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
}

@media (max-width: 700px) {
  form.fiche {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }

  form.fiche > label {
    margin-top: 0.75rem;
  }
}

@media (max-width: 700px) {
  /* Le repli sans JavaScript : « liste puis contenu », la décision de P21. */
  .volets {
    display: block;
    min-height: 0;
  }

  .volet-gauche {
    margin-bottom: 1.5rem;
    border-right: none;
    border-bottom: 1px solid var(--habillage-trait);
  }

  .volet-droit {
    padding: 1.5rem 1.25rem 4rem;
  }

  /* Avec JavaScript (`data-tiroir` posé) : la barre en haut, le rail en
   * tiroir fixe qui RECOUVRE le contenu — il ne le pousse pas. */
  .volets[data-tiroir] .barre-mobile:not([hidden]) {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 1.25rem;
    border-bottom: 1px solid var(--habillage-trait);
  }

  .volets[data-tiroir] .barre-mobile .marque {
    margin: 0;
  }

  .volets[data-tiroir] .volet-gauche {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(80vw, 17rem);
    z-index: 10;
    overflow-y: auto;
    margin: 0;
    border-right: 1px solid var(--habillage-bordure-forte);
    border-bottom: none;
  }

  .volets[data-tiroir]:not(.tiroir-ouvert) .volet-gauche {
    display: none;
  }

  /* Une seule marque visible à la fois : celle de la barre. */
  .volets[data-tiroir] .rail-marque {
    display: none;
  }

  /* LE RAIL RÉDUIT N'EXISTE PAS SUR TÉLÉPHONE — P23 (point 5), décision de
   * Kenny : il n'a pas de sens sur un écran étroit, le tiroir de P22 reste
   * le comportement, et le bouton de réduction ne s'y affiche pas — quoi
   * qu'en dise le script ou la mémoire du navigateur. */
  #rail-reduire {
    display: none;
  }
}

/* LE PIED DE DÉCONNEXION — P21 (26/08), §17.5. La même retenue que la bascule
 * de thème qu'il côtoie : un geste de pied de page, jamais une action de
 * l'écran. Sa marge haute est plus courte parce qu'il suit la bascule quand
 * les deux sont servis — deux fois 3 rem feraient un trou. */
.deconnexion {
  margin-top: 1rem;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.deconnexion[hidden] {
  display: none;
}

.deconnexion button {
  min-height: 2.75rem;
  font-size: var(--corps-legende);
}

/* ── LA ZONE DE NOTATION DE LA RESTITUTION — `DU-46`, 24/08 ───────────────
 *
 * Le fragment de notation mathématique qu'un texte de `libelles.yaml` déclare
 * entre accents graves, servi par la macro `avec_notation` du gabarit. UNE
 * SEULE PROPRIÉTÉ, ET ELLE EST FONCTIONNELLE : une notation ne se coupe pas en
 * fin de ligne — « a − r » scindé après le signe se lit comme deux choses.
 *
 * RIEN D'AUTRE, ET C'EST LE §26 bis QUI LE REFUSE : aucune taille (l'échelle
 * des corps est close), aucune graisse (400 y suffit), aucune `font-family`
 * (interdite hors rendu mathématique, §26 bis.1.1 — les glyphes de notation
 * passent par le même `unicode-range` que le reste de la prose), aucune
 * couleur, aucun rayon, aucune animation. */

.notation {
  white-space: nowrap;
}

/* ── LE RELEVÉ DE LA RESTITUTION ──────────────────────────────────────────
 *
 * CE N'EST PAS DE LA DÉCORATION, ET LE MOTIF EST DE JUSTESSE.
 *     Constaté à l'usage le 17/08 : les lignes et les colonnes du tableau du
 *     §20.7 ne se distinguaient pas — le navigateur rend un `<table>` nu sans
 *     aucune séparation —, et on lit alors la donnée d'une ligne en croyant
 *     lire celle d'une autre. Un tableau qu'on lit de travers DONNE UNE
 *     INFORMATION FAUSSE : la réponse attendue d'une question attribuée à la
 *     question du dessus est une erreur de contenu, pas un défaut d'agrément.
 *
 * AUCUNE COULEUR NE PORTE D'INFORMATION (CLAUDE.md §7) : l'alternance de
 * fond marche une ligne sur deux sans distinguer juste de faux, et un lecteur
 * qui ne verrait aucune couleur lirait le même tableau.
 *
 * LES QUATRE MOYENS SONT STRICTEMENT FONCTIONNELS, ET CHACUN RÉPOND À UN DÉFAUT
 *     · bordures horizontales + alternance de fond — SÉPARER LES LIGNES ;
 *     · en-têtes distincts (fond, graisse, alignement haut) — dire où commence
 *       la donnée ;
 *     · alignement stable en haut de cellule — sans quoi une cellule d'une
 *       ligne se lit à la hauteur d'une autre dès qu'un énoncé fait trois
 *       lignes, ce qui est le cas de plusieurs items de `S1-STA-028` ;
 *     · largeurs bornées — un énoncé long écrasait les trois autres colonnes
 *       jusqu'à les rendre illisibles.
 *
 * LE DÉBORDEMENT HORIZONTAL EST TENU PAR LE CONTENEUR, JAMAIS PAR LA PAGE :
 * sur un téléphone, c'est le tableau qui défile, pas le corps du document. */

.releve {
  margin-top: 2.5rem;
  /* Le tableau défile SEUL. Sans ceci, une ligne large pousse la page entière
   * et tout le texte de la restitution devient illisible sur un téléphone. */
  overflow-x: auto;
  /* LE CONTENEUR QUI FAIT DÉFILER EST AUSSI LE BLOC CONTENANT — P44 (01/09),
   * vu par le premier contrôle de rendu réel avant même son entrée au dépôt.
   * Les mots des lecteurs d'écran (`.m-lu`, position absolue) posés dans les
   * cellules avaient pour bloc contenant la RACINE : le rognage de cette
   * section ne les atteignait pas, et leurs boîtes d'un pixel, posées à la
   * position statique de cellules défilées, poussaient la page à 571 px sur
   * un écran de 375 — la restitution défilait horizontalement vers du vide.
   * `relative` les rattache à la section, qui les rogne avec le reste. */
  position: relative;
}

.releve table {
  /* `collapse` — sinon chaque cellule porte sa propre bordure et le tableau
   * se lit comme une grille de cases, non comme des lignes. */
  border-collapse: collapse;
  width: 100%;
  /* LES COLONNES S'AJUSTENT À LEUR CONTENU — P75 (07/09/2026), décision C de
   * Kenny du 7 septembre, maquette §2 : *« les colonnes courtes ne se coupent
   * pas — la colonne longue absorbe le reste »*.
   *
   * `table-layout: fixed` vivait ici depuis P19, avec une largeur en
   * pourcentage par en-tête, et le motif était juste À L'ÉPOQUE : dans un volet
   * de 40 rem, un énoncé de trois lignes prenait tout et les autres colonnes se
   * réduisaient à un mot par ligne. Le remède a survécu à sa cause — le volet
   * n'est plus figé (voir `.volet-droit`) —, et il coûtait plus qu'il ne
   * rendait : une largeur imposée COUPE un en-tête court quoi qu'il arrive, et
   * « Thématique » se lisait « Thématiqu / e ».
   *
   * `auto` rend l'arbitrage au navigateur, qui donne à chaque colonne ce que
   * son contenu demande. Ce qui empêche l'énoncé de tout prendre n'est plus une
   * largeur : c'est le `nowrap` des colonnes courtes ci-dessous, qui leur
   * réserve leur mot entier — l'énoncé absorbe le reste, et il est le seul dont
   * le contenu se compte en lignes. */
  table-layout: auto;
  min-width: 34rem;
  font-size: var(--corps-petit);
}

.releve th,
.releve td {
  /* SÉPARER LES LIGNES — c'est le défaut constaté, et voici ce qui le ferme.
   * `--habillage-trait` ET NON `--habillage-bordure` depuis le 25/08 (P19) :
   * la bordure de bloc s'efface en sombre, ce trait-ci ne le fait PAS, parce
   * qu'une ligne de relevé lue de travers donne une information fausse. */
  border-bottom: 1px solid var(--habillage-trait);
  padding: 0.55rem 0.6rem;
  /* ALIGNEMENT STABLE. Le défaut par défaut est `middle` : sur une ligne dont
   * l'énoncé fait trois lignes et la réponse un caractère, la réponse flotte
   * au milieu et se lit à la hauteur de la ligne voisine. */
  vertical-align: top;
  text-align: left;
  /* Un énoncé sans espace — une longue expression — déborderait sa colonne. */
  overflow-wrap: anywhere;
}

.releve thead th {
  /* LES EN-TÊTES SE DISTINGUENT DE LA DONNÉE : un fond, une graisse, et une
   * bordure plus marquée. Trois moyens plutôt qu'un, parce qu'aucun n'est une
   * couleur porteuse de sens et qu'ils tiennent chacun sans les autres. */
  background: var(--habillage-surface);
  font-weight: 600;
  border-bottom: 2px solid var(--habillage-bordure-forte);
  /* UN EN-TÊTE NE SE COUPE JAMAIS — P75 (07/09/2026), décision C, maquette §2.
   * « Thématique » se lisait « Thématiqu / e ». Voir le bloc des colonnes plus
   * bas pour ce qui a remplacé les largeurs imposées. */
  white-space: nowrap;
}

.releve tbody tr:nth-child(even) {
  /* L'ALTERNANCE DOUBLE LA BORDURE, elle ne la remplace pas : sur une ligne
   * haute — un énoncé de trois lignes —, deux traits éloignés ne suffisent pas
   * à tenir l'œil sur la bonne rangée. Un fond à peine distinct, et qui ne dit
   * rien : une ligne sur deux n'est ni juste ni fausse. */
  background: var(--habillage-surface-legere);
}

/* ── LES COLONNES COURTES NE SE COUPENT PAS — P75 (07/09/2026), décision C de
 * Kenny du 7 septembre, maquette §2. Le bloc de largeurs en pourcentage qui
 * vivait ici (18 % / 42 % / 20 % / 20 %, puis 12 % pour la cinquième depuis
 * P74) est RETIRÉ avec le `table-layout: fixed` qui le rendait nécessaire :
 * voir `.releve table` plus haut.
 *
 * CE QUI TIENT LA MISE EN PAGE À LEUR PLACE : quatre colonnes sur cinq portent
 * des libellés courts et des réponses brèves, et `nowrap` leur réserve leur mot
 * entier ; la colonne de la QUESTION est la seule dont le contenu se compte en
 * lignes (plusieurs items de `S1-STA-028` et `S1-FCT-005` sont des phrases
 * entières, et elle porte DEUX phrases depuis P74) — elle absorbe donc tout le
 * reste, ce qui est exactement le partage voulu.
 *
 * LE `nowrap` EST SUR L'EN-TÊTE ET SUR LA THÉMATIQUE, PAS SUR LES RÉPONSES : un
 * en-tête est un libellé fixe et court, une thématique est un nom de domaine ;
 * une RÉPONSE peut être longue (« L'ordonnée à l'origine ») et doit pouvoir
 * revenir à la ligne, sans quoi elle pousserait le tableau à défiler.
 *
 * Le `nowrap` des en-têtes vit dans LEUR bloc, plus haut — un second
 * `.releve thead th { }` ici ferait deux endroits où lire la même règle. */

.releve td.thematique {
  white-space: nowrap;
}

.releve table:has(thead th:nth-child(5)) {
  /* Une colonne de plus a besoin de plus de place avant de défiler : sans
   * cela, les quatre premières s'écrasent d'un cinquième chacune. */
  min-width: 40rem;
}

.releve-consigne {
  /* LA CONSIGNE SOUS L'ÉNONCÉ, EN GRAS (maquette §1). Deux moyens plutôt
   * qu'un : le bloc la met sur sa propre ligne, la graisse dit que c'est ELLE
   * qui pose la question. Aucune couleur — un relevé imprimé n'en a pas. */
  display: block;
  margin-top: 0.35rem;
  font-weight: 600;
}

.releve-explication td {
  /* LE PANNEAU PREND LA LIGNE ENTIÈRE, ET IL EST UN BLOC D'EXPLICATION COMME
   * LES AUTRES — P75 (07/09/2026), décision D de Kenny du 7 septembre,
   * maquette §3 : *« fond bleu clair de la charte, comme tous les blocs
   * d'explication »*.
   *
   * IL ÉTAIT GRIS. Son fond (`--habillage-surface-legere`) est CELUI DE
   * L'ALTERNANCE une-ligne-sur-deux du même tableau, quinze lignes plus haut :
   * le panneau se confondait donc avec une rangée paire, et rien ne disait
   * qu'on lisait une explication plutôt qu'une question de plus. Il prend le
   * fond et le FILET de `.pave-info` — le pavé du rappel de semaine et de
   * l'explication resservie —, sans les recopier : les deux mêmes variables.
   *
   * `--habillage-info` N'EST PAS UNE COULEUR SÉMANTIQUE (§26.3, la palette
   * close de `semantique.css`) : c'est de l'HABILLAGE, il change avec le
   * thème, et il ne porte aucune information seul — le filet, le titre du
   * panneau et sa position sous la ligne la portent. */
  background: var(--habillage-info);
  border-left: 3px solid var(--habillage-accent);
  padding: 0.9rem 1rem 0.7rem;
}

.releve-explication .corrige {
  margin: 0.4rem 0 0;
  padding-left: 1.2rem;
}

.releve-explication .corrige li {
  margin-bottom: 0.5rem;
}

.releve-explication .corrige-lien {
  margin: 0.6rem 0 0;
}

.releve-avertissement {
  /* LE BANDEAU DES QUESTIONS INDISPONIBLES — il n'existe que s'il y a lieu.
   * Un filet à gauche le pose comme un cadre, sans alerte : ce qu'il dit est
   * rassurant (« leurs résultats sont conservés »), pas une panne. */
  border-left: 3px solid var(--habillage-bordure-forte);
  padding: 0.5rem 0.8rem;
  margin: 0 0 0.8rem;
}

.releve .sans-verdict {
  /* LE TROISIÈME ÉTAT occupe les deux colonnes de droite (`colspan`), et sa
   * phrase est du texte, non une donnée mathématique : elle se distingue par
   * l'italique, qui survit à l'absence de couleur. */
  font-style: italic;
  color: var(--habillage-texte-attenue);
}

/* ── LE RENDU MATHÉMATIQUE ────────────────────────────────────────────────
 *
 * TOUT LE MOTEUR DE RENDU EST ICI ET DANS `gabarits/mathematiques.html`.
 * Aucune police téléchargée, aucune bibliothèque, aucune requête sortante
 * (ARCHITECTURE §6.1) : une fraction est deux boîtes et une bordure, une
 * barre d'événement contraire est une bordure haute.
 *
 * AUCUNE COULEUR N'EST DÉCLARÉE DANS CE BLOC, et c'est délibéré : la barre
 * de fraction et la barre de `\overline` prennent `currentColor`, donc la
 * couleur du texte — le texte mathématique est composé EN COULEUR
 * D'HABILLAGE, à contraste maximal dans chaque thème, jamais en couleur
 * sémantique (ARCHITECTURE §12.3, SPEC §26.2). C'est ce qui rend `T-03`
 * atteignable au seuil de 4,5:1.
 *
 * LES TAILLES EN `em` DE CE BLOC NE SONT PAS L'ÉCHELLE DU §26 bis : ce sont
 * la géométrie relative des glyphes (numérateur, exposant, délimiteur), et
 * elles suivent la taille du texte qui les porte. L'exposant garde un
 * plancher lisible parce qu'il n'est jamais composé qu'à un niveau (§12.3). */

.maths {
  /* `inline`, ET SURTOUT PAS `inline-flex`. Constaté en rendant les items
   * réels : plusieurs énoncés de `S1-STA-028` et `S1-FCT-005` sont des
   * PHRASES entières passées en `\text{…}` — deux à trois lignes de prose.
   * Dans un conteneur flex, une phrase est un unique élément qui prend sa
   * largeur maximale et déborde au lieu de se couper. En flux normal elle se
   * coupe comme n'importe quel texte, et les fractions s'alignent d'elles-
   * mêmes : elles portent leur propre `vertical-align`. */
  display: inline;
}

.m-var {
  /* Une variable se lit en italique — c'est la convention typographique des
   * mathématiques, et c'est ce qui distingue « t » (une lettre) de « t »
   * (une variable) dans une phrase. */
  font-style: italic;
  padding-right: 0.02em;
}

.m-frac {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
  margin: 0 0.18em;
  font-size: 0.92em;
  line-height: 1.15;
}

.m-num {
  padding: 0 0.28em 0.06em;
}

.m-den {
  /* LA BARRE DE FRACTION. Une bordure, jamais un caractère : un « / » ne se
   * lit pas comme une fraction, et un trait écrit en texte ne suivrait pas
   * la largeur du numérateur. */
  padding: 0.06em 0.28em 0;
  border-top: 0.06em solid currentColor;
}

.m-barre {
  /* `\overline{A}` — l'événement contraire. La barre PORTE LE SENS : sans
   * elle, « A » et « A barre » deviennent le même écran. */
  display: inline-block;
  border-top: 0.06em solid currentColor;
  padding-top: 0.06em;
  margin-top: 0.12em;
}

.m-fleche,
.m-chapeau {
  /* `\vec{u}`, `\overrightarrow{AB}`, `\hat{A}`, `\widehat{BAC}` — 06/09 au
   * soir, P65. Le SIGNE est posé au-dessus du nom, sur toute sa largeur, et
   * le nom garde sa place dans la ligne : la flèche et le chapeau PORTENT
   * LE SENS (un vecteur n'est pas une longueur, un angle n'est pas un
   * produit), et un signe écrit à côté ne dirait pas ce qu'il couvre. */
  position: relative;
  display: inline-block;
  padding-top: 0.32em;
  white-space: nowrap;
}

.m-fleche-signe,
.m-chapeau-signe {
  /* Le signe LUI-MÊME, `aria-hidden` dans le gabarit : `m-lu` porte déjà
   * « vecteur » ou « angle », et une synthèse vocale qui lirait les deux
   * dirait « vecteur flèche vers la droite AB ». */
  position: absolute;
  top: -0.12em;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 0.8em;
  line-height: 1;
}

.m-chapeau-signe {
  top: -0.05em;
  font-size: 1em;
}

.m-delim {
  /* Les parenthèses de `\left( … \right)`. */
  padding: 0 0.02em;
}

.m-delim-haut {
  /* Le contenu porte une fraction : la parenthèse doit l'englober, sinon
   * elle a l'air posée à côté plutôt qu'autour. */
  font-size: 1.7em;
  line-height: 0.7;
  vertical-align: middle;
}

.m-racine {
  /* `\sqrt{12}` — 21/08. Le signe et son radicande forment un bloc, sans quoi
   * une coupure de ligne pourrait les séparer et laisser un « √ » orphelin en
   * fin de ligne. */
  display: inline-block;
  white-space: nowrap;
}

.m-signe {
  /* Le signe radical LUI-MÊME. `aria-hidden` dans le gabarit : `m-lu` porte
   * déjà « racine de », et une synthèse vocale qui lirait les deux dirait
   * « racine de racine carrée de 12 » — ou, selon la voix, ne dirait rien du
   * tout et laisserait le radicande nu. */
  padding-right: 0.05em;
}

.m-radicande {
  /* LA BARRE QUI COUVRE LE RADICANDE. Une bordure, jamais un caractère, pour
   * le motif de `.m-den` : c'est elle qui dit OÙ le radical s'arrête, et un
   * trait écrit en texte ne suivrait pas la largeur de ce qu'il couvre.
   *
   * Sans elle, « √12 + 3 » et « √(12 + 3) » se ressemblent à l'écran, et rien
   * ne les distingue — c'est la même perte d'information que la barre de
   * fraction remplacée par un « / » en gros. */
  border-top: 0.06em solid currentColor;
  padding: 0.08em 0.12em 0 0.08em;
  margin-left: -0.02em;
}

.m-espace {
  /* `\,` et `\ ` — une espace fine. Une espace ordinaire serait avalée par
   * la mise en page HTML ; celle-ci est une boîte, elle survit. */
  display: inline-block;
  width: 0.22em;
}

.maths sup,
.maths sub {
  font-size: 0.72em;
  line-height: 0;
}

.m-lu {
  /* CE QUE LE DESSIN A PRIS, CE MOT LE REND. Une barre de fraction et une
   * barre d'événement contraire sont des BORDURES : elles ne se lisent pas à
   * voix haute, et elles ne se copient pas. La notation TEXTE qu'elles
   * remplacent, elle, portait « y/6 » et « A ∩ B̅ » — des caractères.
   *
   * Sans ceci, les options (c) « A ∩ B » et (d) « A ∩ B̅ » de `S1-STA-028`
   * deviennent LE MÊME TEXTE pour qui n'a pas l'image, et le QCM n'a plus de
   * réponse distincte. Constaté sur la page rendue.
   *
   * `clip-path` et non `display: none` : ce qui est masqué par `display` ou
   * `visibility` n'est pas restitué du tout — ce serait masquer le mot au lieu
   * de le réserver à qui en a besoin. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ══════════════════════════════════════════════════════════════════════════
 * LE BLOC DE CADRAGE DE LA RESTITUTION — §20.8, 18/08.
 * ══════════════════════════════════════════════════════════════════════════
 * Deux phrases, et rien qui les mette en scène : la ligne de cadrage et la
 * date sont du texte, pas un en-tête de document officiel. */

.cadre {
  margin-bottom: 1.6rem;
}

/* La phrase de cadrage vient APRÈS l'en-tête depuis P23 (point 3, décision
 * de Kenny du 27/08) : sa marge est donc AU-DESSUS — elle sépare ce qui
 * situe (l'en-tête) de ce qui dit (la phrase). */
.cadre .cadrage {
  margin: 0.8rem 0 0;
}

/* L'EN-TÊTE DE LETTRE — P21 (point 7), passé à GAUCHE à P22 (point 2 ter),
 * puis AU-DESSUS de la phrase de cadrage à P23 (point 3, décision de Kenny
 * du 27/08) : comme sur une lettre, on lit d'abord à qui elle s'adresse et
 * quand elle est datée, puis ce qu'elle dit. Prénom, classe, date, l'une
 * sous l'autre, sans étiquettes — elles alourdiraient pour dire ce qu'on lit
 * déjà. Même retenue que la date : ces lignes SITUENT le document, elles
 * n'informent pas. */
.cadre .identite {
  margin: 0;
  font-size: var(--corps-legende);
}

.cadre .date {
  margin: 0;
  /* Plus petite que le corps, parce qu'elle SITUE et n'informe pas — le même
   * rapport que le repère de segment sur l'écran d'item. Aucune couleur : ce
   * fichier n'en pose aucune qui porte du sens (CLAUDE.md §7). En `em` jusqu'à
   * P76 — le seul corps de cette feuille que l'échelle close ne tenait pas. */
  font-size: var(--corps-legende);
}

.actions {
  margin-top: 2rem;
}

/* MES DONNÉES, DEUX COLONNES ALIGNÉES — P22 (point 2 quater), décision de
 * Kenny du 26/08 sur maquette validée. Avant : les `th` prenaient le défaut
 * du navigateur (centrés, gras 700 — une graisse synthétisée, §26 bis.1.3),
 * sans filet ni alignement. La forme validée : étiquettes à GAUCHE, alignées
 * entre elles, en texte atténué, graisse normale ; valeurs en encre pleine ;
 * un FILET entre chaque couple, un au-dessus du premier, un sous le dernier ;
 * la colonne d'étiquettes prend la largeur du plus long libellé et ne se
 * replie pas. Sous 700 px (le seuil des volets), l'étiquette passe AU-DESSUS
 * de sa valeur : deux colonnes ne tiennent pas sur un téléphone. */
.donnees {
  border-collapse: collapse;
  width: 100%;
  border-bottom: 1px solid var(--habillage-trait);
}

.donnees th {
  text-align: left;
  font-weight: 400;
  color: var(--habillage-texte-attenue);
  white-space: nowrap;
  width: 1%;
  padding: 0.5rem 1.5rem 0.5rem 0;
  vertical-align: top;
}

.donnees td {
  padding: 0.5rem 0;
}

.donnees tr {
  border-top: 1px solid var(--habillage-trait);
}

/* La phrase du manque (`DU-136`) : pas une donnée — atténuée, en italique
 * (la face embarquée, voir la @font-face). */
.donnees-reste {
  margin-top: 1rem;
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
  font-style: italic;
}

@media (max-width: 700px) {
  .donnees tr {
    display: block;
    padding: 0.5rem 0;
  }

  .donnees th,
  .donnees td {
    display: block;
    width: auto;
    padding: 0;
    white-space: normal;
  }
}

/* ---------------------------------------------------------------------------
 * LE SUPPORT D'ÉNONCÉ — 22/08/2026, passe AG1, `DU-68`. SPEC §2 quater.
 *
 * AUCUNE COULEUR NE PORTE D'INFORMATION. Les en-têtes se distinguent par TROIS
 * moyens dont aucun n'est une couleur porteuse de sens — une balise (`th`), une
 * graisse, un fond neutre — et chacun tient sans les autres.
 *
 * LE PRÉCÉDENT IMITÉ EST `.releve` ci-dessus, et l'écart est délibéré : ce
 * tableau-là porte des énoncés longs, d'où `table-layout: fixed` et une largeur
 * minimale ; celui-ci porte des effectifs et des mots courts, et doit se serrer
 * sur un écran étroit au lieu de forcer un défilement dont il n'a pas besoin.
 * --------------------------------------------------------------------------- */

.support-cadre {
  /* LE TABLEAU DÉFILE SEUL, JAMAIS LA PAGE — même motif que `.releve`, et il
   * est écrit au CDC §11.1 : l'élève travaille le soir sur son téléphone. Un
   * tableau croisé à cinq colonnes dépasse un écran étroit, et sans ceci c'est
   * l'énoncé et la consigne qui deviennent illisibles avec lui. */
  overflow-x: auto;
  margin: 0 0 1.25rem;
}

.support-tableau {
  /* `collapse` — sinon chaque cellule porte sa bordure et la grille se lit
   * comme des cases isolées, ce qui est exactement le contresens à éviter sur
   * un tableau croisé : ses lignes et ses colonnes SONT le sens. */
  border-collapse: collapse;
  /* Les effectifs s'alignent en colonne : des chiffres de largeurs différentes
   * rendent une colonne de nombres illisible à la comparaison. */
  font-variant-numeric: tabular-nums;
}

.support-tableau caption {
  /* La légende est au-dessus et se lit comme un titre, pas comme une cellule. */
  caption-side: top;
  text-align: left;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  margin-bottom: 0.4rem;
}

.support-tableau th,
.support-tableau td {
  /* Le trait FONCTIONNEL, comme `.releve` : les lignes et les colonnes d'un
   * tableau croisé SONT le sens, et elles se voient dans les deux thèmes. */
  border: 1px solid var(--habillage-trait);
  /* Ce padding est un jugement déclaré : assez pour que deux effectifs voisins
   * ne se touchent pas, assez peu pour qu'un tableau à cinq colonnes tienne
   * sur un écran de téléphone sans défiler. */
  padding: 0.45rem 0.7rem;
  text-align: right;
  vertical-align: middle;
}

.support-tableau thead th,
.support-tableau tbody th {
  /* TROIS MOYENS, AUCUN N'ÉTANT UNE COULEUR PORTEUSE DE SENS (CDC §11.1 bis) :
   * la balise `th` — que la synthèse vocale annonce —, la graisse, et un fond
   * neutre déjà présent dans ce fichier. Chacun tient sans les deux autres. */
  background: var(--habillage-surface);
  font-weight: 600;
  text-align: left;
}

.support-tableau .support-coin {
  /* Le coin ne nomme rien : ni fond, ni graisse, ni bordure marquée. Le vêtir
   * comme un en-tête le ferait lire comme le nom de la première colonne. */
  background: transparent;
  border-color: transparent;
}

/* ---------------------------------------------------------------------------
 * LES FIGURES — P48 (02/09), SPEC §2 quinquies. LE SVG EST DANS LA PAGE, PAS
 * ICI (décision C de Kenny) : tout ce qui le fait lire — traits, motifs,
 * marqueurs, corps du texte, couleurs de la palette sémantique — est un
 * attribut porté par le nœud, et la figure s'affiche SANS cette feuille comme
 * une image. Cette feuille ne fait que BORNER : la largeur de la figure, son
 * rythme dans la page, et les descriptions en clair du spécimen.
 * --------------------------------------------------------------------------- */

.figure {
  margin: 0 0 1.25rem;
  max-width: 34rem;
}

.figure svg {
  display: block;
  width: 100%;
  height: auto;
}

.figure-descriptions {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  margin-top: 0.5rem;
}

.figure-descriptions p {
  margin: 0 0 0.4rem;
}

.figure-servie {
  font-style: italic;
}

/* LE SPÉCIMEN — chaque primitive dans les deux thèmes, L'UN SOUS L'AUTRE :
 * mesuré le 02/09, deux colonnes dans les 40 rem de `main` donnaient des
 * figures de 304 px, où un corps de 13 unités sur 600 se lit à 6 px. En
 * colonne, la figure prend la largeur qu'elle aura sur un écran d'item. Un
 * bloc `.theme-force` porte SON fond et SON encre depuis les variables que
 * son attribut remappe (voir les blocs de thème en tête de cette feuille). */
.specimen {
  margin: 0 0 2.5rem;
}

.specimen-themes {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

.theme-force {
  background: var(--habillage-fond);
  color: var(--habillage-texte);
  border: 1px solid var(--habillage-bordure);
  border-radius: var(--habillage-rayon);
  padding: 0.75rem;
}

.specimen-theme-nom {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  margin: 0 0 0.5rem;
}

/* LES CASES D'UNE FIGURE — P50 (03/09), SPEC §2 sexies. Le champ est un
 * champ de formulaire ORDINAIRE (un `<input>` et son `<label>`), rendu par la
 * macro `figure` sous le SVG ; cette feuille le POSE sur la figure, à la
 * position que le rendu a calculée en pourcentage du cadre (`left`/`top`, en
 * ligne sur le label — la seule valeur de mise en page qui vienne des
 * données). Sans cette feuille, les cases restent des champs, l'un sous
 * l'autre, chacun avec son nom : c'est le mode dégradé, et il est exigé.
 *
 * DEUX FAMILLES DE CASES :
 *   · les cases POSÉES (`.figure-case-posee`) — sur une branche d'arbre : le
 *     nom est masqué à l'œil (il est écrit sur la figure) et gardé pour la
 *     voix ; le champ recouvre la case vide que le SVG dessine ;
 *   · les cases EN LIGNE — sous un vecteur : le nom reste visible, et les
 *     deux champs se lisent comme un couple.
 *
 * LA CIBLE TACTILE (§26 bis.2.2) : 2,75 rem pour les cases en ligne. Les
 * cases posées font 1,9 rem — c'est la contrainte de la figure : deux cases
 * voisines du second niveau d'un arbre sont à `PAS_DES_FEUILLES / 2` unités,
 * soit 31 px sur un téléphone de 375 px (rendu.py), et une case plus haute
 * chevaucherait sa voisine. Mesuré avant d'écrire. */
.figure {
  position: relative;
}

.figure-cases {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin-top: 0.5rem;
}

.figure-case {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: var(--corps-petit);
}

.figure-case input[type="text"] {
  width: 5rem;
  min-height: 2.75rem;
  margin-bottom: 0;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* LA CASE CORRIGÉE — P75 (07/09/2026), décision B de Kenny du 7 septembre.
 * Elle ne paraît QUE sur les espèces dont les cases ne sont pas dessinées dans
 * le SVG (le vecteur, la droite graduée, le cercle trigonométrique) : celles qui
 * les dessinent écrivent la valeur DANS la figure, et n'émettent plus de champ.
 *
 * LE CADRE PLUTÔT QU'UN FOND, ET AUCUNE COULEUR : c'est le même parti que le
 * `rect` à trait épais du SVG (`rendu.py`, `_case_vide`), pour que la case
 * corrigée se lise pareil dedans et dehors — et parce que la palette sémantique
 * est CLOSE (SPEC §26.3). Le trait et la graisse tiennent chacun seul, en
 * niveaux de gris comme à l'impression. */
.figure-case-valeur {
  border: 2px solid var(--habillage-bordure-forte);
  border-radius: 4px;
  padding: 0.15rem 0.5rem;
  font-variant-numeric: tabular-nums;
}

.figure-case-posee {
  position: absolute;
  transform: translate(-50%, -50%);
  margin: 0;
}

.figure-case-posee .figure-case-nom {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.figure-case-posee input[type="text"] {
  width: 3.75rem;
  min-height: 1.9rem;
  padding: 0.2rem 0.3rem;
  font-size: var(--corps-petit);
}

/* LA CASE RAPPELÉE — P85 (10/09/2026) : la question d'ALORS, rappelée en tête
 * de l'explication resservie, montre ses cases telles qu'elles étaient — vides
 * — sans poser de champ (`figures.html`, `saisissable=false`). Le même cadre
 * que la case corrigée, la même taille qu'un champ : elle se lit comme une
 * case, elle ne se remplit pas. */
.figure-case-vide {
  display: inline-block;
  width: 5rem;
  min-height: 2.75rem;
  box-sizing: border-box;
  border: 2px solid var(--habillage-bordure-forte);
  border-radius: 4px;
}

.figure-case-posee .figure-case-vide {
  width: 3.75rem;
  min-height: 1.9rem;
}

/* LA SURFACE DE CLIC (P50, 2.2) : quand le script est là, le repère d'un
 * vecteur à placer répond au clic — le curseur le dit. Sans script, rien ne
 * change : les deux cases suffisent. */
svg[data-saisie="point"] {
  cursor: crosshair;
  touch-action: manipulation;
}

/* L'AIDE AU CLIC — révélée par le script, comme les touches d'insertion : un
 * conseil de clic sur une page qui ne sait pas cliquer se lirait comme une
 * panne. */
.aide-clic {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  margin: -0.5rem 0 1rem;
}

/* L'AIDE DE LA SAISIE D'UN ENSEMBLE — P117-UX (29/09/2026), `DU-210`. Sous le
 * champ, comme la maquette validée le 23/09/2026 la pose (`.aide` : atténuée,
 * plus petite que le corps, collée au champ) — le corps de légende de
 * l'échelle close (§26 bis.1.2), celui de l'aide au clic. Le champ pose 1 rem
 * sous lui ; la marge négative en reprend 0,6 : l'aide est à 0,4 rem du champ,
 * l'écart de la maquette. Toujours servie : ce n'est pas une aide au clic, elle
 * ne dépend d'aucun script. */
.aide-ensemble {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  margin: -0.6rem 0 1rem;
}

/* ══════════════════════════════════════════════════════════════════════════
 * L'IMPRESSION — §20.8. « Télécharger en PDF » = « imprimer, puis enregistrer
 * en PDF », que tout navigateur et tout téléphone savent faire.
 * ══════════════════════════════════════════════════════════════════════════
 *
 * L'IMPRESSION EST TOUJOURS SUR FOND CLAIR : le papier est blanc, et forcer
 * l'encre du thème sombre sur une feuille gaspillerait le toner pour un fond
 * que l'imprimante ne rend pas. Les variables effectives sont remises à la
 * colonne claire — remappage pur, comme les blocs de thème. */

@media print {
  :root,
  :root[data-theme="sombre"] {
    --habillage-fond: var(--habillage-fond-clair);
    --habillage-texte: var(--habillage-texte-clair);
    --habillage-texte-fort: var(--habillage-texte-fort-clair);
    --habillage-texte-attenue: var(--habillage-texte-attenue-clair);
    --habillage-bordure: var(--habillage-bordure-clair);
    --habillage-trait: var(--habillage-trait-clair);
    --habillage-bordure-forte: var(--habillage-bordure-forte-clair);
    --habillage-surface: var(--habillage-surface-clair);
    --habillage-surface-legere: var(--habillage-surface-legere-clair);
    --habillage-surface-active: var(--habillage-surface-active-clair);
    --habillage-champ: var(--habillage-champ-clair);
    --habillage-accent: var(--habillage-accent-clair);
    --habillage-sur-accent: var(--habillage-sur-accent-clair);
    --habillage-anneau: var(--habillage-anneau-clair);
  }

  /* CE QUI N'A AUCUN SENS SUR PAPIER. Un bouton imprimé est une tache d'encre
   * qui promet une action ; le bouton d'impression imprimé serait, en plus,
   * l'aveu que la feuille ne s'est pas appliquée. La bascule de thème suit. */
  .actions,
  .bascule-theme,
  .deconnexion,
  button,
  form {
    display: none;
  }

  main {
    /* La page de papier fait la marge ; la colonne de lecture de l'écran la
     * ferait deux fois et gaspillerait un tiers de la feuille. */
    max-width: none;
    margin: 0;
    padding: 0;
  }

  /* LE RAIL À L'IMPRESSION — P24 (point 5 septies) : la restitution prend le
   * rail à l'écran, et le document imprimé ne change pas. Les volets
   * redeviennent un flux (plus de colonne) ; le volet de gauche passe en
   * `display: contents` — il ne génère AUCUNE boîte, donc ni fond ni
   * bordure peints, sans poser ici aucune des propriétés que le garde de
   * cette feuille refuse (il mord jusque dans les commentaires, constaté en
   * écrivant celui-ci) — et seuls ses enfants utiles
   * restent : la marque (elle imprimait déjà quand elle vivait dans `main`,
   * la perdre changerait le document). La navigation et le pied de compte
   * disparaissent ; le bouton de réduction est un `button`, déjà masqué.
   * Le volet de contenu reprend les valeurs de `main` ci-dessus. */
  .volets {
    display: block;
    min-height: 0;
  }

  .volet-gauche {
    display: contents;
  }

  .volet-gauche ul,
  .volet-pied {
    display: none;
  }

  .volet-droit {
    max-width: none;
    margin: 0;
    padding: 0;
  }

  .releve {
    /* À l'écran le tableau défile SEUL dans sa boîte (voir plus haut). Sur
     * papier il n'y a pas de défilement : une boîte à débordement caché
     * COUPERAIT les dernières colonnes, et l'élève imprimerait un tableau
     * amputé sans le savoir. */
    overflow-x: visible;
  }

  .releve table {
    /* `fixed` répartit les colonnes sur une largeur d'écran ; sur papier la
     * largeur change, et `auto` laisse le contenu décider. */
    table-layout: auto;
    min-width: 0;
  }

  .releve thead {
    /* LES EN-TÊTES SE RÉPÈTENT À CHAQUE PAGE. Sans cela, la deuxième feuille
     * porte quatre colonnes anonymes, et « Ta réponse juste » ne se distingue
     * plus de « Ce qu'il fallait répondre » — deux colonnes dont la confusion
     * inverse le sens de la ligne. */
    display: table-header-group;
  }

  .releve tr,
  .releve td,
  .releve th {
    /* UNE LIGNE NE SE COUPE PAS EN DEUX. Une question en bas d'une feuille et
     * sa réponse en haut de la suivante se lisent comme deux lignes
     * différentes. `page-break-inside` d'abord pour les navigateurs qui ne
     * connaissent que lui. */
    page-break-inside: avoid;
    break-inside: avoid;
  }

  .cadre,
  .priorites li,
  .reserves p {
    page-break-inside: avoid;
    break-inside: avoid;
  }

  h1,
  h2 {
    /* Un titre seul en bas de page annonce ce qui est ailleurs. */
    page-break-after: avoid;
    break-after: avoid;
  }
}

/* =============================================================================
 * LA SÉANCE QUOTIDIENNE — V0.2, tranche 1 (a), P45 (01/09), sur les DEUX
 * maquettes validées par Kenny le 1er septembre (`docs/maquettes/`). Trois
 * composants neufs et les sous-entrées du rail — RIEN du chrome de V0.1 n'est
 * redessiné : le rail, le menu de compte, le tiroir, les gestes restent ceux
 * de `volets.html` et des règles ci-dessus.
 * ========================================================================== */

/* LES SOUS-ENTRÉES DU RAIL — « Diagnostic → Prérequis » (§3.1 de P45), la
 * forme de la maquette : une liste imbriquée, filet à gauche, corps plus
 * petit. Sous 700 px le tiroir n'empile pas deux niveaux, et le rail RÉDUIT
 * n'a pas d'icône à leur donner : cachées dans les deux cas — l'entrée
 * parente mène au même endroit dans cette tranche, rien n'est perdu. */
/* LE RETRAIT SUIT LE LIBELLÉ DU PARENT, PAS SON ICÔNE — P89 (12/09/2026),
 * décision de Kenny : les sous-entrées désignent des dates et des étapes, pas
 * des lieux du produit, et elles ne portent aucune icône. Sans ce décalage,
 * deux alignements se disputeraient le bord gauche.
 *
 * 0,9 rem est CALCULÉ, pas choisi : le libellé d'une entrée commence à
 * 0,5 rem (le rembourrage du lien) + 16 px (la colonne d'icône, soit 0,94 rem
 * sur la base de 17 px) + 0,35 rem (la gouttière) = 1,79 rem ; le texte d'une
 * sous-entrée commence à `margin` + 0,5 rem (ce rembourrage) + 0,4 rem (celui
 * de son propre lien). 0,9 + 0,9 = 1,80 rem. Le filet, lui, reste à gauche du
 * mot — c'est ce que la maquette montre. *La valeur ne bouge donc pas : le
 * retrait d'avant P89 tombait déjà juste, et c'est la gouttière de 0,35 rem
 * qui l'y ramène.* */
.volet-gauche ul ul {
  margin: 0.15rem 0 0.25rem 0.9rem;
  border-left: 1px solid var(--habillage-trait);
  padding-left: 0.5rem;
}

.volet-gauche ul ul a {
  font-size: var(--corps-legende);
  padding: 0.3rem 0.4rem;
  /* Un titre SANS BLANC (une formule) se coupe au lieu de déborder : le rail
   * redimensionnable (`overflow: auto`) défilerait de côté (P117-UX2,
   * relecture B du commit 1, fond 2). */
  overflow-wrap: anywhere;
}

.volets.rail-reduit .volet-gauche ul ul {
  display: none;
}

@media (max-width: 700px) {
  .volet-gauche ul ul {
    display: none;
  }
}

/* LA DATE DU JOUR — la ligne atténuée au-dessus du titre (maquette). */
.date-de-seance {
  margin: 0 0 0.15rem;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

/* UN BLOC DE SÉANCE — la durée à gauche, le nom et le contenu à droite, sur
 * une surface légère (maquette : grid 3.5rem / 1fr). */
.bloc-de-seance {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 0.75rem;
  padding: 0.65rem 0.8rem;
  background: var(--habillage-surface-legere);
  border-radius: var(--habillage-rayon);
  margin: 0 0 0.5rem;
}

.bloc-de-seance p {
  margin: 0;
}

.bloc-de-seance .mention {
  margin: 0;
}

.bloc-duree {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.bloc-nom {
  font-weight: 600;
}

/* LE TITRE D'UNE COMPÉTENCE, EN DONNÉE SUR SA PROPRE LIGNE — jamais enchâssé
 * dans une phrase (la leçon du §20.3). La graisse le désigne, rien d'autre. */
.priorite-visee {
  font-weight: 600;
}

/* LE PAVÉ D'INFORMATION — le second emploi du rôle `--habillage-info`,
 * décidé par les maquettes du 01/09 (voir la palette) : le fond
 * d'information, le filet d'accent de `.objectif`, l'idiome exact de
 * l'encadré de la console. */
.pave-info {
  background: var(--habillage-info);
  border-left: 3px solid var(--habillage-accent);
  padding: 0.65rem 0.8rem;
  margin: 0 0 1.1rem;
  box-sizing: border-box;
}

.pave-info p {
  margin: 0;
}

.pave-info .pave-titre {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.pave-info .priorite-visee {
  margin-top: 0.15rem;
}

/* L'EXPLICATION RESSERVIE EN TÊTE DE LA QUESTION — P74 (07/09), décision D.
 * Le pavé d'information existant, et rien de neuf : c'est une information, du
 * même registre que le rappel de la semaine. Sa liste reprend la mise en page
 * du corrigé de la revue, pour que l'élève reconnaisse ce qu'il relit. */
.explication-revue .corrige {
  margin: 0.5rem 0 0;
  padding-left: 1.2rem;
}

.explication-revue .corrige li {
  margin-bottom: 0.45rem;
}

.explication-revue .corrige li:last-child {
  margin-bottom: 0;
}

/* LA QUESTION D'ALORS — P85 (10/09/2026), décision de Kenny du 9 septembre,
 * maquette `maquette_reprise_explication.html` : l'énoncé, le support et la
 * consigne de l'item dont l'explication est resservie, dans un encadré À PART
 * en tête du pavé — la surface active et le rayon du dépôt, rien de neuf —,
 * sous un titre atténué qui porte la date. Sans lui, l'explication parlait de
 * « juniors » quand la question du jour parlait de « débutants » (vu en
 * production le 9 septembre). */
.question-d-alors {
  background: var(--habillage-surface-active);
  border-radius: var(--habillage-rayon);
  padding: 0.6rem 0.75rem;
  margin: 0.5rem 0 0.7rem;
}

.question-d-alors .question-d-alors-titre {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 0.3rem;
}

.question-d-alors .enonce {
  margin: 0 0 0.4rem;
}

.question-d-alors .consigne {
  margin: 0;
}

/* L'ÉCRAN « LA MÉTHODE » — P109 (24/09/2026), décision 71 (5) de Kenny, la
 * VARIANTE 1 de `maquette_place_des_rappels_et_ensemble_20260923_v2.html` :
 * la ligne de contexte atténuée (`.fil` de la maquette), puis le bloc de la
 * méthode — le fond d'information et le filet d'accent du pavé (le rôle
 * validé le 01/09, voir la palette), épaissi et arrondi comme la maquette le
 * dessine. Les corps sont ceux de l'échelle close les plus proches de la
 * maquette : 0,85 rem → `--corps-legende`, 0,93 rem → `--corps-petit` ; le
 * titre du bloc, un <h1> — l'écran n'en a pas d'autre, et chaque écran porte
 * le sien (`DU-45`) —, au corps du titre de bloc (1,1 rem, la maquette 1 rem),
 * pour rester au-dessus du corps (§26 bis.1.5). Un rappel par écran. Devant
 * le créneau de clôture, le titre de la compétence suit la ligne de contexte
 * sur sa propre ligne (`.priorite-visee`, comme à l'accueil). */
.fil-de-seance {
  margin: 0 0 0.6rem;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.methode {
  background: var(--habillage-info);
  border-left: 4px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  padding: 0.9rem 1.1rem;
  margin: 0.8rem 0;
  box-sizing: border-box;
}

.methode h1 {
  margin: 0 0 0.5rem;
  font-size: var(--corps-titre-de-bloc);
  color: var(--habillage-texte-fort);
}

.methode p {
  margin: 0.35rem 0;
  font-size: var(--corps-petit);
  line-height: 1.45;
}

.fil-de-seance + .priorite-visee {
  margin: -0.35rem 0 0.6rem;
}

/* Le bouton sous le bloc, à gauche, séparé de lui comme sur la maquette
 * (1,8 rem : le bas du bloc et le haut du bouton y s'additionnent). */
.methode + .ligne-de-bouton {
  margin-top: 1.8rem;
}

/* LA LIGNE D'ACTION — le fait de la semaine à gauche, LE bouton d'accent à
 * droite (maquette) ; au bilan, l'action seule, poussée à droite. */
.ligne-d-action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

.ligne-d-action form {
  margin-left: auto;
}

.ligne-d-action button {
  margin: 0;
}

.ligne-d-action .mention {
  margin: 0;
}

/* « TON DIAGNOSTIC » — P46 (V0.2, tranche 1 b), sur la maquette validée du
 * 01/09 : la mention de titre atténuée à côté d'un <h2>, la ligne de
 * domaine (le nom à gauche, « 2 sur 5 sondés » à droite), la BARRE — un
 * fond de surface active, un remplissage à l'accent, dont la largeur est
 * une donnée de la route et dont le MOT voisin porte l'information —, le
 * dépliant natif (`<details>`, replié sans script, le marqueur du
 * navigateur : aucun glyphe à couvrir), et l'avancement de la sonde sur
 * l'écran d'item, à la place de la frise. Les corps sont sur l'échelle
 * close (0,9 · 0,95). */
.mention-de-titre {
  font-weight: 400;
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
}

.ligne-de-bouton {
  margin: 0 0 1.25rem;
}

.ligne-de-bouton button {
  margin: 0;
}

.pave-info .ligne-d-action {
  margin-top: 0;
}

/* LES TROIS PRIMITIVES DE BARRE — `.domaines-de-sonde`, `.domaine-ligne`,
   `.barre-de-sonde` — sont PARTAGÉES depuis P84 (12/09/2026) : l'écran du
   diagnostic (P46) et « Ma progression » (V0.2, tranche 3) montrent la même
   géométrie, « n sur m » à côté du trait. Les noms gardent leur origine ;
   une seule géométrie, un seul endroit où la corriger. */
.domaines-de-sonde {
  display: grid;
  gap: 0.9rem;
  margin: 0 0 1rem;
}

.domaine-ligne {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: var(--corps-petit);
  margin: 0 0 0.25rem;
}

.domaine-ligne .mention {
  margin: 0;
  font-size: var(--corps-legende);
}

.barre-de-sonde {
  height: 0.5rem;
  background: var(--habillage-surface-active);
  border-radius: 0.25rem;
  overflow: hidden;
}

.barre-de-sonde i {
  display: block;
  height: 100%;
  background: var(--habillage-accent);
}

.domaine-de-sonde > .mention {
  margin: 0.35rem 0 0;
}

/* « MA PROGRESSION » — P84 (12/09/2026), maquette
   `docs/maquettes/maquette_progression_v4.html`. DEUX BLOCS, et rien de plus :
   tout le reste de l'écran réutilise ce que le dépôt porte déjà (`.pave-info`
   pour le pavé de la semaine, les trois primitives de barre ci-dessus,
   `.mention` pour les phrases atténuées). */

/* LA LÉGENDE DES MOTS — en FIN DE PAGE, après les deux blocs (décision de
   Kenny du 09/09). Une liste de définitions : le mot, puis ce qu'il veut
   dire. Sur téléphone la grille se replie en une colonne — le mot au-dessus
   de son sens, jamais deux colonnes écrasées. */
.legende-des-mots {
  background: var(--habillage-surface-legere);
  border-radius: var(--habillage-rayon);
  padding: 0.8rem 0.9rem;
  margin: 1.4rem 0 0;
}

.legende-des-mots dl {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.8rem;
  font-size: var(--corps-petit);
}

.legende-des-mots dt {
  font-weight: 600;
  white-space: nowrap;
}

.legende-des-mots dd {
  margin: 0;
}

@media (max-width: 40rem) {
  .legende-des-mots dl {
    grid-template-columns: 1fr;
    gap: 0.15rem;
  }

  .legende-des-mots dt {
    margin-top: 0.4rem;
  }
}

/* LES RANGÉES DE PREMIÈRE — PR2 (16/09/2026), maquette v5. Leur libellé
   porte une mention (« — en classe en ce moment », la précision des
   transversales) et devient long : sur téléphone, le décompte se coupait
   alors en « 1 / sur / 1 » sur trois lignes (mesuré à 375 px). Le décompte
   est UNE unité de lecture, il ne se coupe pas ; c'est le libellé qui passe
   à la ligne. Borné à ce bloc : les autres rangées ne bougent pas. */
.rangees-de-premiere .domaine-ligne > .mention {
  white-space: nowrap;
}

/* L'ÉTAT VIDE DU BLOC « MON ANNÉE DE PREMIÈRE » — un cadre en pointillé : ce
   qui n'est pas encore rempli se distingue de ce qui l'est, par la FORME du
   trait et non par la couleur seule (CLAUDE.md §7). */
.bloc-a-venir {
  border: 1px dashed var(--habillage-trait);
  border-radius: var(--habillage-rayon);
  padding: 0.7rem 0.85rem;
  margin: 1.2rem 0 0;
}

.bloc-a-venir > :first-child {
  margin-top: 0;
}

.bloc-a-venir > :last-child {
  margin-bottom: 0;
}

.depliant-de-sonde {
  margin: 0.35rem 0 0;
}

.depliant-de-sonde summary {
  font-size: var(--corps-legende);
  color: var(--habillage-accent);
  cursor: pointer;
}

.depliant-de-sonde ul {
  margin: 0.35rem 0 0;
  padding-left: 1.1rem;
  font-size: var(--corps-petit);
  line-height: 1.7;
}

/* LA QUESTION DU CHAPITRE — P47 (V0.2, tranche 2), sur la maquette de
 * l'annexe B : une liste de CHOIX pleine largeur (des boutons radio habillés
 * en lignes de la forme d'un bouton — la maquette dessinait des boutons),
 * le choix coché sur la surface active et en graisse (le mot et la graisse
 * portent l'état, jamais la couleur seule), les autres chapitres sous le
 * dépliant natif, et la ligne d'actions : « C'est celui-là » en aplat à
 * DROITE bien que PREMIER dans le formulaire (l'action par défaut de la
 * touche Entrée), « Passer » en contour à gauche. Aucun corps neuf : tout
 * hérite. */
.choix-de-chapitre {
  display: grid;
  gap: 0.4rem;
  margin: 0 0 1.25rem;
}

.choix-de-chapitre .choix {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.75rem;
  padding: 0.55rem 0.8rem;
  box-sizing: border-box;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  cursor: pointer;
}

.choix-de-chapitre .choix:has(input:checked) {
  background: var(--habillage-surface-active);
  font-weight: 600;
}

.choix-de-chapitre .choix input {
  margin: 0;
}

.choix-de-chapitre .choix .mention {
  margin: 0;
  font-weight: 400;
}

.depliant-de-sonde .choix-de-chapitre {
  margin: 0.5rem 0 0;
}

.actions-du-chapitre {
  display: flex;
  flex-direction: row-reverse;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

.actions-du-chapitre button {
  margin: 0;
}

/* LE CALENDRIER SUR L'ÉCRAN DE LA QUESTION — P63 (06/09), maquettes 11 à 13
 * du 6 septembre : la ligne ESTIMÉE porte une bordure appuyée (2 px, la
 * même couleur — l'état se lit au mot de la mention, jamais à la seule
 * bordure), sa mention est en accent et en graisse 600 (`.mention.forte`,
 * le `.mention.b` de la maquette) ; la PARTIE d'une séquence (« — partie 3 »)
 * suit le nom en atténué et reste en graisse 400 quand la ligne cochée passe
 * en 600 ; les gestes secondaires (« Passer », « On est en vacances ») se
 * rangent ensemble à gauche, l'aplat reste seul à droite ; les blocs du bas
 * sont ceux de l'accueil de séance, avec la ligne des prérequis. */
.choix-de-chapitre .choix.estime {
  border-width: 2px;
}

.choix-de-chapitre .choix .partie,
.bloc-de-seance .partie,
.pave-info .partie {
  color: var(--habillage-texte-attenue);
  font-weight: 400;
}

.choix-de-chapitre .choix .mention.forte {
  color: var(--habillage-accent);
  font-weight: 600;
}

.actions-du-chapitre .secondaires {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.bloc-de-seance .prerequis {
  margin: 0.15rem 0 0;
  font-size: var(--corps-petit);
}

.titre-de-section {
  margin-top: 1.75rem;
}

.avancement-de-sonde {
  margin: 0;
  font-size: var(--corps-petit);
  color: var(--habillage-texte-attenue);
}

/* LES LISTES DU BILAN — des lignes, pas des puces : le titre désigné par sa
 * graisse, la mention atténuée à sa suite. */
.bilan-liste {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
}

.bilan-liste li {
  margin: 0 0 0.4rem;
}

/* LES QUATRE MOTS DE L'ÉLÈVE — quatre boutons à contour (l'aplat reste à
 * « Terminer », règle de P21) ; le choix fait se marque par `aria-pressed`
 * et prend la surface active, comme la bascule de thème — LE MOT porte
 * l'état pour la voix, la surface le montre à l'œil. */
.mots-de-l-eleve {
  margin: 0 0 0.75rem;
}

.mots-de-l-eleve button[aria-pressed="true"] {
  background: var(--habillage-surface-active);
  font-weight: 600;
}

/* « SUR QUOI AS-TU GALÉRÉ ? » — P86 (11/09/2026), sur la maquette validée par
 * Kenny le 10 septembre : les questions de la séance, à COCHER, une ligne
 * chacune — la question telle qu'elle a été servie, aucun statut. LA COCHE EST
 * BLEUE : la case remplie à l'accent (`accent-color` ; le navigateur y pose la
 * coche claire), la ligne cochée au fond d'information et bordée d'accent — ni
 * vert, ni rouge : la charte n'en a pas, et la couleur ne porte jamais seule
 * l'information (la case cochée la porte). Une question longue défile DANS sa
 * ligne, jamais la page (l'idiome de `.releve`). */
.questions-de-la-seance {
  list-style: none;
  margin: 0.5rem 0 0.8rem;
  padding: 0;
}

.questions-de-la-seance li {
  margin: 0 0 0.45rem;
}

.questions-de-la-seance label {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--habillage-trait);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  cursor: pointer;
}

.questions-de-la-seance label:has(input:checked) {
  border-color: var(--habillage-accent);
  background: var(--habillage-info);
}

.questions-de-la-seance input[type="checkbox"] {
  flex: 0 0 auto;
  width: 1.2rem;
  height: 1.2rem;
  margin: 0.2rem 0 0;
  accent-color: var(--habillage-accent);
}

.question-de-la-seance {
  min-width: 0;
  overflow-x: auto;
}

.question-de-la-seance .consigne-de-la-question {
  display: block;
}

/* « PASSER » — un LIEN habillé en bouton À CONTOUR (P86) : son geste NAVIGUE
 * vers le bilan et n'écrit rien, donc un `<a>` ; l'aplat reste à « Valider »,
 * l'action principale de l'écran. Les cotes et le contour sont ceux de
 * `button` ; le soulignement disparaît ici seulement. */
a.bouton-a-contour,
a.bouton-a-contour:visited {
  display: inline-block;
  box-sizing: border-box;
  min-height: 2.75rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  background: var(--habillage-champ);
  color: inherit;
  text-decoration: none;
}

/* =============================================================================
 * CE QUE L'USAGE A REMONTÉ LE 3 SEPTEMBRE — P52 (04/09), sur les maquettes 6
 * à 9 validées par Kenny le 4 septembre (`docs/maquettes/`). Quatre composants
 * neufs et une règle de charte — RIEN du chrome n'est redessiné.
 * ========================================================================== */

/* LES PICTOGRAMMES — la charte, décision de Kenny du 4 septembre (SPEC §26
 * bis.6) : SVG inclus dans la page (`gabarits/pictos.html`), LA TAILLE DU
 * TEXTE (1em), L'ENCRE COURANTE (currentColor — marine en clair, l'encre du
 * texte en sombre : le pictogramme suit le mot qu'il accompagne), un trait de
 * 1,8 aux bouts arrondis, jamais un aplat. Toujours suivi d'un mot : le garde
 * `test_pictogrammes_accompagnes.py` refuse un pictogramme seul. La
 * dimension est en `em` — c'est la géométrie relative d'un glyphe, pas un
 * corps de texte (§26 bis.1.2, l'exception que le garde de l'échelle écrit). */
.picto {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  margin-right: 0.35em;
  flex: none;
}

/* LE PAVÉ DE RAPPEL DE LA SONDE — maquette 6 (décision 6 du 3 septembre) : le
 * pavé d'information (`.pave-info`, le rôle validé le 01/09), l'éclair en
 * tête, le nombre de questions restantes en graisse 600, et « Continuer » à
 * droite — une surface qui PERMET D'AGIR là où une ligne atténuée obligeait à
 * chercher le rail. Sous 700 px, le bouton passe sous la phrase. */
.pave-rappel {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.1rem;
}

.pave-rappel .picto {
  width: 1.35em;
  height: 1.35em;
  vertical-align: -0.3em;
  color: var(--habillage-accent);
}

.pave-rappel p {
  flex: 1 1 auto;
  font-size: var(--corps-petit);
}

.pave-rappel form {
  flex: none;
  margin: 0;
}

.pave-rappel button {
  margin: 0;
  font-size: var(--corps-legende);
  padding: 0.35rem 0.7rem;
  min-height: 2.75rem;
}

@media (max-width: 700px) {
  .pave-rappel {
    flex-wrap: wrap;
  }
}

/* LE DOMAINE AU-DESSUS DE LA QUESTION — maquette 8 (décision 5 du 3
 * septembre) : sur la SONDE des fondamentaux, le domaine de la question (le
 * mot du code est `domaine` ; la maquette disait « famille » — c'est le même
 * objet, et c'est le mot du code qui vaut). JAMAIS la compétence (§17.4) : le
 * domaine situe sans souffler. Un cartouche à contour d'accent, corps 0,9. */
.domaine {
  display: inline-block;
  font-size: var(--corps-legende);
  font-weight: 600;
  color: var(--habillage-accent);
  border: 1px solid var(--habillage-accent);
  border-radius: var(--habillage-rayon);
  padding: 0.1rem 0.6rem;
  margin: 0 0 0.5rem;
}

/* LE FAIT DE LA SEMAINE, quand la ligne continue (maquette 6, décision 3) :
 * « Aucune séance terminée cette semaine — termine celle-ci, ce sera la
 * première. » La suite est en graisse 600 et en encre pleine : c'est le fait
 * saillant de la ligne (la charte : les états se nomment). */
.ligne-d-action .mention b {
  font-weight: 600;
  color: var(--habillage-texte);
}

/* « MES SÉANCES » — P52 (04/09), maquette 7 : le classeur d'une semaine (une
 * rangée par jour : la date, le titre de la séance, l'ÉTAT en mot), la ligne
 * de compte, les semaines d'un mois, le détail d'une séance terminée (les
 * blocs de l'accueil de séance, réemployés) et le relevé replié sous un
 * <details> natif. Les états sont des mots ; la graisse 600 marque le fait
 * saillant de la ligne. Les corps sont sur l'échelle close (0,9 · 0,95). */
.decompte-de-semaine {
  margin: 0 0 0.75rem;
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.classeur {
  border-top: 1px solid var(--habillage-trait);
  margin: 0 0 1rem;
}

.rangee {
  display: grid;
  grid-template-columns: 6.5rem 1fr 7rem;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--habillage-trait);
  font-size: var(--corps-petit);
}

.rangee .mention {
  margin: 0;
}

.rangee a {
  color: var(--habillage-accent);
}

.etat {
  font-size: var(--corps-legende);
  color: var(--habillage-texte-attenue);
}

.etat b {
  font-weight: 600;
  color: var(--habillage-texte);
}

.semaines-du-mois {
  padding-left: 1.2rem;
  margin: 0 0 1rem;
}

.semaines-du-mois a,
.navigation-des-mois a {
  color: var(--habillage-accent);
}

.navigation-des-mois a + a {
  margin-left: 1rem;
}

.detail-de-seance h2 {
  margin-top: 1.5rem;
}

.releve-de-seance summary {
  color: var(--habillage-accent);
  cursor: pointer;
  margin: 0.75rem 0 0;
}

/* Sous 700 px, deux colonnes : la date et l'état sur la première ligne, le
 * titre de la séance sur toute la largeur en dessous (le relevé de la
 * restitution défile dans son propre cadre, comme partout). */
@media (max-width: 700px) {
  .rangee {
    grid-template-columns: 1fr auto;
  }

  .rangee > :nth-child(2) {
    grid-column: 1 / -1;
    grid-row: 2;
  }
}

/* LES CLÉS D'ACCÈS DE « MES DONNÉES » — P52 (04/09), maquette 9 : les
 * rangées du classeur à deux colonnes (la clé et son appareil, « Retirer »),
 * la ligne d'ajout, le pavé du chemin de secours. « Retirer » est un bouton
 * de formulaire (sans script, un lien ne poste pas) en contour, jamais en
 * aplat : l'action principale de la page n'est pas de retirer. */
.cles .rangee {
  grid-template-columns: 1fr auto;
  align-items: center;
}

.cles .rangee form {
  margin: 0;
}

/* LE BOUTON D'AJOUT DE CLÉ TIENT SUR UNE LIGNE — P74 (07/09/2026), décision H
 * de Kenny, maquette §2. « + Ajouter une clé sur cet appareil » est le plus
 * long libellé de bouton du dépôt ; posé dans une ligne d'action à côté de sa
 * mention, il se coupait en deux. Le texte NE CHANGE PAS — c'est le geste
 * qu'il nomme, et l'abréger le rendrait vague ; seuls le corps et
 * l'interdiction du retour à la ligne changent.
 *
 * `nowrap` SEUL SUFFIRAIT À LE FAIRE DÉBORDER sur un écran très étroit : la
 * mention qui le précède cède donc sa place la première (`min-width: 0` la
 * laisse se réduire), et le bouton prend la largeur disponible plutôt que de
 * se couper. Mesuré au rendu réel sur les trois bornes, 375 px compris. */
.ajout-de-cle button {
  margin: 0;
  white-space: nowrap;
  font-size: var(--corps-legende);
  padding: 0.5rem 0.9rem;
}

.cles .rangee button {
  margin: 0;
  font-size: var(--corps-legende);
  padding: 0.35rem 0.7rem;
}

.ajout-de-cle {
  margin-top: 1rem;
}

.ajout-de-cle .mention {
  flex: 1 1 auto;
  /* Elle cède la place avant le bouton (P74) : sans `min-width: 0`, un
   * élément de flex ne descend pas sous la largeur de son contenu, et c'est
   * le bouton qui aurait été comprimé. */
  min-width: 0;
}

.pave-secours {
  margin-top: 1.25rem;
}

.cles-titre {
  margin-top: 1.5rem;
}

/* LE RETOUR APRÈS L'ERREUR — P53 (04/09), maquette 10 validée le 4 septembre :
 * les deux cartouches côte à côte (« Ta réponse », « La bonne réponse »), le
 * pavé d'information (`.pave-info`, le rôle validé le 01/09) qui porte la
 * cause ou le corrigé, la liste ordonnée des étapes avec leur ligne de calcul
 * en atténué, la ligne de l'avis et ses deux boutons en contour — l'aplat
 * reste à « Question suivante ». Aucune couleur neuve : l'état « faux » n'est
 * jamais dit par une couleur, il est dit par les mots. */
.reponses {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin: 0 0 1rem;
}

.reponses > div {
  background: var(--habillage-surface-legere);
  border-radius: var(--habillage-rayon);
  padding: 0.6rem 0.8rem;
}

.reponses p {
  margin: 0;
}

.reponses .valeur {
  /* La maquette 10 disait 1,15 rem ; l'échelle close du §26 bis.1.2 n'a pas
   * ce corps — 1,1 est le voisin, et l'écart est publié au rapport de P53. */
  font-size: var(--corps-titre-de-bloc);
  font-weight: 600;
  color: var(--habillage-texte-fort);
}

/* LA GRILLE À UNE SEULE CELLULE — P76 (08/09/2026) : quand la bonne réponse
 * est une FIGURE, elle quitte la grille pour le bloc du retour (ci-dessous),
 * et « Ta réponse » reste seule : une cellule sur deux colonnes se lirait
 * comme une case vide à côté. */
.reponses-seule {
  grid-template-columns: 1fr;
}

/* LA FIGURE CORRIGÉE EST LISIBLE — P76 (08/09/2026), décision E de Kenny du
 * 7 septembre, maquette `maquette_retour_et_parcours.html` §1. Vu en
 * production le 7 : l'arbre corrigé en VIGNETTE dans une cellule grise de
 * demi-largeur, puis l'énumération des pondérations en corps 1,1 rem gras,
 * serrée dans la cellule. LA RÈGLE, et elle vaut pour les onze espèces parce
 * qu'elle vit dans `.figure` (la même règle que la figure de l'énoncé, rendue
 * par la même macro) et non dans un gabarit par figure : la figure corrigée se
 * rend À LA MÊME TAILLE que la figure de l'énoncé — même `max-width`, même
 * `width: 100 %` du SVG —, dans le BLOC BLEU PLEINE LARGEUR du retour (P75),
 * et le texte sous elle est le corrigé, au corps courant. L'énumération
 * textuelle des cases ne disparaît pas — elle reste ce que lit une synthèse
 * vocale et ce que voit un écran sans image — mais elle n'est plus AFFICHÉE :
 * la figure la porte déjà (`.lecture-seule`). */
.retour .bonne-reponse-figure {
  margin-bottom: 0.75rem;
}

.retour .bonne-reponse-figure .figure {
  margin: 0.5rem 0 0;
}

/* LU, JAMAIS VU — le même idiome que le nom d'une case posée sur une figure
 * (`.figure-case-posee .figure-case-nom`) : la boîte existe pour la synthèse
 * vocale et le navigateur sans image, et n'occupe aucune place à l'écran. */
.lecture-seule {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* AU RELEVÉ, LA FIGURE CORRIGÉE N'EST PAS UNE VIGNETTE NON PLUS — P76 : la
 * cellule « Ce qu'il fallait répondre » est la plus étroite des cinq (P75), et
 * un arbre à 9 rem ne se lit pas. Un plancher, et le tableau défile dans son
 * conteneur (`.releve`, `overflow-x: auto`) — l'idiome écrit du dépôt : le
 * débordement est tenu par le conteneur, jamais par la page. */
.releve .attendu .figure {
  min-width: 18rem;
}

.retour .cause,
.retour .corrige-lien,
.retour .corrige-titre {
  margin-top: 0.5rem;
}

.retour .corrige {
  margin: 0.5rem 0 0;
  padding-left: 1.2rem;
}

.retour .corrige li + li {
  margin-top: 0.5rem;
}

.retour .calcul {
  color: var(--habillage-texte-attenue);
  margin-top: 0.15rem;
}

.retour .corrige-lien a {
  color: var(--habillage-accent);
}

.avis {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
}

.avis form {
  display: contents;
}

.avis p {
  margin: 0;
}

.avis button {
  margin: 0;
  font-size: var(--corps-legende);
  padding: 0.35rem 0.8rem;
  min-height: 0;
}

.avis .picto {
  margin-right: 0.35em;
}

.pas-compris {
  margin-top: 0.5rem;
}

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