/* GEST M24 — jetons sémantiques et composants du socle.
 *
 * Ce fichier est l'implémentation de docs/03-CHARTE-GRAPHIQUE.md. Deux règles
 * gouvernent sa lecture :
 *
 *  1. AUCUNE couleur en dur hors du bloc `:root`. Un composant qui écrit
 *     `#2a618f` interdit le thème d'un autre cabinet (D-006, D-022, P8).
 *  2. Les neutres et le « chrome » (en-tête, navigation) portent la direction
 *     visuelle retenue — **F « Le Cabinet Calme »**, actée à quatre le
 *     25/08/2026 (D-038) : la densité entière, mais la couleur devient RARE,
 *     et les neutres sont chauds (papier) plutôt que froids. Les couleurs
 *     d'ÉTAT et de MARQUE, elles, ne relèvent pas d'un goût : elles portent de
 *     l'information juridique (charte §4). Un délai critique est rouge.
 *
 * Pas de Tailwind, pas d'étape de construction, pas de Node (D-032) : le code
 * étant monté en volume, une correction ici est visible au simple rechargement.
 */

:root {
  /* ── neutres — direction F, relevés sur la maquette (D-038) ──────────── */
  --page-fond:          #eceae5;
  --surface:            #fbfaf8;
  --surface-alt:        #f2f0eb;
  --surface-enfoncee:   #eae7e0;
  --texte:              #1a1a18;   /* jamais du noir pur */
  /* Assombri par rapport à la maquette (#8a877f) : celle-ci tombait à 2,98:1
   * sur le fond de page, sous le seuil AA, alors que ce jeton porte TOUT le
   * texte secondaire. Teinte et saturation conservées, clarté seule abaissée
   * — même traitement que le vert et l'orange du cabinet (charte §2.2).
   * Pire contraste mesuré : 4,52:1 sur `--surface-enfoncee`. */
  --texte-doux:         #6a6861;
  --texte-inverse:      #ffffff;
  --bordure:            #dedbd4;
  --bordure-forte:      #c9c5bb;
  /* L'intercalaire au repos (planche lotDF, variation B retenue) : un papier
   * plus foncé que la surface, plus clair que la bordure — l'onglet actif,
   * lui, est le papier de la page qu'il ouvre. */
  --onglet-repos:       #e3dfd7;

  /* ── chrome : le FRONTON du registre ─────────────────────────────────── */
  /* 🔴 26/08 (nuit) — DIRECTION « LE REGISTRE », choisie par MT après comparaison
   * de quatre propositions sur le même écran.
   *
   * Le chrome revient au papier : ce n'est pas un retour en arrière, c'est le
   * principe de la direction. Un registre n'a pas de bandeau de couleur, il a un
   * FRONTON — un filet épais doublé d'un filet fin — et c'est ce trait qui sépare
   * l'en-tête du travail. La couleur de marque, elle, ne sert plus de fond : elle
   * ne reste que sur l'action principale et sur le filigrane du logo.
   *
   * La barre bleue (proposition B, premier choix de LM au vote de D-038) a été
   * écartée à ce moment-là : elle est conservée en mémoire, pas dans le code.
   */
  --chrome-fond:        #fbfaf8;
  --chrome-texte:       #1a1a18;
  --chrome-texte-doux:  #6a6861;
  --chrome-bordure:     #dedbd4;
  --chrome-champ-fond:  #fbfaf8;
  --chrome-champ-doux:  #6a6861;
  --chrome-champ-texte: #1a1a18;
  --chrome-champ-bordure: #c9c5bb;
  /* Sur du papier, `--lien` mesure 3,33:1 : au-dessus du seuil de 3:1 des
   * éléments non textuels. Le focus n'a donc rien de particulier à faire ici,
   * contrairement au chrome bleu qui exigeait du blanc. */
  --chrome-focus:       var(--lien);
  --chrome-jeton-fond:  #2a618f;
  --chrome-jeton-texte: #ffffff;
  --nav-fond:           #f6f4f0;
  --nav-texte:          #3d3b37;
  --nav-doux:           #6a6861;
  --nav-actif-fond:     #eae7e0;
  --nav-actif-texte:    #1a1a18;   /* actif = poids et fond, pas couleur */
  --nav-bordure:        #dedbd4;

  /* ── marque M24 (identité réelle du cabinet — pas négociable) ────────── */
  --primaire:           #2a618f;
  --primaire-survol:    #21507a;
  --primaire-doux:      #e9edf1;   /* le bleu de marque reste, mais discret */
  --lien:               #0281e2;
  /* La page de CONNEXION est le seul écran où le bleu du cabinet couvre une
   * surface : c'est la seule page qui n'a pas de travail à montrer, donc la
   * seule où la marque a le droit de parler (direction F : la couleur est rare,
   * elle n'est pas absente). Mesuré sur `#2a618f`, la plus claire des deux
   * bornes du dégradé, donc le pire cas : blanc = 6,55:1, `#cfe0ee` = 4,85:1 —
   * les deux au-dessus de 4,5. Aucune opacité : la leçon de D26. */
  --connexion-fond:       #2a618f;
  --connexion-texte:      #ffffff;
  --connexion-texte-doux: #cfe0ee;

  /* ── états métier (charte §4) ────────────────────────────────────────── */
  /* Les teintes de REMPLISSAGE sont celles du cabinet, non négociables. Seules
   * les variantes de TEXTE sont réaccordées au fond chaud : sur du papier, le
   * rouge vif du cabinet vibre et perd en lisibilité — or la lisibilité d'un
   * délai est une fonction métier (charte §2.2). Les fonds atténués suivent :
   * une pastille suffit, un aplat crierait. Toutes ces paires sont à 4,5:1 ou
   * mieux sur `--surface` comme sur leur propre fond atténué. */
  --succes-fond:  #71c563;  --succes-texte:  #2f7a24;  --succes-doux:  #edf3ea;
  --alerte-fond:  #ffae00;  --alerte-texte:  #7a5a12;  --alerte-doux:  #f8f1e0;
  --danger-fond:  #de0021;  --danger-texte:  #a3251f;  --danger-doux:  #f7e9e8;
  --info-fond:    #0281e2;  --info-texte:    #2a618f;  --info-doux:    #e9edf1;
  --ia-accent:    #8c2585;  --ia-doux:       #f6eef5;

  /* ── formes ──────────────────────────────────────────────────────────── */
  --rayon:        6px;
  /* Le voile d'un <dialog> modal (D-093) : l'encre à 45 %. */
  --voile:        rgb(26 26 24 / .45);
  --rayon-petit:  4px;
  --ombre-carte:  0 1px 2px rgb(26 26 24 / .05);
  --ombre-flottante: 0 8px 28px rgb(26 31 38 / .18);

  /* ── espacements : une seule échelle, multiples de 4 ─────────────────── */
  --e1: 4px; --e2: 8px; --e3: 12px; --e4: 16px; --e6: 24px; --e8: 32px; --e12: 48px;

  /* ── typographie ─────────────────────────────────────────────────────── */
  --police-marque:  "Bebas Neue", Impact, Haettenschweiler, sans-serif;
  --police-ui:      Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Noto Sans Mono et non Source Code Pro (D-039) : cette dernière dessine un
   * zéro POINTÉ par construction, et aucun réglage OpenType ne l'enlève. Le
   * zéro est partout ici — `XXX26-0500`, `1 000,00 €`, `0:45`. */
  --police-donnees: "Noto Sans Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
  /* Deux jetons pour une même famille, et c'est délibéré : le TITRE est en
   * romain, l'EXERGUE en italique, et les deux se règlent séparément le jour où
   * l'un des deux change. */
  --police-titre:   "Playfair Display", Georgia, serif;
  --police-exergue: "Playfair Display", Georgia, serif;

  /* ── densité de travail (charte §7) : 36 px de ligne, pas 56 ─────────── */
  --ligne-tableau: 36px;
  --largeur-nav:   208px;
  --hauteur-entete: 52px;
  /* La colonne « En marge » (D-047, étape 2). 236 px, et ce nombre est MESURÉ.
   *
   * Le plancher n'est pas une affaire de goût : c'est le plus large jeton
   * insécable que ces notes portent, `calendrier.ajouter_mois` en police de
   * données, qui mesure **192 px** à 12 px de corps. Relevé au navigateur, en
   * faisant varier ce seul jeton sur `/agenda/calendrier` :
   *
   *   180 px de colonne → la marge déborde de 32 px
   *   196 px            → déborde de 16 px
   *   204 px            → déborde de  8 px
   *   208 px            → déborde de  4 px
   *   212 px            → premier palier SANS débordement (24 à 27 signes/ligne)
   *   236 px            → 30 à 33 signes par ligne, 19 px de réserve
   *   300 px            → 38 à 43 signes : ce n'est plus une marge, c'est une
   *                       seconde colonne de lecture, et cette direction la refuse
   *
   * 236 se tient donc à 24 px au-dessus du plancher — de quoi absorber un jeton
   * un peu plus long sans réviser la feuille — et reproduit la maquette de
   * D-047 (238 px), au multiple de 4 près de l'échelle du programme. Le total
   * consommé est 236 + 32 de gouttière = 268 px, exactement celui de la
   * maquette. Le seuil de repli se calcule à partir d'ici : voir `.avec-marge`. */
  --largeur-marge: 236px;
}

/* ─────────────────────────────────────────────── base ─────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--page-fond);
  color: var(--texte);
  font-family: var(--police-ui);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--primaire); text-decoration: none; }
/* D25 — le survol FONCE, il n'éclaircit pas : `--lien` (#0281e2) tombe à 3,33:1
 * sur le fond de page, donc le contraste DIMINUAIT au moment où l'on interagit
 * (WCAG 1.4.3 s'applique à tous les états, survol compris). `--primaire-survol`
 * donne 7,01 à 8,07:1 selon le fond, et c'est déjà le jeton du survol des
 * boutons — même geste, même couleur. `--lien` reste employé pour le contour de
 * focus (seuil 3:1, atteint), le remplissage `--info-fond` et quatre codes
 * d'agenda : la palette n'est pas touchée. */
a:hover { color: var(--primaire-survol); text-decoration: underline; }

/* 🔴 Un lien AU MILIEU D'UN PARAGRAPHE doit se distinguer autrement que par la
 * couleur. Mesuré par Lighthouse sur le déploiement réel le 26/08/2026 :
 * « link-in-text-block » — les deux liens de la page de connexion (`/healthz`,
 * `/api/docs`) ont **1,17:1** de contraste avec le texte qui les entoure, et
 * aucun soulignement. Pour un daltonien, ce ne sont pas des liens : ce sont des
 * mots. WCAG demande 3:1 entre le lien et son voisinage, OU un indice non
 * coloré — le soulignement est le seul qui ne coûte rien.
 *
 * La règle ne s'applique qu'aux liens EN CONTEXTE DE TEXTE. Les liens de
 * navigation, les boutons et les entrées de tableau sont exclus : leur position
 * et leur forme les désignent déjà, et les souligner tous ferait un chrome
 * bruyant. C'est la distinction que WCAG fait lui-même.
 *
 * ⚠️ Défaut invisible à un audit statique : il ne se voit qu'en comparant la
 * couleur du lien à celle de son PARENT, ce qui demande le rendu. */
p a:not(.bouton), .note-marge a, .petit a, dd a, li a:not(.nav-lien),
.carte-pied a:not(.bouton), .connexion-pied a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Le focus est VISIBLE partout : la charte §7 exige « le clavier d'abord », donc
 * on doit voir où l'on est. Jamais `outline: none` sans remplacement. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--lien);
  outline-offset: 1px;
  border-radius: var(--rayon-petit);
}

/* Texte réservé au lecteur d'écran. Il n'y avait aucun moyen, dans ce
 * programme, d'écrire un mot pour la voix sans l'écrire pour l'œil : une
 * colonne « Qui » abrégée, un compte de résultats de recherche, la différence
 * entre un droit accordé et un droit refusé — tout cela se voyait et ne
 * s'entendait pas. Cette classe est ce qui manquait.
 * `clip-path` et non `display: none` : ce dernier retire du rendu ET de l'arbre
 * d'accessibilité, donc ne servirait à rien ici. */
.visuellement-cache {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Lien d'évitement : le clavier arrive sur 14 entrées de navigation avant
 * d'atteindre le tableau, sur CHAQUE écran. Il ne se voit qu'au focus. */
.lien-evitement {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  padding: var(--e2) var(--e3);
  background: var(--surface); color: var(--texte);
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
}
.lien-evitement:focus { left: var(--e2); top: var(--e2); }

/* 🔴 Les TITRES passent à Playfair Display — la direction « Le Registre ». La
 * police était nommée par la charte §2.3 et par `--police-exergue` depuis le
 * 22/08, et elle n'était pas embarquée : les exergues se rendaient en Georgia.
 * Elle est désormais servie par nous (quatre woff2, 121 Ko, OFL 1.1).
 *
 * `h3` reste en Inter : ce n'est pas un titre mais une ÉTIQUETTE de section
 * (capitales, interlettrage ouvert, gris doux). Un serif en capitales de 13 px
 * se lit mal, et le rôle des deux n'est pas le même. */
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
h1, h2 { font-family: var(--police-titre); }
h1 { font-size: 24px; letter-spacing: -0.012em; }
h2 { font-size: 16.5px; }
/* `h4` partage le rendu de `h3` : un sous-titre à l'intérieur d'une carte est
 * au même niveau visuel, et il descend d'un cran dans l'arbre dès que le
 * titre de la carte est lui-même un `h3` (page de démonstration). Sans cette
 * ligne, un `h4` correct pour un lecteur d'écran retomberait sur le rendu par
 * défaut du navigateur — donc le bon arbre coûterait le rendu. */
h3, h4 { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--texte-doux); }

/* 🟡 D17 et D19 — la hiérarchie des titres ne suit pas toujours la hiérarchie
 * VISUELLE. Un sur-titre de section est petit et discret alors qu'il domine ce
 * qui le suit ; un titre de carte imbriquée est gros alors qu'il est subordonné.
 * Ces deux classes permettent de corriger la BALISE (donc l'arbre lu par un
 * lecteur d'écran et la navigation par titres) sans toucher au rendu — c'est
 * exactement le cas d'usage qu'elles servent, et rien d'autre. */
