/*
 * FICHIER GÉNÉRÉ — NE PAS MODIFIER À LA MAIN.
 * Produit par `scripts/design/assembler-css.mjs` (npm run assembler:css) à
 * partir de :
 *   - design/tokens.css
 *   - design/monde.css
 *   - src/styles/planche-socle.css
 *   - src/styles/planche-accueil.css
 *   - src/styles/planche-theme.css
 *   - src/styles/planche-composition.css
 *   - src/styles/planche-rangs.css
 *   - src/styles/planche-montant.css
 *   - src/styles/planche-pied.css
 *   - src/styles/planche-ariane.css
 *   - src/styles/planche-tableau.css
 *   - src/styles/planche-recherche.css
 *   - src/styles/planche-parlement.css
 *   - src/styles/planche-sources.css
 *   - src/styles/planche-mentions.css
 *   - src/styles/planche-methode.css
 *   - src/styles/planche-contact.css
 *   - src/styles/planche-accessibilite.css
 *   - src/styles/planche-suivi.css
 *   - src/styles/planche-chiffrage.css
 *   - src/styles/planche-candidat.css
 *   - src/styles/planche-proposition.css
 * Toute retouche doit se faire dans l'un de ces fichiers source, puis
 * relancer l'assemblage.
 */

/* =========================================================================
   design/tokens.css — « L'argent public : d'où il vient, où il va »
   Propriétaire : agent UX-DESIGN. Voir design/02-design-system.md.

   Variables CSS pures. Aucun import, aucune police distante, aucun framework.

   DIRECTION VISUELLE : éditoriale et chaleureuse. Le sujet est aride ; les
   neutres sont donc légèrement chauds (papier plutôt qu'écran), l'échelle
   typographique est franchement contrastée entre titre et corps, et les
   séparations reposent d'abord sur des fonds et de l'air, ensuite seulement
   sur des filets.

   TROIS ÉTATS DE THÈME :
     1. système  — aucun attribut sur <html> : le bloc @media décide, et suit
                   les changements de réglage en direct, sans JavaScript ;
     2. clair    — <html data-theme="clair"> neutralise le bloc @media ;
     3. sombre   — <html data-theme="sombre"> force la palette sombre.

   Le bloc sombre est écrit DEUX FOIS, à l'identique. CSS ne sait pas
   réunir une media-query et un sélecteur dans la même règle, et toute
   astuce d'indirection coûterait le même nombre de lignes en moins lisible.
   TOUTE MODIFICATION DE L'UN DOIT ÊTRE REPORTÉE DANS L'AUTRE.

   Contrastes : mesurés, jamais supposés. Voir §14.
   ========================================================================= */

:root {
  /* Indique au navigateur comment peindre ses propres surfaces (barres de
     défilement, contrôles natifs). En « système », les deux sont offerts. */
  color-scheme: light dark;

  /* ---------------------------------------------------------------------
     1. SURFACES ET NEUTRES — thème clair
     Neutres tièdes : une pointe de jaune-rouge éloigne le fond du gris
     d'interface sans lui donner de teinte reconnaissable.
     --------------------------------------------------------------------- */
  --c-bg:            #FFFFFF;  /* fond de page */
  --c-surface:       #FFFFFF;  /* cartes, panneaux */
  --c-surface-2:     #F3EFE7;  /* zones enfoncées, en-têtes de tableau */
  --c-surface-3:     #E8E2D6;  /* pistes de barres, squelettes */

  --c-border:        #E6DFD3;  /* filet discret, non porteur d'information */
  --c-border-strong: #958E7C;  /* bordure de contrôle — 3,05:1 sur --c-bg */

  --c-text:          #1C1A15;
  --c-text-muted:    #5A5449;
  --c-text-on-fill:  #FFFFFF;  /* texte posé sur une couleur catégorielle */

  /* ---------------------------------------------------------------------
     2. ACCENT INTERACTIF (liens, boutons, sélection)
     L'accent n'est JAMAIS utilisé pour coder une valeur monétaire.
     --------------------------------------------------------------------- */
  --c-accent:        #1A57AE;
  --c-accent-hover:  #123F80;
  --c-focus:         #0E3A78;  /* anneau de focus */
  --c-focus-halo:    #FFFFFF;  /* double anneau pour rester visible sur couleur */

  /* ---------------------------------------------------------------------
     3. ÉTATS D'INFORMATION — jamais appliqués à un montant
     Réservés aux messages système : chargement, absence de donnée, méthode.
     Pas de sémantique « bien / mal ».
     --------------------------------------------------------------------- */
  --c-info-bg:       #F1EEE7;
  --c-info-border:   #CBC3B4;
  --c-info-text:     #2D2A22;
  --c-note-bg:       #F7F0E1;  /* encart méthode / avertissement de périmètre */
  --c-note-border:   #D3C39F;
  --c-note-text:     #493C20;

  /* ---------------------------------------------------------------------
     4. PALETTE CATÉGORIELLE — 10 fonctions COFOG
     Une couleur STABLE par fonction, identique sur tous les écrans.
     Ces dix teintes sont INCHANGÉES par la refonte : elles portent une
     identité que le lecteur retient d'un écran à l'autre, et leur
     séparabilité sous daltonisme a été arbitrée fonction par fonction.
       --cat-NN        remplissage (tuile, segment, pastille)

     ENCRES CATÉGORIELLES — dix divisions COFOG, fixes et légendées.

     Elles n'existent QUE dans les arbres dont la nomenclature est la COFOG.
     Dans les douze autres arbres, une seule encre : celle du texte. C'est la
     suppression définitive du hachage d'identifiant — il n'y a plus rien à
     hacher (spec §4.2).

     L'encre 01 est un ardoise et NON l'encre de texte : en thème sombre, le
     fond vaut #101820, et une catégorie peinte de la couleur du fond serait
     invisible.
     --------------------------------------------------------------------- */
  --cat-01: #4A5560; /* Fonctionnement de l'État et dette */
  --cat-02: #6B4E9E; /* Défense */
  --cat-03: #0E7C86; /* Sécurité et justice */
  --cat-04: #B5760C; /* Économie, transports et emploi */
  --cat-05: #5F9130; /* Environnement */
  --cat-06: #8A5A2B; /* Logement et équipements des villes */
  --cat-07: #0B4F9E; /* Santé */
  --cat-08: #C2185B; /* Culture, sport et loisirs */
  --cat-09: #1E7A4C; /* Éducation */
  --cat-10: #D42E12; /* Protection sociale */

  /* Catégorie « autre / regroupement » et donnée absente */
  --pattern-none:    repeating-linear-gradient(45deg,
                       #C9C2B6 0 6px, #B4AC9E 6px 12px);

  /* ---------------------------------------------------------------------
     5. TYPOGRAPHIE
     Pile système uniquement : zéro requête réseau, rendu français correct.
     Aucune police n'a été ajoutée : sur un texte de cette longueur, le gain
     esthétique d'une police auto-hébergée ne vaut pas les octets et le
     saut de rendu qu'elle coûterait au premier affichage.
     --------------------------------------------------------------------- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
               Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  /* Échelle nettement plus contrastée qu'auparavant : le corps monte à 17 px,
     les titres montent plus vite que lui. Un chiffre-clé se compose comme un
     titre, jamais comme une cellule de tableau. */
  --fs-3xs: 0.75rem;    /* 12 px — mentions légales, unité d'axe */
  --fs-xs:  0.875rem;   /* 14 px — métadonnées, source */
  --fs-sm:  0.9375rem;  /* 15 px — texte secondaire, tableaux */
  --fs-md:  1.0625rem;  /* 17 px — texte courant (plancher) */
  --fs-lg:  1.25rem;    /* 20 px — chapô pédagogique */
  --fs-xl:  1.5rem;     /* 24 px — titre de section */
  --fs-2xl: clamp(1.875rem, 1.4rem + 1.9vw, 2.375rem);  /* titre d'écran */
  --fs-3xl: clamp(2.25rem, 1.6rem + 2.6vw, 3rem);       /* chiffre secondaire */

  --lh-tight: 1.08;
  --lh-snug:  1.25;
  --lh-base:  1.65;

  --fw-regular: 400;
  --fw-medium:  500;
  --fw-semibold: 600;
  --fw-bold:    700;

  --ls-display: -0.022em;
  --ls-titre:   -0.012em;
  --ls-caps:     0.07em;

  /* Chiffres tabulaires : OBLIGATOIRE sur tout montant, %, année, €/hab. */
  --numeric: tabular-nums lining-nums;

  /* ---------------------------------------------------------------------
     6. ESPACEMENTS — base 4 px, échelle élargie dans le haut
     Les crans 5 à 9 servent les respirations entre blocs : c'est là que se
     joue la sensation d'air, pas dans les remplissages de composants.
     --------------------------------------------------------------------- */
  --sp-1: 0.25rem;   /*  4 px */
  --sp-2: 0.5rem;    /*  8 px */
  --sp-3: 0.875rem;  /* 14 px */
  --sp-4: 1.25rem;   /* 20 px */
  --sp-5: 1.75rem;   /* 28 px */
  --sp-6: 2.5rem;    /* 40 px */
  --sp-7: 3.5rem;    /* 56 px */

  --tap-min: 44px;          /* cible tactile minimale */
  --content-max: 76rem;     /* largeur de contenu */
  --prose-max: 38rem;       /* largeur de lecture — environ 66 signes à 17 px */

  /* ---------------------------------------------------------------------
     7. RAYONS, BORDURES, OMBRES
     Rayons plus généreux et ombres très diffuses : ce sont elles qui
     remplacent les bordures sèches pour détacher une carte de son fond.
     --------------------------------------------------------------------- */
  --r-xs: 3px;
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 16px;
  --r-pill: 999px;

  --bw-hair: 1px;
  --bw-focus: 3px;

  --sh-1: 0 1px 2px rgba(58, 46, 28, 0.05), 0 1px 3px rgba(58, 46, 28, 0.04);
  --sh-2: 0 2px 4px rgba(58, 46, 28, 0.05), 0 6px 16px rgba(58, 46, 28, 0.06);

  /* ---------------------------------------------------------------------
     8. MOUVEMENT — court, jamais décoratif sur un changement de niveau
     --------------------------------------------------------------------- */
  --dur-fast: 90ms;
  --ease: cubic-bezier(0.2, 0, 0.2, 1);

  /* ---------------------------------------------------------------------
     9. ÉCARTS SIGNÉS  (écran « Ce que le Parlement a changé »)
     Aucune teinte ne code le signe. La direction se lit sur la position
     dans l'axe ; l'origine et l'arrivée se distinguent par le remplissage
     (point creux = projet, point plein = voté) ; le signe est écrit.
     --------------------------------------------------------------------- */
  --delta-track:   #97907F;
  --delta-from-bg: #FFFFFF;
  --delta-from-bd: #5A5449;
  --delta-to:      #1C1A15;
  --delta-axis:    #E6DFD3;
}

/* ===========================================================================
   THÈME SOMBRE — 1/2 : PRÉFÉRENCE SYSTÈME
   Ne s'applique pas si le lecteur a explicitement demandé le thème clair.
   =========================================================================== */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='clair']) {
    color-scheme: dark;

    --c-bg:            #101820;
    --c-surface:       #1E1A15;
    --c-surface-2:     #272219;
    --c-surface-3:     #342E23;

    --c-border:        #332D24;
    --c-border-strong: #6E6656;

    --c-text:          #EEE9DF;
    --c-text-muted:    #ABA392;
    --c-text-on-fill:  #16130F;

    --c-accent:        #93B6F2;
    --c-accent-hover:  #B4CEF8;
    --c-focus:         #B4CEF8;
    --c-focus-halo:    #101820;

    --c-info-bg:       #211D17;
    --c-info-border:   #3C362B;
    --c-info-text:     #D3CCBE;
    --c-note-bg:       #27200F;
    --c-note-border:   #514626;
    --c-note-text:     #E7D8B2;

    /*
     * Teintes DÉRIVÉES des encres claires (même teinte, luminosité relevée)
     * et MESURÉES par `scripts/design/contrastes.mjs` contre le fond sombre
     * #101820 — jamais transposées à l'œil (spec §4.2).
     */
    --cat-01: #788797; /* Fonctionnement de l'État et dette */
    --cat-02: #927ABD; /* Défense */
    --cat-03: #28B1BD; /* Sécurité et justice */
    --cat-04: #BD8628; /* Économie, transports et emploi */
    --cat-05: #71AC39; /* Environnement */
    --cat-06: #B77739; /* Logement et équipements des villes */
    --cat-07: #4287D7; /* Santé */
    --cat-08: #DB558A; /* Culture, sport et loisirs */
    --cat-09: #2DB873; /* Éducation */
    --cat-10: #D95F4A; /* Protection sociale */

    --pattern-none: repeating-linear-gradient(45deg,
                      #3B362C 0 6px, #4B4538 6px 12px);

    --delta-track: #6E6656;  --delta-from-bg: #1E1A15;  --delta-from-bd: #ABA392;
    --delta-to: #EEE9DF;  --delta-axis: #332D24;

    /* En sombre, une ombre ne se voit pas : elle est doublée d'un liseré. */
    --sh-1: 0 0 0 1px rgba(255, 248, 235, 0.05);
    --sh-2: 0 0 0 1px rgba(255, 248, 235, 0.07), 0 2px 10px rgba(0, 0, 0, 0.5);
  }
}