.titre-section { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--texte-doux); font-weight: 600; }
/* 🔴 DEUX CLASSES, parce qu'il y avait DEUX besoins sous un seul nom, et que
 * la confusion a produit une régression le 26/08 au soir.
 *
 * `.titre-carte` existait pour « corriger la BALISE sans toucher au rendu ».
 * Elle servait deux cas opposés : le titre d'une carte de PREMIER niveau qu'un
 * sur-titre de section domine (les 25 cartes de `/demonstration`, sous un
 * `h2.titre-section`), et le titre d'une carte IMBRIQUÉE dans une autre
 * (`agenda_regles`, `agenda_propositions`). Le premier doit ressembler à un
 * titre de carte ; le second doit se voir subordonné.
 *
 * D-047 a mis `h1, h2` sur Playfair sans suivre `.titre-carte`, qui rendait donc
 * en Inter 15 px — laid, mais distinct. En l'alignant sur 16,5 px Playfair, le
 * titre imbriqué a pris EXACTEMENT le poids de celui qui le domine : mesuré par
 * un agent utilisateur, « identique : true », et la hiérarchie était inversée
 * sur `/demonstration`, où l'enfant était plus gros et plus noir que son parent.
 *
 * Donc : `.titre-carte` = un vrai titre de carte. `.titre-sous-carte` = un titre
 * subordonné, même famille (c'est la même reliure), un cran plus petit et sans
 * la graisse — assez pour que l'œil range les deux sans avoir à les comparer. */
.titre-carte   { font-family: var(--police-titre); font-size: 16.5px; text-transform: none;
                 letter-spacing: -0.01em; color: var(--texte); font-weight: 600; }
.titre-sous-carte { font-family: var(--police-titre); font-size: 14px; text-transform: none;
                 letter-spacing: 0; color: var(--texte); font-weight: 500; }

/* Un motif, une note ou une référence collée n'a pas de coupure naturelle : un
 * seul mot de 300 caractères faisait défiler l'écran des objectifs de 2 204 px
 * — mesuré — et, rien n'étant supprimable, le cassait pour toujours. La coupure
 * est autorisée ICI et pas globalement : `anywhere` sur tout le programme
 * couperait des références de dossier au milieu, ce qui est pire. */
.motif, td.motif { overflow-wrap: anywhere; }
/* Un échantillon typographique n'est pas un titre : il MONTRE un titre. Il ne
 * doit donc pas peupler la navigation par titres (D18). */
.echantillon-h1 { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; margin: 0; }
.echantillon-h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; margin: 0; }
.echantillon-h3 { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--texte-doux); margin: 0; }

p { margin: 0 0 var(--e3); }

/* Références, montants, dates, durées : chiffres à largeur fixe, sinon un
 * tableau de montants ne s'aligne pas (charte §2.3). */
.donnees { font-family: var(--police-donnees); font-variant-numeric: tabular-nums; }
.doux    { color: var(--texte-doux); }
.petit   { font-size: 12px; }
.exergue { font-family: var(--police-exergue); font-style: italic; }
.nombre  { font-family: var(--police-donnees); font-variant-numeric: tabular-nums; text-align: right; }

.pile   { display: flex; flex-direction: column; gap: var(--e3); }
.rangee { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.pousse { margin-left: auto; }

/* ─────────────────────────────────────── coquille applicative ─────────── */

.coquille {
  /* La navigation est épinglée, donc haute d'un écran : sans ce fond, la colonne
   * s'arrête au bas de l'écran et laisse voir le fond de page en dessous. */
  background: linear-gradient(
    to right,
    var(--nav-fond) 0 var(--largeur-nav),
    var(--page-fond) var(--largeur-nav)
  );
  display: grid;
  grid-template-columns: var(--largeur-nav) 1fr;
  grid-template-rows: var(--hauteur-entete) 1fr;
  grid-template-areas: "entete entete" "nav contenu";
  min-height: 100vh;
}

/* Bandeau des données d'essai (D-035) : il prend une RANGÉE, il ne flotte pas
 * au-dessus. Un bandeau superposé masque une ligne de tableau, et un bandeau
 * qui masque finit par être fermé — or celui-ci ne doit pas pouvoir l'être.
 * La classe est posée par le gabarit : pas de `:has()`, pour que la grille soit
 * juste même là où le navigateur ne le connaît pas. */
.coquille.avec-bandeau-essai {
  grid-template-rows: auto var(--hauteur-entete) 1fr;
  grid-template-areas: "essai essai" "entete entete" "nav contenu";
}

.bandeau-essai {
  grid-area: essai;
  display: flex; align-items: baseline; gap: var(--e3); flex-wrap: wrap;
  padding: var(--e2) var(--e4);
  background: var(--alerte-doux);
  color: var(--alerte-texte);
  border-bottom: 2px solid var(--alerte-fond);
  font-size: 13px;
}
.bandeau-essai strong {
  font-family: var(--police-donnees);
  letter-spacing: .08em;
  white-space: nowrap;
}

.entete-app {
  grid-area: entete;
  display: flex; align-items: center; gap: var(--e4);
  padding: 0 var(--e4);
  background: var(--chrome-fond);
  color: var(--chrome-texte);
  /* Le filet double du registre, sur le fronton du programme. */
  border-bottom: 2px solid var(--texte);
  box-shadow: 0 3px 0 -2px var(--texte);
  position: sticky; top: 0; z-index: 20;
}

.marque {
  font-family: var(--police-marque);
  font-size: 24px; line-height: 1;
  letter-spacing: .04em;
  color: var(--chrome-texte);
  text-decoration: none; white-space: nowrap;
}
.marque:hover { text-decoration: none; color: var(--chrome-texte); }

/* Un lien de l'en-tête prend les couleurs du chrome, pas celles du contenu :
 * l'en-tête est une surface à part, et c'est ce qui permet à un thème de
 * cabinet de la traiter séparément sans réécrire les liens. */
.entete-app a:not(.marque):not(.bouton) { color: var(--chrome-texte-doux); }
.entete-app a:not(.marque):not(.bouton):hover { color: var(--chrome-texte); }
/* 🔴 `.marque .marque-suite { opacity: .62 }` est SUPPRIMÉE. Elle calculait un
 * `#799bb7` sur fond clair, soit **2,8:1** — un échec AA sur la première chose
 * que voit l'utilisateur, mesuré par Lighthouse le 26/08/2026. Les trois gabarits
 * emploient désormais le monogramme en courbes, qui prend `currentColor` et n'a
 * donc aucune opacité à composer. La classe n'existe plus nulle part. */

/* Le monogramme M24 — le tracé en courbes de D-039, dans
 * `statique/monogramme-m24.svg`. Sa boîte est serrée sur l'encre : la hauteur
 * demandée ici EST la hauteur des capitales, et c'est tout l'intérêt du tracé
 * sur la composition en Bebas Neue, dont 24 px de corps ne donnent que 17 px de
 * hauteur d'œil.
 *
 * Le fichier est employé comme MASQUE et non recopié dans le gabarit : c'est la
 * seule façon qu'un SVG servi à part prenne la couleur du texte (en `<img>` il
 * est un document isolé et resterait noir sur fond sombre). Le dessin n'existe
 * donc qu'en un seul endroit, et il ne peut pas dériver du gabarit. La couleur
 * vient de `currentColor`, donc du chrome, donc du thème du cabinet (P7) : rien
 * n'est écrit en dur ici.
 *
 * `17em / 24` n'est pas un tâtonnement : Bebas Neue donne 17 px de hauteur de
 * capitale pour 24 px de corps (mesuré). Le monogramme épouse donc le « GEST »
 * qui le précède à toutes les tailles — 24 px dans l'en-tête, 34 px à la
 * connexion, 40 px sur la démonstration — sans un seul réglage par écran. Et à
 * la taille de l'en-tête le compte tombe juste sur 17 px entiers, ce qui
 * compte : à 17,28 px (0,72 em) les fûts, larges de 2,3 px, tombent entre deux
 * pixels et grisent — vu au pixel. Ne pas descendre sous 16 px de haut : en
 * dessous, les deux contrepoinçons du M se ferment et la marque devient un
 * pâté. C'est aussi pourquoi le plancher de 20 px de la charte §2.3 se tient en
 * montant les DEUX ensemble — 28 px de corps sur `.marque` suffisent.
 *
 * Le `?v=` est à incrémenter à la main si le tracé change : l'empreinte
 * automatique de `version_statique` versionne l'URL de CETTE feuille, pas les
 * URL écrites DEDANS — le navigateur garderait l'ancien dessin en cache. */
.marque-monogramme {
  /* `inline-block` + `baseline` : la boîte du tracé s'arrête exactement sur la
   * ligne de pied des capitales, donc son bord bas EST la ligne de base du
   * « GEST » qui le précède. En `block` il passerait à la ligne. */
  display: inline-block; vertical-align: baseline;
  height: calc(17em / 24); aspect-ratio: 39.83 / 20;
  background-color: currentColor;
  -webkit-mask: url("/statique/monogramme-m24.svg?v=1") no-repeat center / contain;
          mask: url("/statique/monogramme-m24.svg?v=1") no-repeat center / contain;
}

.nav-laterale {
  grid-area: nav;
  background: var(--nav-fond);
  border-right: 1px solid var(--nav-bordure);
  padding: var(--e3) var(--e2);
  display: flex; flex-direction: column; gap: 2px;
  position: sticky; top: var(--hauteur-entete);
  height: calc(100vh - var(--hauteur-entete));
  overflow-y: auto;
}

/* 🔴 LE LOGO DU CABINET, EN FILIGRANE, EN BAS DE LA COLONNE. Demandé par MT le
 * 26/08, et il règle un vrai vide : la navigation compte dix entrées et la
 * colonne en fait huit cents pixels — les six cents du bas étaient nus.
 *
 * C'est le SENS POSITIF de la marque (encre sur clair), que MT a fourni ce
 * soir-là : `logo-m24.png`, le lettrage AVEC son cadre en équerre. D-039 avait
 * écarté ce cadre pour le monogramme de l'en-tête, à raison — « le rapport du
 * lettrage au cadre EST le dessin de la marque », et à 17 px de hauteur d'œil ce
 * rapport ne tient pas. À cent trente pixels, il tient, et c'est tout l'intérêt.
 *
 * En MASQUE et non en `<img>` : le tracé prend `background-color`, donc la
 * couleur d'un jeton, donc le thème d'un autre cabinet (P7). Un `<img>` figerait
 * l'encre. C'est la même technique que `.marque-monogramme`.
 *
 * L'opacité est ici légitime — c'est un décor sur lequel rien ne se lit, à la
 * différence du filigrane de la page d'entrée (leçon de D26). */
.nav-laterale::after {
  content: ""; display: block; flex: none;
  width: 132px; height: 132px; margin: auto 0 var(--e4) var(--e2);
  background-color: var(--primaire); opacity: .13;
  -webkit-mask: url("/statique/logo-m24.png?v=1") no-repeat center / contain;
          mask: url("/statique/logo-m24.png?v=1") no-repeat center / contain;
}

/* L'icône d'une entrée de navigation, et de tout ce qui en porte une. Elle ne
 * rétrécit jamais : un tracé écrasé devient illisible avant d'être petit. */
.icone { flex: none; vertical-align: -0.18em; }
.nav-lien { gap: var(--e3); }

.nav-laterale .nav-titre {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--nav-doux); padding: var(--e3) var(--e2) var(--e1);
}

.nav-lien {
  display: flex; align-items: center; gap: var(--e2);
  padding: 7px var(--e2);
  border-radius: var(--rayon-petit);
  color: var(--nav-texte); font-size: 13.5px; text-decoration: none;
}
.nav-lien:hover { background: var(--nav-actif-fond); color: var(--nav-actif-texte); text-decoration: none; }
.nav-lien[aria-current="page"] {
  background: var(--nav-actif-fond); color: var(--nav-actif-texte); font-weight: 600;
}
.nav-lien .compteur {
  margin-left: auto; font-family: var(--police-donnees); font-size: 11px;
  color: var(--nav-doux);
}
.nav-lien[aria-current="page"] .compteur { color: var(--nav-actif-texte); }

.contenu { grid-area: contenu; padding: var(--e6); min-width: 0; }

/* ─────────────────────────────────────────────── fil d'Ariane ─────────── */

.ariane { display: flex; gap: var(--e2); align-items: center; font-size: 12px; color: var(--texte-doux); margin-bottom: var(--e2); }
.ariane a { color: var(--texte-doux); }
.ariane .sep { opacity: .5; }

/* L'en-tête d'un écran de travail. Ce qui fait tenir la page de connexion — une
 * hiérarchie typographique franche et un rythme délibéré — se transpose ici SANS
 * sa couleur : la direction F réserve l'aplat de marque à la seule page qui n'a
 * pas de travail à montrer (D-038). Ce qui se transpose, c'est le filet qui
 * sépare l'en-tête du contenu, et le fait que le titre domine vraiment. */
.entete-page {
  display: flex; align-items: flex-start; gap: var(--e4);
  padding-bottom: var(--e3); margin-bottom: var(--e6);
  border-bottom: 2px solid var(--texte);
  box-shadow: 0 3px 0 -2px var(--texte);
}
.entete-page h1 { font-size: 27px; letter-spacing: -0.012em; }
/* La ligne de contexte sous le titre : le même gris partout, la même taille
 * partout. Elle porte les volumes, les périodes, les comptes — jamais un état. */
.entete-page .titres > p { margin: 3px 0 0; }
.entete-page .titres { min-width: 0; }
.entete-page .actions { margin-left: auto; display: flex; gap: var(--e2); flex-shrink: 0; }

/* ─────────────────────────────────────────────── boutons ─────────────── */

.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--e2);
  padding: 7px var(--e3); min-height: 32px;
  font: inherit; font-size: 13.5px; font-weight: 500;
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
  background: var(--surface); color: var(--texte);
  cursor: pointer; text-decoration: none; white-space: nowrap;
}
.bouton:hover { background: var(--surface-enfoncee); text-decoration: none; color: var(--texte); }

.bouton-primaire { background: var(--primaire); border-color: var(--primaire); color: var(--texte-inverse); }
.bouton-primaire:hover { background: var(--primaire-survol); border-color: var(--primaire-survol); color: var(--texte-inverse); }

.bouton-discret { border-color: transparent; background: transparent; color: var(--primaire); }
.bouton-discret:hover { background: var(--primaire-doux); }