/* ===========================================================================
   THÈME SOMBRE — 2/2 : CHOIX EXPLICITE DU LECTEUR
   COPIE STRICTE du bloc ci-dessus. Toute retouche s'y reporte à l'identique.
   =========================================================================== */
:root[data-theme='sombre'] {
  color-scheme: dark;

  --c-bg:            #101820;
  --c-surface:       #1E1A15;
  --c-surface-2:     #272219;
  --c-surface-3:     #342E23;

  --c-border:        #332D24;
  --c-border-strong: #6E6656;

  --c-text:          #EEE9DF;
  --c-text-muted:    #ABA392;
  --c-text-on-fill:  #16130F;

  --c-accent:        #93B6F2;
  --c-accent-hover:  #B4CEF8;
  --c-focus:         #B4CEF8;
  --c-focus-halo:    #101820;

  --c-info-bg:       #211D17;
  --c-info-border:   #3C362B;
  --c-info-text:     #D3CCBE;
  --c-note-bg:       #27200F;
  --c-note-border:   #514626;
  --c-note-text:     #E7D8B2;

  /*
   * Teintes DÉRIVÉES des encres claires (même teinte, luminosité relevée)
   * et MESURÉES par `scripts/design/contrastes.mjs` contre le fond sombre
   * #101820 — jamais transposées à l'œil (spec §4.2).
   */
  --cat-01: #788797; /* Fonctionnement de l'État et dette */
  --cat-02: #927ABD; /* Défense */
  --cat-03: #28B1BD; /* Sécurité et justice */
  --cat-04: #BD8628; /* Économie, transports et emploi */
  --cat-05: #71AC39; /* Environnement */
  --cat-06: #B77739; /* Logement et équipements des villes */
  --cat-07: #4287D7; /* Santé */
  --cat-08: #DB558A; /* Culture, sport et loisirs */
  --cat-09: #2DB873; /* Éducation */
  --cat-10: #D95F4A; /* Protection sociale */

  --pattern-none: repeating-linear-gradient(45deg,
                    #3B362C 0 6px, #4B4538 6px 12px);

  --delta-track: #6E6656;  --delta-from-bg: #1E1A15;  --delta-from-bd: #ABA392;
  --delta-to: #EEE9DF;  --delta-axis: #332D24;

  --sh-1: 0 0 0 1px rgba(255, 248, 235, 0.05);
  --sh-2: 0 0 0 1px rgba(255, 248, 235, 0.07), 0 2px 10px rgba(0, 0, 0, 0.5);
}

/* Le thème clair explicite ne redéfinit rien : il neutralise seulement le
   bloc de préférence système, déjà écarté par son sélecteur. */
:root[data-theme='clair'] {
  color-scheme: light;
}

/* ===========================================================================
   PRÉFÉRENCES UTILISATEUR
   =========================================================================== */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0ms;
  }
}

@media (prefers-contrast: more) {
  :root {
    --c-border: var(--c-border-strong);
    --c-text-muted: var(--c-text);
  }
}

/*
 * LE MONDE — « L'unité répétée », ISOTYPE, Vienne 1925.
 *
 * Ce fichier ne contient que ce qui fait le monde : la police et la matière.
 * Les couleurs vivent dans `tokens.css`, les composants ailleurs.
 *
 * Aucune requête distante. La police est copiée dans `public/polices/` par
 * la Task 4 du plan, et sert depuis le même domaine que le site.
 */

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/polices/jost-latin-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/polices/jost-latin-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/polices/jost-latin-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/polices/jost-latin-700.woff2') format('woff2');
}

:root {
  --police: 'Jost', system-ui, -apple-system, 'Segoe UI', sans-serif;
  /*
   * Chiffres tabulaires partout, sans exception : un montant qui change de
   * chasse d'un rendu à l'autre fait sauter les colonnes d'un tableau.
   */
  --chiffres: tabular-nums lining-nums;
}

/*
 * LA COQUILLE PORTE LE MONDE JUSQU'AU DOCUMENT — sans ce bloc, `--police` et
 * `--c-bg` restent des jetons définis mais jamais consommés : le shell PHP
 * ne charge que `tokens.css` + `monde.css` + les morceaux de la planche,
 * jamais `src/styles/base.css` (qui n'est bundlé que dans l'app React). Le rendu
 * public retombait donc sur la police système et un fond transparent,
 * c'est-à-dire exactement le monde que cette tâche remplace.
 */
html {
  color-scheme: light dark;
}

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--police);
  font-size: var(--fs-md, 1.0625rem);
  line-height: var(--lh-base, 1.65);
  -webkit-text-size-adjust: 100%;
}

/*
 * L'UNITÉ. Une forme géométrique neutre, jamais un pictogramme figuratif —
 * dessiner « indemnisation du chômage » ferait basculer la neutralité par la
 * porte que la suppression du hachage vient de fermer (spec §8).
 */
.unite {
  display: block;
  inline-size: 10px;
  block-size: 10px;
  flex: 0 0 10px;
  background: var(--encre, var(--c-text));
}

/* Demi-unité : le seul fractionnement autorisé. */
.unite--demie {
  inline-size: 5px;
  flex-basis: 5px;
}

/* Donnée non publiée : une trame, jamais un aplat gris. */
.unite--absente {
  background: var(--pattern-none);
}

/*
 * LE SOCLE DE LA PLANCHE — chrome commun à tous les écrans du monde
 * « L'unité répétée » : lien d'évitement, en-tête du site, conteneur de page,
 * titre, exergue et section.
 *
 * SCINDÉ DE `planche.css` (ruling de la tâche 2 du plan 2a). Le fichier
 * unique était monté à 772 lignes contre la règle des 500, et chacune des
 * tâches restantes de ce plan devait encore y écrire. Les morceaux sont
 * assemblés DANS L'ORDRE par `scripts/design/assembler-css.mjs` : une
 * déclaration plus tardive l'emporte en cas de collision de sélecteur, et
 * `tests/assemblage-css.test.ts` verrouille cet ordre.
 */

/*
 * Lien d'évitement : premier élément focalisable, visible au focus seul.
 *
 * IL PORTE SON PLANCHER DE CIBLE COMME TOUT AUTRE ÉLÉMENT INTERACTIF. Sa
 * hauteur ne venait que de son remplissage — donc de la hauteur de ligne du
 * texte, qu'un réglage de police pouvait faire passer sous les 44 px sans
 * que rien ne le signale. `tests/lib/rgaa.php`, appelé par chaque harnais
 * d'écran, le relevait : c'était la seule cible du site qu'aucune règle de
 * plancher ne couvrait.
 */
.evitement {
  position: absolute;
  inset-inline-start: -9999px;
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  padding: 12px 18px;
  background: var(--c-text);
  color: var(--c-bg);
  z-index: 100;
}
.evitement:focus {
  inset-inline-start: 0;
  inset-block-start: 0;
}

/*
 * TEXTE VISUELLEMENT MASQUÉ, MAIS LU — ni `display: none` ni `visibility:
 * hidden`, qui retireraient le texte des technologies d'assistance aussi
 * bien que de l'écran. Sert notamment à annoncer qu'un lien ouvre une
 * nouvelle fenêtre (RGAA 4.1, critère 6.2) sans l'écrire à l'écran sur
 * chaque lien externe. Même recette que `.theme__intitule`
 * (`planche-theme.css`), partagée ici plutôt que recopiée une nouvelle fois.
 */
.visuellement-masque {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/*
 * L'EN-TÊTE — commune à toutes les pages de ce monde. Sans ces règles, ses
 * liens restent au style par défaut du navigateur : bleu, souligné, et à
 * peine 18 px de haut — sous la cible tactile de 44 px que ce monde s'impose
 * partout ailleurs (RGAA 4.1, critère 12.8 ; `--tap-min` de `tokens.css`).
 */
.entete {
  border-block-end: 1px solid var(--c-border);
}
.entete__nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1.5rem;
  max-width: 72rem;
  margin-inline: auto;
  padding-inline: 6vw;
}
.entete__nav a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--c-text);
  text-decoration: none;
}
.entete__nav a:hover,
.entete__nav a:focus-visible {
  text-decoration: underline;
}
.entete__marque {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  gap: 0.5rem;
}
/*
 * LE SYMBOLE — cinq unités 2 — 1 — 2, dessinées dans `Document::SYMBOLE`.
 * `currentColor` hérite `--c-text` de la règle `.entete__nav a` ci-dessus :
 * aucune couleur propre au logo, correcte dans les deux thèmes par
 * construction. Le carré fait la hauteur x, pas la hauteur de ligne
 * entière : un symbole calé sur `--tap-min` déborderait la marque sur les
 * petits corps de texte.
 */
.entete__symbole {
  inline-size: 1.1em;
  block-size: 1.1em;
  flex: 0 0 auto;
}

.planche-page {
  max-width: 72rem;
  margin-inline: auto;
  padding: 3.5rem 6vw 3rem;
}

/*
 * TOUT LIEN DE CONTENU, UNE SEULE RÈGLE.
 *
 * `.entete__nav a`, `.ariane a` et `.tableau a` étaient chacun repris à la
 * main ; trois contextes ne l'étaient pas — « Descendre d'un niveau » du
 * panneau calme, l'unique sortie de la page 404, et le lien du générique vers
 * la page des sources. Ces trois-là restaient au style par défaut du
 * navigateur : `rgb(0,0,238)` en clair, `rgb(158,158,255)` en sombre, et
 * 25 px de haut. Le monde interdit explicitement le bleu d'interaction sur du
 * contenu (`--c-accent` est réservé au chrome), et `--tap-min` vaut 44 px
 * partout ailleurs (RGAA 4.1, critère 12.8). Une règle unique posée sur le
 * conteneur de page vaut mieux qu'un quatrième rattrapage ponctuel.
 *
 * Placée AVANT `.ariane a` et `.tableau th a`, de spécificité égale ou
 * supérieure : ces deux-là gardent donc leurs alignements propres, et cette
 * règle ne fait que rattraper ce qui n'est repris nulle part ailleurs.
 */
.planche-page a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  color: var(--c-text);
}

.planche-page h1 {
  font-size: clamp(1.9rem, 4.2vw, 3.4rem);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.02;
  max-width: 16ch;
  margin: 0 0 0.4rem;
}

.exergue {
  font-size: 0.95rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--c-text-muted);
  margin: 0 0 2.6rem;
}

.planche-section {
  border-block-start: 3px solid var(--c-text);
  padding-block-start: 1.4rem;
  margin-block-end: 2.4rem;
}

.planche-section h2 {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 1rem;
}

/*
 * CHIFFRES TABULAIRES — chasse fixe, pour que les colonnes de montants
 * s'alignent et qu'un chiffre ne « saute » pas d'un rendu à l'autre. Ce
 * n'est pas une police à chasse fixe : seule la variante numérique change.
 *
 * DÉPLACÉ ICI DEPUIS `src/styles/composants.css` (relecture de la tâche 2 du
 * plan 2b) : cette règle existait déjà, à l'identique, mais dans un fichier
 * jamais enregistré dans `SOURCES` — `/parlement`, déjà porté, rendait donc
 * ses `<span class="num">` sans chasse tabulaire depuis sa livraison, sans
 * qu'aucun test ne le remarque (la suite vérifie des classes présentes dans
 * le balisage, jamais ce qui est peint). `composants.css` reste la version
 * consommée par le rendu React ; celle-ci est la SEULE dans la planche
 * assemblée que sert PHP — les deux ne peuvent diverger que si l'une des
 * deux copies est corrigée sans l'autre, exactement le risque que porte
 * toute règle dupliquée sur ce projet.
 */
.num {
  font-variant-numeric: var(--chiffres, tabular-nums lining-nums);
}

td.num,
th.num {
  text-align: right;
}

/*
 * PARTIEL DE L'ACCUEIL — la liste des treize arbres de lecture, sous le bloc
 * des deux masses.
 *
 * POURQUOI CE FICHIER EXISTE. L'accueil a été réduit à une seule planche (les
 * deux masses) ; les onze autres arbres de lecture n'avaient plus aucun point
 * d'entrée nommé, et huit d'entre eux — 654 nœuds sur 1 070 — n'étaient plus
 * atteignables du tout en suivant des liens depuis l'accueil. Cette liste,
 * posée par `api/pages/accueil.php` SOUS le bloc des deux masses (jamais au-
 * dessus : il ne doit pas être concurrencé), nomme les treize et dit pour
 * chacun ce qu'il permet de lire.
 *
 * ATTENTION À LA SPÉCIFICITÉ (voir `planche-socle.css`, `.planche-page a`,
 * et `planche-rangs.css`, `.planche-page .planche__lien`, même piège déjà
 * rencontré ici). `.planche-page a` (spécificité 0,1,1) pose déjà
 * `display: inline-flex` et `min-block-size: var(--tap-min, 44px)` sur TOUT
 * lien de `.planche-page` — l'accueil en fait partie. Le lien de cette liste
 * veut un agencement en colonne (libellé au-dessus de sa description) : une
 * règle `.arbres-liste__lien` seule (0,1,0) serait de spécificité INFÉRIEURE
 * à `.planche-page a` et perdrait silencieusement son `display`, quel que
 * soit l'ordre d'assemblage. Le sélecteur ci-dessous est donc
 * `.planche-page .arbres-liste__lien` (0,2,0), comme `.planche__lien`
 * l'est déjà pour la même raison.
 */

.arbres-section > p {
  max-width: 60ch;
  color: var(--c-text-muted);
  margin: 0 0 1.2rem;
}

.arbres-liste {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 0.75rem;
  margin: 0;
  padding: 0;
}

.planche-page .arbres-liste__lien {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  min-block-size: var(--tap-min, 44px);
  padding: 0.9rem 1rem;
  border: 1px solid var(--c-border);
  color: var(--c-text);
  text-decoration: none;
}
.planche-page .arbres-liste__lien:hover,
.planche-page .arbres-liste__lien:focus-visible {
  border-color: var(--c-text);
}
.planche-page .arbres-liste__lien:hover .arbres-liste__libelle,
.planche-page .arbres-liste__lien:focus-visible .arbres-liste__libelle {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}

.arbres-liste__libelle {
  font-size: 0.9rem;
  font-weight: 600;
}

.arbres-liste__description {
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--c-text-muted);
}

/*
 * LE SÉLECTEUR DE THÈME DE L'EN-TÊTE — deuxième morceau de la planche
 * assemblée, juste après `planche-socle.css`. Voir son en-tête pour l'ordre
 * d'assemblage.
 *
 * `Document::entete()` rend ce contrôle sur CHAQUE écran du monde
 * « L'unité répétée » depuis la tâche 7 du plan 2a — jusque-là, seule la
 * page PHP du formulaire de contact en portait un (voir
 * `src/styles/theme-selecteur.css`, sa feuille jumelle assemblée dans
 * `contact.css` pour l'application React). Les deux stylent le MÊME
 * balisage — `[data-controle-theme]`, trois boutons radio `.theme__radio` —
 * sans se partager de règle : celle-ci utilise les jetons `--c-*` de
 * `design/tokens.css`, l'autre le pont de noms de l'application React que
 * le shell PHP ne charge jamais.
 *
 * Boutons radio natifs déguisés en interrupteur segmenté&nbsp;: l'état
 * courant est annoncé nativement au lecteur d'écran, les flèches parcourent
 * le groupe, et chaque option atteint la cible tactile minimale de 44 px
 * dans les DEUX dimensions (RGAA 4.1, critère 12.8 ; `--tap-min`). L'état
 * retenu ne se lit pas à la seule couleur&nbsp;: la pastille active change
 * aussi de fond, de graisse et de relief.
 *
 * DÉGRADATION SANS JAVASCRIPT : ce groupe naît avec l'attribut `hidden` ;
 * seul `public/theme.js` le dévoile, une fois le choix mémorisé lu. Sans
 * script, il reste cité dans le document mais jamais affiché — un contrôle
 * qui resterait visible sans pouvoir agir serait pire qu'absent.
 */
.theme {
  border: 0;
  margin-inline-start: auto;
  padding: 0;
  min-width: 0;
}

.theme__intitule {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.theme__options {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--c-surface-2);
  border: var(--bw-hair) solid var(--c-border);
  border-radius: var(--r-pill);
}

.theme__choix {
  position: relative;
  display: inline-flex;
}

/*
 * Le bouton radio couvre toute l'option : il reste dans l'ordre de
 * tabulation et reçoit le clic, mais c'est l'étiquette qui porte
 * l'apparence.
 */
.theme__radio {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.theme__nom {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--tap-min);
  min-inline-size: var(--tap-min);
  padding-inline: var(--sp-3);
  border: var(--bw-hair) solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  color: var(--c-text-muted);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease);
}

.theme__radio:checked + .theme__nom {
  background: var(--c-surface);
  border-color: var(--c-border);
  box-shadow: var(--sh-1);
  color: var(--c-text);
  font-weight: var(--fw-semibold);
}

.theme__radio:focus-visible + .theme__nom {
  outline: var(--bw-focus) solid var(--c-focus);
  outline-offset: 2px;
}

.theme__radio:hover + .theme__nom {
  color: var(--c-text);
}

/*
 * LA BARRE DE COMPOSITION — deuxième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LA BARRE DE COMPOSITION — de quoi ce poste est fait.
 *
 * Une seule barre valant 100 % du poste courant, un segment par poste, dans
 * l'ordre de la liste. Elle est posée en tête de section, avant le marqueur de
 * redénomination et avant la planche : « de quoi ce poste est fait » se lit
 * avant « combien pèse chaque morceau ».
 *
 * LA COULEUR EST UN ORDRE, JAMAIS UNE IDENTITÉ. Le remplissage vient de la
 * rampe séquentielle `--seq-1` … `--seq-6` de `design/tokens.css` : le segment
 * le plus large reçoit le pas le plus dense, les suivants descendent la rampe,
 * et la rampe se reprend au sommet tous les six rangs. Aucune teinte n'est
 * attachée à un poste, aucune ne peut donc porter d'appréciation — c'est
 * exactement ce que le hachage d'identifiant faisait et que cette refonte a
 * supprimé.
 *
 * LA BARRE NE REPOSE JAMAIS SUR SON REMPLISSAGE. Les deux premiers pas de la
 * rampe sont, par construction, à peine distincts du fond de page (1,16:1 en
 * clair, 1,08:1 en sombre) : c'est le propre d'une rampe séquentielle, dont le
 * pas le plus clair est le voisin du papier. Ce qui rend la barre et chacune
 * de ses frontières visibles, c'est donc le FILET — `--c-border-strong`, qui
 * tient 3,26:1 sur le fond clair et 3,15:1 sur le fond sombre, et que
 * `scripts/design/contrastes.mjs` mesure à chaque exécution. Le remplissage
 * n'ajoute qu'un rang ; le rang est de toute façon écrit sur la liste.
 */
.composition {
  margin-block-end: 1.2rem;
}

.composition__barre {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  block-size: 1.5rem;
  overflow: hidden;
  border: 1px solid var(--c-border-strong);
  background: var(--c-bg);
}

/*
 * LARGEUR MINIMALE RENDUE : 0,4 % de la barre, décidé et appliqué par
 * `construireComposition` (pipeline), doublé ici d'un plancher de 3 px pour
 * que le segment tienne aussi sur un écran étroit. Un segment plus fin
 * disparaîtrait dans son voisin, et la barre montrerait moins de segments
 * qu'il n'y a de postes.
 *
 * La somme des largeurs émises vaut exactement 100 : le relèvement au plancher
 * est PRIS SUR LES AUTRES segments, jamais ajouté à la barre. `flex-shrink: 1`
 * ne sert qu'au cas extrême où le plancher de 3 px l'emporterait sur un écran
 * très étroit ; la barre reste alors pleine, sans débord.
 */
.composition__segment {
  flex: 0 1 var(--largeur);
  min-inline-size: 3px;
  box-sizing: border-box;
  background: var(--teinte, var(--c-surface-3));
  border-inline-end: 1px solid var(--c-border-strong);
}
.composition__segment:last-child {
  border-inline-end: 0;
}

.composition__legende {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--c-text-muted);
  max-width: 68ch;
}

/*
 * LES RANGS DE LA PLANCHE — troisième morceau de la planche assemblée : les
 * rangs et leurs carrés, la clé des dix encres, le marqueur de
 * redénomination, le panneau calme et le bloc de générique.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

.planche__rangs {
  list-style: none;
  margin: 0;
  padding: 0;
}

/*
 * Le nom, la bande, le montant, la part — sur la MÊME ligne. Le tableau qui
 * reprenait dessous les mêmes postes avec les mêmes montants a disparu : une
 * seule liste porte désormais les quatre informations, et le nom n'est
 * JAMAIS tronqué au point de disparaître — c'est le défaut que la planche
 * existe pour corriger, 14 tuiles nommées sur 45 dans l'ancienne treemap.
 */
.planche__rang {
  border-block-end: 1px solid var(--c-border);
}

/*
 * LA LIGNE, LIEN OU NON.
 *
 * Sur l'écran d'exploration, TOUT rang est un lien : la page d'un poste existe
 * qu'il ait ou non des enfants, et elle porte son montant, ses repères, sa
 * source et sa passerelle. Le rang muet ne subsiste que sur la planche des
 * deux masses de l'accueil, dont un rang peut ne viser aucun arbre produit.
 * Les deux portent la MÊME grille — le lecteur ne doit pas voir la liste se
 * déformer selon la forme du rang — et la même hauteur minimale de 44 px
 * (RGAA 4.1, critère 12.8).
 *
 * `.planche-page .planche__lien` et non `.planche__lien` seul : la règle
 * générale `.planche-page a` pose `display: inline-flex` sur tout lien de
 * contenu, et une spécificité égale la laisserait gagner ou perdre selon
 * l'ordre du fichier assemblé.
 */
.planche__ligne,
.planche-page .planche__lien {
  display: grid;
  grid-template-columns: minmax(9rem, 15vw) 1fr minmax(5.5rem, auto) minmax(3.5rem, auto);
  gap: 1rem;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  padding-block: 0.5rem;
  color: var(--c-text);
  text-decoration: none;
}
.planche-page .planche__lien:hover .planche__nom,
.planche-page .planche__lien:focus-visible .planche__nom {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}

/*
 * LE NOM DU RANG, ET LA PASTILLE DE SON SEGMENT.
 *
 * LA LÉGENDE DE LA BARRE EST LA LISTE : aucun bloc de légende séparé ne répète
 * des libellés déjà écrits ici. La pastille reprend la teinte du segment ;
 * elle est décorative (`aria-hidden`), le rang porte déjà son nom, son montant
 * et sa part, et le segment porte de son côté son propre nom accessible.
 *
 * Le filet de la pastille est le même que celui des segments : sans lui, une
 * pastille des deux premiers pas de la rampe serait un carré invisible.
 */
.planche__nom {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: 0.9rem;
  font-weight: 500;
}
.planche__pastille {
  flex: 0 0 12px;
  inline-size: 12px;
  block-size: 12px;
  margin-block-start: 0.2em;
  box-sizing: border-box;
  border: 1px solid var(--c-border-strong);
  background: var(--teinte, transparent);
}

.planche__bande {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
}

.planche__montant {
  text-align: end;
  font-size: 0.9rem;
  font-weight: 600;
  font-variant-numeric: var(--chiffres);
}

/* La part du total, sur la même ligne que le montant qu'elle qualifie. */
.planche__part {
  text-align: end;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--c-text-muted);
  font-variant-numeric: var(--chiffres);
}

.planche__legende {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-block-start: 0.85rem;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--c-text-muted);
}

.planche__ecartes,
.avertissement {
  margin-block-start: 0.8rem;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--c-text-muted);
  max-width: 68ch;
}

/*
 * LA CLÉ DES DIX ENCRES, imprimée immédiatement sous les rangs.
 *
 * La spec l'exige trois fois (§4.2 « fixes et légendées », §4.4 « la clé des
 * dix encres imprimée immédiatement dessous », §5.1 « légendée sur place »)
 * et le contrat de direction, premier enfant de chaque `<body>`, l'affirme au
 * lecteur. Sans elle, le seul argument qui autorise le rouge dans ce monde —
 * dix teintes qui « ne se rencontrent que dans une liste de dix, chacune
 * étiquetée » — tombe.
 *
 * La pastille reprend exactement la géométrie de `.unite` : le lecteur doit
 * reconnaître le carré qu'il vient de compter, pas une forme voisine.
 */
.planche__encres {
  margin-block-start: 0.9rem;
  padding-block-start: 0.7rem;
  border-block-start: 1px solid var(--c-border);
}
.planche__encres-titre {
  margin: 0 0 0.5rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--c-text-muted);
}
.planche__encres ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.3rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.78rem;
  font-weight: 500;
}
.planche__encres li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.planche__encres-pastille {
  display: block;
  inline-size: 10px;
  block-size: 10px;
  flex: 0 0 10px;
  background: var(--encre, var(--c-text));
}

/* Le marqueur de redénomination : un événement, pas une note. */
.planche__redenomination {
  display: inline-block;
  margin-block-end: 0.8rem;
  padding: 0.35rem 0.7rem;
  border: 2px solid var(--c-text);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
}