.bouton-danger { background: var(--danger-fond); border-color: var(--danger-fond); color: var(--texte-inverse); }
.bouton-danger:hover { background: var(--danger-texte); border-color: var(--danger-texte); color: var(--texte-inverse); }
/* β #189/#191 : un `.doux` posé DANS un bouton plein gardait son gris de
 * papier — illisible sur rouge ou bleu. Dans un bouton plein, tout texte
 * hérite de la couleur du bouton ; la nuance passe par l'opacité. */
.bouton-danger .doux, .bouton-primaire .doux { color: inherit; opacity: .9; }
/* β #206 : un badge-INTERRUPTEUR (« client ✓ » qui se renfonce d'un clic). */
button.badge { cursor: pointer; font-family: inherit; }
button.badge:hover { filter: brightness(.96); }

.bouton-petit { min-height: 26px; padding: 3px var(--e2); font-size: 12.5px; }

/* Le bouton de navigation où l'on EST DÉJÀ. Il ne prend pas l'aplat de marque —
 * celui-ci désigne l'action principale de l'écran, et il n'y en a qu'une. Le
 * fond enfoncé et le poids suffisent, exactement comme `.nav-lien[aria-current]`
 * dans la barre : même geste, même traitement. */
.bouton-actif {
  background: var(--surface-enfoncee); border-color: var(--bordure-forte);
  font-weight: 600; color: var(--texte);
}
.bouton-actif:hover { background: var(--surface-enfoncee); color: var(--texte); }

/* Un bouton indisponible devient NEUTRE ; il ne s'estompe pas. `opacity: .5`
 * laissait un bouton primaire BLEU, donc invitant, alors qu'il n'est pas
 * cliquable : l'œil lit la couleur de marque avant de mesurer une
 * transparence, et l'on clique dans le vide. L'aplat gris, lui, dit « pas
 * maintenant » sans rien à interpréter. Le poids 600 garde le libellé lisible
 * malgré le texte doux — un bouton mort doit encore pouvoir se LIRE, c'est
 * souvent lui qui explique ce qui manque pour l'activer.
 *
 * `[aria-disabled="true"]` est traité comme `:disabled` : un lien-bouton
 * (`<a class="bouton">`) ne peut pas porter l'attribut natif, et il ne doit
 * pas pour autant paraître cliquable. Ces règles viennent APRÈS les variantes
 * pour gagner sur `.bouton-primaire:hover` à spécificité égale.
 *
 * ⚠️ D4 — le texte est `--texte`, PAS `--texte-doux`. `--texte-doux` sur
 * `--bordure-forte` mesure 3,24:1, sous le seuil AA de 4,5:1 — et c'est
 * précisément le libellé qui explique le refus (« Retrait impossible »,
 * « Mettre en service », « Confirmer ») qui était le texte le moins lisible de
 * tout le programme. `--texte` sur le même aplat gris donne 10,12:1 : le bouton
 * reste NEUTRE — c'est l'aplat gris qui dit « pas maintenant », pas la pâleur du
 * texte, comme le dit le paragraphe ci-dessus — et il redevient lisible. */
.bouton:disabled, .bouton[aria-disabled="true"] {
  background: var(--bordure-forte);
  border-color: var(--bordure-forte);
  color: var(--texte);
  font-weight: 600;
  cursor: not-allowed;
}
.bouton:disabled:hover, .bouton[aria-disabled="true"]:hover {
  background: var(--bordure-forte);
  border-color: var(--bordure-forte);
  color: var(--texte);
}

/* ─────────────────────────────────────────────── formulaires ─────────── */

.champ { display: flex; flex-direction: column; gap: var(--e1); }
.champ > label { font-size: 12.5px; font-weight: 500; color: var(--texte); }
.champ .aide { font-size: 12px; color: var(--texte-doux); }
.champ .erreur { font-size: 12px; color: var(--danger-texte); font-weight: 500; }
/* D21 — l'astérisque seule est reprise par la voix comme « astérisque », ce qui
 * ne veut rien dire. `content` accepte un texte de remplacement depuis CSS 3 :
 * l'œil voit l'étoile, la voix entend le mot. Chrome, Edge et Safari le lisent ;
 * Firefox l'ignore et n'annonce alors RIEN, ce qui est moins mauvais. Ce qui
 * porte vraiment l'information reste l'attribut `required` sur le champ ; ceci
 * n'en est que le supplément. */
.champ-obligatoire > label::after { content: " *"; color: var(--danger-texte); }
/* ⚠️ Le texte de remplacement est posé SÉPARÉMENT, sous `@supports`. Firefox ne
 * prend pas en charge `<content-list> / <string>` — et une déclaration dont la
 * valeur n'est pas reconnue n'est pas « partiellement appliquée » : elle est
 * jetée. `::after` retombe alors sur `content: normal`, qui pour un
 * pseudo-élément vaut `none` : la boîte n'est pas générée et **l'astérisque
 * disparaît**. On aurait perdu le seul repère visuel du champ obligatoire pour
 * gagner une annonce que Firefox ne fait pas non plus. Ce qui porte
 * l'information reste l'attribut `required` sur le champ ; ceci est un
 * supplément, et il ne doit rien coûter. */
@supports (content: "x" / "y") {
  .champ-obligatoire > label::after { content: " *" / " (obligatoire)"; }
}

input[type="text"], input[type="search"], input[type="date"], input[type="number"],
input[type="email"], input[type="time"], select, textarea {
  font: inherit; font-size: 13.5px;
  padding: 6px var(--e2); min-height: 32px;
  color: var(--texte); background: var(--surface);
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
  width: 100%;
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-enfoncee); color: var(--texte-doux); cursor: not-allowed;
}
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--danger-fond);
}
textarea { min-height: 72px; resize: vertical; }
input.donnees { font-family: var(--police-donnees); }

.interrupteur { display: inline-flex; align-items: center; gap: var(--e2); font-size: 13.5px; }
.interrupteur input { width: auto; min-height: 0; }