/* LE PANNEAU CALME — relevé par l'affiche placardée. Jamais décoré. */
.calme {
  margin-block-start: 2.5rem;
  border: 2px solid var(--c-text);
  padding: 1.4rem 1.5rem;
  max-width: 40rem;
  background: var(--c-bg);
}
.calme__cle {
  margin: 0 0 0.35rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--c-text-muted);
}
.calme__grand {
  margin: 0;
  font-size: clamp(1.4rem, 3vw, 2.1rem);
  font-weight: 700;
  line-height: 1.1;
}

/* LE BLOC DE GÉNÉRIQUE — relevé par l'affiche de film noir. */
.generique {
  margin-block-start: 2.8rem;
  border-block-start: 2px solid var(--c-text);
  padding-block-start: 0.7rem;
  columns: 3;
  column-gap: 2rem;
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--c-text-muted);
}
.generique p { margin: 0 0 0.3rem; break-inside: avoid; }
.generique b { color: var(--c-text); }

/*
 * LE MONTANT DE LA PAGE ET SES REPÈRES — quatrième morceau de la planche
 * assemblée : le grand montant, son étiquette, ses trois repères d'échelle,
 * la description du poste et la passerelle entre arbres.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LE MONTANT DE LA PAGE — le chiffre dont l'écran parle, sous son titre.
 * `.num` ne portait aucune règle hors du tableau : le grand montant héritait
 * donc du corps de texte, à la taille d'un paragraphe. Chiffres tabulaires,
 * comme partout dans ce monde (spec §4.2).
 */
.planche-page__montant {
  margin: 1.6rem 0 0;
  font-size: clamp(1.6rem, 4vw, 2.6rem);
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: var(--chiffres);
}

/*
 * LES TROIS REPÈRES D'ÉCHELLE — « toujours donner l'échelle » (PRODUCT.md,
 * troisième principe). Un montant nu ne dit rien à un citoyen ; ces trois
 * lignes sont la seule chose qui le rende comparable à quelque chose qu'il
 * connaît. Filets pleine chasse, comme le reste du monde.
 */
.reperes {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 2.5rem;
  max-width: 68ch;
}
.reperes li {
  padding-block: 0.35rem;
  font-size: 0.9rem;
  font-weight: 500;
  font-variant-numeric: var(--chiffres);
}

/* L'étiquette du montant : statut, millésime, grandeur, référentiel. */
.planche-page__etiquette {
  margin: 0.2rem 0 0;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--c-text-muted);
}

.planche-page__description {
  margin: 0 0 1rem;
  font-size: 0.95rem;
  line-height: 1.55;
  max-width: 68ch;
}

/* L'escalier de passage entre arbres : un événement, pas une note. */
.passerelle {
  margin-block-start: 1.2rem;
  font-size: 0.9rem;
  font-weight: 600;
}

/*
 * LE PIED DU SITE — cinquième morceau de la planche assemblée, avec la classe
 * réservée aux technologies d'assistance.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LE PIED DU SITE — distinct du bloc de générique, et jamais son doublon.
 * Le générique porte la provenance des CHIFFRES DE LA PAGE ; le pied porte
 * l'identité éditoriale et légale DU SITE. Les deux se suivent, réglés de la
 * même façon, et ne disent pas la même chose.
 */
.pied {
  border-block-start: 1px solid var(--c-border);
  margin-block-start: 3rem;
  padding: 1.4rem 6vw 3rem;
  max-width: 72rem;
  margin-inline: auto;
  font-size: 0.8rem;
  color: var(--c-text-muted);
}
.pied__liens {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.8rem;
}
.pied a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  color: var(--c-text);
  font-weight: 500;
}
.pied p {
  margin: 0 0 0.3rem;
  max-width: 68ch;
}
.pied code {
  font-family: inherit;
  font-weight: 600;
  color: var(--c-text);
}

/* Réservé aux technologies d'assistance : jamais visible à l'écran. */
.vh {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/*
 * LE FIL D'ARIANE — sixième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LE FIL D'ARIANE — chaque maillon porte son échelon, son montant et son
 * millésime, jamais un simple libellé. Le séparateur (`.ariane__sep`, `›`)
 * et la rupture de comparabilité (`.ariane__rupture`, glyphe `‖` doublé d'un
 * texte pour lecteur d'écran, jamais une couleur seule) sont des ÉLÉMENTS
 * émis par `FilAriane::rendre()`, jamais un `::before` : une règle posée sur
 * TOUT `li` non premier aurait une spécificité plus forte qu'une exception
 * ciblée sur `.ariane__rupture`, et le chevron ordinaire regagnerait sa
 * place à côté du marqueur de rupture quel que soit l'ordre des règles.
 * Deux éléments distincts, jamais les deux à la fois, rendent les deux
 * marqueurs mutuellement exclusifs par construction.
 */
.ariane {
  border-block-end: 1px solid var(--c-border);
}
.ariane ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  max-width: 72rem;
  margin: 0 auto;
  padding: 0.6rem 6vw;
  gap: 0.3rem 0;
}
.ariane li {
  display: flex;
  /*
   * `flex-start`, pas `center` : dès que nom + montant + millésime ne
   * tiennent plus sur une ligne (768 px, 375 px, ou tout libellé long à
   * n'importe quelle largeur), le rang s'étale sur deux lignes. Un
   * séparateur centré verticalement contre un rang de deux lignes se
   * retrouve à mi-hauteur, DANS le rang, au lieu de rester au niveau de sa
   * première ligne.
   */
  align-items: flex-start;
}
.ariane__sep {
  margin-inline: 0.5rem;
  padding-block-start: 0.2rem;
  color: var(--c-text-muted);
}
.ariane__rupture {
  margin-inline: 0.5rem;
  padding-block-start: 0.2rem;
  color: var(--c-text);
  font-weight: 700;
}
.ariane a,
.ariane span[aria-current] {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  min-block-size: var(--tap-min, 44px);
  padding-block: 0.2rem;
  font-size: 0.78rem;
  color: var(--c-text);
  text-decoration: none;
}
.ariane a:hover,
.ariane a:focus-visible {
  text-decoration: underline;
}
.ariane__nom {
  font-weight: 600;
}
.ariane__montant {
  font-weight: 500;
  color: var(--c-text-muted);
}
.ariane span[aria-current] .ariane__nom {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.ariane__millesime {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-text-muted);
}

/*
 * LE TABLEAU COMPLET — septième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LE TABLEAU COMPLET — filet pleine chasse, jamais de zébrage porté par la
 * couleur seule : la lisibilité vient des filets, pas d'un fond alterné.
 */
.tableau {
  width: 100%;
  border-collapse: collapse;
  margin-block-start: 2.4rem;
  font-size: 0.85rem;
}
.tableau caption {
  text-align: start;
  margin-block-end: 0.6rem;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-text-muted);
}
.tableau th,
.tableau td {
  padding: 0.55rem 0.6rem;
  border-block-end: 1px solid var(--c-border);
  text-align: start;
}
.tableau thead th {
  border-block-end: 2px solid var(--c-text);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
}
.tableau .num {
  text-align: end;
  font-variant-numeric: var(--chiffres);
}
.tableau th a,
.tableau td a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  color: var(--c-text);
}
.tableau th a:hover,
.tableau th a:focus-visible {
  text-decoration-thickness: 2px;
}

@media (max-width: 48rem) {
  .planche__ligne,
  .planche-page .planche__lien { grid-template-columns: 1fr; gap: 0.3rem; }
  .planche__montant,
  .planche__part { text-align: start; }
  .reperes { gap: 0; }
  .generique { columns: 1; }
  .tableau { font-size: 0.78rem; }
}

/*
 * L'ÉCRAN DE RECHERCHE — huitième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

/*
 * LA RECHERCHE — le seul écran du site qui cherche sans JavaScript.
 *
 * Le champ et le bouton sont deux cibles interactives ordinaires : elles
 * suivent `--tap-min` (44 px, RGAA 4.1 critère 12.8) comme tout le reste de
 * ce monde, et le bouton porte un intitulé écrit, jamais une icône seule.
 */
.recherche__formulaire {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1rem;
  margin-block-end: 2rem;
}
.recherche__formulaire label {
  flex-basis: 100%;
  font-weight: 600;
}
.recherche__champ {
  flex: 1 1 18rem;
  min-width: 0;
  min-block-size: var(--tap-min, 44px);
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md, 10px);
  background: var(--c-surface);
  color: var(--c-text);
  font: inherit;
  /* Sous 16 px, les navigateurs mobiles zooment à la mise au point. */
  font-size: max(1rem, 16px);
}
.recherche__bouton {
  min-block-size: var(--tap-min, 44px);
  padding: 0.5rem 1.4rem;
  border: 1px solid transparent;
  border-radius: var(--r-pill, 999px);
  background: var(--c-accent);
  color: var(--c-bg);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.recherche__bouton:hover,
.recherche__bouton:focus-visible {
  background: var(--c-accent-hover);
}

.recherche__aide,
.recherche__total {
  color: var(--c-text-muted);
  font-size: 0.9rem;
  margin-block-end: 1.4rem;
}

/*
 * UNE LISTE, PAS UN TABLEAU : chaque résultat porte quatre informations
 * — libellé, arbre, montant, millésime — et deux résultats de même libellé
 * doivent rester distinguables. La critique du 2026-08-31 relevait
 * précisément « deux résultats au titre identique, montants différents,
 * aucun millésime » : les quatre informations sont donc TOUJOURS écrites,
 * jamais seulement le libellé.
 */
.recherche__resultats {
  list-style: none;
  margin: 0;
  padding: 0;
}
.recherche__resultats li {
  border-block-end: 1px solid var(--c-border);
}
.recherche__resultat {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 1rem;
  row-gap: 0.15rem;
  align-items: baseline;
  padding-block: 0.8rem;
  min-block-size: var(--tap-min, 44px);
  color: var(--c-text);
}
.recherche__nom {
  grid-column: 1;
  font-weight: 600;
}
.recherche__montant {
  grid-column: 2;
  font-variant-numeric: var(--chiffres);
  font-weight: 600;
  text-align: end;
}
.recherche__arbre {
  grid-column: 1;
  font-size: 0.85rem;
  color: var(--c-text-muted);
}
/*
 * LE CHEMIN — l'ascendance du poste, sur toute la largeur de la ligne.
 *
 * C'est LUI qui sépare deux homonymes du MÊME arbre : le millésime est lu
 * par arbre et chaque arbre n'en a qu'un, il ne distingue donc rien à
 * l'intérieur d'un arbre. Restauré depuis `src/views/VueRecherche.tsx` par
 * la revue d'ensemble du 2026-09-02.
 */
.recherche__chemin {
  grid-column: 1 / -1;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--c-text-muted);
}

.recherche__millesime {
  grid-column: 2;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-text-muted);
  text-align: end;
}

@media (max-width: 30rem) {
  .recherche__resultat { grid-template-columns: 1fr; }
  .recherche__chemin,
  .recherche__montant,
  .recherche__millesime { grid-column: 1; text-align: start; }
}

/*
 * « CE QUE LE PARLEMENT A CHANGÉ » — neuvième et dernier morceau de la
 * planche assemblée. Voir l'en-tête de `planche-socle.css` pour l'ordre.
 *
 * L'ÉCRAN LE PLUS EXPOSÉ DU SITE EN MATIÈRE DE NEUTRALITÉ (design system
 * §7.4). Il est le seul de toute cette refonte à ne porter AUCUN jeton de la
 * palette catégorielle ni de la rampe séquentielle, et ce n'est pas un oubli :
 * une teinte posée sur un écart signé code fatalement une appréciation, quelle
 * que soit la palette. Ni rouge/vert, ni chaud/froid, ni saturé/désaturé.
 *
 * Les noms de ces deux familles de jetons ne sont pas même ÉCRITS ici : c'est
 * `tests/parlement-rendu.php` qui vérifie leur absence, en cherchant leur
 * préfixe dans ce fichier autant que dans le document rendu.
 *
 * TROIS CANAUX NON CHROMATIQUES portent le sens, et eux seuls :
 *  1. la POSITION des deux points sur un axe commun à toutes les lignes ;
 *  2. le REMPLISSAGE du point — creux pour le montant proposé, plein pour le
 *     montant voté ;
 *  3. le SIGNE écrit en toutes lettres, doublé d'un chevron plein pour une
 *     hausse et évidé pour une baisse, de sorte que la distinction survive à
 *     une impression en niveaux de gris.
 *
 * Les jetons employés ici — `--delta-axis`, `--delta-track`, `--delta-from-bg`,
 * `--delta-from-bd`, `--delta-to` — sont ceux que `design/tokens.css` réserve
 * à ce composant. Ce sont des valeurs d'ENCRE et de PAPIER, jamais des
 * teintes : elles ne changent pas selon le signe de l'écart.
 */

.parlement__intro {
  max-width: var(--prose-max, 38rem);
  line-height: 1.55;
  color: var(--c-text-muted);
}