.grille-champs {
  display: grid; gap: var(--e3);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* ─────────────────────────────────────────────── cartes et panneaux ──── */

/* 🔴 LA CARTE N'EST PLUS UNE CARTE : c'est une SECTION séparée par un filet.
 *
 * C'est le cœur de la direction « Le Registre », et tout passe par ici — les 22
 * gabarits n'ont pas une ligne à changer, parce qu'ils ne décrivent que la
 * structure (`.carte`, `.carte-entete`, `.carte-corps`) et jamais l'apparence.
 * C'est exactement ce que le découplage par jetons devait permettre, et c'est la
 * première fois qu'on s'en sert.
 *
 * Ce qui disparaît : le fond, le cadre, l'arrondi, l'ombre. Ce qui reste : le
 * FILET, et le rapport des blancs. Un registre ne met pas ses colonnes dans des
 * boîtes ; il les sépare par un trait et les aligne sur la même verticale.
 *
 * ⚠️ Le `padding` horizontal tombe à zéro : sans cadre, un retrait n'a plus rien
 * à quoi se rapporter, et le texte doit s'aligner sur la marge du contenu. C'est
 * ce qui donne à la page sa verticale — et c'est aussi ce qui rend le
 * `padding: 0 var(--e4)` d'un tableau visuellement juste, puisque le tableau
 * garde sa propre gouttière. */
.carte {
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
  margin-bottom: var(--e6);
}
.carte-entete {
  display: flex; align-items: baseline; gap: var(--e3);
  padding: 0 0 var(--e2);
  border-bottom: 2px solid var(--texte);
  box-shadow: 0 3px 0 -2px var(--texte);
  margin-bottom: var(--e4);
}
.carte-entete h2 { font-size: 16.5px; }
.carte-corps { padding: 0; }
.carte-corps-serre { padding: 0; }
.carte-pied {
  padding: var(--e2) 0 0; border-top: 1px solid var(--bordure);
  margin-top: var(--e3); font-size: 12px; color: var(--texte-doux);
}

/* Une carte IMBRIQUÉE ne peut pas porter le même filet que sa parente, sinon
 * deux reliures se superposent. Elle prend un filet simple et un retrait : c'est
 * le décalage d'un paragraphe cité dans un acte. */
.carte .carte { margin-bottom: var(--e4); padding-left: var(--e4); border-left: 1px solid var(--bordure); }
.carte .carte > .carte-entete {
  border-bottom-width: 1px; box-shadow: none; margin-bottom: var(--e3);
}
.carte .carte > .carte-entete h2 { font-size: 14.5px; }

/* Cartes porteuses d'un état. Une carte qui liste trois pièces illisibles n'est
 * pas une carte comme les autres : son état se lit AVANT son contenu, donc il
 * est dans la bordure et dans l'en-tête, jamais dans le corps — un aplat de
 * couleur sur tout le corps rendrait le texte du corps moins lisible que le
 * texte des cartes voisines, alors que c'est justement celui-là qu'il faut
 * lire. Le compteur de l'en-tête prend la couleur de TEXTE de l'état, pas
 * celle de remplissage (charte §2.2). Déclinées par jeton, jamais en dur. */
/* Sans cadre, l'état d'une section se porte par un FILET LATÉRAL épais — le
 * trait qu'on tire dans la marge d'un acte à relire. Le filet du fronton prend
 * la couleur de l'état, le titre aussi ; le corps reste sur le papier, pour la
 * raison écrite au-dessus : c'est lui qu'il faut lire. */
.carte-danger, .carte-alerte, .carte-info {
  padding-left: var(--e4); border-left: 3px solid var(--bordure-forte);
}
.carte-danger { border-left-color: var(--danger-fond); }
.carte-danger > .carte-entete {
  border-bottom-color: var(--danger-fond); box-shadow: 0 3px 0 -2px var(--danger-fond);
}
.carte-danger > .carte-entete h2, .carte-danger > .carte-entete .compteur { color: var(--danger-texte); }

.carte-alerte { border-left-color: var(--alerte-fond); }
.carte-alerte > .carte-entete {
  border-bottom-color: var(--alerte-fond); box-shadow: 0 3px 0 -2px var(--alerte-fond);
}
.carte-alerte > .carte-entete h2, .carte-alerte > .carte-entete .compteur { color: var(--alerte-texte); }

.carte-info { border-left-color: var(--info-fond); }
.carte-info > .carte-entete {
  border-bottom-color: var(--info-fond); box-shadow: 0 3px 0 -2px var(--info-fond);
}
.carte-info > .carte-entete h2, .carte-info > .carte-entete .compteur { color: var(--info-texte); }

/* Le compteur d'un en-tête de carte : un nombre se lit en police de données,
 * même seul, sinon il saute d'un pixel quand il passe de 9 à 10. */
.carte-entete .compteur {
  margin-left: auto;
  font-family: var(--police-donnees); font-variant-numeric: tabular-nums;
  font-size: 12px; color: var(--texte-doux);
}

.grille-cartes { display: grid; gap: var(--e4); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grille-stats  { display: grid; gap: var(--e4); grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }

/* Une tuile de chiffre n'a pas besoin de 80 px de hauteur pour porter un nombre.
 * `.carte-corps-chiffre` resserre le remplissage sur une carte qui ne contient
 * QUE une statistique : le reste du programme travaille à 36 px de ligne, une
 * tuile creuse au milieu casse ce rythme. Le nombre, lui, grandit — c'est lui
 * qu'on vient lire. */
.carte-corps-chiffre { padding: var(--e3) var(--e4); }
.carte-corps-chiffre .stat .valeur { font-size: 28px; }

/* ─────────────────────────────────────────────── statistique ─────────── */

.stat { display: flex; flex-direction: column; gap: 2px; }
.stat .valeur { font-family: var(--police-donnees); font-size: 24px; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat .libelle { font-size: 12px; color: var(--texte-doux); }
.stat-danger .valeur { color: var(--danger-texte); }
.stat-alerte .valeur { color: var(--alerte-texte); }
.stat-succes .valeur { color: var(--succes-texte); }

/* ─────────────────────────────────────────────── tableau dense ───────── */

/* 🔴 D9 — `overflow-x: auto` seul n'est PAS atteignable au clavier : un
 * conteneur défilable non focalisable ne se défile pas aux flèches, et les
 * colonnes qui débordent à droite deviennent inaccessibles — or ce sont les
 * dernières colonnes qui portent « Confirmé par » et « S'en saisir »
 * (WCAG 2.2, 2.1.1, sur un programme dont la charte dit « le clavier d'abord »).
 * Les 26 gabarits posent `tabindex="0"` + `role="region"` + `aria-label` ; la
 * règle ci-dessous ne fait qu'éviter que ce focus soit invisible. Le contour est
 * INTÉRIEUR (`offset: -2px`) : posé à l'extérieur, il serait coupé par la carte
 * qui contient l'enveloppe. */
.tableau-enveloppe { overflow-x: auto; }
/* `outline-offset: 0` et non `-2px` : à l'intérieur, le contour se peint dans
 * la couche du fond du parent, et `table.tableau th` est `sticky` avec
 * `z-index: 1` et un fond opaque — le bord SUPÉRIEUR du contour était donc
 * recouvert sur toute la largeur de l'en-tête. La crainte inverse (« posé à
 * l'extérieur, il serait coupé par la carte ») ne tient pas : `.carte` n'a pas
 * d'`overflow: hidden`. Mesuré : `--lien` sur `--surface-enfoncee` = 3,25:1,
 * au-dessus du seuil de 3:1 de SC 1.4.11. */
.tableau-enveloppe:focus-visible { outline: 2px solid var(--lien); outline-offset: 0; }

table.tableau {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: 13px; background: var(--surface);
}
table.tableau th {
  text-align: left; font-size: 11.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--texte-doux); background: var(--surface-enfoncee);
  padding: var(--e2) var(--e3); white-space: nowrap;
  border-bottom: 1px solid var(--bordure);
  position: sticky; top: 0; z-index: 1;
}
table.tableau td {
  padding: 0 var(--e3); height: var(--ligne-tableau);
  border-bottom: 1px solid var(--bordure);
  vertical-align: middle;
}
table.tableau tbody tr:nth-child(even) { background: var(--surface-alt); }
table.tableau tbody tr:hover { background: var(--primaire-doux); }
table.tableau tbody tr.ligne-critique td:first-child { box-shadow: inset 3px 0 0 var(--danger-fond); }
table.tableau tbody tr.ligne-alerte  td:first-child { box-shadow: inset 3px 0 0 var(--alerte-fond); }
/* β #177 : la ligne qu'un geste vient de créer (pièce générée…) se voit. */
table.tableau tbody tr.ligne-marquee td { background: var(--primaire-doux); }
table.tableau tbody tr.ligne-marquee td:first-child { box-shadow: inset 3px 0 0 var(--primaire); }
table.tableau tbody tr.ligne-ia      td:first-child { box-shadow: inset 3px 0 0 var(--ia-accent); }
/* Même famille, même filet de 3 px : la ligne SÉLECTIONNÉE (celle dont la
 * pièce est ouverte dans la visionneuse) se marque comme un état, avec la
 * couleur de marque. Elle doit tenir sans le survol : la souris est ailleurs
 * pendant qu'on lit la pièce, et le fond de survol aurait alors disparu. */
table.tableau tbody tr.ligne-selectionnee { background: var(--primaire-doux); }
table.tableau tbody tr.ligne-selectionnee td:first-child { box-shadow: inset 3px 0 0 var(--primaire); }
table.tableau .cellule-nombre { font-family: var(--police-donnees); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
table.tableau .cellule-ref { font-family: var(--police-donnees); white-space: nowrap; }
/* Une cellule qui ÉLIDE plutôt que de replier. Elle sert aux colonnes de
 * classement — type, département, matière — dont le libellé du cabinet est long
 * (« Cosulata — droit des affaires et commercial ») alors que la colonne fait
 * 132 px. Sans elle, la ligne passait de 36 à 70 px et la densité de l'écran,
 * qui est tout son argument (D-038), s'effondrait. Le libellé complet vit dans
 * le `title` du gabarit ET dans son texte, donc rien n'est perdu à la voix.
 * ⚠️ Ne PAS l'employer sur une colonne qui porte une décision : un intitulé de
 * dossier ou l'objet d'un fil se replient (D6, D29). Une colonne de classement,
 * elle, se balaie. */
table.tableau .cellule-elidee {
  max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.pied-tableau {
  display: flex; align-items: center; gap: var(--e3);
  padding: var(--e2) var(--e3); font-size: 12px; color: var(--texte-doux);
  border-top: 1px solid var(--bordure);
}

/* ─────────────────────────────────────────────── badges et pastilles ─── */

.badge {
  display: inline-flex; align-items: center; gap: var(--e1);
  padding: 1px var(--e2); border-radius: 999px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
}
.badge-neutre   { background: var(--surface-enfoncee); color: var(--texte-doux); border-color: var(--bordure); }
.badge-primaire { background: var(--primaire-doux); color: var(--primaire);      border-color: var(--primaire); }
.badge-succes   { background: var(--succes-doux);   color: var(--succes-texte);  border-color: var(--succes-fond); }
.badge-alerte   { background: var(--alerte-doux);   color: var(--alerte-texte);  border-color: var(--alerte-fond); }
.badge-danger   { background: var(--danger-doux);   color: var(--danger-texte);  border-color: var(--danger-fond); }
.badge-info     { background: var(--info-doux);     color: var(--info-texte);    border-color: var(--info-fond); }

/* Le SEUL badge en aplat de la couleur de marque, et il a un seul emploi : le
 * filtre ACTIF d'une rangée de filtres (voir `.filtres`). Là, le contraste
 * actif/inactif n'est pas décoratif, il est fonctionnel — on doit voir sans
 * chercher sur quoi la liste est filtrée, sinon on lit un extrait en croyant
 * lire le tout. `.badge-primaire`, en fond doux, ne donne pas cet écart.
 * Partout ailleurs un badge reste en fond atténué : un écran semé d'aplats
 * bleus ne hiérarchise plus rien (direction F, D-038). */
.badge-plein { background: var(--primaire); color: var(--texte-inverse); border-color: var(--primaire); }

/* L'IA propose, l'humain dispose (P3, charte §6) : TOUJOURS en contour, jamais
 * en plein. La validation humaine retire cette classe — c'est ainsi qu'on voit
 * d'un coup d'œil ce qui reste à relire. */
.badge-ia, .propose-par-ia {
  background: transparent; color: var(--ia-accent);
  border: 1px dashed var(--ia-accent);
}
.propose-par-ia { border-radius: var(--rayon); padding: var(--e1) var(--e2); }

/* Jamais la couleur seule (charte §4) : chaque état porte aussi une pastille
 * ET un libellé. Un tiers des hommes voit mal le rouge et le vert. */
.pastille { width: 8px; height: 8px; border-radius: 999px; flex: 0 0 8px; display: inline-block; }
.pastille-succes { background: var(--succes-fond); }
.pastille-alerte { background: var(--alerte-fond); }
.pastille-danger { background: var(--danger-fond); }
.pastille-info   { background: var(--info-fond); }
.pastille-neutre { background: var(--bordure-forte); }
.pastille-ia     { background: transparent; border: 2px solid var(--ia-accent); }

/* ─────────────────────────────────────────────── progression ─────────── */

.progression { height: 6px; background: var(--surface-enfoncee); border-radius: 999px; overflow: hidden; }
.progression > span { display: block; height: 100%; background: var(--succes-fond); }
.progression-alerte   > span { background: var(--alerte-fond); }
.progression-danger   > span { background: var(--danger-fond); }
.progression-info     > span { background: var(--info-fond); }
.progression-primaire > span { background: var(--primaire); }
.progression-neutre   > span { background: var(--bordure-forte); }
.progression-succes   > span { background: var(--succes-fond); }

/* ─────────────────────────────────────────────── bandeaux et vides ───── */

.bandeau {
  display: flex; gap: var(--e3); align-items: flex-start;
  padding: var(--e3) var(--e4); border-radius: var(--rayon);
  border: 1px solid var(--bordure); background: var(--surface-alt);
  font-size: 13px;
}
.bandeau-info   { background: var(--info-doux);   border-color: var(--info-fond);   color: var(--info-texte); }
.bandeau-succes { background: var(--succes-doux); border-color: var(--succes-fond); color: var(--succes-texte); }
.bandeau-alerte { background: var(--alerte-doux); border-color: var(--alerte-fond); color: var(--alerte-texte); }
.bandeau-danger { background: var(--danger-doux); border-color: var(--danger-fond); color: var(--danger-texte); }
.bandeau-ia     { background: var(--ia-doux);     border-color: var(--ia-accent);   color: var(--ia-accent); }
.bandeau strong { font-weight: 600; }

/* Un état vide DIT QUOI FAIRE (charte §5) — « aucun résultat » n'aide personne. */
.etat-vide { padding: var(--e12) var(--e6); text-align: center; color: var(--texte-doux); }
.etat-vide .titre { font-size: 14px; font-weight: 600; color: var(--texte); margin-bottom: var(--e1); }
.etat-vide .quoi-faire { font-size: 13px; margin-bottom: var(--e4); }

/* ─────────────────────────────────────────────── onglets ─────────────── */

.onglets { display: flex; gap: var(--e1); border-bottom: 1px solid var(--bordure); margin-bottom: var(--e4); }
.onglet {
  padding: var(--e2) var(--e3); font-size: 13.5px; color: var(--texte-doux);
  border-bottom: 2px solid transparent; text-decoration: none;
}
.onglet:hover { color: var(--texte); text-decoration: none; }
.onglet[aria-current="page"] { color: var(--primaire); border-bottom-color: var(--primaire); font-weight: 600; }
/* Pendant exact de `.nav-lien .compteur` : un onglet qui porte un nombre
 * (« Pièces 41 ») dit combien il y a AVANT qu'on l'ouvre, ce qui évite un
 * aller-retour par onglet vide. En police de données pour la même raison que
 * partout ailleurs : le nombre ne doit pas décaler le libellé en changeant. */
.onglet .compteur {
  margin-left: var(--e1);
  font-family: var(--police-donnees); font-variant-numeric: tabular-nums;
  font-size: 11.5px; color: var(--texte-doux);
}
.onglet[aria-current="page"] .compteur { color: var(--primaire); }

/* ── les intercalaires du DOSSIER (planche lotDF, variation B retenue par MT,
 * 13/11) : les onglets d'un vrai classeur. Au repos : papier foncé, 46 px.
 * Actif : papier clair, 54 px, filet bleu au sommet, SOUDÉ au contenu.
 * Scopé — les autres rangées d'onglets gardent le soulignement sobre. */
.onglets-classeur {
  align-items: flex-end; gap: 3px; padding: 0 6px;
  border-bottom: 1px solid var(--bordure-forte);
}
.onglets-classeur .onglet {
  flex: 1; height: 46px; display: flex; align-items: center;
  justify-content: center; gap: var(--e2); padding: 0 var(--e2);
  border: 1px solid var(--bordure); border-bottom: none;
  border-radius: 8px 8px 0 0; background: var(--onglet-repos);
  font-size: 15px; font-weight: 500; color: var(--nav-texte);
  white-space: nowrap; min-width: 0; overflow: hidden;
}
.onglets-classeur .onglet:hover { color: var(--texte); background: var(--surface-alt); }
.onglets-classeur .onglet[aria-current="page"] {
  height: 54px; margin-bottom: -1px; position: relative; z-index: 2;
  background: var(--surface); border-color: var(--bordure-forte);
  box-shadow: inset 0 3px 0 var(--primaire);
  font-weight: 700; color: var(--texte);
}
.onglets-classeur .onglet .compteur { font-size: 12px; font-weight: 700; color: var(--nav-texte); margin-left: 0; }
.onglets-classeur .onglet[aria-current="page"] .compteur { color: var(--primaire); }
/* F-2 (mobile) : un ruban à huit onglets en 390 px en cache cinq — la GRILLE
 * 4 × 2 les montre tous ; l'actif est le seul bouton plein. */
@media (max-width: 720px) {
  .onglets-classeur {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px; padding: 0; border-bottom: none;
  }
  .onglets-classeur .onglet,
  .onglets-classeur .onglet[aria-current="page"] {
    height: 56px; margin-bottom: 0; flex-direction: column; gap: 2px;
    border: 1px solid var(--bordure-forte); border-radius: 6px;
    box-shadow: none; font-size: 12.5px;
  }
  .onglets-classeur .onglet[aria-current="page"] {
    background: var(--primaire); border-color: var(--primaire);
    color: var(--texte-inverse);
  }
  .onglets-classeur .onglet[aria-current="page"] .compteur { color: var(--texte-inverse); }
}

/* ─────────────────────────────────────────────── fil chronologique ───── */

/* Le fil d'un dossier n'est pas un journal décoratif : c'est la réponse à
 * « qu'est-ce qui s'est passé sur ce dossier, et par qui ». Il porte donc
 * quatre colonnes fixes — la NATURE (badge), le QUAND, le QUOI, le QUI — et il
 * se lit à la même densité que le reste, 36 px de ligne. Colonnes fixes et non
 * `auto` : sur trente lignes, une colonne de dates qui respire au gré des
 * libellés rend le fil illisible en diagonale. Le QUOI, seul, s'élide.
 *
 * L'alternance de fond est celle du tableau dense, pour la même raison : sans
 * elle, l'œil saute d'une ligne en suivant une ligne à l'horizontale. */
.fil { list-style: none; margin: 0; padding: 0; }
.fil > li {
  display: grid;
  /* ⚠️ 148 px, et c'est MESURÉ au navigateur, pas estimé : le plus long des
   * treize libellés de nature, « Ouverture du dossier », rend un badge de
   * **135 px**, et le badge est en `nowrap`. À 104 px il DÉBORDAIT sur la colonne
   * de la date — deux textes l'un sur l'autre, sur l'onglet le plus consulté d'un
   * dossier. La piste `max-content` ne marche pas ici : chaque `<li>` est sa
   * PROPRE grille, donc les colonnes ne s'aligneraient plus d'une ligne à
   * l'autre. D'où une largeur fixe, avec treize pixels de garde pour l'italique
   * et pour l'allemand. Défaut invisible hors rendu — et une première tentative
   * à 132 px avait été perdue en silence. */
  grid-template-columns: 148px 108px minmax(0, 1fr) auto;
  align-items: center; gap: var(--e3);
  min-height: var(--ligne-tableau); padding: var(--e1) var(--e2);
  border-bottom: 1px solid var(--bordure); font-size: 13px;
}
.fil > li:nth-child(even) { background: var(--surface-alt); }
.fil .nature { justify-self: start; }
.fil .quand {
  font-family: var(--police-donnees); font-variant-numeric: tabular-nums;
  font-size: 12px; color: var(--texte-doux); white-space: nowrap;
}
/* 🟠 D29 — deux lignes plutôt qu'une, et une coupure de mot en dernier recours.
 *
 * ⚠️ ARBITRAGE ASSUMÉ, ET IL N'EST PAS DE MOI À FAIRE SEUL : cette règle porte la
 * ligne du fil de 36 px à ≈ 46 px dès qu'un objet se replie — ce qui est la
 * règle et non l'exception, c'est l'argument même de D29. Elle contredit donc
 * D-038 (densité de 36 px) et le commentaire qui la précédait disait l'inverse.
 * L'audit posait cet arbitrage comme NON tranché (« il vous revient, et LM ou le
 * designer peuvent le refuser ») : à confirmer par LM. Le repli conforme à
 * D-038 serait de garder une ligne et de laisser le `title` du gabarit sur un
 * élément focalisable — ce qui règle le clavier sans changer la hauteur.
 *
 * un objet de fil fait « Conclusions déposées pour DUPONT Jean / SOCIÉTÉ … »
 * suivi d'un état, donc la troncature est la RÈGLE et non l'exception. Le
 * `title` du gabarit ne rattrapait que la souris ; ceci rattrape tout le monde,
 * et l'allemand en plus (+30 %). Le fil est l'écran de reprise d'un dossier par
 * un autre avocat : un objet coupé au milieu y coûte cher. */
.fil .quoi {
  min-width: 0; overflow: hidden; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.fil .qui { justify-self: end; }

/* ────────────────────────────────── le filet double du registre ──────── */

/* 🔴 LE GESTE DE LA DIRECTION, et il tient en deux lignes. Un trait épais doublé
 * d'un trait fin, trois pixels en dessous : c'est la reliure d'un registre, et
 * c'est ce qui remplace la BORDURE d'une carte. Partout où le programme
 * séparait par un cadre, il sépare maintenant par ce filet.
 *
 * `box-shadow` et non un second élément : un filet n'a pas à exister dans le
 * balisage, et une ombre nulle en flou ne coûte rien à peindre. */
.filet-double {
  border-bottom: 2px solid var(--texte);
  box-shadow: 0 3px 0 -2px var(--texte);
}

/* ─────────────────────────────────────────── replis (`<details>`) ────── */

/* Le marqueur natif se posait AU-DESSUS du libellé, seul sur sa ligne, parce que
 * `summary` est un bloc et que la puce n'a pas d'alignement défini. On le retire
 * et on dessine le chevron nous-mêmes, sur la ligne de base du texte, comme une
 * flèche de tableau. Il tourne à l'ouverture : c'est le seul indice qu'il y a
 * quelque chose dessous. */
/* 🔴 Le focus DANS la barre. `--lien` y mesure **2,10:1** : le contour serait
 * quasi invisible sur le seul contrôle que tout le monde emploie (la recherche).
 * Blanc : 8,42:1. Cette règle ne vaut QUE dans le chrome — sur du papier,
 * `--lien` reste juste et il n'y a rien à changer. */
.entete-app :where(a, button, input, select, [tabindex]):focus-visible {
  outline-color: var(--chrome-focus);
}

details > summary { display: flex; align-items: center; gap: var(--e2); }
details > summary::-webkit-details-marker { display: none; }
details > summary::marker { content: ""; }
details > summary::before {
  content: "›"; flex: none;
  font-size: 15px; line-height: 1; color: var(--texte-doux);
  transform: rotate(90deg) translateY(1px);
}
details[open] > summary::before { transform: rotate(-90deg) translateY(1px); }

/* ─────────────────────────────────────────────── recherche globale ───── */

.recherche-globale { position: relative; flex: 1 1 380px; max-width: 520px; }
.recherche-globale input {
  background: var(--chrome-champ-fond, var(--surface));
  color: var(--chrome-champ-texte, var(--texte));
  border-color: var(--chrome-champ-bordure, var(--bordure-forte));
}
.recherche-globale input::placeholder { color: var(--chrome-champ-doux, var(--texte-doux)); }
.raccourci {
  font-family: var(--police-donnees); font-size: 11px;
  padding: 1px 5px; border-radius: var(--rayon-petit);
  border: 1px solid var(--chrome-bordure); color: var(--chrome-texte-doux);
}
.resultats-recherche {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  background: var(--surface); border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon); box-shadow: var(--ombre-flottante);
  max-height: 60vh; overflow-y: auto;
}
.resultats-recherche:empty { display: none; }
.resultats-recherche a {
  display: block; padding: var(--e2) var(--e3); font-size: 13px;
  color: var(--texte); border-bottom: 1px solid var(--bordure);
}
.resultats-recherche a:hover, .resultats-recherche a.survole { background: var(--primaire-doux); text-decoration: none; }
.resultats-recherche .ref { font-family: var(--police-donnees); color: var(--primaire); margin-right: var(--e2); }

/* ─────────────────────────────────────────────── en-tête de dossier ──── */

.entete-dossier { display: flex; flex-direction: column; gap: var(--e2); }
.entete-dossier .reference { font-family: var(--police-donnees); font-size: 18px; font-weight: 600; color: var(--primaire); }
.entete-dossier.confidentiel { border-left: 3px solid var(--primaire); padding-left: var(--e3); }

/* ─────────────────────────────────────────────── divers ──────────────── */

.compte-utilisateur { display: flex; align-items: center; gap: var(--e2); font-size: 13px; color: var(--chrome-texte); }
.jeton-trigramme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 999px;
  /* Inversé pour la barre : `--primaire` sur `--chrome-fond` mesurait 1,29:1,
   * donc le disque disparaissait purement et simplement. */
  background: var(--chrome-jeton-fond); color: var(--chrome-jeton-texte);
  font-family: var(--police-donnees); font-size: 12px; font-weight: 600;
}

.separateur { height: 1px; background: var(--bordure); border: 0; margin: var(--e4) 0; }
/* Le même filet, mais À L'INTÉRIEUR d'une fiche : `--e4` de part et d'autre
 * sépare deux SECTIONS, ce qui est trop aéré pour séparer deux blocs d'une même
 * fiche — le filet finit par y créer une rupture qu'on n'a pas voulue. */
.separateur-serre { height: 1px; background: var(--bordure); border: 0; margin: var(--e2) 0; }

/* Page de connexion — la seule page sans coquille. */
/* ─────────────────────────── la page de connexion ─────────────────────── */

/* C'est la première chose que voit un associé, et la seule page du programme qui
 * n'a aucun travail à afficher. Elle a donc le droit de porter la marque en
 * grand — c'est le seul endroit où la direction F autorise un aplat de bleu.
 * Deux panneaux : à gauche ce que le programme est, à droite un unique geste.
 *
 * Trois choses ne se négocient pas ici, parce que c'est la page où l'on est le
 * plus tenté de les oublier :
 *   · le contraste (les trois jetons `--connexion-*` sont mesurés au :root) ;
 *   · le clavier — un seul contrôle, il reçoit le focus visible comme partout ;
 *   · l'absence de dépendance : aucune image, aucun script, aucune police
 *     supplémentaire. Le tracé du monogramme est déjà là (D-039).
 */
.page-connexion {
  min-height: 100vh; display: grid; place-items: center;
  background: var(--page-fond); padding: var(--e6);
}

.connexion-cadre {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  width: 100%; max-width: 940px;
  background: var(--surface);
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  box-shadow: var(--ombre-flottante);
  overflow: hidden;
}

/* Le panneau de marque. Le dégradé va du bleu du cabinet à son survol : deux
 * jetons existants, aucune couleur nouvelle. */
.connexion-marque {
  position: relative; overflow: hidden;
  padding: var(--e12) var(--e8);
  background: linear-gradient(160deg, var(--connexion-fond), var(--primaire-survol));
  color: var(--connexion-texte);
  display: flex; flex-direction: column; gap: var(--e6);
}

/* Le monogramme en très grand, en filigrane. Décoratif — donc `aria-hidden` sur
 * l'élément, et une opacité y est légitime : ce n'est pas du texte, il ne porte
 * aucune information, et rien ne se lit dessus. */
.connexion-filigrane {
  position: absolute; right: -8%; bottom: -10%;
  width: 58%; aspect-ratio: 39.83 / 20;
  /* Pas d'`opacity` — et la leçon de D26 vaut aussi pour un décor SUR LEQUEL
   * du texte se lit (la règle voisine `> *:not(.connexion-filigrane)` est écrite
   * précisément pour ça). Un blanc à 8 % éclaircissait le fond sous
   * `.connexion-points` et faisait tomber son contraste à 4,03:1 sur la borne
   * claire du dégradé : la conformité ne tenait que par le sens du dégradé, à un
   * demi-point près. `--primaire` est la couleur du dégradé elle-même : le
   * filigrane se voit par sa forme, et le contraste du texte est INCHANGÉ. */
  background-color: var(--primaire);
  -webkit-mask: url("/statique/monogramme-m24.svg?v=1") no-repeat center / contain;
          mask: url("/statique/monogramme-m24.svg?v=1") no-repeat center / contain;
}

/* ⚠️ `:not(.connexion-filigrane)` n'est pas décoratif : sans lui, cette règle
 * écrase le `position: absolute` du filigrane (même spécificité, elle vient
 * après), le tracé retombe DANS le flux, pousse le panneau en hauteur et vient
 * se coucher derrière le titre. Défaut invisible à la lecture du fichier : il ne
 * se voit qu'au rendu. */
.connexion-marque > *:not(.connexion-filigrane) { position: relative; }

.connexion-titre {
  font-family: var(--police-marque); font-weight: 400;
  font-size: 56px; line-height: 1; letter-spacing: .04em;
  color: var(--connexion-texte); margin: 0;
}
.connexion-titre .marque-monogramme { height: calc(17em / 24); }

.connexion-accroche {
  margin: 0; font-size: 15px; color: var(--connexion-texte);
  max-width: 30ch;
}

/* Playfair, une seule fois, sur une seule phrase : c'est la police d'exergue de
 * la charte et elle ne sert qu'à ça. */
.connexion-citation {
  margin: 0; font-size: 17px; line-height: 1.4;
  color: var(--connexion-texte-doux);
  border-left: 2px solid var(--connexion-texte-doux);
  padding-left: var(--e4); max-width: 34ch;
}

.connexion-points { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--e3); }
.connexion-points li {
  display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: var(--e3);
  font-size: 13px; color: var(--connexion-texte-doux);
}
/* Le filet, et non une puce ni une icône : la charte ne pose pas d'iconographie
 * décorative, et un tiret cadratin serait lu par la voix. */
.connexion-points li::before {
  content: ""; align-self: center;
  height: 1px; background: var(--connexion-texte-doux);
}
.connexion-points strong { color: var(--connexion-texte); font-weight: 600; }

.connexion-acces {
  padding: var(--e12) var(--e8);
  display: flex; flex-direction: column; gap: var(--e6);
  justify-content: center; min-width: 0;
}
.connexion-acces h2 { font-size: 17px; }
.connexion-acces .bouton-primaire { width: 100%; min-height: 40px; font-size: 14px; }

.connexion-pied {
  margin: 0; padding-top: var(--e4);
  border-top: 1px solid var(--bordure);
  font-size: 12px; color: var(--texte-doux);
}

/* L'unique mouvement du programme, et il est ici : la page d'accueil se pose,
 * elle n'apparaît pas d'un coup. 
 * ⚠️ C'est la SEULE `@keyframes` de la feuille — c'est pourquoi le garde
 * `prefers-reduced-motion` arrive avec elle et non avant : jusqu'ici il n'avait
 * rien à réduire. Une seule règle, une seule fois, au chargement ; rien ne
 * bouge ensuite, et rien ne bouge nulle part ailleurs. */
@keyframes connexion-pose {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.connexion-cadre { animation: connexion-pose .32s ease-out both; }
@media (prefers-reduced-motion: reduce) {
  .connexion-cadre { animation: none; }
}

@media (max-width: 860px) {
  /* Le panneau de marque passe DERRIÈRE l'accès : sur un téléphone, ce qu'on
   * vient faire est se connecter, pas lire la marque. Il ne disparaît pas — il
   * cesse d'être en premier. */
  .connexion-cadre { grid-template-columns: minmax(0, 1fr); }
  .connexion-marque { order: 2; padding: var(--e8) var(--e6); gap: var(--e4); }
  .connexion-acces  { order: 1; padding: var(--e8) var(--e6); }
  .connexion-titre { font-size: 40px; }
  .connexion-citation { font-size: 15px; }
}

/* ─────────────────────────── assistant IA : rail et panneau ───────────── */

/* 🔴 Le panneau de l'assistant POUSSE le contenu, il ne flotte pas. C'est une
 * COLONNE DE GRILLE — la troisième de l'espace de travail (navigation, contenu,
 * assistant) — dont la largeur passe de 44 px (rail replié) à 396 px (panneau
 * déplié). La variante flottante (`position: absolute`, panneau posé par-dessus
 * la fiche) a été explicitement écartée : elle masque précisément la ligne de
 * tableau ou la pièce qu'on est en train de lire, et l'on ne relit pas une
 * proposition de l'IA sans voir ce qui la fonde (P3, charte §6). Aucun
 * `position: absolute` ici, jamais — si le panneau ne tient pas, c'est la
 * grille qu'on change, pas la position du panneau.
 *
 * En dessous de 860 px, la colonne passe sous le contenu : voir le bloc mobile. */
.avec-assistant {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: stretch;
}

.rail-assistant {
  width: 44px; flex: none;
  display: flex; flex-direction: column; align-items: center;
  gap: 10px; padding: var(--e3) 0;
  background: var(--nav-fond);
  border-left: 1px solid var(--bordure);
}
.rail-bouton {
  width: 30px; height: 30px; flex: none; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
  background: var(--surface); color: var(--nav-texte);
  font: inherit; font-size: 13px; line-height: 1; cursor: pointer;
}
.rail-bouton:hover { background: var(--surface-enfoncee); color: var(--texte); }
/* Même doctrine que `.bouton` : indisponible = aplat neutre, pas transparence.
 * Et même correctif D4 : `--texte` et non `--texte-doux`, qui mesurait 3,24:1
 * sur cet aplat. Ce site-ci n'était pas dans les 73 couples mesurés (le rail a
 * été écrit pendant l'audit), mais c'est le même couple, donc le même échec. */
.rail-bouton:disabled, .rail-bouton[aria-disabled="true"] {
  background: var(--bordure-forte); border-color: var(--bordure-forte);
  color: var(--texte); cursor: not-allowed;
}
/* Le pointillé de l'IA jusque dans le rail : le bouton qui OUVRE l'assistant
 * porte déjà la même signature que ce qu'il va produire. Contour et jamais
 * plein, comme tout ce qui vient de la machine. */
.rail-bouton-ia {
  border: 1.5px dashed var(--ia-accent); background: transparent; color: var(--ia-accent);
}
.rail-bouton-ia:hover { background: var(--ia-doux); color: var(--ia-accent); }
/* Étiquette tournée : 44 px de large ne laissent pas la place d'un mot à
 * l'horizontale, et un rail sans mot n'est qu'une colonne d'icônes muettes. */
.rail-etiquette {
  display: inline-block;   /* `writing-mode` n'a aucun effet sur une boîte inline */
  writing-mode: vertical-rl;
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--texte-doux); white-space: nowrap;
}
.rail-etiquette-ia { color: var(--ia-accent); font-weight: 600; }
.rail-separateur { width: 24px; height: 1px; flex: none; background: var(--bordure); border: 0; }

.panneau-assistant {
  width: 396px; flex: none; min-width: 0;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
}
.panneau-assistant-entete {
  display: flex; align-items: center; gap: var(--e2);
  padding: 10px var(--e3); border-bottom: 1px solid var(--bordure);
}
.panneau-assistant-entete .picto {
  width: 24px; height: 24px; flex: none;
  display: grid; place-items: center;
  border: 1.5px dashed var(--ia-accent); border-radius: var(--rayon-petit);
  color: var(--ia-accent); font-size: 12px;
}
.panneau-assistant-entete .titres { min-width: 0; }
.panneau-assistant-entete .titre { font-size: 13.5px; font-weight: 600; }
.panneau-assistant-entete .sous-titre { font-size: 11.5px; color: var(--texte-doux); }
.panneau-assistant-entete .actions { margin-left: auto; display: flex; gap: var(--e1); flex: none; }
/* Les deux boutons d'en-tête (replier, effacer) sont des carrés de 24 px : à
 * côté d'un titre de 13,5 px, un bouton de 32 px pèserait plus que le titre. */
.panneau-assistant-entete .bouton-petit {
  width: 24px; height: 24px; min-height: 24px; padding: 0;
}
.panneau-assistant-corps {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column; gap: 10px;
  padding: var(--e3); overflow-y: auto;
}
.panneau-assistant-pied {
  display: flex; align-items: flex-end; gap: var(--e2);
  padding: 10px var(--e3); border-top: 1px solid var(--bordure);
}
.panneau-assistant-pied textarea, .panneau-assistant-pied input { min-height: 32px; }
.panneau-assistant-pied textarea { resize: vertical; }

/* La demande de l'HUMAIN, alignée à droite et en couleur de marque : dans une
 * colonne où tout le reste est en pointillé violet, ce qui est bleu et à droite
 * est ce que l'avocat a écrit lui-même. C'est la seule chose du panneau qui ne
 * demande aucune relecture. */
.bulle-demande {
  align-self: flex-end; max-width: 88%;
  background: var(--primaire-doux); border: 1px solid var(--primaire);
  border-radius: var(--rayon);
  padding: var(--e2) 10px; font-size: 13px; line-height: 1.45;
}
.bulle-demande .sur-titre {
  display: block; font-size: 11px; font-weight: 600; color: var(--primaire);
}

/* ─────────────────────── proposition de l'IA : le composant central ───── */

/* Tout ce que la machine produit tient dans cette boîte, et cette boîte le dit
 * dans son en-tête : « Proposition — rien n'est fait ». Le libellé est FIXE et
 * il répond à la seule question qui compte devant une proposition — est-ce que
 * ça a déjà été envoyé ? (P3, charte §6.)
 *
 * Contour en POINTILLÉ et jamais plein, comme `.propose-par-ia` : c'est le même
 * marqueur, il n'en existe qu'un. Le pied revient au fond de surface, parce que
 * c'est là que sont les boutons — donc la seule zone de la boîte où l'humain
 * agit, et elle doit se distinguer de ce qu'il relit. */
.proposition-ia {
  border: 1.5px dashed var(--ia-accent); background: var(--ia-doux);
  border-radius: var(--rayon);
}
.proposition-entete {
  padding: 6px 10px; border-bottom: 1px dashed var(--ia-accent);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ia-accent);
}
.proposition-corps {
  display: flex; flex-direction: column; gap: 6px;
  padding: 9px 10px; font-size: 13px; line-height: 1.5;
}
.proposition-pied {
  display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap;
  padding: var(--e2) 10px; border-top: 1px dashed var(--ia-accent);
  background: var(--surface); border-radius: 0 0 var(--rayon) var(--rayon);
}

/* Une valeur proposée ne vient JAMAIS seule : elle vient avec sa source. Sans
 * la source, l'avocat doit rouvrir la pièce pour vérifier, et il ne le fera pas
 * trente fois — donc il validera sans vérifier. La source est le composant. */
.valeur-sourcee {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--surface); border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit); padding: 6px var(--e2);
}
.valeur-etiquette {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--texte-doux);
}
.valeur-contenu { font-size: 12.5px; }
.valeur-source { font-size: 11px; color: var(--ia-accent); }
/* Variante « ce que je ne sais pas ». C'est de l'ALERTE et non du danger : une
 * information manquante n'est pas une erreur, c'est un travail qui reste à
 * faire — et le dire vaut mieux que de combler le trou par une valeur
 * plausible. Une IA qui énumère ses lacunes est une IA qu'on peut relire. */