.parlement__methode {
  max-width: var(--prose-max, 38rem);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--c-text-muted);
  border-top: 1px solid var(--c-border);
  padding-top: 0.75rem;
  margin-top: 2rem;
}

.parlement__liste {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
}

/*
 * UNE LIGNE PAR MISSION, AMENDÉE OU NON. Une mission laissée intacte par
 * l'examen parlementaire est un fait de la même nature qu'une mission
 * amendée : l'omettre laisserait croire que le Parlement a touché à tout.
 */
.parlement__ligne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.25rem 1rem;
  align-items: baseline;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--c-border);
}

.planche-page .parlement__mission {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min, 44px);
  font-weight: 600;
  color: var(--c-text);
}

/*
 * L'ÉCART SIGNÉ, ÉCRIT. Le mot « hausse » ou « baisse » précède le montant,
 * qui est repris TEL QUE LA DONNÉE LE PORTE, signe compris : le gabarit ne
 * prend jamais la valeur absolue d'un montant, il n'a pas le droit de
 * produire un nombre que le fichier ne contient pas.
 */
.parlement__ecart {
  font-variant-numeric: tabular-nums lining-nums;
  font-weight: 600;
  color: var(--c-text);
  text-align: end;
}

.parlement__chevron {
  margin-inline-end: 0.25rem;
}

.parlement__montants {
  grid-column: 1 / -1;
  font-size: 0.85rem;
  color: var(--c-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
}

/*
 * L'HALTÈRE — deux points reliés sur un axe commun à toutes les lignes, pour
 * que les écarts restent comparables d'une mission à l'autre.
 *
 * LES DEUX POSITIONS SONT LUES, JAMAIS CALCULÉES. Elles arrivent en
 * pourcentage, écrites par le pipeline (`scripts/pipeline/steps/haltere.ts`),
 * dans les propriétés personnalisées `--propose` et `--vote`. La barre qui
 * relie les deux points en dérive ENTIÈREMENT EN CSS, par `min()` et `max()` :
 * PHP ne compare ni ne soustrait rien, il n'écrit que deux nombres reçus.
 *
 * Rendue en HTML et non en SVG : un SVG étiré en largeur transformerait les
 * points en ellipses.
 */
.haltere {
  grid-column: 1 / -1;
  position: relative;
  display: block;
  height: 18px;
  margin-top: 0.15rem;
}

.haltere__axe {
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 1px;
  background: var(--delta-axis);
}

.haltere__barre {
  position: absolute;
  top: 50%;
  height: 2px;
  transform: translateY(-50%);
  background: var(--delta-track);
  left: min(var(--propose), var(--vote));
  width: calc(max(var(--propose), var(--vote)) - min(var(--propose), var(--vote)));
}

/*
 * Douze pixels de diamètre au minimum (design system §7.4). Le remplissage
 * est la SEULE différence entre les deux points : même taille, même filet,
 * même encre. Le creux est le papier de la page, jamais une teinte pâle.
 */
.haltere__propose,
.haltere__vote {
  position: absolute;
  top: 50%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 2px solid var(--delta-from-bd);
}

.haltere__propose {
  left: var(--propose);
  background: var(--delta-from-bg);
}

.haltere__vote {
  left: var(--vote);
  background: var(--delta-to);
  border-color: var(--delta-to);
}

@media (max-width: 30rem) {
  .parlement__ligne {
    grid-template-columns: minmax(0, 1fr);
  }

  .parlement__ecart {
    text-align: start;
  }
}

/*
 * L'ÉCRAN DES SOURCES — dixième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 *
 * TRAÇABILITÉ : chaque fiche porte son titre, à quoi elle sert, son
 * producteur, son millésime, sa date de récupération et sa licence — les
 * mêmes six informations pour les vingt-neuf sources, jamais un sous-
 * ensemble abrégé pour les moins consultées.
 */

.sources {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 1.4rem;
}

.source-fiche {
  border-block-start: 1px solid var(--c-border);
  padding-block-start: 1rem;
}

.source-fiche__titre {
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 0.4rem;
}

.source-fiche__usage {
  margin: 0 0 0.7rem;
  font-size: 0.92rem;
  line-height: 1.5;
  max-width: 68ch;
}

.source-fiche__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 2rem;
  margin: 0;
  font-size: 0.82rem;
}
.source-fiche__meta dt {
  font-weight: 500;
  color: var(--c-text-muted);
}
.source-fiche__meta dd {
  margin: 0;
  font-weight: 500;
}

.source-fiche__fictive {
  margin: 0.6rem 0 0;
  font-weight: 600;
}

/*
 * « CE QUE CES CHIFFRES NE DISENT PAS » — un engagement éditorial, pas de
 * l'habillage : ces points sont donc des articles pleinement lus, pas
 * un accordéon qui en cacherait le contenu par défaut.
 */
.note-pedago {
  border-block-start: 3px solid var(--c-text);
  padding-block-start: 1.2rem;
  margin-block-end: 2rem;
}
.note-pedago h3 {
  font-size: 1.02rem;
  font-weight: 600;
  margin: 0 0 0.6rem;
}
.note-pedago p,
.note-pedago ul {
  max-width: 68ch;
  font-size: 0.92rem;
  line-height: 1.55;
}

.planche-page__note {
  font-size: 0.85rem;
  color: var(--c-text-muted);
  max-width: 68ch;
}

.planche-page__renvoi {
  margin-block-start: 2rem;
  font-size: 0.9rem;
  border-block-start: 1px solid var(--c-border);
  padding-block-start: 1.2rem;
  max-width: 68ch;
}

/*
 * L'ÉCRAN DES MENTIONS LÉGALES — onzième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 */

.planche-page__liste-plate {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  gap: 0.4rem;
  font-size: 0.9rem;
}

/*
 * L'ÉCRAN DE MÉTHODE — douzième et dernier morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 *
 * LE JETON DE NIVEAU DE SOURCE N'EST PORTÉ NULLE PART AILLEURS DANS LE MONDE
 * PHP : cette page en montre la forme exacte, pour que les fiches candidates
 * du plan 2b la reprennent telle quelle. Quatre NATURES de source, pas un
 * classement : aucune teinte ne les distingue, seul le style de bordure
 * porte l'écart, lisible en niveaux de gris comme en monochrome (design
 * system §1.2).
 */

.methode__sommaire {
  margin-block-end: 2.2rem;
  padding-block-end: 1.4rem;
  border-block-end: 1px solid var(--c-border);
}
.methode__sommaire ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem 1.5rem;
}
@media (min-width: 640px) {
  .methode__sommaire ul {
    grid-template-columns: 1fr 1fr;
  }
}
.methode__sommaire a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min, 44px);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--c-text);
}
.methode__sommaire a:hover,
.methode__sommaire a:focus-visible {
  text-decoration: underline;
}

.methode__niveaux {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0;
  display: grid;
  gap: 1rem;
}
.methode__niveau-explication {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--c-text-muted);
  max-width: 60ch;
}

.methode__precision {
  margin-block-start: 1.2rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--c-note-border, var(--c-border));
  background: var(--c-note-bg, var(--c-surface-2));
  color: var(--c-note-text, var(--c-text));
  font-size: 0.9rem;
  max-width: 68ch;
}

/*
 * LE JETON DE NIVEAU DE SOURCE — même balisage que `JetonNiveauSource.tsx`.
 * Le style de bordure distingue les quatre niveaux, jamais une teinte : la
 * même encre et le même fond partout, seule la forme du filet change.
 */
.jeton-source {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.7rem;
  border: 2px solid var(--c-border-strong);
  background: var(--c-surface);
  font-size: 0.85rem;
  font-weight: 500;
}
.jeton-source--n1 { border-style: solid; }
.jeton-source--n2 { border-style: dashed; }
.jeton-source--n3 { border-style: dotted; }
.jeton-source--n4 { border-style: double; border-width: 4px; }

.jeton-source__rang {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1.4rem;
  block-size: 1.4rem;
  font-weight: 700;
}

/*
 * L'ÉCRAN DE CONTACT — treizième morceau de la planche assemblée.
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage.
 *
 * `api/lib/PageContact.php` rend ce même balisage pour deux points d'entrée
 * — la route `/contact` et `api/contact.php`, le point d'entrée hérité que
 * l'application React poste toujours en secours sans JavaScript
 * (`tests/pages.test.ts` verrouille la parité des noms de champs) — tous
 * deux chargés désormais par `Document::ouvrir()` (tâche 6, plan 2a).
 *
 * `.page` et `.page__chapeau` sont repris tels quels du balisage existant :
 * ne pas les renommer ici, ce serait perdre leur style sur les DEUX points
 * d'entrée à la fois. `<h1>` n'a volontairement AUCUNE règle ci-dessous :
 * `.planche-page h1` de `planche-socle.css` suffit, et c'est elle qui doit
 * gagner, pour que ce titre porte la même typographie que tout autre écran.
 */

.page__chapeau {
  max-width: 34rem;
  font-size: var(--fs-lg);
  line-height: var(--lh-base);
  color: var(--c-text-muted);
  margin: 0 0 1.6rem;
}

/*
 * L'AIGUILLAGE VERS « /sources », entre le chapeau et le formulaire.
 * Restauré depuis la vue React par la revue d'ensemble du 2026-09-02 : il
 * retient un signalement d'erreur portant sur un écart normal, et c'est le
 * seul lien du formulaire vers les limites de la page des sources.
 */
.page__aiguillage {
  max-width: 34rem;
  line-height: var(--lh-base);
  margin: 0 0 1.6rem;
}

/* --- Formulaire ------------------------------------------------------------ */

.formulaire {
  display: grid;
  gap: 1.6rem;
  max-width: 34rem;
  margin-block-start: 1.2rem;
}

.formulaire__requis {
  margin: 0;
  color: var(--c-text-muted);
  font-size: var(--fs-xs);
}

.formulaire__bloc {
  display: grid;
  gap: 0.4rem;
}

.formulaire__intitule {
  font-weight: var(--fw-semibold);
}

.formulaire__aide {
  margin: 0;
  color: var(--c-text-muted);
  font-size: var(--fs-xs);
}

.formulaire__champ {
  box-sizing: border-box;
  width: 100%;
  /* Cible tactile : jamais sous le minimum du design system. */
  min-block-size: var(--tap-min);
  padding: 0.5rem 0.9rem;
  /* Bordure de CONTRÔLE, pas filet décoratif : le pourtour d'un champ de
     saisie doit rester perceptible — au moins 3:1 sur son fond. */
  border: var(--bw-hair) solid var(--c-border-strong);
  border-radius: var(--r-md);
  background: var(--c-surface);
  color: var(--c-text);
  font: inherit;
  /* Sous 16 px, les navigateurs mobiles zooment à la mise au point du champ
     et le formulaire sort de l'écran. */
  font-size: max(1rem, 16px);
  transition: border-color var(--dur-fast) var(--ease);
}
.formulaire__champ:hover {
  border-color: var(--c-text-muted);
}
textarea.formulaire__champ {
  resize: vertical;
  min-block-size: 10rem;
  line-height: var(--lh-base);
}

/*
 * L'erreur est portée par le texte et par l'épaisseur de la bordure, jamais
 * par la seule couleur (contrainte globale du plan).
 */
.formulaire__bloc--erreur .formulaire__champ {
  border-width: 2px;
  border-color: var(--c-text);
}

.formulaire__erreur {
  margin: 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--c-text);
}
.formulaire__erreur::before {
  content: '⚠ ';
}

.formulaire__rgpd {
  margin: 0;
  max-width: 34rem;
  color: var(--c-text-muted);
  font-size: var(--fs-xs);
  line-height: var(--lh-base);
}

.formulaire__envoi {
  justify-self: start;
  min-block-size: var(--tap-min);
  padding: 0.5rem 1.6rem;
  border: var(--bw-hair) solid transparent;
  border-radius: var(--r-pill);
  background: var(--c-accent);
  color: var(--c-bg);
  font: inherit;
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.formulaire__envoi:hover,
.formulaire__envoi:focus-visible {
  background: var(--c-accent-hover);
}

/*
 * Champ piège : sorti du flux et du parcours au clavier, mais NON masqué par
 * `display: none`, que certains robots savent reconnaître. Si la feuille de
 * style ne se charge pas, le champ redevient visible avec son intitulé
 * « Ne remplissez pas ce champ » : personne n'est piégé par accident.
 */
.formulaire__piege {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- Retours à l'utilisateur ------------------------------------------------ */

.alerte {
  border-radius: var(--r-md);
  padding: 0.9rem 1.2rem;
  margin: 0 0 1.4rem;
  max-width: 34rem;
}
.alerte p {
  margin: 0;
}
.alerte--confirmation {
  background: var(--c-info-bg);
  color: var(--c-info-text);
  border-inline-start: 4px solid var(--c-info-border);
}
.alerte--erreur {
  background: var(--c-note-bg);
  color: var(--c-note-text);
  border-inline-start: 4px solid var(--c-note-border);
  font-weight: var(--fw-semibold);
}

/*
 * L'ÉCRAN D'ACCESSIBILITÉ — quatorzième et dernier morceau de la planche
 * assemblée. Voir l'en-tête de `planche-socle.css` pour l'ordre
 * d'assemblage.
 *
 * Même patron visuel que le sommaire de `/methode`
 * (`.methode__sommaire` dans `planche-methode.css`), sous son propre nom :
 * chaque écran de ce monde définit ses propres classes préfixées, jamais
 * partagées entre deux fichiers.
 */

.accessibilite__sommaire {
  margin-block-end: 2.2rem;
  padding-block-end: 1.4rem;
  border-block-end: 1px solid var(--c-border);
}
.accessibilite__sommaire ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem 1.5rem;
}
@media (min-width: 640px) {
  .accessibilite__sommaire ul {
    grid-template-columns: 1fr 1fr;
  }
}
.accessibilite__sommaire a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--c-text);
}
.accessibilite__sommaire a:hover,
.accessibilite__sommaire a:focus-visible {
  text-decoration: underline;
}