.valeur-sourcee-alerte { border-color: var(--alerte-fond); }
.valeur-sourcee-alerte .valeur-etiquette,
.valeur-sourcee-alerte .valeur-contenu,
.valeur-sourcee-alerte .valeur-source { color: var(--alerte-texte); }

/* ─────────────────── explication sous une ligne de tableau ────────────── */

/* Un tableau de pièces EST un tableau : on reste en `<table class="tableau">`,
 * et une explication déployée sous une ligne est donc un
 * `<tr class="ligne-explication"><td colspan="…">`. La maquette dessinait ses
 * lignes en `display: grid` : refusé — la sémantique de tableau est ce qui
 * permet à un lecteur d'écran d'annoncer « colonne Échéance, ligne 12 », et le
 * composant existe déjà.
 *
 * La ligne d'explication neutralise l'alternance et le survol du tableau : ce
 * n'est pas une ligne de données, elle ne doit pas se compter ni se survoler
 * comme telle. Le fond est posé sur la CELLULE, qui gagne visuellement sur le
 * fond de la rangée. */
table.tableau tbody tr.ligne-explication:hover { background: transparent; }
table.tableau tbody tr.ligne-explication > td {
  height: auto; padding: 0; box-shadow: none;
}
.explication-corps { display: flex; align-items: center; gap: var(--e3); }
.explication-corps .actions { margin-left: auto; display: flex; gap: var(--e2); flex: none; }