/*
 * TAUX DE CONFORMITÉ — tâche 4 : le paragraphe qui porte le pourcentage
 * mesuré. Le texte reste lisible sans lui : la couleur n'y ajoute rien qui
 * ne soit déjà dans les chiffres et les mots.
 */
.accessibilite__taux {
  padding: var(--sp-3, 1rem);
  background: var(--c-info-bg);
  color: var(--c-info-text);
  border-inline-start: 4px solid var(--c-info-border);
  font-size: 1.05rem;
}

/* CONSTATS, PAR THÉMATIQUE — une liste sans puce, un constat par point. */
.accessibilite__constats {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}
.accessibilite__constat {
  padding: 0.75rem 1rem;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md, 6px);
}
.accessibilite__constat p {
  margin: 0;
}
.accessibilite__constat-detail {
  margin-block-start: 0.35rem !important;
  color: var(--c-text-muted);
  font-size: 0.92rem;
}

/*
 * PASTILLE DE STATUT — jamais la couleur seule : le libellé (« Conforme »,
 * « Non conforme »…) est toujours du texte, la couleur ne fait que le
 * souligner. Les quatre couleurs et leur texte réutilisent des paires déjà
 * mesurées par `scripts/design/contrastes.mjs` (jetons `--c-info-*` et
 * `--c-note-*`) ; conforme/sans objet/non vérifié empruntent le couple
 * neutre `--c-info-*`, non conforme le couple `--c-note-*`, plus marqué.
 */
.accessibilite__statut {
  display: inline-block;
  margin-inline-end: 0.5rem;
  padding: 0.1rem 0.55rem;
  border-radius: var(--r-pill, 999px);
  font-size: 0.78rem;
  font-weight: var(--fw-semibold, 600);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.accessibilite__statut--conforme,
.accessibilite__statut--non-applicable {
  background: var(--c-info-bg);
  color: var(--c-info-text);
}
.accessibilite__statut--non-conforme,
.accessibilite__statut--non-verifie {
  background: var(--c-note-bg);
  color: var(--c-note-text);
}

/*
 * LE SUIVI DES CANDIDATURES — premier morceau de vocabulaire partagé par les
 * trois écrans du plan 2b (`/suivi`, `/candidat/<id>`, `/proposition/<id>/<id>`).
 * Voir l'en-tête de `planche-socle.css` pour l'ordre d'assemblage : ce
 * fichier vient APRÈS les autres morceaux de la planche, pour que ses
 * déclarations l'emportent sur celles du monde et des jetons en cas de
 * collision de sélecteur.
 *
 * CE FICHIER EST LE PREMIER D'UNE SÉRIE (arbitrage PV-2, plan 2b tâche 2) :
 * il porte aujourd'hui ce dont `/suivi` a besoin — la vue d'ensemble à trente
 * fiches. Les tâches 3 et 4 l'ÉTENDRONT, plutôt que d'ouvrir chacune un
 * partiel séparé, pour que les trois écrans du suivi restent un seul
 * vocabulaire de fiche.
 *
 * SOURCE DES RÈGLES : adaptation assumée de `src/styles/suivi.css` et
 * `src/styles/suivi-apercu.css` (React), réduite aux classes que ce gabarit
 * PHP émet réellement. Une classe non consommée par PHP n'est pas reprise
 * ici : mieux vaut l'ajouter avec son premier consommateur que de porter du
 * style mort.
 *
 * ┌─ TROIS INTERDITS, IDENTIQUES À LA VERSION REACT ────────────────────────┐
 * │ 1. AUCUNE COULEUR D'APPRÉCIATION — ni sur un montant, ni sur un         │
 * │    décompte, ni sur une candidature.                                    │
 * │ 2. AUCUNE COULEUR DE CANDIDAT. Les trente fiches partagent exactement   │
 * │    la même surface, le même fond et la même bordure.                    │
 * │ 3. AUCUNE INFORMATION PORTÉE PAR LA SEULE COULEUR.                      │
 * └───────────────────────────────────────────────────────────────────────┘
 */

/* --- Vue d'ensemble : titre, chapeau, cadence des notes ------------------- */

/*
 * TOUR DE RELECTURE 1, F5 — `h1.` AJOUTÉ EXPRÈS. `.suivi__titre` seul
 * (0,1,0) perdait TOUJOURS contre `.planche-page h1` (0,1,1,
 * `planche-socle.css`, assemblée plus tôt) : mesuré, le titre rendait en
 * capitales à 16ch de large au lieu de la taille et de la casse voulues
 * ici. `h1.suivi__titre` (0,1,1) EST DE MÊME SPÉCIFICITÉ — départagé
 * par l'ordre d'assemblage, qui place ce fichier APRÈS `planche-socle.css`
 * (voir `scripts/design/assembler-css.mjs`), donc en notre faveur.
 */
h1.suivi__titre {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-2xl, 1.75rem);
  line-height: var(--lh-snug, 1.3);
  /*
   * LES QUATRE PROPRIÉTÉS SUIVANTES SONT UN RESET EXPLICITE, PAS UN OUBLI —
   * tour de relecture 1, F5, second passage : `h1.` seul relève LA
   * SPÉCIFICITÉ à égalité avec `.planche-page h1`, ce qui ne fait gagner
   * QUE les propriétés que CETTE règle redéclare elle-même — la cascade
   * CSS s'applique propriété par propriété, jamais règle contre règle en
   * bloc. `.planche-page h1` pose aussi `text-transform`, `max-width`,
   * `letter-spacing` et `font-weight` : sans les redéclarer ICI, elles
   * restent actives quelle que soit la spécificité gagnée sur `font-size`.
   * Mesuré après ce second passage : voir le rapport de tâche.
   */
  font-weight: var(--fw-semibold, 600);
  letter-spacing: normal;
  text-transform: none;
  max-width: none;
}

/* PLEINE LARGEUR (commande du 2026-09-02) — le chapeau était confiné à la
 * colonne de lecture générique, pensée pour un paragraphe isolé, alors que
 * cet écran est large (`.planche-page` fait 72rem). ICI SEULEMENT. */
.suivi__chapeau {
  max-width: none;
  margin: 0 0 var(--sp-7);
  font-size: var(--fs-lg, 1.125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/*
 * `.note-pedago` (bordure, fond, `h3`) vient déjà de `planche-sources.css`,
 * assemblé plus tôt : ce bloc ne le redéclare pas, il ajoute seulement ce que
 * ce contexte-ci demande en plus — le déséquilibre et la date du balayage
 * sont rendus en `<h2>`, jamais en `<h3>`, parce que ce sont les DEUX
 * premiers titres de niveau 2 de l'écran après le titre de page lui-même.
 */
.note-pedago h2 {
  font-size: 1.02rem;
  font-weight: 600;
  margin: 0 0 0.6rem;
}

.suivi > .note-pedago {
  margin: 0 0 var(--sp-5);
}

/* PLEINE LARGEUR, SUITE — `.note-pedago p/ul` (`planche-sources.css`)
 * confine à 68ch. `.suivi .note-pedago p/ul` (0,3,0) l'emporte PAR
 * SPÉCIFICITÉ, quel que soit l'ordre d'assemblage ; `.suivi` en ancêtre
 * exclut les autres écrans qui partagent `.note-pedago`. */
.suivi .note-pedago p,
.suivi .note-pedago ul {
  max-width: none;
}

/* --- Accordéons natifs des deux notes pédagogiques -------------------------
 * `<details>`/`<summary>` NATIFS, REPLIÉS PAR DÉFAUT — plus de JavaScript sur
 * ce site : clavier sans script, restitués comme un bouton d'expansion à
 * l'état annoncé nativement, contenu replié trouvable au Ctrl+F. Chevron
 * dessiné en CSS, pivoté par `[open]`. `.note-pedago summary` COUVRE LES
 * DEUX FORMES ÉMISES : « balayage » EST le `<details>` (résumé enfant
 * direct) ; « déséquilibre » l'ENVELOPPE dans une `<section>` (résumé
 * descendant) — un sélecteur descendant couvre les deux sans dupliquer. */
.note-pedago summary {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  cursor: pointer;
  list-style: none;
  min-block-size: var(--tap-min, 44px);
}

.note-pedago summary::-webkit-details-marker { display: none; }
.note-pedago summary h2 { margin: 0; }
.note-pedago__chevron {
  flex: none;
  inline-size: 0.5em;
  block-size: 0.5em;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

/* Pivote par `[open]`, sans script — un sélecteur par forme (voir ci-dessus). */
.note-pedago[open] .note-pedago__chevron,
.note-pedago details[open] .note-pedago__chevron { transform: rotate(45deg); }

/* Réserve du déséquilibre, visible hors du dépliant (cadrage §17.4, voir
 * `NotesSuivi::noteDesequilibre()`) : enfant direct de la `<section>`, hors
 * de `.note-pedago p` — pleine largeur, cohérente avec le chapeau. */
.note-pedago__visible { margin: 0 0 var(--sp-4); font-weight: var(--fw-semibold, 600); }

.suivi__calendrier {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-1) var(--sp-4);
  margin: var(--sp-7) 0 var(--sp-5);
  max-width: var(--prose-max, 42rem);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

.suivi__calendrier > div {
  display: contents;
}

.suivi__calendrier dt {
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: var(--numeric);
  white-space: nowrap;
}

.suivi__calendrier dd {
  margin: 0;
}

/* --- Date du dernier balayage, échéances qui la périment ------------------ */

.balayage__echeances {
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
}

.balayage__echeances li {
  padding: var(--sp-1) 0;
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm, 0.875rem);
}

.balayage__echeances li:last-child {
  border-bottom: 0;
}

/* --- Alphabet cliquable, au-dessus de la grille des fiches -----------------
 * Vingt-six lettres, TOUJOURS LES VINGT-SIX : les lettres sans candidature
 * restent affichées, INERTES plutôt que masquées (voir le docblock
 * d'`api/pages/suivi.php`) — distinguo sur DEUX signaux non colorés (tracé
 * du contour, graisse du texte). `flex-wrap: wrap` replie la barre. */
.suivi__alphabet { margin: var(--sp-6) 0 var(--sp-5); }

.alphabet__liste {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.alphabet__liste li { display: contents; }

.alphabet__lettre {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--tap-min, 44px);
  min-block-size: var(--tap-min, 44px);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm, 0.875rem);
  font-variant-numeric: var(--numeric);
}

a.alphabet__lettre {
  border: 1px solid var(--c-border-strong, var(--c-border));
  font-weight: var(--fw-semibold, 600);
  color: var(--c-text);
  text-decoration: none;
}

a.alphabet__lettre:hover,
a.alphabet__lettre:focus-visible { text-decoration: underline; }

/* Lettre inerte : contour pointillé (contre le contour plein des actives),
 * graisse régulière, couleur atténuée en signal SUPPLÉMENTAIRE — jamais seul. */
span.alphabet__lettre { border: 1px dashed var(--c-border); font-weight: var(--fw-regular, 400); color: var(--c-text-muted); }

/* --- Grille des trente fiches ---------------------------------------------
 *
 * `1fr` sur chaque colonne et `align-items: stretch` : les trente cartes
 * occupent exactement la même largeur et la même hauteur, quelle que soit la
 * quantité de documentation. Une carte plus haute que les autres se lirait
 * comme une mise en avant — GARDE-FOU N° 2, tenu ici par la mise en page,
 * pas seulement par le balisage identique de `api/pages/suivi.php`.
 */
.suivi__fiches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: var(--sp-4);
  align-items: stretch;
  margin: var(--sp-6) 0;
  padding: 0;
  list-style: none;
}

.fiche-apercu {
  display: flex;
}

/*
 * Carte de candidature. Le gabarit reste STRICTEMENT identique pour les
 * trente — même fond, même relief, même rayon, même remplissage. Rien dans
 * cette règle ne peut varier d'une candidature à l'autre : il n'existe aucun
 * sélecteur `.fiche-apercu--*`, par construction.
 */
.fiche-apercu > article {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r-lg, 16px);
  background: var(--c-surface);
  box-shadow: var(--sh-1);
}

.fiche-apercu__nom {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-lg, 1.25rem);
  font-weight: var(--fw-semibold, 600);
  letter-spacing: var(--ls-titre, -0.012em);
  line-height: var(--lh-snug, 1.25);
}

.fiche-apercu__nom a {
  color: inherit;
}

.fiche-apercu__formation {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

/* Statut et date de déclaration, sur toutes les cartes sans exception. */
.fiche-apercu__statut {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/*
 * Réserve de rattachement et mention de vacuité partagent la même trame
 * neutre : ce sont deux informations que le lecteur doit voir sans cliquer,
 * et aucune des deux n'est un jugement sur la personne. Ni rouge, ni vert.
 */
.fiche-apercu__reserve {
  margin: var(--sp-2) 0 0;
  padding: var(--sp-2) var(--sp-4);
  border-left: 3px solid var(--c-border-strong, var(--c-border));
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

.fiche-apercu__vacuite {
  margin: var(--sp-4) 0 0;
  padding: var(--sp-4);
  border: 1px solid var(--c-note-border);
  border-radius: var(--r-md);
  max-width: none;
  background: var(--c-note-bg);
  color: var(--c-note-text);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

/* Le lien de fin de carte est poussé en bas : les trente pieds s'alignent. */
.fiche-apercu__acces {
  margin: auto 0 0;
  padding-top: var(--sp-4);
}

.fiche-apercu__acces a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min, 44px);
  font-weight: var(--fw-medium, 500);
}

/* --- Indicateur de complétude, forme brève --------------------------------
 *
 * GARDE-FOU N° 2 DU CADRAGE §17.4 : les trois qualités — complètes /
 * partielles / insuffisantes — partagent rigoureusement le même balisage, la
 * même classe, sans le moindre modificateur qui varierait selon l'état.
 * Aucune couleur ne code un niveau de complétude. `tests/suivi-rendu.php`
 * vérifie qu'aucune classe ni aucun attribut de `.fiche-apercu` ne dépend du
 * candidat ni de sa qualité de chiffrage.
 */
.completude--bref {
  margin: var(--sp-4) 0 0;
  padding: 0;
  border: 0;
  background: none;
}

.completude--bref .completude__qualite {
  margin: 0 0 var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-left: 3px solid var(--c-border);
  background: var(--c-surface-2);
  font-size: var(--fs-sm, 0.875rem);
  font-weight: var(--fw-semibold, 600);
}

.completude__ligne {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
  font-variant-numeric: var(--numeric);
}

.completude--bref .completude__profondeur {
  margin: 0;
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/* --- Absences justifiées, publiées en fin d'écran -------------------------
 *
 * Dans le flux, jamais repliées : ce n'est pas une antichambre. Le gabarit y
 * est volontairement plus sobre que celui d'une fiche, pour qu'aucune de ces
 * entrées ne se lise comme une candidature.
 */
.suivi__absences {
  margin: var(--sp-7) 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--c-border);
}

.suivi__absences h2 {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-xl, 1.375rem);
}

.absences__liste {
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
}

.absences__liste > li {
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--c-border);
}

.absences__nom {
  margin: 0;
  font-weight: var(--fw-medium, 500);
}

.absences__motif {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

.absences__fait,
.absences__divergence,
.absences__reouverture {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

.absences__divergence,
.absences__reouverture {
  color: var(--c-text-muted);
}

.suivi__precision {
  max-width: var(--prose-max, 42rem);
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

/* --- Pièces écartées et adresses inaccessibles -----------------------------
 *
 * Publiées, jamais cachées : c'est ce qui rend le tri des sources vérifiable
 * par un lecteur. Le bloc est donc dans le flux, en fin d'écran.
 */
.suivi__ecartees {
  margin: var(--sp-7) 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--c-border);
}

.suivi__ecartees h2 {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-xl, 1.375rem);
}

.completude__intitule {
  margin: var(--sp-4) 0 var(--sp-1);
  font-size: var(--fs-3xs, 0.6875rem);
  font-weight: var(--fw-semibold, 600);
  letter-spacing: var(--ls-caps, 0.06em);
  text-transform: uppercase;
  color: var(--c-text-muted);
}

.ecartees__liste {
  margin: 0 0 var(--sp-5);
  padding: 0;
  list-style: none;
}

.ecartees__liste > li {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm, 0.875rem);
}

.ecartees__url {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-xs, 0.8125rem);
  overflow-wrap: anywhere;
}

.ecartees__date,
.ecartees__motif {
  display: block;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* --- État vide : aucun relevé publié (relecture, finding F2) -------------- */

/*
 * `Etats::vide()` (`api/lib/suivi/Etats.php`) — jamais consommé avant cette
 * correction, donc jamais peint. Jetons NATIFS de `design/tokens.css`
 * directement (finding F1) : ce fichier n'importe rien d'autre.
 */
.etat {
  padding: var(--sp-5);
  border-radius: var(--r-lg);
  background: var(--c-surface-2);
  max-width: var(--prose-max, 38rem);
}

.etat__titre {
  font-size: var(--fs-lg, 1.25rem);
  font-weight: var(--fw-semibold, 600);
  margin: 0;
}

.etat__motif {
  color: var(--c-text-muted);
  margin: var(--sp-1) 0 0;
}

/*
 * VOCABULAIRE DU CHIFFRAGE — complétude détaillée, jeton de source compact,
 * montant estimé, « non chiffré ». Troisième morceau du suivi (plan 2b,
 * tâche 3), scindé de `planche-candidat.css` pour la même raison que celui-ci
 * a été scindé de `planche-suivi.css` : la limite de 500 lignes du projet.
 *
 * `.jeton-source` (forme normale) est déjà posé par `planche-methode.css`,
 * assemblé plus tôt — ce fichier n'ajoute QUE ce que cette page-ci demande en
 * plus : la forme compacte et le conteneur de plusieurs jetons. Aucune
 * classe ici ne redéclare `.jeton-source` lui-même.
 *
 * SOURCE DES RÈGLES : adaptation assumée des sections « complétude »,
 * « jeton de nature de source » et « montant estimé : fourchette et
 * "non chiffré" » de `src/styles/suivi.css` (React), réduites aux classes que
 * `IndicateurCompletude::rendre(..., true)` et `MontantEstime` émettent
 * réellement. Les trois interdits de `planche-suivi.css` s'appliquent ici à
 * l'identique.
 */

/* --- Complétude, forme détaillée (fiche candidat) -------------------------- */

.completude {
  margin: var(--sp-3) 0 0;
}

.completude__decomptes {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-1) var(--sp-3);
  margin: 0 0 var(--sp-3);
}

.completude__decomptes > div {
  display: contents;
}