/* Variante DANGER : la pièce n'a pas pu être lue. Elle est sous la ligne et non
 * dans un bandeau de page, parce que la question « laquelle ? » se pose au
 * moment où l'on regarde la liste, pas en haut de l'écran. Deux boutons, pas
 * plus : relancer la lecture, ou ouvrir la pièce telle quelle. */
table.tableau tbody tr.ligne-explication-danger > td {
  background: var(--danger-doux); color: var(--danger-texte);
  font-size: 12px; padding: var(--e2) var(--e3);
  border-bottom: 1px solid var(--bordure);
}

/* Variante NEUTRE : l'historique des versions d'une pièce. Ce n'est pas un
 * état, c'est du détail — donc plus petit, plus discret, et sur le fond de
 * surface pour ne pas se confondre avec la ligne de données au-dessus. */
table.tableau tbody tr.ligne-explication-neutre > td {
  background: var(--surface); color: var(--nav-texte);
  font-size: 11.5px; padding: 5px var(--e3);
  border-bottom: 1px solid var(--bordure);
}

/* ─────────────────────────────── fardes d'un dossier ─────────────────── */

/* Colonne des sous-fardes. 250 px : c'est ce qu'il faut pour « Dossier
 * répressif » suivi d'un compteur à quatre chiffres sans élider le libellé. */
.liste-fardes { width: 250px; flex: none; display: flex; flex-direction: column; gap: 1px; }
.farde {
  display: block; padding: 6px var(--e2);
  border-radius: var(--rayon-petit);
  color: var(--nav-texte); font-size: 13px; text-decoration: none;
}
.farde:hover { background: var(--surface-alt); color: var(--texte); text-decoration: none; }
.farde-ligne { display: flex; align-items: center; gap: var(--e2); min-width: 0; }
.farde-ligne .nom { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.farde .donnees { margin-left: auto; font-size: 11.5px; color: var(--texte-doux); flex: none; }
/* Actif = fond et poids, pas couleur (direction F) : la couleur est rare, et
 * elle est réservée aux états métier. */
.farde[aria-current] { background: var(--surface-enfoncee); color: var(--texte); font-weight: 600; }
.farde-sous-titre { display: block; font-size: 11px; color: var(--texte-doux); font-weight: 400; margin-top: 2px; }
.farde .progression { height: 5px; margin-top: 5px; }
.farde-legende { display: block; font-size: 11px; color: var(--texte-doux); font-weight: 400; margin-top: 2px; }
/* Ce filet est métier, pas décoratif : en pénal, il sépare le DOSSIER RÉPRESSIF
 * — la pièce du parquet, qui n'est pas une farde du cabinet et qu'on ne
 * réorganise pas — des six sous-fardes ordinaires. Les mélanger dans une même
 * liste laisserait croire qu'on peut y déposer une pièce. */
.separateur-fardes { height: 1px; background: var(--bordure); border: 0; margin: 5px var(--e1); }

/* ─────────────────────── dépôt de pièces et visionneuse ──────────────── */

/* Zone de glisser-déposer. Pointillé : le pointillé dit « pas encore rempli »
 * ici comme il dit « pas encore validé » pour l'IA — dans les deux cas, une
 * forme en attente. Deux lignes : ce qu'on peut faire, puis ce qui est accepté. */
.zone-depot {
  border: 1px dashed var(--bordure-forte); background: var(--surface-alt);
  border-radius: var(--rayon); padding: 10px var(--e2);
  text-align: center; font-size: 12px; color: var(--texte-doux);
}
.zone-depot .seconde { display: block; font-size: 11px; margin-top: 2px; }

.visionneuse {
  width: 400px; flex: none; min-width: 0;
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon); box-shadow: var(--ombre-carte);
}
.visionneuse-entete {
  display: flex; align-items: center; gap: var(--e2);
  padding: 6px var(--e2); border-bottom: 1px solid var(--bordure);
}
/* Le nom de fichier s'élide, il ne passe pas à la ligne : l'en-tête de la
 * visionneuse doit garder la même hauteur d'une pièce à l'autre, sinon la page
 * rendue en dessous sautille quand on parcourt la liste. */
.visionneuse-entete .nom {
  flex: 1; min-width: 0; font-size: 11.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.visionneuse-entete .actions { display: flex; gap: var(--e1); flex: none; }
.visionneuse-corps {
  flex: 1; display: grid; place-items: center;
  background: var(--surface-alt); padding: var(--e3);
}
/* La page rendue est une SURFACE posée sur le fond enfoncé du corps : c'est ce
 * qui la fait lire comme du papier, et non comme un cadre de l'application. */
.visionneuse-page {
  width: 100%; height: 284px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--bordure-forte);
  padding: 18px 20px; font-size: 11px; color: var(--texte-doux);
}
.visionneuse-pied {
  display: flex; align-items: center; gap: var(--e2);
  padding: 6px var(--e3); border-top: 1px solid var(--bordure);
  font-size: 11.5px; color: var(--texte-doux);
}

/* ───────────────────────── bandeau d'indexation ──────────────────────── */

/* Le seul bandeau qui tient sur UNE ligne, qui est centré verticalement et qui
 * porte un bouton. Il dit une chose et une seule : le dossier n'est pas encore
 * entièrement lisible, donc ne concluez pas à l'absence d'une pièce. C'est le
 * garde-fou anti-faux-négatif, et il doit tenir sans faire défiler.
 *
 * La barre est à LARGEUR FIXE : une progression qui s'étire au gré du texte ne
 * se compare pas d'un dossier à l'autre, et c'est bien la comparaison qu'on
 * cherche (« ça avance ? »). Le bouton reste en `flex: none` pour ne jamais
 * être comprimé par la phrase. */
.bandeau-indexation { align-items: center; }
.bandeau-indexation .phrase { flex: 1; min-width: 0; }
.bandeau-indexation .progression { width: 180px; flex: none; }
.bandeau-indexation .bouton { flex: none; }
/* Version compacte, pour un bandeau posé DANS une carte plutôt qu'en tête de
 * page : le même message, mais qui ne prend pas la place de deux lignes de
 * tableau. */
.bandeau-serre { padding: 5px 9px; font-size: 11.5px; }

/* ─────────────────────── comparaison et choix expliqué ───────────────── */

/* Le doublon détecté au dépôt. Deux colonnes côte à côte et non un texte qui
 * décrit la différence : la différence se VOIT, elle ne se raconte pas. La
 * colonne de gauche est ce qui est déjà au dossier, celle de droite ce qu'on
 * dépose — et c'est celle-là qui porte l'alerte, parce que c'est celle-là dont
 * le sort est en jeu. */
.comparaison { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.comparaison-colonne {
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon); overflow: hidden;
}
.comparaison-entete {
  padding: 5px 9px; background: var(--surface-alt);
  border-bottom: 1px solid var(--bordure);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--texte-doux);
}
.comparaison-corps {
  display: flex; flex-direction: column; gap: 3px;
  padding: var(--e2) 9px; font-size: 12px;
}
.comparaison-colonne-depot { border-color: var(--alerte-fond); }
.comparaison-colonne-depot .comparaison-entete {
  background: var(--alerte-doux); color: var(--alerte-texte); border-bottom-color: var(--alerte-fond);
}
.comparaison-colonne-depot .comparaison-corps {
  background: var(--alerte-doux); color: var(--alerte-texte);
}

/* 🔴 Le meilleur motif de la livraison, et il s'emploie POUR TOUTE DÉCISION
 * IRRÉVERSIBLE : un bouton, et à côté de lui la phrase de sa conséquence. Pas
 * une infobulle, pas un dialogue de confirmation après le clic — la phrase est
 * là AVANT, à côté du bouton, au moment où la main hésite. « Remplacer » et
 * « Garder les deux » ne se distinguent pas par leur libellé ; ils se
 * distinguent par ce qui arrive ensuite, donc c'est ça qu'on écrit.
 *
 * Empilable : `.choix-explique-pile` met 6 px entre deux choix — assez pour
 * qu'ils ne se confondent pas, assez peu pour qu'on les lise comme une même
 * question. */
.choix-explique {
  display: flex; align-items: flex-start; gap: 10px;
  border: 1px solid var(--bordure-forte); border-radius: var(--rayon);
  padding: var(--e2) 10px;
}
.choix-explique > .bouton { flex: none; }
.choix-explique .explication { font-size: 12px; line-height: 1.45; color: var(--nav-texte); }
.choix-explique-pile { display: flex; flex-direction: column; gap: 6px; }

/* Un GROUPE DE CHOIX EXCLUSIFS, avec sa question et son explication. Posé pour
 * D-051 : « le secrétariat garde-t-il accès à ce dossier ? ». Ce sont les
 * premiers boutons radio du programme, et le patron est celui de
 * `.choix-explique` — l'explication CONTRE le choix, avant le clic — appliqué à
 * un ensemble dont on doit prendre exactement une branche.
 *
 * 🔴 Aucune réponse n'est pré-cochée, et c'est une règle de fond et non de
 * style : une case cochée d'avance décide à la place de l'associé. Le CSS ne
 * doit donc RIEN faire qui suggère une réponse — pas de branche mise en avant,
 * pas de couleur d'accent sur l'une des deux. Les deux se ressemblent
 * exactement, et c'est voulu. */
.groupe-choix {
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  padding: var(--e2) var(--e3) var(--e3);
  margin: 0;
  display: flex; flex-direction: column; gap: var(--e2);
}
.groupe-choix > legend {
  padding: 0 var(--e1);
  font-family: var(--police-titre);
  font-size: 14px; font-weight: 500;
}
.groupe-choix > .motivation { margin: 0; }