.completude__decomptes dt {
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

.completude__decomptes dd {
  margin: 0;
  font-variant-numeric: var(--numeric);
  font-weight: var(--fw-semibold, 600);
  text-align: right;
  white-space: nowrap;
}

.completude__profondeur,
.completude__precision,
.completude__aucun {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/*
 * Décompte par nature de source (« × 1 ») — corrigé relecture, tour de
 * relecture 1, finding F3 : sans règle, ce chiffre sortait à la taille du
 * corps de texte (17px) à côté d'un jeton à 12px. Un nombre, jamais une
 * barre : même famille visuelle que le reste de la complétude détaillée.
 */
.completude__compte {
  font-size: var(--fs-3xs, 0.6875rem);
  font-variant-numeric: var(--numeric);
  color: var(--c-text-muted);
}

.completude__domaines {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--sp-4);
  margin-top: var(--sp-3);
}

.completude__liste {
  margin: var(--sp-2) 0 0;
  padding-left: var(--sp-4);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

/* « non annoncé » : un mot, jamais une couleur. */
.completude__mention {
  font-style: italic;
  color: var(--c-text-muted);
}

@media (max-width: 720px) {
  .completude__decomptes {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}

/* --- Jeton de nature de source, forme compacte, et son conteneur ----------- */

.jeton-source--compact {
  padding: 0.2rem 0.5rem;
  font-size: var(--fs-3xs, 0.6875rem);
}

.jetons-source {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.jetons-source__aucun {
  margin: 0;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* --- Montant estimé : fourchette ponctuelle ou étendue ---------------------- */

.montant-estime {
  display: block;
}

.montant-estime__valeur {
  display: block;
  font-size: var(--fs-xl, 1.375rem);
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: var(--numeric);
  white-space: nowrap;
}

/*
 * « de … à … » : les deux mots de liaison sont composés plus petits et en
 * graisse normale. Une fourchette ne doit jamais se lire comme un montant
 * unique suivi d'une décoration.
 */
.montant-estime__mot {
  font-size: var(--fs-sm, 0.875rem);
  font-weight: var(--fw-regular, 400);
  color: var(--c-text-muted);
}

.montant-estime__base {
  display: block;
  margin-top: var(--sp-1);
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* La période d'un montant estimé décide si deux chiffres sont comparables. */
.montant-estime__periode {
  font-weight: var(--fw-semibold, 600);
  color: var(--c-text);
}

/*
 * « Non chiffré » — visuellement distinct d'un zéro. Aucun chiffre n'est
 * composé : la trame occupe la place où un montant s'afficherait, et le mot
 * est écrit à côté. Même trame que la donnée non publiée de l'axe 1.
 */
.non-chiffre {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
}

.non-chiffre__trame {
  flex: none;
  width: 3rem;
  height: 1.5rem;
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
  background-image: var(--pattern-none);
}

/*
 * Bloc : sans cela, le mot et son motif ne s'alignent pas sur la même
 * verticale. `min-width: 0` : élément de flex, il se dimensionnait sinon sur
 * son mot le plus long et poussait le motif hors de la fiche.
 */
.non-chiffre__texte {
  display: block;
  min-width: 0;
}

.non-chiffre__mot {
  display: block;
  font-size: var(--fs-md, 1rem);
  font-weight: var(--fw-semibold, 600);
}

.non-chiffre__motif {
  display: block;
  margin-top: var(--sp-1);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/*
 * LA FICHE D'UN CANDIDAT — second morceau du vocabulaire de suivi (plan 2b,
 * tâche 3), qui prolonge `planche-suivi.css` (tâche 2) sans le rouvrir : à
 * 381 lignes, l'ajout de tout ce dont `/candidat/<id>` a besoin l'aurait fait
 * franchir la limite de 500 lignes du projet. Même ordre d'assemblage :
 * APRÈS `planche-suivi.css`, pour surcharger le monde et les jetons en cas
 * de collision de sélecteur (voir `scripts/design/assembler-css.mjs`).
 *
 * SOURCE DES RÈGLES : adaptation assumée de `src/styles/suivi-fiches.css`,
 * `src/styles/suivi-identite.css` et des sections « complétude détaillée »,
 * « jeton de source » et « montant estimé » de `src/styles/suivi.css`
 * (React), réduites aux classes que `api/pages/candidat.php` et
 * `api/lib/suivi/PropositionsCandidat.php` émettent réellement.
 *
 * LES TROIS INTERDITS DE `planche-suivi.css` S'APPLIQUENT ICI À L'IDENTIQUE
 * (aucune couleur d'appréciation, aucune couleur de candidat, aucune
 * information portée par la seule couleur) — voir son en-tête.
 */

/* --- En-tête de fiche ------------------------------------------------------ */

.candidat {
  max-width: var(--content-max);
}

.candidat__retour {
  margin-bottom: var(--sp-3);
  font-size: var(--fs-sm, 0.875rem);
}

.candidat__retour a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min, 44px);
}

/*
 * TOUR DE RELECTURE 1, F5 — MÊME DÉFAUT ET MÊME CORRECTION QUE
 * `planche-suivi.css` (`.suivi__titre`) : `.candidat__nom` seul (0,1,0)
 * perdait contre `.planche-page h1` (0,1,1, `planche-socle.css`).
 * `h1.candidat__nom` (0,1,1) l'emporte par l'ordre d'assemblage.
 */
h1.candidat__nom {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-2xl, 1.75rem);
  line-height: var(--lh-snug, 1.3);
  /* Reset explicite des quatre propriétés que `.planche-page h1` impose
     sinon (voir le commentaire complet dans `planche-suivi.css`). */
  font-weight: var(--fw-semibold, 600);
  letter-spacing: normal;
  text-transform: none;
  max-width: none;
}

.candidat__formation {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

.candidat__chapeau,
.candidat__precision {
  max-width: var(--prose-max, 42rem);
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

.candidat__etat,
.candidat__documents,
.candidat__propositions,
.candidat__vide,
.candidat__divergences,
.candidat__masses,
.candidat__orientations {
  margin: var(--sp-5) 0;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border);
}

/*
 * `NotesSuivi::limitesDesMontants()` suit la liste des propositions —
 * corrigé relecture, tour de relecture 1, finding F3 : `.suivi >
 * .note-pedago` (`planche-suivi.css`) ne s'applique jamais ici, le parent
 * de cette section est `main.planche-page.candidat`, pas `.suivi`. Même
 * rythme que les autres sections de la fiche plutôt qu'un repli à 0.
 */
.candidat > .limites-montants {
  margin: var(--sp-5) 0;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border);
}

.candidat h2 {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xl, 1.375rem);
}

.candidat__resume {
  max-width: var(--prose-max, 42rem);
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-lg, 1.125rem);
  line-height: var(--lh-base, 1.55);
}

.candidat__vide-suite {
  max-width: var(--prose-max, 42rem);
  margin: var(--sp-3) 0 0;
  line-height: var(--lh-base, 1.55);
}

.candidat__divergences ul,
.orientations,
.documents,
.groupe-domaine__liste {
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
}

.candidat__divergences li,
.orientations li {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

/* --- L'acte de déclaration -------------------------------------------------
 *
 * UNE RÉSERVE SE VOIT, OU ELLE N'EXISTE PAS : trois blocs de cette feuille
 * portent des faits que le lecteur doit voir sans cliquer — la borne d'une
 * date non établie, la réserve sur un rattachement, le motif de
 * non-attribution d'un document de parti. Aucun n'est distingué par une
 * teinte : la marque est une bordure et une trame.
 */
.candidat__declaration {
  margin: var(--sp-4) 0;
  padding: var(--sp-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
}

.candidat__declaration-titre {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-lg, 1.125rem);
  line-height: var(--lh-snug, 1.3);
}

.candidat__borne {
  margin: 0 0 var(--sp-2);
  padding-left: var(--sp-3);
  border-left: 3px solid var(--c-border-strong);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

.candidat__acte {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

.candidat__acte-sources,
.candidat__statut-precision {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

/* --- Cause de la vacuité, à côté du vide ----------------------------------- */

.candidat__vacuite-cause {
  margin: var(--sp-3) 0;
  padding: var(--sp-3);
  border: 1px solid var(--c-note-border);
  border-radius: var(--r-md);
  background: var(--c-note-bg);
  color: var(--c-note-text);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

/* --- Adresses rattachées --------------------------------------------------- */

.candidat__sites {
  margin: var(--sp-5) 0;
}

.sites {
  margin: 0;
  padding: 0;
  list-style: none;
}

.sites__item {
  margin-bottom: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}

/* Un rattachement non établi porte une bordure plus épaisse, jamais une teinte. */
.sites__item--reserve {
  border-left-width: 4px;
  border-left-color: var(--c-border-strong);
}

.sites__url {
  margin: 0 0 var(--sp-1);
  font-family: var(--font-mono);
  font-size: var(--fs-sm, 0.875rem);
  overflow-wrap: anywhere;
}

.sites__url-texte {
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}

.sites__etat {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs, 0.8125rem);
  font-weight: var(--fw-medium, 500);
  text-transform: lowercase;
  color: var(--c-text-muted);
}

.sites__constat,
.sites__reserve {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

.sites__reserve {
  padding-top: var(--sp-2);
  border-top: 1px solid var(--c-border);
  color: var(--c-text-muted);
}

/* --- Documents de parti antérieurs à la déclaration ------------------------ */

.candidat__parti-anterieur {
  margin: var(--sp-5) 0;
}

.parti-anterieur {
  margin: var(--sp-3) 0 0;
  padding: 0;
  list-style: none;
}

.parti-anterieur__item {
  margin-bottom: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--c-border);
  border-left-width: 4px;
  border-radius: var(--r-md);
}

.parti-anterieur__libelle {
  margin: 0 0 var(--sp-1);
  font-weight: var(--fw-medium, 500);
}

.parti-anterieur__dates {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  font-variant-numeric: var(--numeric);
  color: var(--c-text-muted);
}

.parti-anterieur__contenu,
.parti-anterieur__motif {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
}

.parti-anterieur__source {
  margin: 0;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* --- Documents relevés ------------------------------------------------------ */

.documents__item {
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--c-border);
}

.documents__libelle {
  margin: 0;
  font-weight: var(--fw-semibold, 600);
}

.documents__date,
.documents__fictive {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* --- Enveloppes globales annoncées ------------------------------------------
 *
 * Cartes équivalentes, pas une énumération ordonnée : rien n'oblige à n'en
 * montrer qu'une par ligne. Une enveloppe annoncée est un TEXTE, jamais un
 * nombre exploitable : elle n'entre dans aucune addition.
 */
.masses {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
  gap: var(--sp-3);
  align-items: start;
}

.masses__item {
  padding: var(--sp-4);
  border: 1px solid var(--c-note-border);
  border-radius: var(--r-md);
  background: var(--c-note-bg);
  color: var(--c-note-text);
}

.masses__libelle {
  margin: 0;
  font-weight: var(--fw-semibold, 600);
}

.masses__valeur {
  margin: var(--sp-1) 0;
  font-size: var(--fs-xl, 1.375rem);
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: var(--numeric);
}

.masses__motif,
.masses__source {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
}

/* --- Propositions groupées par poste ---------------------------------------- */

.groupe-domaine {
  margin-bottom: var(--sp-5);
}

.groupe-domaine__titre {
  margin: 0 0 var(--sp-2);
  padding-bottom: var(--sp-1);
  border-bottom: 2px solid var(--c-border);
  font-size: var(--fs-3xs, 0.6875rem);
  font-weight: var(--fw-semibold, 600);
  letter-spacing: var(--ls-caps, 0.06em);
  text-transform: uppercase;
  color: var(--c-text-muted);
}

/*
 * Une ligne de proposition : le montant et la nature de sa source sont dans
 * le MÊME bloc visuel, jamais séparés par un pli ni renvoyés en note.
 */
.proposition-ligne {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: var(--sp-2) var(--sp-4);
  align-items: start;
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--c-border);
}

.proposition-ligne__lien {
  grid-column: 1;
  display: block;
  min-height: var(--tap-min, 44px);
  font-size: var(--fs-md, 1rem);
  font-weight: var(--fw-medium, 500);
  line-height: var(--lh-base, 1.55);
}

.proposition-ligne__type {
  grid-column: 1;
  margin: 0;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

.proposition-ligne__chiffrage {
  grid-column: 2;
  grid-row: 1 / span 2;
}

.proposition-ligne__niveaux {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
}

.proposition-ligne__structurelle {
  margin: 0;
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

.proposition-ligne__quantites {
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
  color: var(--c-text-muted);
}

.proposition-ligne__quantites li {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm, 0.875rem);
}

@media (max-width: 720px) {
  .proposition-ligne {
    grid-template-columns: 1fr;
  }

  .proposition-ligne__lien,
  .proposition-ligne__type,
  .proposition-ligne__chiffrage {
    grid-column: 1;
    grid-row: auto;
  }
}

/*
 * LA FICHE D'UNE PROPOSITION — troisième et dernier morceau du vocabulaire de
 * suivi (plan 2b, tâche 4), qui prolonge `planche-suivi.css` (tâche 2) et
 * `planche-candidat.css` (tâche 3) sans les rouvrir : `planche-suivi.css`
 * était déjà à 381 lignes, et cet écran a son propre vocabulaire (une
 * estimation, sa fiche de provenance, la justification d'un classement
 * structurel, la passerelle depuis un poste de l'axe 1). Même ordre
 * d'assemblage : APRÈS `planche-candidat.css`, dernier morceau de la planche
 * (voir `scripts/design/assembler-css.mjs`).
 *
 * SOURCE DES RÈGLES : adaptation assumée de `src/styles/suivi-fiches.css`
 * (React), réduite aux classes qu'`api/pages/proposition.php` et
 * `api/lib/suivi/AccesPropositionsDuPoste.php` émettent réellement.
 *
 * LES TROIS INTERDITS DE `planche-suivi.css` S'APPLIQUENT ICI À L'IDENTIQUE
 * (aucune couleur d'appréciation, aucune couleur de candidat, aucune
 * information portée par la seule couleur) — voir son en-tête.
 */

/* --- En-tête de fiche ------------------------------------------------------ */

.proposition {
  max-width: var(--content-max);
}

.proposition__retour {
  margin-bottom: var(--sp-3);
  font-size: var(--fs-sm, 0.875rem);
}

.proposition__retour a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min, 44px);
}

/*
 * TOUR DE RELECTURE 1, F5 (mesuré sur `/proposition/branco/
 * branco-suppression-taxes`) — `.proposition__intitule` seul (0,1,0)
 * perdait contre `.planche-page h1` (0,1,1, `planche-socle.css`) :
 * `font-size` déclaré 28px, obtenu 53,76px ; capitales, largeur bridée à
 * 16ch — un titre de proposition (une phrase entière) rendait en pavé
 * capitales de cinq lignes. Même correction que `planche-suivi.css` et
 * `planche-candidat.css` : `h1.proposition__intitule` (0,1,1) l'emporte
 * par l'ordre d'assemblage.
 */
h1.proposition__intitule {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-2xl, 1.75rem);
  line-height: var(--lh-snug, 1.3);
  /* Reset explicite des quatre propriétés que `.planche-page h1` impose
     sinon (voir le commentaire complet dans `planche-suivi.css`). */
  font-weight: var(--fw-semibold, 600);
  letter-spacing: normal;
  text-transform: none;
  max-width: none;
}

.proposition__candidat,
.proposition__type {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm, 0.875rem);
  color: var(--c-text-muted);
}

.proposition__precision {
  max-width: var(--prose-max, 42rem);
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-sm, 0.875rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

.proposition__note {
  max-width: var(--prose-max, 42rem);
  margin: 0 0 var(--sp-4);
  line-height: var(--lh-base, 1.55);
}

.proposition__cible,
.proposition__chiffrage,
.proposition__estimations,
.proposition__calendrier,
.proposition__quantites {
  margin: var(--sp-5) 0;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border);
}

.proposition h2 {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xl, 1.375rem);
}

.proposition__cible-libelle {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-lg, 1.125rem);
  font-weight: var(--fw-semibold, 600);
}

/* --- Une estimation, avec sa fiche de provenance complète ------------------ */

.estimations {
  margin: 0;
  padding: 0;
  list-style: none;
}

.estimation {
  margin-bottom: var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
}

.estimation__montant {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
  margin-bottom: var(--sp-3);
}

.estimation__meta {
  display: grid;
  grid-template-columns: minmax(10rem, auto) minmax(0, 1fr);
  gap: var(--sp-1) var(--sp-4);
  margin: 0;
}

.estimation__meta > div {
  display: contents;
}

.estimation__meta dt {
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

.estimation__meta dd {
  margin: 0;
  font-size: var(--fs-sm, 0.875rem);
}

.estimation__note,
.estimation__fictive {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/*
 * Justification d'un classement « sans effet budgétaire direct ». Elle
 * emprunte le fond « explication de méthode », déjà appris ailleurs sur le
 * site pour une réserve (`.masses__item`, `planche-candidat.css`) : ce
 * classement est un choix éditorial, et il doit se voir comme tel — jamais
 * par une teinte d'appréciation.
 */
.structurelle {
  padding: var(--sp-4);
  border: 1px solid var(--c-note-border);
  border-radius: var(--r-md);
  background: var(--c-note-bg);
  color: var(--c-note-text);
}

.structurelle__etat {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-lg, 1.125rem);
  font-weight: var(--fw-semibold, 600);
}

.structurelle__intitule {
  margin: var(--sp-3) 0 var(--sp-1);
  font-size: var(--fs-3xs, 0.6875rem);
  font-weight: var(--fw-semibold, 600);
  letter-spacing: var(--ls-caps, 0.06em);
  text-transform: uppercase;
}

.structurelle__justification,
.structurelle__manque {
  margin: 0;
  line-height: var(--lh-base, 1.55);
}

.structurelle__manque {
  font-style: italic;
}

/* --- Quantités physiques annoncées ----------------------------------------- */

.quantites {
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
}

.quantites li {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm, 0.875rem);
}

.quantites__valeur {
  display: block;
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: var(--numeric);
}

.quantites__libelle,
.quantites__source {
  display: block;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

/* --- Accès depuis un poste de l'axe 1 (arbitrage PV-4) --------------------- */

.acces-propositions {
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border);
}

.acces-propositions h3 {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-md, 1rem);
}

.acces-propositions__precision {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xs, 0.8125rem);
  line-height: var(--lh-base, 1.55);
  color: var(--c-text-muted);
}

.acces-propositions__liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

.acces-propositions__liste a {
  display: block;
  min-height: var(--tap-min, 44px);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
}

.acces-propositions__candidat {
  display: block;
  font-size: var(--fs-xs, 0.8125rem);
  color: var(--c-text-muted);
}

.acces-propositions__intitule {
  display: block;
  font-weight: var(--fw-medium, 500);
}

/* --- Petit écran ------------------------------------------------------------ */

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

  .estimation__meta dd {
    margin-bottom: var(--sp-2);
  }
}