/* Une branche. `align-items: flex-start` pour que le bouton reste sur la
 * première ligne quand l'explication en fait trois — sinon il se centre
 * verticalement et on ne sait plus à quoi il se rapporte.
 *
 * Cible tactile : le libellé ENTIER est cliquable (c'est un `<label>`), donc la
 * surface fait la hauteur du texte et non celle du seul bouton de 13 px. */
.choix-exclusif {
  display: flex; align-items: flex-start; gap: 10px;
  padding: var(--e2) 10px;
  border: 1px solid var(--bordure); border-radius: var(--rayon-petit);
  cursor: pointer;
  font-size: 13px; line-height: 1.45;
}
.choix-exclusif:hover { background: var(--surface-alt); }
.choix-exclusif > input { flex: none; margin-top: 2px; }
/* Le contour de focus doit se voir sur la LIGNE et pas seulement sur le petit
 * rond : au clavier, c'est la ligne qu'on parcourt. */
.choix-exclusif:focus-within {
  outline: 2px solid var(--lien); outline-offset: 1px;
}

/* ──────────────────── pièces d'une fiche : le texte du dossier ────────── */

/* Liste clé/valeur d'une fiche. Colonne d'étiquettes à largeur FIXE : c'est ce
 * qui permet de balayer les valeurs à la verticale sans relire les étiquettes.
 * `align-items: baseline` pour qu'une valeur de deux lignes n'aille pas décaler
 * son étiquette vers le milieu. */
.definitions {
  display: grid; grid-template-columns: 104px 1fr; gap: 5px 10px;
  align-items: baseline; margin: 0;
}
.definitions dt { font-size: 12.5px; color: var(--texte-doux); }
.definitions dd { margin: 0; font-size: 13px; }

/* 🔴 Citation d'une MOTIVATION — typiquement celle d'une levée de conflit
 * d'intérêts. Ni italique ni serif : ce n'est PAS `.exergue`. `.exergue` sert à
 * mettre en valeur une phrase, ce qui suppose qu'on la lit une fois ; ceci est
 * une pièce déontologique, elle se relit, elle se cite, et elle doit se lire
 * comme du texte de dossier. Le filet à gauche dit « rapporté », rien de plus. */
.motivation {
  border-left: 3px solid var(--bordure-forte); padding-left: 10px;
  font-size: 12.5px; line-height: 1.55; color: var(--texte);
}

/* Dire une règle sans crier. Un bandeau d'alerte pour rappeler qu'un délai de
 * recours court à partir de la signification, c'est un bandeau qu'on finira par
 * ne plus voir — les alertes s'épuisent. La note en marge, elle, reste lisible
 * indéfiniment parce qu'elle ne réclame rien. */
.note-marge {
  border-left: 2px solid var(--bordure); padding-left: var(--e2);
  font-size: 11.5px; line-height: 1.45; color: var(--texte-doux);
}
/* Une note peut porter plusieurs paragraphes (c'est le cas dans la colonne de
 * marge). Le dernier ne traîne pas une gouttière derrière lui. */
.note-marge > :last-child { margin-bottom: 0; }

/* ─────────────────────────── la colonne « En marge » (D-047, étape 2) ──── */

/* 🔴 CE QUI EXPLIQUE NE S'ENTASSE PAS DANS LE CORPS, IL SE TIENT DANS LA MARGE.
 *
 * D-047 a posé la direction « Le Registre » et l'a laissée inachevée sur ce
 * point précis : « la colonne de marge n'est posée que dans la maquette ». La
 * voici. C'est le geste d'un avocat qui annote un acte au crayon à côté du
 * texte — jamais dans le texte, jamais dans une infobulle (charte §7 : « aucune
 * information vitale derrière un survol »).
 *
 * Ce qu'elle répare, recensé au navigateur le 26/08 sur les 33 écrans rendus par
 * `outils/exporter_ecrans.py`, à 1 440 px de fenêtre : **vingt-trois blocs
 * d'explication sur dix-sept écrans** (variantes d'URL non comptées) se
 * rendaient sur toute la largeur du contenu, de 720 à 1 184 px, à 11,5 px de
 * corps — plus vingt-trois dans le seul catalogue de composants. Une ligne de
 * 1 184 px à cette taille ne se lit pas, elle se saute ; et l'explication finit
 * sous le contenu qu'elle devait éclairer. Dans la marge, la même note tient 30
 * à 33 signes par ligne.
 *
 * Cinq de ces vingt-trois blocs sont passés dans une marge (trois écrans de
 * l'agenda) ; dix-huit attendent, et c'est délibéré — la marge ne s'applique
 * qu'aux écrans où ce qui explique pèse plus que ce qu'il explique.
 *
 * FLEX ET NON GRID, et ce n'est pas un détail. Une grille à deux colonnes
 * réserve la seconde piste même quand le gabarit n'émet aucune marge — et son
 * `gap` avec. En flex, un enfant absent ne coûte rien : ni piste, ni gouttière.
 * Le `:has()` aurait fait la même chose, mais la grille de la coquille s'en
 * passe exprès (« pour que la grille soit juste même là où le navigateur ne le
 * connaît pas »), et on ne s'autorise pas ici ce qu'on refuse là.
 *
 * Le gabarit ne décrit que la STRUCTURE — un corps, une marge — jamais
 * l'apparence : c'est la même propriété qui a permis à `.carte` de cesser
 * d'être une carte sans qu'aucun des 22 gabarits change d'une ligne. */
.avec-marge {
  display: flex; align-items: flex-start; gap: var(--e8);
}
.avec-marge > .colonne-registre { flex: 1 1 auto; min-width: 0; }

/* `flex-basis` et non `width` : au repli (voir le `@media` plus bas), la boîte
 * repasse en flux de bloc et la base flexible est alors ignorée d'elle-même.
 * Une largeur, elle, aurait survécu au repli et laissé une colonne de 236 px
 * perdue au milieu de la page. */
.avec-marge > .en-marge { flex: 0 0 var(--largeur-marge); min-width: 0; }

/* Le filet de la marge : un trait simple, celui qu'on trace au crayon le long
 * d'un acte. Il ne peut pas être le filet DOUBLE du fronton — deux reliures qui
 * se croisent ne se lisent plus, c'est la raison écrite pour la carte
 * imbriquée. */
.en-marge {
  padding-left: var(--e6);
  border-left: 1px solid var(--bordure);
  display: flex; flex-direction: column; gap: var(--e4);
}

/* « En marge » est une ÉTIQUETTE de colonne, pas un titre — même doctrine que
 * `h3`, et pour la même raison : un serif en capitales ne se lit pas à cette
 * taille. La balise reste un titre pour que la colonne se retrouve à la voix et
 * dans la navigation par titres.
 *
 * ⚠️ `font-family` est déclarée EXPLICITEMENT. Sans elle, `h1, h2` remettrait
 * Playfair par-dessus : c'est exactement le défaut de `.titre-carte`, qui a
 * rendu deux titres en Inter pendant une journée parce qu'une classe « garde le
 * rendu » ne garde rien de ce qu'elle ne déclare pas. */
.en-marge-titre {
  margin: 0;
  font-family: var(--police-ui);
  font-size: 11px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--texte-doux);
}

/* Dans la marge, la note n'est plus une annotation glissée dans le corps : elle
 * EST le texte de sa colonne. Elle monte donc de 11,5 à 12 px et passe de
 * `--texte-doux` à `--texte` — mesuré : 14,50:1 sur le fond de page, contre
 * 4,64:1. Ce n'est pas du zèle : ces notes portent des règles de procédure. */
.en-marge .note-marge {
  font-size: 12px; line-height: 1.5; color: var(--texte);
  padding-left: var(--e3);
}

/* L'objet de la note — la ligne qui dit de quoi elle parle. Icône + mot, jamais
 * l'icône seule (`icone()` pose `aria-hidden` de toute façon). */
.note-marge-objet {
  display: flex; align-items: center; gap: var(--e1);
  margin: 0 0 var(--e1);
  font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--texte-doux);
}
.note-marge-objet .icone { flex: none; }

/* 🔴 UNE NOTE QUI PORTE UN ÉTAT PREND LA COULEUR DE **TEXTE** DE CET ÉTAT, ET
 * PAS SA COULEUR DE REMPLISSAGE — c'est le seul endroit du programme où le
 * filet latéral s'écarte de ce que font `.carte-danger` et consorts, et la
 * raison est une mesure. Un filet de carte fait 3 px sur 200 px de haut ; un
 * filet de note en fait 2 sur 60. À cette taille, `--alerte-fond` ne se voit
 * pas : 1,54:1 sur le fond de page, contre 5,30:1 pour `--alerte-texte`. Le
 * filet et l'objet prennent donc le même jeton, et la note se lit comme un
 * bloc. La palette d'état n'est pas touchée — on choisit l'autre moitié de la
 * même paire (charte §2.2), celle qui a été calculée pour être lisible.
 *
 * Il n'y a délibérément PAS de variante « succès ». Mesuré : `--succes-texte`
 * tombe à 4,44:1 sur le fond de page, sous le seuil AA de 4,5 pour du texte de
 * 12 px. Une bonne nouvelle n'a pas besoin d'un filet vert ; un délai raté,
 * lui, a besoin d'un rouge qui se lit.
 *
 * Et la règle de la charte §4 tient entière : la couleur ne dit jamais rien
 * toute seule. L'objet de la note porte son icône ET ses mots. */
.note-marge-danger { border-left-color: var(--danger-texte); }
.note-marge-danger .note-marge-objet { color: var(--danger-texte); }
.note-marge-alerte { border-left-color: var(--alerte-texte); }
.note-marge-alerte .note-marge-objet { color: var(--alerte-texte); }
.note-marge-info { border-left-color: var(--info-texte); }
.note-marge-info .note-marge-objet { color: var(--info-texte); }

/* Le pied de la marge : une phrase en exergue, séparée par un filet. Playfair
 * italique, une seule fois par écran — c'est la police d'exergue de la charte
 * (§2.3) et elle ne sert qu'à ça. */
.en-marge-exergue {
  margin: 0; padding-top: var(--e3);
  border-top: 1px solid var(--bordure);
  font-size: 14px; line-height: 1.45; color: var(--texte-doux);
}

/* 🔴 LE REPLI — et le seuil est MESURÉ, pas estimé.
 *
 * La consigne est claire : sous une certaine largeur la marge revient SOUS le
 * contenu ; elle ne disparaît pas (ce qui perdrait des règles de procédure) et
 * elle ne comprime pas le corps (ce qui ferait défiler un tableau de contrôle
 * à l'horizontale pour loger une note).
 *
 * Le seuil se calcule à partir de la seule chose qui ne se comprime pas : le
 * `min-content` du plus large tableau des écrans qui portent une marge. Mesuré
 * au navigateur sur les écrans rendus par `outils/exporter_ecrans.py` :
 * **776 px** pour la table de contrôle de `/agenda/calendrier` (contre 688 pour
 * les règles inactives et 667 pour les propositions du cabinet, tous deux
 * relevés sur une reproduction fidèle — le cabinet de démonstration n'a ni
 * règle ni proposition, donc ces deux tableaux ne se rendent jamais à l'export).
 *
 *   largeur de fenêtre = 776 (le tableau)
 *                      + 236 (la marge) + 32 (la gouttière)
 *                      + 208 (la navigation) + 48 (le remplissage du contenu)
 *                      = 1 300 px
 *
 * Vérifié dans les deux sens : à 1 301 px la marge est à côté et le corps
 * mesure 777 px — un pixel de plus que le tableau, qui ne défile donc pas ; à
 * 1 300 px elle passe dessous. Ce n'est pas un nombre rond, et c'est voulu :
 * un nombre rond aurait été une estimation.
 *
 * Le repli se fait en `display: block` : la boîte repasse en flux, la base
 * flexible de la marge est ignorée d'elle-même, et rien ne reste à défaire.
 * La marge y prend le filet DOUBLE du registre — à l'horizontale, un filet
 * simple ne se distinguerait pas d'une bordure de tableau — et ses notes se
 * rangent en colonnes de 240 px au lieu de courir sur toute la largeur : le
 * défaut qu'on répare ne doit pas revenir par la porte du repli. */
@media (max-width: 1300px) {
  .avec-marge { display: block; }
  .en-marge {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--e4) var(--e8); align-items: start;
    margin-top: var(--e6);
    padding-left: 0; padding-top: var(--e3);
    border-left: 0;
    border-top: 2px solid var(--texte);
    box-shadow: 0 3px 0 -2px var(--texte);
  }
  .en-marge-titre { grid-column: 1 / -1; }
  .en-marge-exergue { grid-column: 1 / -1; }
}

/* Numéro d'étape d'une marche à suivre. Carré et non rond : un rond de 20 px
 * ressemble à une pastille d'état, et une pastille porte de l'information
 * juridique — on ne prête pas cette forme à un numéro d'ordre. */
.jeton-etape {
  display: inline-grid; place-items: center; flex: none;
  width: 20px; height: 20px; border-radius: var(--rayon-petit);
  background: var(--surface-enfoncee);
  font-family: var(--police-donnees); font-variant-numeric: tabular-nums;
  font-size: 11px; font-weight: 600; color: var(--texte-doux);
}
/* L'étape qui a échoué, ou l'étape irréversible : le seul cas où un numéro
 * d'ordre a le droit d'être rouge. */
.jeton-etape-danger { background: var(--danger-fond); color: var(--surface); }

/* 🔴 DÉFAUT LATENT DE `.rangee`, TROUVÉ EN MESURANT LE 26/08 — et il n'a rien à
 * voir avec la marge, c'est elle qui l'a seulement révélé.
 *
 * `.rangee` est `flex-wrap: wrap`, et un enfant flexible sans `flex` déclaré a
 * pour taille de base son `max-content`. Une marche à suivre s'écrit
 * `<li class="rangee">[numéro]<span>la phrase</span></li>` : dès que la phrase
 * est plus longue que la place restante, ce n'est pas la PHRASE qui se coupe,
 * c'est le `<span>` ENTIER qui passe à la ligne suivante — le numéro reste seul
 * sur la sienne, et il ne désigne plus rien.
 *
 * Mesuré sur `/agenda/regles` : les quatre exigences de la §9.6.3 tenaient sur
 * une ligne à 1 184 px de colonne et se cassaient en deux à 916 px. Le défaut
 * se déclenche donc sur toute fenêtre étroite, marge ou pas — un écran de
 * 1 130 px le produisait déjà.
 *
 * Ciblé sur ce qui SUIT un numéro d'étape, et sur rien d'autre : une rangée de
 * badges est faite de `<span>` qu'il ne faut surtout pas étirer. Et le numéro
 * s'aligne sur la PREMIÈRE ligne du texte, pas sur son milieu. */
.rangee > .jeton-etape { align-self: flex-start; }
.rangee > .jeton-etape + * { flex: 1 1 0; min-width: 0; }

/* Une ancienne référence (celle de Forlex, celle d'avant une renumérotation).
 * Encadrée et douce : elle doit rester CHERCHABLE — on la reçoit encore par
 * courrier — sans venir concurrencer la référence en vigueur. */
.ref-historique {
  display: inline-block;
  font-family: var(--police-donnees); font-variant-numeric: tabular-nums;
  padding: 1px 6px; border: 1px solid var(--bordure); border-radius: var(--rayon-petit);
  background: var(--surface-alt); font-size: 12.5px; color: var(--texte-doux);
}

/* ─────────────────────────────── rangée de filtres ───────────────────── */

/* Filtres en pilules. Inactif = `.badge-neutre` allégé à 500 : un filtre
 * inactif est une possibilité, pas une information, donc il ne pèse pas.
 * Actif = `.badge-plein`, seul emploi de l'aplat de marque — voir son
 * commentaire : savoir sur quoi la liste est filtrée n'est pas un agrément,
 * c'est ce qui évite de lire un extrait en croyant lire le tout.
 *
 * D11 — WCAG 2.2 AA, 2.5.8 (taille de cible) : la pilule mesurait 20,7 px de
 * haut avec 4 px d'écart, donc l'exception d'espacement ne jouait pas. Ces
 * pilules commandent ce que la liste CONTIENT (horizon des délais, état de
 * facturation, axe de regroupement, vue de l'agenda, familles du fil) — on ne
 * se permet pas de les rater d'un pixel. Ce n'est que du remplissage vertical
 * et de l'écart : aucune taille de texte ne change, la hauteur passe à 26 px. */
.filtres { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.filtre {
  font-family: inherit; font-weight: 500; cursor: pointer;
  min-height: 26px; padding-top: 3px; padding-bottom: 3px;
}
.filtre.badge-plein { font-weight: 600; }
.filtre:disabled, .filtre[aria-disabled="true"] { cursor: not-allowed; }
/* Une pilule de filtre peut être un LIEN et non un bouton : le fil d'un dossier
 * se navigue par l'URL, pour qu'un fil filtré s'envoie par lien, se mette en
 * favori et se colle dans un ticket. Elle ne se souligne pas pour autant — un
 * badge souligné se lit comme du texte cassé, et c'est la seule différence que
 * le lien introduisait. Même correctif que `.onglet:hover`. */
a.filtre:hover { text-decoration: none; }

/* ─────────────────────────── mobile : mêmes jetons, cibles de 44 px ──── */

@media (max-width: 860px) {
  .coquille { grid-template-columns: 1fr; grid-template-areas: "entete" "nav" "contenu"; }
  .coquille.avec-bandeau-essai {
    grid-template-rows: auto auto auto 1fr;
    grid-template-areas: "essai" "entete" "nav" "contenu";
  }
  .coquille { background: var(--page-fond); }
  .nav-laterale {
    position: static; height: auto; flex-direction: row; overflow-x: auto;
    border-right: 0; border-bottom: 1px solid var(--nav-bordure);
  }
  .nav-laterale .nav-titre { display: none; }
  .nav-lien { min-height: 44px; }
  .bouton { min-height: 44px; }
  table.tableau td { height: 44px; }
  .contenu { padding: var(--e4); }

  /* L'assistant passe SOUS le contenu : il pousse toujours, il ne flotte
   * toujours pas. Un panneau de 396 px sur un écran de 400 px ne laisserait
   * rien du dossier — or c'est le dossier qu'on relit. */
  .avec-assistant { grid-template-columns: minmax(0, 1fr); }
  .rail-assistant {
    width: auto; flex-direction: row; justify-content: flex-start;
    padding: var(--e2) var(--e3);
    border-left: 0; border-top: 1px solid var(--bordure);
  }
  .rail-etiquette { writing-mode: horizontal-tb; }
  .rail-separateur { width: 1px; height: 24px; }
  .panneau-assistant, .visionneuse, .liste-fardes { width: auto; }
  /* Deux colonnes de comparaison de 180 px sont illisibles : on empile, et
   * l'ordre du document (« au dossier » puis « ce que vous déposez ») garde le
   * sens de la comparaison. */
  .comparaison { grid-template-columns: minmax(0, 1fr); }
  .rail-bouton, .filtre { min-height: 44px; }
  .rail-bouton { width: 44px; height: 44px; }
  /* Le fil passe à deux colonnes sur deux rangs — nature/quand, puis
   * quoi/qui : quatre colonnes sur 400 px n'élident pas le QUOI, elles le
   * suppriment. */
  .fil > li { grid-template-columns: minmax(0, 1fr) auto; row-gap: var(--e1); }
}

/* ─────────────────────────── impression : les documents ont leur style ── */

@media print {
  .entete-app, .nav-laterale, .actions { display: none !important; }
  /* Le bandeau d'essai, lui, s'imprime : une liasse sortie pendant un
   * import à blanc ne doit pas pouvoir passer pour un document réel. */
  .bandeau-essai { border: 1px solid #999; }
  .coquille { display: block; }
  body { background: #fff; font-size: 11pt; }
  .carte { box-shadow: none; border-color: #999; break-inside: avoid; }
  table.tableau tbody tr:hover { background: transparent; }
}

/* ── Propositions de la machine (D-057) ───────────────────────────────────
   P3 : une proposition est en CONTOUR, jamais en plein. Le trait est en tirets
   — la même grammaire que `.badge-ia` — pour que « pas encore décidé » se lise
   sans couleur, et donc aussi en noir et blanc et pour qui ne distingue pas le
   violet. Aucun vert, aucun rouge : une proposition n'est ni un succès ni un
   danger, c'est une question. */
.proposition {
  /* `--ia-accent`, et non `--ia` : ce jeton-là n'existe pas. CSS ne l'aurait
     pas signalé — il jette la déclaration ENTIÈRE en silence, donc la bordure
     aurait simplement disparu et la proposition se serait affichée comme une
     ligne ordinaire. C'est `test_jetons_css.py` qui l'a vu. */
  border: 1px dashed var(--ia-accent);
  border-radius: var(--rayon);
  padding: var(--e3) var(--e4);
  background: var(--surface-alt);
}
.proposition + .proposition { margin-top: var(--e3); }
/* Décidée : le trait devient plein et discret. La ligne reste — une proposition
   refusée qui disparaîtrait laisserait croire que la machine n'a rien proposé. */
.proposition-decidee {
  border: 1px solid var(--bordure);
  background: transparent;
}
.proposition .definitions { margin: var(--e2) 0; }
.citation-source {
  margin: var(--e2) 0 0;
  padding-left: var(--e3);
  border-left: 2px solid var(--bordure);
  color: var(--texte-doux);
  font-style: italic;
}
.rangee-propositions {
  margin-top: var(--e3);
  gap: var(--e4);
  align-items: flex-end;
  flex-wrap: wrap;
}
.champ-serre { min-width: 180px; margin: 0; }

/* ── La jauge (macros.html `jauge`, 07/11/2026) : demi-arc de mesure.
   Piste = bordure (discrète), remplissage = ENCRE ; le vert n'arrive que
   quand l'objectif est ATTEINT — la couleur informe, elle ne décore pas. */
.jauge { display: flex; flex-direction: column; align-items: center; min-width: 136px; }
.jauge svg { width: 136px; height: 79px; display: block; }
.jauge-piste { fill: none; stroke: var(--bordure); stroke-width: 8; stroke-linecap: round; }
.jauge-remplie { fill: none; stroke: var(--texte); stroke-width: 8; stroke-linecap: round; }
.jauge-remplie.jauge-atteinte { stroke: var(--succes-texte); }
.jauge-centre { margin-top: -40px; text-align: center; }
.jauge-pct { font-size: 19px; font-weight: 600; line-height: 1.1; }
.jauge-valeur { margin-top: 10px; font-size: 12.5px; color: var(--texte-doux); }
.jauge-libelle { margin-top: 2px; font-size: 12.5px; color: var(--texte-doux); text-align: center; max-width: 180px; }
.jauge-sous { font-size: 11.5px; color: var(--texte-doux); }

/* ── Le hub Paramètres (07/11/2026) : cartes cliquables et liste à puces. */
.carte-lien { display: block; text-decoration: none; color: inherit; }
.carte-lien:hover, .carte-lien:focus-visible { border-color: var(--bordure-forte); }
.carte-lien .carte-corps { color: var(--texte-doux); font-size: 13.5px; line-height: 1.5; }
.liste-puces { margin: 0; padding-left: 1.2em; display: grid; gap: var(--e2); line-height: 1.5; }

/* ── D-093 : le bouton « β Améliorer » et sa boîte — PROVISOIRES, et ils le
 * disent. L'insigne β est le seul élément du programme qui s'affiche en
 * couleur de marque sur la barre : il doit se voir comme un échafaudage se
 * voit sur une façade — on sait qu'il partira. */
.beta-bouton { width: 100%; text-align: left; border: none; background: none;
               cursor: pointer; font: inherit; }
.beta-insigne { display: inline-block; width: 18px; height: 18px;
                border-radius: 50%; background: var(--lien); color: var(--surface);
                font-family: var(--police-donnees); font-size: 12px;
                line-height: 18px; text-align: center; margin-right: 10px; }
.beta-dialogue { border: 1px solid var(--bordure-forte);
                 border-radius: var(--rayon); padding: 0; max-width: 460px;
                 width: calc(100vw - 32px); background: var(--surface); }
.beta-dialogue::backdrop { background: var(--voile); }
.beta-forme { display: flex; flex-direction: column; gap: var(--e2);
              padding: var(--e4); margin: 0; }
.beta-forme h2 { margin: 0; font-size: 17px; }
.beta-note { margin: 0; color: var(--texte-doux); font-size: 13px; }
.beta-forme textarea { width: 100%; box-sizing: border-box; resize: vertical;
                       font: inherit; padding: var(--e2);
                       border: 1px solid var(--bordure-forte);
                       border-radius: var(--rayon-petit); }
.beta-rangee { display: flex; align-items: center; gap: var(--e2); }
.beta-etat { font-size: 12.5px; color: var(--texte-doux); }

/* ─────────────────────── ouvrir un dossier — planche lotOD O-1 (β #214) ────
 * Un sommaire d'ancres à gauche (collant), cinq sections numérotées à droite,
 * filet épais sous chaque titre. Les rôles de parties en pastilles-radios :
 * les six tiennent sous les yeux, la liste reste fermée. */
.ouverture { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: var(--e8); align-items: start; }
.ouverture-sommaire { position: sticky; top: var(--e4); display: flex; flex-direction: column; gap: var(--e1);
  background: var(--surface-alt); border: 1px solid var(--bordure); border-radius: 6px; padding: var(--e4) var(--e3); }
.ouverture-sommaire a { display: flex; align-items: center; gap: var(--e2); padding: var(--e2); border-radius: 4px;
  color: var(--texte); text-decoration: none; font-size: 13.5px; font-weight: 600; }
.ouverture-sommaire a:hover { background: var(--surface-enfoncee); }
.ouverture-pastille { width: 22px; height: 22px; flex: none; border-radius: 50%; border: 1.5px solid var(--bordure-forte);
  display: grid; place-items: center; font-family: "Noto Sans Mono", monospace; font-size: 11px; font-weight: 700; }
.ouverture-corps { display: flex; flex-direction: column; gap: var(--e6); max-width: 900px; min-width: 0; }
.ouverture-corps h1 { margin: 0; }
.ouverture-section { display: flex; flex-direction: column; gap: var(--e3); scroll-margin-top: var(--e4); }
.ouverture-section > h2 { display: flex; align-items: baseline; gap: var(--e2); flex-wrap: wrap; margin: 0;
  padding-bottom: var(--e2); border-bottom: 1.5px solid var(--texte); font-size: 16px; }
.ouverture-numero { font-family: "Noto Sans Mono", monospace; font-size: 12px; font-weight: 700; color: var(--primaire); }
.ouverture-ligne { display: grid; gap: var(--e3); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ouverture-ligne-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ouverture-ligne-ref { grid-template-columns: 220px minmax(0, 1fr); }
.ouverture-reference { font-size: 18px; font-weight: 700; letter-spacing: .02em; }
.ouverture-partie { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: var(--e3);
  padding: var(--e3) 0; border-bottom: 1px solid var(--bordure); }
.ouverture-partie-n { padding-top: 8px; color: var(--texte-doux); }
.ouverture-roles { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e1); border: 0; padding: 0; margin: 0; }
.ouverture-qualite { width: auto; margin-left: auto; font-size: 13px; }
.pastille-choix { position: relative; display: inline-flex; }
.pastille-choix input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.pastille-choix span { display: inline-grid; place-items: center; height: 28px; padding: 0 10px; border: 1px solid var(--bordure-forte);
  border-radius: 14px; font-size: 12.5px; color: var(--texte); background: var(--surface); cursor: pointer; }
.pastille-choix input:checked + span { background: var(--texte); color: var(--texte-inverse); border-color: var(--texte); font-weight: 600; }
.pastille-choix input:focus-visible + span { outline: 2px solid var(--primaire); outline-offset: 2px; }
.ouverture-envoi { flex-direction: row; justify-content: flex-end; gap: var(--e2); padding-top: var(--e3); border-top: 1px solid var(--bordure); }
@media (max-width: 860px) {
  .ouverture { grid-template-columns: minmax(0, 1fr); }
  .ouverture-sommaire { position: static; flex-direction: row; flex-wrap: wrap; }
  .ouverture-sommaire .nav-titre { flex-basis: 100%; }
  .ouverture-ligne, .ouverture-ligne-4, .ouverture-ligne-ref { grid-template-columns: minmax(0, 1fr); }
  .ouverture-qualite { margin-left: 0; }
}
