/* =========================================================================
   LA COUCHE MAQUETTE GLOBALE — v2 (9 aout 2026, apres l'audit des 3 agents)
   LOCALISATION : front/static/front/css/maquette.css
   Chargee EN DERNIER (base.html). SOURCE UNIQUE des tokens (le :root de
   _style_maquette.html a ete retire — audit statique I1). Elle REMAPPE
   les ~50 tokens de hypostasia.css vers la palette papier (I2) au lieu
   d'empiler des selecteurs, repare le chrome (menus deroulants, focus
   barre sombre), re-teinte les fonds semantiques DANS leur teinte (I3),
   habille l'accueil, la lecture (Georgia — decision D1 : Lora quitte le
   corps de texte, la maquette fait foi), et redonne leurs couleurs aux
   surlignages d'extraction (statuts riches restes en base).
   / v2 after the three audit reports: token source of truth + remap,
   chrome fixes, semantic tints, homepage, reading body, extraction marks.
   ========================================================================= */

:root {
  /* --- Les tokens de l'etalon (source unique). --- */
  --papier:#fdfcfa; --papier-creux:#f5f3ef; --papier-panneau:#faf8f4;
  --encre:#1c1b19; --encre-douce:#6b6862; --filet:#e4e0d8;
  --barre:#1c1b19; --barre-texte:#fdfcfa;
  --statut-nouveau:#999999; --statut-commente:#E69F00;
  --revelation:#f0ede6;
  --cible:#7C5CBF; --danger:#b91c1c; --succes:#047857; --info:#4338ca;
  --verifie:#047857; --faible:#b45309; --non-source:#b91c1c;
  --source-debat:#0e7490; --preuve:26rem;
  /* LA GEOMETRIE DE L'ETALON (§ 1 « JETONS » de maquette.html). Elle
     manquait : seul `--gouttiere` existait, et encore, en scope local sur
     #readability-content. Sans `--panneau` sur :root, la largeur de la
     colonne de droite etait ecrite DEUX FOIS — `min(36rem, 100vw)` dans
     le style inline du gabarit, et la meme expression recopiee dans la
     compensation de la zone de lecture. Les deux copies etaient d'accord
     (576px, et 576 + 2rem de respiration), mais toute correction de
     l'une devait etre reportee a la main sur l'autre. Le token supprime
     la copie, il ne repare pas une divergence.
     / The etalon's geometry tokens. The panel width was written twice —
     inline, and again inside the compensation expression. The copies
     agreed; the token removes the duplication, not a drift. */
  /* UNE SEULE LARGEUR POUR LES DEUX PANNEAUX. Celui des analyses
     mesurait 23rem quand celui des preuves en fait 26 : deux panneaux
     qui vivent au meme endroit, se basculent du meme bouton et se
     lisent l'un apres l'autre n'ont aucune raison d'avoir deux
     largeurs — le bord de la zone de lecture sautait de 48px en
     passant d'une note a un article.
     / One width for both panels: they share a place and a toggle, so
     the reading zone's edge must not jump by 48px between them. */
  --panneau: var(--preuve);
  /* La largeur de la COLONNE DE TEXTE, hors gouttiere (etalon § 1).
     Elle vit ici et non dans `.lecture-zone-conteneur` parce que la
     boite de lecture se calcule desormais a partir d'elle.
     / The text column width, gutter excluded. */
  --largeur-lecture: 40rem;
  /* LA BARRE D'OUTILS FAIT 44px, comme l'etalon (§ 1). Elle en faisait
     48 (`h-12`), et le token suivait le produit pour que le haut du
     panneau se pose pile dessous. Les deux valeurs etaient donc
     coherentes entre elles, mais toutes deux a 4px de l'etalon — et ces
     4px se cumulaient avec les 2px de la bande du fil ci-dessous : le
     texte commencait 6px plus bas que dans la maquette. Le gabarit
     porte desormais `h-11`, et ce token le suit.
     / The toolbar is 44px, as in the etalon: it was 48 (h-12), and with
     the strip's 2px the text started 6px lower than the mock. */
  --barre-outils: 44px;
  /* LA BANDE DU FIL D'ARIANE (etalon § 1). Elle n'etait DECLAREE nulle
     part : chaque usage tombait sur son repli `var(--barre-fil, 2rem)`,
     soit 32px pour 30 attendus. Un repli qui sert de valeur reelle est
     une valeur par accident — nommee ici, elle cesse d'en etre une.
     / It was never declared: every use fell back to 2rem (32px) where
     the etalon asks 30. A fallback used as the value is an accident. */
  --barre-fil: 30px;
  /* LA HAUTEUR DE LA BARRE DU LECTEUR AUDIO (etalon § 19).
     Elle vaut 0 par defaut, et 62px quand `.lecteur` est presente : la
     barre est `fixed`, donc hors du flux, et sans cette reserve elle
     recouvrirait les derniers tours de parole — ceux-la memes qu'on
     lit quand on ecoute la fin de l'enregistrement.
     / 0 by default, 62px when the player is present: the bar is fixed,
     so without this reserve it would cover the last speech turns. */
  --barre-audio: 0px;
  /* AJOUT au vocabulaire de l'etalon (ecart assume, verification T5) :
     l'etalon dessine ses champs et ses boutons avec --filet, qui tombe
     a 1,28:1 sur le papier — or WCAG 1.4.11 demande 3:1 pour le
     contour d'un controle quand c'est son seul indice visuel. Ce filet
     RENFORCE est reserve aux controles : 3,27:1, sans alourdir les
     separateurs decoratifs qui, eux, restent en --filet.
     / Control borders need 3:1; the etalon's --filet gives 1.28:1. */
  --filet-controle: color-mix(in srgb, var(--encre-douce) 70%, var(--filet));

  /* LES HUIT TEINTES DE COTE (lot « liste des bases », 12 aout 2026).
     Une base de connaissances n'a le plus souvent PAS d'image de
     couverture : le champ vient d'etre ajoute et il est vide partout.
     Sa carte porte donc, en haut, un filet teinte derive de son slug —
     la bande de couleur d'une boite d'archives, qui distingue les
     cartes d'un coup d'oeil sans mentir sur une image absente.
     La teinte ne porte JAMAIS seule une information : le nom de la
     base et sa cote sont ecrits juste en dessous. Elle reste malgre
     tout au-dessus de 3:1 (WCAG 1.4.11), parce qu'un filet qu'on ne
     distingue pas du papier ne distingue rien.
     Depart : la palette Wong (colorblind-safe) deja employee par les
     categories du corpus, ASSOMBRIE pour le papier clair — les valeurs
     Wong pures tombent a 1,1:1 (le jaune) et 2,1:1 (le bleu clair).
     Ratios mesures sur --papier / --papier-creux :
       1 bleu    4,68  2 orange  4,32  3 vert   4,88  4 rose   4,81
       5 ambre   5,19  6 cyan    5,55  7 prune  6,19  8 brique 6,18
     / The eight call-number tints. A base rarely has a cover image, so
     its card carries a slug-derived tinted rule instead — the coloured
     band of an archive box. Wong palette, darkened for light paper. */
  --cote-1:#0072B2; --cote-2:#A85400; --cote-3:#00795A; --cote-4:#A24E80;
  --cote-5:#846000; --cote-6:#186A80; --cote-7:#6B48A0; --cote-8:#A3341F;

  /* --- REMAPPAGE des tokens de hypostasia.css (audit I2) : toutes les
     surfaces/bordures/textes composant l'ancien chrome suivent la
     palette papier d'un coup. --- */
  --surface-primary: var(--papier);
  --surface-secondary: var(--papier-creux);
  --surface-tertiary: var(--papier-creux);
  --text-primary: var(--encre);
  --text-secondary: var(--encre-douce);
  --text-tertiary: var(--encre-douce);
  --border-default: var(--filet);
  --border-strong: var(--filet);
  /* Le halo d'ancre (flash au clic depuis le drawer) passe du bleu
     Tailwind a l'ambre de l'etalon (audit I8). Format RGB attendu. */
  --ancre-color: 230, 159, 0;
}

/* =========================================================================
   LOT T7 — LE THEME SOMBRE
   Mecanisme a TROIS etats, celui de l'etalon (corpus.html:71-82) :
     :root                              -> clair, valeurs par defaut
     @media (prefers-color-scheme:dark) -> suit le systeme, SAUF si
       l'utilisateur a explicitement demande le clair
     :root[data-theme="dark"]           -> sombre force
   L'etalon bascule les 8 tokens de surface et LAISSE ses accents tels
   quels ; verification faite, ses accents sont alors ILLISIBLES :
   --info tombe a 2,26:1 sur le papier sombre, --cible a 3,51:1,
   --faible a 3,50:1. La doctrine du chantier est de faire mieux que
   l'etalon en accessibilite : les accents basculent donc aussi, vers
   les memes teintes eclaircies (ratios recalcules en commentaire).
   / Three-state theme. The etalon keeps its accents in dark mode and
   they fall under 3:1; here they lighten too.
   ========================================================================= */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Les 8 surfaces de l'etalon, a l'identique. */
    --papier:#16151a; --papier-creux:#1e1d23; --papier-panneau:#1a191f;
    --encre:#eceae4; --encre-douce:#9b968d; --filet:#33313a;
    --revelation:#26242c; --barre:#0d0c10;
    /* Les accents, eclaircis (l'etalon ne le fait pas). */
    --info:#a5b4fc;        /*  8,9:1 */
    --danger:#fca5a5;      /*  9,4:1 */
    --succes:#6ee7b7;      /* 11,7:1 */
    --cible:#c4b5fd;       /* 10,0:1 */
    --faible:#fbbf24;      /* 10,6:1 */
    --verifie:#6ee7b7; --non-source:#fca5a5; --source-debat:#67e8f9;
    --statut-nouveau:#8a8a8a;
    /* Le contour des controles : sur fond sombre, c'est l'inverse — il
       faut ECLAIRCIR le filet pour tenir les 3:1. */
    --filet-controle: color-mix(in srgb, var(--encre-douce) 75%, var(--filet));
    /* Les huit teintes de cote, ECLAIRCIES : les valeurs claires sont
       assombries POUR le papier clair, elles tombent donc sous 3:1 sur
       le papier sombre (le bleu #0072B2 y fait 2,6:1). Meme doctrine
       que les accents ci-dessus. La 7 rejoint --cible sombre : c'est
       la meme prune, il n'y a pas de raison d'en avoir deux.
       Ratios mesures sur --papier / --papier-creux sombres :
         1  9,54  2  9,16  3 10,30  4  9,54
         5 10,64  6 11,13  7  9,84  8  8,81
       / The eight tints, lightened: the light values are darkened FOR
       light paper and fall under 3:1 on dark paper. */
    --cote-1:#7FC4EE; --cote-2:#F0A87A; --cote-3:#6ED6B0; --cote-4:#E9A9CE;
    --cote-5:#E8C25C; --cote-6:#86D6EE; --cote-7:#C4B5FD; --cote-8:#F2A08C;
    /* Les 8 familles d'hypostases sont employees EN TEXTE : leurs
       valeurs claires tombent toutes sous 3:1 sur fond sombre. */
    --hypostase-epistemique-text:#a5b4fc; --hypostase-empirique-text:#6ee7b7;
    --hypostase-speculatif-text:#fbbf24;  --hypostase-structurel-text:#cbd5e1;
    --hypostase-normatif-text:#d8b4fe;    --hypostase-problematique-text:#fca5a5;
    --hypostase-mode-text:#67e8f9;        --hypostase-objet-text:#cbd5e1;
    --ancre-color: 230, 159, 0;
    /* Les deux tokens de STATUT en texte (hypostasia.css:38-42), cites
       en style= inline par l'accueil : leurs valeurs claires tombent a
       2,31:1 et 3,48:1 sur fond sombre (verification T7).
       / The two textual status tokens do not survive a dark background. */
    --statut-nouveau-text:#c0bcb4; --statut-commente-text:#fbbf24;
    --statut-nouveau-bg:#26242c; --statut-commente-bg:#2a2416;
  }
}
:root[data-theme="dark"] {
  --papier:#16151a; --papier-creux:#1e1d23; --papier-panneau:#1a191f;
  --encre:#eceae4; --encre-douce:#9b968d; --filet:#33313a;
  --revelation:#26242c; --barre:#0d0c10;
  --info:#a5b4fc; --danger:#fca5a5; --succes:#6ee7b7; --cible:#c4b5fd;
  --faible:#fbbf24;
  --verifie:#6ee7b7; --non-source:#fca5a5; --source-debat:#67e8f9;
  --statut-nouveau:#8a8a8a;
  --filet-controle: color-mix(in srgb, var(--encre-douce) 75%, var(--filet));
  --hypostase-epistemique-text:#a5b4fc; --hypostase-empirique-text:#6ee7b7;
  --hypostase-speculatif-text:#fbbf24;  --hypostase-structurel-text:#cbd5e1;
  --hypostase-normatif-text:#d8b4fe;    --hypostase-problematique-text:#fca5a5;
  --hypostase-mode-text:#67e8f9;        --hypostase-objet-text:#cbd5e1;
  --ancre-color: 230, 159, 0;
  --statut-nouveau-text:#c0bcb4; --statut-commente-text:#fbbf24;
  --statut-nouveau-bg:#26242c; --statut-commente-bg:#2a2416;
  /* Les huit teintes de cote, eclaircies (voir le bloc @media ci-dessus
     pour les ratios mesures). / The eight lightened call-number tints. */
  --cote-1:#7FC4EE; --cote-2:#F0A87A; --cote-3:#6ED6B0; --cote-4:#E9A9CE;
  --cote-5:#E8C25C; --cote-6:#86D6EE; --cote-7:#C4B5FD; --cote-8:#F2A08C;
}

/* En sombre, les fonds pleins des anciens boutons (.bg-blue-600 &c.,
   remappes sur --encre) auraient du texte blanc sur une surface
   presque blanche : on les repasse en --info, dont le texte reste
   lisible. / Solid dark-ink buttons would go white-on-white. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bg-blue-600,
  :root:not([data-theme="light"]) .bg-blue-700,
  :root:not([data-theme="light"]) .bg-blue-500,
  :root:not([data-theme="light"]) .bg-indigo-600,
  :root:not([data-theme="light"]) .bg-indigo-700 {
    background-color: var(--info) !important;
    color: var(--papier) !important;
  }
}
:root[data-theme="dark"] .bg-blue-600, :root[data-theme="dark"] .bg-blue-700,
:root[data-theme="dark"] .bg-blue-500, :root[data-theme="dark"] .bg-indigo-600,
:root[data-theme="dark"] .bg-indigo-700 {
  background-color: var(--info) !important;
  color: var(--papier) !important;
}
/* Idem pour le bouton principal du design system, qui est « encre sur
   papier » : en sombre il devient papier-sur-encre, donc invisible. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .zone-corpus .bouton-plat.principal {
    background:var(--info); color:var(--papier); border-color:var(--info);
  }
}
:root[data-theme="dark"] .zone-corpus .bouton-plat.principal {
  background:var(--info); color:var(--papier); border-color:var(--info);
}

/* Les ombres portees sont noires : sur un fond sombre elles ne separent
   plus rien, et le bord d'un tiroir devient imperceptible (verification
   T7). En sombre, c'est le FILET qui porte la separation.
   / Black shadows vanish on dark surfaces; the border takes over. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #drawer-overlay,
  :root:not([data-theme="light"]) #bottom-sheet {
    box-shadow: 0 0 0 1px var(--filet-controle), 0 10px 40px rgba(0, 0, 0, .55) !important;
  }
  :root:not([data-theme="light"]) .surface-formulaire,
  :root:not([data-theme="light"]) .zone-corpus .surface-formulaire {
    box-shadow: none;
  }
}
:root[data-theme="dark"] #drawer-overlay,
:root[data-theme="dark"] #bottom-sheet {
  box-shadow: 0 0 0 1px var(--filet-controle), 0 10px 40px rgba(0, 0, 0, .55) !important;
}
:root[data-theme="dark"] .zone-corpus .surface-formulaire { box-shadow: none; }

/* Le bouton de bascule du theme, dans la barre sombre. */
#bascule-theme {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .7rem; line-height: 1;
  background: transparent; border: 1px solid transparent;
  border-radius: 4px; cursor: pointer;
  /* Cible tactile : 24px de cote au minimum (WCAG 2.5.8). */
  min-width: 1.6rem; min-height: 1.6rem; padding: .3rem .45rem;
  color: color-mix(in srgb, var(--barre-texte) 78%, transparent);
}
#bascule-theme:hover { background: rgba(255, 255, 255, .12); color: var(--barre-texte); }
/* SUR MOBILE AUSSI, ET C'EST UNE DECISION DU MAINTENEUR (20 aout) :
   « le bouton doit etre bien visible, sur desktop comme sur mobile ».
   Il etait `display: none` sous 640px depuis que la bascule avait
   trois etats et que le theme suivait le systeme — on pouvait donc s'en
   passer. Le clair est desormais le defaut TOUJOURS : sans bouton
   visible, un utilisateur de telephone n'a plus aucun moyen de passer
   en sombre.
   Il se resserre au lieu de disparaitre, et c'est le TITRE qui cede —
   il est deja tronque par construction sur cette largeur.
   / Hidden below 640px back when the theme followed the system, so it
   was dispensable. Light is now always the default: without a visible
   toggle, a phone user has no way to reach dark mode at all. */
@media (max-width: 640px) {
  nav.h-12 #bascule-theme { padding: .3rem .3rem; }
  /* IL FALLAIT PRENDRE CES PIXELS QUELQUE PART. La barre porte six
     controles a 390px ; en ajoutant la bascule, le titre du document
     tombait a ZERO — mesure au navigateur, et c'est ce que le test
     `test_navbar_titre_tronque_visible` interdit.
     On les prend sur les DEUX liens de navigation (« Bases »,
     « Carnets »), qui sont deja repris juste dessous par le fil
     d'Ariane et dans le menu utilisateur : ils se resserrent, le titre
     garde un minimum lisible.
     / Six controls at 390px squeezed the title to zero. The two nav
     links are repeated by the breadcrumb right below and by the user
     menu; they give the room back. */
  nav.h-12 [data-testid="lien-nav-bases"],
  nav.h-12 [data-testid="lien-nav-carnets"] { font-size: .75rem; }
  /* LE TITRE DU DOCUMENT SORT DE LA BARRE, et le fil d'Ariane le porte.
     A 390px, la barre tient six controles ; le titre y tombait a 72px
     et n'affichait plus que deux lettres — « Ba » pour « Badgeons la
     Normandie ». Le fil d'Ariane, juste dessous, le donne en entier et
     avec sa hierarchie.
     Deux lettres ne sont pas un titre : c'est de la place prise a des
     controles qui, eux, ne peuvent pas se lire ailleurs.
     / Two letters are not a title: it was space taken from controls
     that cannot be read anywhere else, while the breadcrumb right
     below gives the name in full. */
  /* MAIS SEULEMENT SI LE FIL D'ARIANE EST LA POUR LE PORTER. Une note
     qui n'appartient a aucun carnet n'en a pas : masquer le titre sans
     condition la laisserait SANS AUCUN titre, nulle part — on aurait
     troque un titre illisible contre pas de titre du tout. Le test
     mobile l'a trouve avant l'utilisateur.
     / Only when the breadcrumb is there to carry it: a note in no
     notebook has none, and hiding unconditionally would trade an
     unreadable title for no title at all. */
  body:has(#fil-ariane) nav.h-12 [data-testid="titre-toolbar"] {
    display: none;
  }
}
/* Hors de la barre (pages nues), il vit sur du papier. */
.zone-corpus #bascule-theme { color: var(--encre-douce); border-color: var(--filet-controle); }
/* Au survol le fond passe a --revelation : le filet de controle y
   tombe a 2,87:1, on prend l'encre douce (5,2:1). */
.zone-corpus #bascule-theme:hover {
  background: var(--revelation); color: var(--encre); border-color: var(--encre-douce);
}

/* ---- Le socle : Georgia sur papier, partout. ---- */
body {
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  background: var(--papier);
  color: var(--encre);
}
h1, h2, h3, h4 { letter-spacing: -.012em; }
code, pre, .mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; }
/* Le corps de lecture aussi (agent visuel n°3 — D1 tranchee : la
   maquette fait foi, Lora quitte le corps ; les ilots .typo-* de
   provenance restent). / Reading body in Georgia too. */
.lecture-article, #readability-content {
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", serif !important;
  color: var(--encre) !important;
}

/* ---- Re-teinte des utilitaires Tailwind. ---- */
.bg-white { background-color: var(--papier) !important; }
.bg-slate-50, .bg-slate-100, .bg-gray-50, .bg-gray-100 { background-color: var(--papier-creux) !important; }
.border-slate-100, .border-slate-200, .border-gray-200 { border-color: var(--filet) !important; }
/* -300 habille des CONTROLES (selects, champs) : filet renforce, 3:1
   (verification T7). / -300 dresses controls: reinforced border. */
.border-slate-300, .border-slate-400 { border-color: var(--filet-controle) !important; }
.text-slate-400, .text-slate-500, .text-gray-400, .text-gray-500 { color: var(--encre-douce) !important; }
.text-slate-600, .text-slate-700, .text-slate-800, .text-slate-900 { color: var(--encre) !important; }
.text-blue-600, .text-blue-700, .text-blue-800 { color: var(--info) !important; }
.bg-blue-600, .bg-blue-700, .bg-blue-500, .bg-indigo-600, .bg-indigo-700 { background-color: var(--encre) !important; }
.hover\:bg-blue-600:hover, .hover\:bg-blue-700:hover, .hover\:bg-indigo-700:hover { background-color: var(--encre-douce) !important; }
.hover\:bg-slate-50:hover, .hover\:bg-slate-100:hover, .hover\:bg-gray-50:hover { background-color: var(--revelation) !important; }
.hover\:bg-slate-200:hover { background-color: var(--revelation) !important; }
.hover\:text-blue-600:hover, .hover\:text-blue-800:hover { color: var(--info) !important; }
.text-indigo-600 { color: var(--info) !important; }

/* ---- REMAPPAGE DES PALETTES TAILWIND bleu/indigo (lot T4).
   Meme doctrine que le remappage des tokens de hypostasia.css : on
   change la SOURCE plutot que d'empiler un selecteur par utilitaire.
   Une seule redefinition couvre d'un coup toutes les variantes que la
   liste ci-dessus ne pouvait pas atteindre — hover:, prose-a:,
   prose-blockquote:, border-l-, etc. Les regles :root de tailwind.css
   ne sont pas modifiees : elles sont surchargees ici, feuille chargee
   en dernier. / Remap Tailwind's blue/indigo scales at the source, so
   every variant (hover:, prose-*:, border-*) follows at once. ---- */
:root {
  --color-blue-50:   color-mix(in srgb, var(--info) 8%, var(--papier));
  --color-blue-200:  color-mix(in srgb, var(--info) 35%, var(--filet));
  --color-blue-300:  color-mix(in srgb, var(--info) 45%, var(--filet));
  --color-blue-400:  color-mix(in srgb, var(--info) 60%, var(--filet));
  --color-blue-500:  var(--info);
  --color-blue-600:  var(--info);
  --color-blue-700:  var(--info);
  --color-indigo-50:  color-mix(in srgb, var(--info) 8%, var(--papier));
  --color-indigo-100: color-mix(in srgb, var(--info) 14%, var(--papier));
  --color-indigo-200: color-mix(in srgb, var(--info) 35%, var(--filet));
  --color-indigo-400: var(--info);
  --color-indigo-500: var(--info);
  --color-indigo-600: var(--info);
  --color-indigo-700: var(--info);
}

/* ---- LOT T10 — LE RESTE DES ECHELLES TAILWIND, remappe a la source.
   Les ecrans staff de hypostasis_extractor (configuration LLM, editeur
   d'analyseur, versions, exemples, tests) sont ecrits ENTIEREMENT en
   utilitaires : 300 occurrences sur 18 fichiers. Les enumerer classe
   par classe aurait double la taille de cette feuille sans jamais
   couvrir les variantes (`hover:`, `peer-checked:`, `focus:`,
   `border-l-`). On remappe donc les echelles elles-memes, comme pour
   le bleu et l'indigo.
   Le NIVEAU porte le role : 50-100 = fond, 200-300 = filet, 400+ =
   teinte pleine. Les ambres partent sur --faible et non sur
   --statut-commente : en texte, l'ambre pur ne fait que 2,2:1.
   / The staff screens are pure utility classes; remap the scales
   themselves rather than enumerate. Level carries the role. ---- */
:root {
  /* Gris de structure. */
  --color-slate-50:  var(--papier-creux);  --color-gray-50:  var(--papier-creux);
  --color-slate-100: var(--papier-creux);  --color-gray-100: var(--papier-creux);
  --color-slate-200: var(--filet);         --color-gray-200: var(--filet);
  --color-slate-300: var(--filet-controle);--color-gray-300: var(--filet-controle);
  --color-slate-400: var(--encre-douce);   --color-gray-400: var(--encre-douce);
  --color-slate-500: var(--encre-douce);   --color-gray-500: var(--encre-douce);
  --color-slate-600: var(--encre);         --color-gray-600: var(--encre);
  --color-slate-700: var(--encre);         --color-gray-700: var(--encre);
  --color-slate-800: var(--encre);         --color-gray-800: var(--encre);
  --color-slate-900: var(--encre);         --color-gray-900: var(--encre);

  /* Vert = succes / etat calcule favorable. */
  --color-green-50:  color-mix(in srgb, var(--succes) 8%, var(--papier));
  --color-emerald-50:color-mix(in srgb, var(--succes) 8%, var(--papier));
  --color-green-100: color-mix(in srgb, var(--succes) 14%, var(--papier));
  --color-emerald-100:color-mix(in srgb, var(--succes) 14%, var(--papier));
  --color-green-200: color-mix(in srgb, var(--succes) 35%, var(--filet));
  --color-emerald-200:color-mix(in srgb, var(--succes) 35%, var(--filet));
  --color-green-300: color-mix(in srgb, var(--succes) 45%, var(--filet));
  --color-emerald-300:color-mix(in srgb, var(--succes) 45%, var(--filet));
  --color-green-400: var(--succes);  --color-emerald-400: var(--succes);
  --color-green-500: var(--succes);  --color-emerald-500: var(--succes);
  --color-green-600: var(--succes);  --color-emerald-600: var(--succes);
  --color-green-700: var(--succes);  --color-emerald-700: var(--succes);
  --color-green-800: var(--succes);  --color-emerald-800: var(--succes);

  /* Rouge = danger / etat calcule defavorable. */
  --color-red-50:  color-mix(in srgb, var(--danger) 10%, var(--papier));
  --color-rose-50: color-mix(in srgb, var(--danger) 10%, var(--papier));
  --color-red-100: color-mix(in srgb, var(--danger) 16%, var(--papier));
  --color-red-200: color-mix(in srgb, var(--danger) 40%, var(--filet));
  --color-rose-200:color-mix(in srgb, var(--danger) 40%, var(--filet));
  --color-red-300: color-mix(in srgb, var(--danger) 50%, var(--filet));
  --color-red-400: var(--danger);  --color-red-500: var(--danger);
  --color-red-600: var(--danger);  --color-red-700: var(--danger);
  --color-red-800: var(--danger);  --color-red-900: var(--danger);

  /* Ambres = etat intermediaire. En TEXTE, --faible (4,9:1) et non
     --statut-commente (2,2:1) ; en filet, l'ambre garde sa place. */
  --color-amber-50:  color-mix(in srgb, var(--statut-commente) 12%, var(--papier));
  --color-yellow-50: color-mix(in srgb, var(--statut-commente) 12%, var(--papier));
  --color-orange-50: color-mix(in srgb, var(--statut-commente) 12%, var(--papier));
  --color-amber-100: color-mix(in srgb, var(--statut-commente) 18%, var(--papier));
  --color-amber-200: color-mix(in srgb, var(--statut-commente) 55%, var(--filet));
  --color-yellow-200:color-mix(in srgb, var(--statut-commente) 55%, var(--filet));
  --color-amber-300: color-mix(in srgb, var(--statut-commente) 65%, var(--filet));
  --color-amber-400: var(--faible);  --color-amber-500: var(--faible);
  --color-amber-600: var(--faible);  --color-amber-700: var(--faible);
  --color-amber-800: var(--faible);  --color-orange-500: var(--faible);
  --color-orange-600: var(--faible); --color-yellow-600: var(--faible);
  --color-yellow-700: var(--faible); --color-yellow-800: var(--faible);
  --color-amber-900: var(--faible);  --color-orange-800: var(--faible);

  /* Violet = la « cible » de l'etalon. Il servait en pastille de type
     d'analyseur et etait reste au violet Tailwind brut. */
  --color-violet-50:  color-mix(in srgb, var(--cible) 8%, var(--papier));
  --color-purple-50:  color-mix(in srgb, var(--cible) 8%, var(--papier));
  --color-violet-100: color-mix(in srgb, var(--cible) 14%, var(--papier));
  --color-violet-200: color-mix(in srgb, var(--cible) 35%, var(--filet));
  --color-violet-300: color-mix(in srgb, var(--cible) 45%, var(--filet));
  --color-violet-400: var(--cible); --color-violet-500: var(--cible);
  --color-violet-600: var(--cible); --color-violet-700: var(--cible);
  --color-violet-900: var(--cible); --color-purple-500: var(--cible);
  --color-purple-600: var(--cible); --color-purple-700: var(--cible);
}

/* ---- Les corrections que le seul remappage d'echelle ne pouvait pas
   faire (verification T10). ---- */

/* Un fond SEMANTIQUE PLEIN porte du texte papier, pas du blanc : en
   theme sombre ces fonds s'eclaircissent et le blanc y tombe a 1,52:1
   (badge « IA active »). --papier suit le theme, lui. / A filled
   semantic background carries paper-colored text, not white. */
.bg-emerald-600, .bg-emerald-500, .bg-green-600, .bg-green-500,
.bg-red-600, .bg-red-500, .bg-amber-500, .bg-amber-400,
.bg-violet-500, .bg-violet-600 {
  color: var(--papier) !important;
}

/* `.text-slate-300` sert de TEXTE (numeros d'ordre a 9px), alors que
   le niveau 300 de l'echelle est reserve aux filets de controle : en
   texte il tombait a 3,02:1. La classe explicite prend l'encre douce,
   l'echelle garde son role de bordure. / Level 300 is a control
   border; as text it needs the soft ink. */
.text-slate-300, .text-gray-300 { color: var(--encre-douce) !important; }

/* Les ecrans staff dessinent leurs CHAMPS avec `border-slate-200`,
   un filet decoratif a 1,19:1 — 241 champs concernes. On ne peut pas
   remonter l'echelle entiere (elle habille aussi de vrais
   separateurs) : on cible les elements de formulaire.
   / Staff screens draw controls with a decorative border; target the
   form elements themselves. */
input.border-slate-200, select.border-slate-200, textarea.border-slate-200,
input.border-slate-100, select.border-slate-100, textarea.border-slate-100,
input.border-gray-200, select.border-gray-200, textarea.border-gray-200 {
  border-color: var(--filet-controle) !important;
}

/* `focus:outline-none` (0,2,0) battait le focus visible global
   (0,1,0) : sur les ecrans staff, un champ focalise au clavier ne
   montrait plus rien des que son `ring` etait absent. Le focus est un
   invariant d'accessibilite (WCAG 2.4.7), il passe en !important.
   / focus:outline-none was beating the global focus ring. */
:focus-visible { outline: 2px solid var(--info) !important; outline-offset: 1px; }
nav.h-12 :focus-visible { outline-color: var(--barre-texte) !important; }

/* Les quatre INTERRUPTEURS de l'editeur sont un `input` visuellement
   masque (`sr-only` + `clip-path: inset(50%)`, boite de 1x1px) suivi
   d'un rail et d'une pastille en `div`. L'anneau de focus s'appliquait
   donc a un element decoupe : au clavier, RIEN ne montrait quel
   interrupteur avait le focus (verification T10). On reporte l'anneau
   sur le rail, qui est le frere suivant.
   / The toggles hide their input; move the focus ring to the rail. */
.toggle-contexte:focus-visible + div {
  outline: 2px solid var(--info) !important;
  outline-offset: 2px;
  border-radius: 999px;
}

/* La pastille de marge « nouveau » etait cerclee de --statut-nouveau
   (#999999) : 2,78:1 sur le papier clair, sous les 3:1 d'un indicateur
   d'etat. Le meme token qui a corrige les soulignements convient ici —
   la FORME (cercle creux vs triangle plein) reste le porteur
   daltonien-safe de l'information. / The dot's ring was under 3:1 in
   light mode; the shape keeps carrying the meaning. */
.indicateur-statut[data-statut="nouveau"] {
  border-color: var(--filet-statut-nouveau) !important;
}

/* Les ecrans staff denses : sous 640px, les rangees de controles
   (piece de prompt, attribut) poussaient leurs boutons HORS du
   viewport, sans defilement pour les rattraper — un « Sauver »
   inatteignable au doigt (verification T10). / Dense staff rows
   pushed their buttons off-screen on mobile. */
@media (max-width: 640px) {
  #analyseur-editor .flex.items-center.gap-2,
  #analyseur-editor .flex.items-center.gap-3 { flex-wrap: wrap; }
}

/* Les deux ecrans de versions n'avaient aucun conteneur : ils
   commencaient au bord gauche de la fenetre quand la liste et
   l'editeur sont centres. / The two version screens had no container. */
#versions-list, #versions-diff {
  max-width: 64rem; margin: 0 auto;
}

/* ---- Les fonds SEMANTIQUES gardent leur sens, dans la palette
   (audit I3) : teintes franches, bordures assorties. ---- */
.bg-amber-50, .bg-amber-50\/30 { background-color: color-mix(in srgb, var(--statut-commente) 12%, var(--papier)) !important; }
.border-amber-200, .border-amber-300 { border-color: color-mix(in srgb, var(--statut-commente) 55%, var(--filet)) !important; }
.bg-emerald-50 { background-color: color-mix(in srgb, var(--succes) 10%, var(--papier)) !important; }
.border-emerald-200 { border-color: color-mix(in srgb, var(--succes) 40%, var(--filet)) !important; }
.bg-sky-50, .bg-blue-50 { background-color: color-mix(in srgb, var(--info) 8%, var(--papier)) !important; }
.border-sky-200, .border-blue-200 { border-color: color-mix(in srgb, var(--info) 35%, var(--filet)) !important; }
.bg-rose-50, .bg-red-50 { background-color: color-mix(in srgb, var(--danger) 10%, var(--papier)) !important; }
.border-rose-200, .border-red-200 { border-color: color-mix(in srgb, var(--danger) 40%, var(--filet)) !important; }

/* ---- La barre du haut : sombre, monospace. Les regles ne descendent
   QUE sur les enfants de la barre elle-meme — jamais dans les menus
   deroulants qui, eux, sont des panneaux papier (audits B1/B2/B3). ---- */
nav.h-12 {
  background: var(--barre) !important;
  border-bottom: none !important;
  color: var(--barre-texte);
  font-family: ui-monospace, Menlo, monospace;
}
nav.h-12 > a, nav.h-12 > button, nav.h-12 > label, nav.h-12 > span,
nav.h-12 > div > button, nav.h-12 > div > a {
  color: color-mix(in srgb, var(--barre-texte) 78%, transparent) !important;
}
nav.h-12 > a:hover, nav.h-12 > button:hover, nav.h-12 > label:hover,
nav.h-12 > div > button:hover {
  color: var(--barre-texte) !important;
  background: rgba(255, 255, 255, .12) !important;
}
nav.h-12 [data-testid="lien-accueil"] {
  color: var(--barre-texte) !important;
  font-weight: 700; letter-spacing: .04em;
}
#titre-toolbar { color: color-mix(in srgb, var(--barre-texte) 60%, transparent) !important; }
nav.h-12 .bg-slate-200 { background-color: rgba(255, 255, 255, .25) !important; }
.btn-toolbar {
  font-family: ui-monospace, Menlo, monospace;
  color: color-mix(in srgb, var(--barre-texte) 78%, transparent);
  background: transparent; border: none;
}
.btn-toolbar:hover { background: rgba(255, 255, 255, .12); color: var(--barre-texte); }
nav.h-12 .btn-toolbar-actif {
  background: rgba(255, 255, 255, .16) !important;
  color: var(--barre-texte) !important;
}
/* Le compteur du bouton taches : blanc sur le vert Tailwind en dur
   (hypostasia.css:1936) donnait 3,30:1 pour un texte de 11px.
   ATTENTION — c'est ici qu'on retombe dans le piege deja rencontre au
   lot T10 : un fond semantique PLEIN ne peut pas porter du `white`.
   En theme sombre, --succes/--info/--danger sont des pastels clairs
   (ils y servent de TEXTE sur fond sombre) : le blanc figé de
   hypostasia.css:1920 y tombait a 1,52:1 — pire qu'avant le correctif.
   --papier, lui, suit le theme : quasi blanc en clair, sombre en
   sombre. / A filled semantic background cannot carry hard-coded
   white: in dark mode these tokens are light. --papier follows. */
/* Le fond PAR DEFAUT du badge (hypostasia.css:1901, #94a3b8 avec du
   blanc) restait le dernier `white` fige du composant : 2,56:1. Il ne
   s'affiche jamais en pratique — un badge sans tache n'est pas peint —
   mais un rescape de remap finit toujours par ressortir un jour.
   / The default state was the last hard-coded white here. */
.badge-taches { background: var(--encre-douce) !important; }
.badge-taches,
.btn-taches-succes .badge-taches,
.btn-taches-en_cours .badge-taches,
.btn-taches-erreur .badge-taches { color: var(--papier) !important; }
.btn-taches-succes .badge-taches { background: var(--succes) !important; }
.btn-taches-en_cours .badge-taches { background: var(--info) !important; }
.btn-taches-erreur .badge-taches { background: var(--danger) !important; }

/* Les 4 etats du bouton taches, lisibles sur la barre (audit a11y I1). */
nav.h-12 .btn-taches-neutre { color: color-mix(in srgb, var(--barre-texte) 70%, transparent) !important; }
nav.h-12 .btn-taches-en_cours { color: #9ec5ff !important; }
nav.h-12 .btn-taches-succes { color: #7fd9b9 !important; }
nav.h-12 .btn-taches-erreur { color: #ffa4a2 !important; }
/* Le bouton du menu utilisateur : son libelle est un <span> a quatre
   niveaux sous nav.h-12, donc hors de portee des selecteurs « enfant
   direct » ci-dessus — il heritait du remappage .text-slate-600 ->
   encre et devenait NOIR SUR NOIR (verification T4 : 1:1). Meme
   histoire pour l'avatar (.bg-blue-100 n'est pas compile par Tailwind :
   aucun fond) et pour le lien Connexion de l'anonyme (--info sur la
   barre : 2,17:1). / User-menu label, avatar and login link were
   invisible or under 3:1 on the dark bar. */
nav.h-12 #btn-user-menu > span,
nav.h-12 [data-testid="btn-login"] {
  color: color-mix(in srgb, var(--barre-texte) 78%, transparent) !important;
}
nav.h-12 #btn-user-menu:hover > span,
nav.h-12 [data-testid="btn-login"]:hover { color: var(--barre-texte) !important; }
nav.h-12 #btn-user-menu > span.rounded-full {
  background: rgba(255, 255, 255, .16) !important;
  color: var(--barre-texte) !important;
}
nav.h-12 #btn-user-menu:hover { background: rgba(255, 255, 255, .12) !important; }

/* Les MENUS DEROULANTS de la barre sont des panneaux papier : texte
   encre, jamais la couleur de la barre (audits B1/B2). */
#user-menu-dropdown, .taches-dropdown-wrapper {
  background: var(--papier) !important;
  border-color: var(--filet) !important;
  font-family: ui-monospace, Menlo, monospace;
}
#user-menu-dropdown a, #user-menu-dropdown button,
.taches-dropdown-wrapper a, .taches-dropdown-wrapper button,
.taches-dropdown-wrapper span, .taches-dropdown-wrapper h3 {
  color: var(--encre) !important;
}
#user-menu-dropdown a:hover, #user-menu-dropdown button:hover,
.taches-dropdown-wrapper a:hover, .taches-dropdown-wrapper button:hover {
  background: var(--revelation) !important;
  color: var(--encre) !important;
}
.taches-dropdown-wrapper .taches-meta-lien { color: var(--info) !important; }

/* ---- Les drawers : papier panneau, filets, metadonnees en monospace
   comme l'etalon (agent visuel n°9). Le tiroir lateral #arbre-overlay
   partageait ces regles ; retire le 12 aout 2026.
   / The drawers; the side tree shared these rules until its removal. ---- */
#drawer-overlay {
  background: var(--papier-panneau) !important;
  box-shadow: 4px 0 24px rgba(0, 0, 0, .14) !important;
  font-family: ui-monospace, Menlo, monospace;
  border-left: 1px solid var(--filet);
}
/* LA CARTE DOIT SE DETACHER DU PANNEAU (etalon § 14 « LA CARTE »)

   L'etalon pose la carte sur `--papier`, PLUS SOMBRE que le
   `--papier-panneau` du panneau qui la porte. Mesure du 12 aout, en
   sombre : carte rgb(22,21,26) sur panneau rgb(26,25,31).

   Le produit la laissait TRANSPARENTE : la liste devenait un bloc
   continu, sans separation entre deux idees. Trois autres ecarts
   mesures avec : rayon 4px pour 5, marge basse 2px
   (hypostasia.css:477) pour 8, et un bord gauche au gris du filet la
   ou l'etalon y met la couleur du STATUT.

   `!important` sur le bord gauche : il faut battre le remappage
   Tailwind `.border-slate-100 { border-color: var(--filet) !important }`
   plus haut dans ce fichier, qui repeint les QUATRE cotes — c'est lui
   qui neutralisait le style inline du gabarit.
   / The card sits on a darker fill than the panel; the left border
   carries the status and must outrank the Tailwind border remap. */
/* LA BARRE D'OUTILS TIENT SA HAUTEUR DU TOKEN, pas d'un utilitaire.
   `h-12` reste sur le gabarit parce qu'elle existe dans le build fige —
   `h-11`, la valeur juste, n'y est pas et serait inerte. Cette regle
   passe donc apres, et `--barre-outils` devient la seule source : la
   barre et le haut du panneau qui se pose dessous ne peuvent plus
   diverger. / The bar's height comes from the token: h-11 is absent
   from the frozen Tailwind build and would be inert. */
.barre-outils { height: var(--barre-outils); }

#drawer-overlay .drawer-carte-compacte {
  background: var(--papier);
  /* Le filet des quatre cotes, ecrit ici plutot qu'en `border-slate-100` :
     il vaut `--filet` comme dans l'etalon, et ne depend plus du remappage
     d'une echelle Tailwind. / The four-side rule, named rather than
     inherited from a remapped Tailwind scale. */
  border: 1px solid var(--filet);
  border-radius: 5px;
  margin-bottom: .5rem;
  border-left-width: 3px;
  border-left-color: var(--statut-nouveau) !important;
  cursor: pointer;
  /* L'etalon anime l'ombre ET le filet, pas « les couleurs » en bloc :
     `transition-colors` faisait aussi glisser la couleur du TEXTE.
     / The etalon animates shadow and border, not every colour. */
  transition: box-shadow .12s, border-color .12s;
}
/* LE PADDING DE L'ETALON — il etait a `px-3 py-2` (12/8px) pour
   10,4/8,8px attendus. Porte par le corps et non par la carte, parce que
   le bandeau « ancre detachee » vit hors de ce corps et garde le sien.
   / The etalon's padding, carried by the body: the detached-anchor
   banner sits outside it and keeps its own. */
#drawer-overlay .drawer-carte-compacte .carte-corps { padding: .55rem .65rem; }

/* Au survol, l'etalon LEVE la carte ; le produit repeignait son filet.
   Une ombre dit « cliquable » sans faire clignoter une frontiere.
   / The etalon lifts the card; the product repainted its border. */
#drawer-overlay .drawer-carte-compacte:hover { box-shadow: 0 1px 6px rgba(0, 0, 0, .07); }

#drawer-overlay .drawer-carte-compacte[data-statut="commente"] {
  border-left-color: var(--statut-commente) !important;
}
/* LA CARTE ACTIVE PORTE UN HALO, elle ne change pas de fond. Le fond
   `--revelation` peignait toute la carte en ambre pale : la couleur du
   STATUT y devenait illisible, et deux cartes actives d'affilee se
   confondaient avec une zone surlignee. L'etalon pose un anneau de 2px
   a 20 % — la carte reste sur son papier, seul son contour repond.
   / The active card gets a ring, not a new fill: the amber wash hid the
   status colour and read like a highlighted region. */
#drawer-overlay .drawer-carte-compacte.drawer-carte-active {
  border-color: var(--statut-commente) !important;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--statut-commente) 20%, transparent);
}

/* L'INDICATEUR DE STATUT fait .78rem dans l'etalon ; il etait a `w-3.5`
   (14px), soit 1,5px de trop pour un objet qui n'en mesure que 12,5.
   / The status indicator is .78rem; it was 14px. */
#drawer-overlay .indicateur-statut { width: .78rem; height: .78rem; flex: none; }

/* =========================================================================
   L'INTERIEUR DE LA CARTE — etalon § 14 « LA CARTE (_card_body.html) »
   =========================================================================

   La carte elle-meme etait deja portee (bord de statut, rayon, fond) ;
   son CONTENU restait ecrit en utilitaires Tailwind arbitraires
   (`text-[10px]`, `bg-slate-50`, `text-slate-500`...). Le rendu etait
   proche, mais rien ne le tenait : chaque valeur vivait dans le gabarit,
   sans nom, hors du systeme, et ne suivait le theme que par le
   remappage des echelles Tailwind.

   `_card_body.html` sert SEPT gabarits — drawer, carte inline, bottom
   sheet, resultats, previsualisation, test d'analyseur, wrapper. Le
   nommer ici, c'est le corriger partout a la fois.

   DEUX ECARTS DE L'ETALON SONT CONSERVES, parce qu'ils sont assumes et
   ecrits dans l'etalon lui-meme :
     · la CITATION reste toujours visible ; l'etalon la replie au repos
       (« ECART ASSUME — le produit l'affiche toujours ») ;
     · la TYPOGRAPHIE suit la charte du projet (B612, B612 Mono, Lora,
       Srisakdi) et non le `ui-monospace` de l'etalon, qui n'est qu'un
       substitut pour rester autonome.
   Ce qui est porté ici, c'est donc la GEOMETRIE et les COULEURS.
   / The card's shell was ported; its contents stayed in ad-hoc Tailwind.
   _card_body.html serves seven templates: naming it here fixes all of
   them. Two documented departures are kept: the citation stays visible,
   and typography follows the project charter, not the mock's fallback.
   ========================================================================= */

.carte-entete { display: flex; align-items: center; gap: .4rem; margin-bottom: .4rem; }

/* Les badges ne doivent jamais pousser le compteur hors de la carte :
   `min-width:0` autorise la boite a retrecir sous son contenu.
   / min-width:0 lets the box shrink below its content. */
.carte-entete .badges { display: flex; flex-wrap: wrap; gap: .2rem; min-width: 0; }

/* CONTOUR, PAS PASTILLE PLEINE. La famille d'hypostase se lit a la
   couleur du TRAIT : trois badges cote a cote ne doivent pas peser plus
   que la phrase qu'ils qualifient. Le produit les remplissait — l'etalon
   notait l'ecart comme assume ; il est leve ici a la demande du
   mainteneur (15 aout).

   `currentColor` fait tout le travail : le gabarit ne pose plus qu'une
   `color` (la teinte de la famille, qui est dynamique et ne peut donc
   pas vivre en CSS), et le fond comme le trait s'en deduisent.
   / Outlined, not filled: the family reads in the stroke. The template
   sets only `color`; currentColor derives the rest. */
.badge-hypostase {
  display: inline-block;
  padding: .08rem .34rem;
  border-radius: 2px;
  white-space: nowrap;
  cursor: help;
  background: none;
  border: 1px solid currentColor;
  opacity: .85;
}

/* Le compteur se pousse a droite et ne se comprime jamais : c'est lui
   qui dit qu'un debat a commence. / Pushed right, never shrunk. */
.carte-entete .compteur-commentaires {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: .15rem;
  font-size: .6rem;
  color: var(--statut-commente);
}

.mots-cles { display: flex; flex-wrap: wrap; gap: .22rem; margin-top: .35rem; }
.mot-cle {
  font-size: .57rem;
  color: var(--encre-douce);
  background: var(--papier-creux);
  padding: .05rem .3rem;
  border-radius: 2px;
  /* LA HAUTEUR DE LIGNE EST ECRITE, pas heritee. L'etalon ne la declare
     pas : elle lui vient de son corps de texte, a 1,7. Le drawer, lui,
     herite du 1,5 de Tailwind — meme police, meme taille, meme padding,
     et pourtant 15,5px la contre 13,7px ici. Un heritage qui differe
     entre deux contextes n'est pas une valeur, c'est un hasard.
     / The mock inherits 1.7 from its body, the drawer 1.5 from Tailwind:
     same font, same padding, 1.8px apart. Inheritance that differs
     between contexts is not a value. */
  line-height: 1.7;
}

/* UNE INTERVENTION DE LECTEUR, pas une ligne de plus. Le filet gauche et
   le fond creux la detachent du texte machine au-dessus : sans eux, le
   commentaire d'une personne se lit comme la suite du resume de l'IA.
   / The rule and recessed fill separate a human voice from the machine
   text above it. */
.carte-commentaire {
  margin-top: .45rem;
  padding: .35rem .5rem;
  background: var(--papier-creux);
  border-left: 2px solid var(--filet);
  border-radius: 3px;
}
/* Le nom en GRAS et petit, pas en Srisakdi : la police de la charte
   arrive a 20px, ce qu'une carte large de quelques centimetres ne peut
   pas porter — la seule autre vue qui l'emploie corrige deja sa taille
   a la main. La provenance se lit ici a la FORME du bloc.
   / Bold and small, not Srisakdi: the charter face lands at 20px, which
   a centimetres-wide card cannot carry. Shape carries provenance here. */
.carte-commentaire .qui {
  display: block;
  margin-bottom: .08rem;
  font-weight: 700;
  font-size: .7rem;
}
.carte-commentaire .qui.contributeur-actif-highlight { color: var(--statut-commente); }
.carte-commentaire .quoi { margin: 0; font-size: .8rem; line-height: 1.45; }

/* ---- L'ACCUEIL (onboarding) : le premier ecran d'un visiteur ne
   reste pas a l'ancien design (agent visuel n°1-2). ---- */
.onboarding-section, .onboarding-etape {
  background: var(--papier-panneau) !important;
  border-color: var(--filet) !important;
}
.onboarding-section h2, .onboarding-section h3 { color: var(--encre) !important; }
.onboarding-section p, .onboarding-section-desc, .onboarding-statut-desc,
.onboarding-section-desc strong {
  color: var(--encre-douce) !important;
}
.onboarding-etape-num {
  background: color-mix(in srgb, var(--statut-commente) 15%, var(--papier)) !important;
  color: var(--encre) !important;
}
.onboarding-btn-import, .btn-synthese {
  background: var(--encre) !important;
  color: var(--papier) !important;
  border-color: var(--encre) !important;
}
/* La couche v2 ne visait que .onboarding-section : le titre, le
   sous-titre, les etapes et les raccourcis vivent AILLEURS dans l'arbre
   et gardaient leurs couleurs Tailwind en dur — invisible des que le
   fond passe au sombre (1,02:1, verification T7). On couvre les vrais
   selecteurs. / The v2 layer only targeted .onboarding-section; the
   header, steps and shortcuts kept hard-coded Tailwind colors. */
.onboarding-titre, .onboarding-etape-titre, .onboarding-section-titre {
  color: var(--encre) !important;
}
.onboarding-sous-titre, .onboarding-sous-titre strong,
.onboarding-etape-desc, .onboarding-raccourci, .onboarding-raccourci span,
.onboarding-statut-desc {
  color: var(--encre-douce) !important;
}
.onboarding-contenu { background: var(--papier) !important; }
/* Le seul ilot reste blanc de toute la campagne : une couleur locale en
   dur, hors de portee des utilitaires re-teintes. */
.onboarding-statut {
  background: var(--papier-panneau) !important;
  color: var(--encre) !important;
}
.aide-kbd {
  background: var(--papier-creux) !important;
  /* La regle qui donnait son cadre a la touche vit dans aide_desktop,
     non inclus sur l'accueil : sans largeur, la pastille restait une
     lettre nue (verification T7). */
  border: 1px solid var(--filet-controle) !important;
  border-radius: 3px; padding: .05rem .3rem;
  color: var(--encre) !important;
  font-family: ui-monospace, Menlo, monospace;
}
/* Les 4 pastilles numerotees portent leur couleur en style= inline :
   le style local gagne, on force. */
.onboarding-etape-num[style] {
  background: color-mix(in srgb, var(--statut-commente) 15%, var(--papier)) !important;
  color: var(--encre) !important;
}
.onglet-accueil { color: var(--encre-douce) !important; font-family: ui-monospace, Menlo, monospace; }
.onglet-accueil.active {
  color: var(--encre) !important;
  /* Meme dette que l'onglet du corpus : ambre pur = 2,20:1 en clair. */
  border-color: var(--filet-statut-commente) !important;
}

/* ---- Les soulignements de STATUT : ce sont des indicateurs d'etat,
   donc soumis aux 3:1 de WCAG 1.4.11. Ceux de hypostasia.css:237-244
   sont ecrits en rgba() a alpha fixe et tombent a 1,09:1 en clair,
   1,37:1 en sombre (verification T7) — aucun remappage de token ne
   pouvait les atteindre. L'ambre pur lui-meme ne fait que 2,2:1 sur le
   papier clair : en clair il est FONCE de 30 % d'encre, en sombre il
   reste tel quel (7,9:1). Le fond, lui, garde sa teinte legere.
   / Status underlines are state indicators: 3:1 required. The old
   rgba() values could not be reached by a token remap. ---- */
:root {
  --filet-statut-nouveau: var(--encre-douce);
  --filet-statut-commente: color-mix(in srgb, var(--statut-commente) 70%, var(--encre));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --filet-statut-commente: var(--statut-commente); }
}
:root[data-theme="dark"] { --filet-statut-commente: var(--statut-commente); }

/* Les <mark> du rendu par elements (BR-D, verif visuelle du 9 aout) :
   sans ces deux lignes, les DEFAUTS NAVIGATEUR de <mark> s'appliquent —
   texte MarkText (noir dur : 1,28:1 en sombre) et fond Mark (jaune
   fluo) pour tout statut sans regle, dont non_pertinent (4 182
   extractions en base). Specificite (0,1,1) calculee pour PERDRE
   contre les fonds par statut (0,2,0) ci-dessous : le reset ne les
   ecrase pas. / <mark> UA defaults reset: MarkText black text and
   fluo-yellow Mark background; specificity chosen to lose against the
   per-status backgrounds below. */
mark.hl-extraction {
  background: none;
  color: inherit;
  padding: .04em 0;
}

.hl-extraction[data-statut="nouveau"] {
  background-color: color-mix(in srgb, var(--encre-douce) 8%, transparent);
  border-bottom-color: var(--filet-statut-nouveau);
}
.hl-extraction[data-statut="commente"] {
  background-color: color-mix(in srgb, var(--statut-commente) 14%, transparent);
  border-bottom-color: var(--filet-statut-commente);
}
/* L'onglet actif du corpus a la meme dette, mais sa regle vit dans
   _style_maquette.html — un <style> inline, donc plus tard dans le
   document qu'une feuille liee : a specificite egale il gagnerait
   toujours. Le token y est applique a la source. */

/* ---- Les surlignages d'extraction : les statuts RICHES restes en
   base (controverse/consensuel/discutable) reprennent des couleurs —
   la couche etait morte pour eux (agent visuel n°4). ---- */
.hl-extraction[data-statut="consensuel"] {
  background: color-mix(in srgb, var(--succes) 14%, transparent);
  border-bottom-color: var(--succes);
}
.hl-extraction[data-statut="controverse"] {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-bottom-color: var(--danger);
}
.hl-extraction[data-statut="discutable"], .hl-extraction[data-statut="discute"] {
  background: color-mix(in srgb, var(--statut-commente) 16%, transparent);
  /* Ambre pur sur son propre fond teinte : 1,77:1, le soulignement le
     plus faible du produit (verification T7). Le token le fonce en
     clair et le laisse pur en sombre. */
  border-bottom-color: var(--filet-statut-commente);
}

/* =========================================================================
   LOT T4 — L'ECRAN DE LECTURE
   Le corps est deja en Georgia/encre (D1). Restaient : les liens et les
   pastilles de marge encore bleus, les legendes et le chrome de la page
   en B612 la ou l'etalon veut du monospace, et la cible tactile des
   pastilles. Aucun markup touche : mark.portion est fige par
   front/tests/test_rendu_elements.py.
   / Reading screen: links and margin dots off blue, mono chrome, dot
   hit area. CSS only — the mark.portion markup is frozen by tests.
   ========================================================================= */

/* Liens du corps : --info, souligne franc (l'etalon souligne ses
   renvois : corpus.html:283). Bat .lecture-article a de hypostasia.css
   (meme specificite, feuille chargee apres) et les .prose-a de
   Tailwind (:where() ne compte pas). */
.lecture-article a, #readability-content a {
  color: var(--info);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--info) 45%, transparent);
  text-underline-offset: 3px;
}
.lecture-article a:hover, #readability-content a:hover {
  color: var(--info);
  text-decoration-color: var(--info);
}
/* Citation en bloc : filet --info a gauche, creux papier (etalon
   corpus.html:176). La COULEUR DU TEXTE doit y etre reprise : sans
   elle, la citation garde le gris de Tailwind Typography et tombe a
   1,07:1 sur le creux sombre — un bloc vide avec une barre bleue
   (verification finale T4). / The text color must be restated, or the
   quote keeps Tailwind's grey and is invisible in dark mode. */
.lecture-article blockquote, #readability-content blockquote {
  border-left-color: var(--info);
  background: var(--papier-creux);
  border-radius: 0 4px 4px 0;
  color: var(--encre);
}

/* Meme cause pour tout le reste de Tailwind Typography : ses couleurs
   vivent dans des variables `--tw-prose-*` en oklch, qu'aucun de nos
   selecteurs ne touche. On les REMAPPE a la source, comme les echelles
   de couleur plus haut — une ligne par role plutot qu'une regle par
   element. / Remap the Typography plugin's own variables at the source. */
.prose, .lecture-article, #readability-content {
  --tw-prose-body: var(--encre);
  --tw-prose-headings: var(--encre);
  --tw-prose-lead: var(--encre-douce);
  --tw-prose-links: var(--info);
  --tw-prose-bold: var(--encre);
  --tw-prose-counters: var(--encre-douce);
  --tw-prose-bullets: var(--filet-controle);
  --tw-prose-hr: var(--filet);
  --tw-prose-quotes: var(--encre);
  --tw-prose-quote-borders: var(--info);
  --tw-prose-captions: var(--encre-douce);
  --tw-prose-code: var(--encre);
  --tw-prose-pre-code: var(--encre);
  --tw-prose-pre-bg: var(--papier-creux);
  --tw-prose-th-borders: var(--filet-controle);
  --tw-prose-td-borders: var(--filet);
}
/* Legendes, en-tetes de tableau, code : le chrome du contenu passe en
   monospace comme l'etalon, le corps reste serif. */
.lecture-article figcaption, #readability-content figcaption {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--encre-douce);
}
.lecture-article th, #readability-content th {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  background: var(--papier-creux);
  color: var(--encre);
}

/* Le chrome de la page de lecture (source, exporter, historique,
   pilules de version) : monospace dense de l'etalon. Le corps de la
   note n'est pas concerne — #readability-content n'est pas cible. */
.lecture-zone-conteneur > div a, .lecture-zone-conteneur > div button,
.lecture-zone-conteneur > div span, #switcher-versions a,
#switcher-versions span {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.lecture-zone-conteneur > div a, .lecture-zone-conteneur > div button {
  letter-spacing: .03em;
}
#switcher-versions a[id^="pill-version-"]:hover {
  background: var(--revelation) !important;
  color: var(--encre) !important;
}
[data-testid="btn-comparer-versions"]:hover {
  background: color-mix(in srgb, var(--statut-commente) 18%, var(--papier)) !important;
  color: var(--encre) !important;
}

.indicateur-statut[data-statut="nouveau"] { border-color: var(--statut-nouveau); }

/* Les ANIMATIONS de flash etaient en bleu/indigo Tailwind ecrits en dur
   dans des @keyframes — un remappage de token ne pouvait pas les
   atteindre (verification Playwright T4 : flash bleu a chaque clic sur
   un passage annote). On REDEFINIT les keyframes du meme nom ici :
   la derniere definition gagne, hypostasia.css n'est pas touchee.
   / Flash keyframes had hard-coded Tailwind blues; same-name keyframes
   redefined here win, without editing the old sheet. */
@keyframes hl-pulse {
  0%   { background-color: color-mix(in srgb, var(--statut-commente) 25%, transparent); }
  70%  { background-color: color-mix(in srgb, var(--statut-commente) 10%, transparent); }
  100% { background-color: transparent; }
}
@keyframes bloc-flash-pulse {
  0%   { box-shadow: 0 0 0 3px color-mix(in srgb, var(--statut-commente) 50%, transparent); }
  50%  { box-shadow: 0 0 0 3px color-mix(in srgb, var(--statut-commente) 20%, transparent); }
  100% { box-shadow: none; }
}
/* Meme famille : le passage isole par le bottom sheet (hypostasia.css
   :1538) etait indigo. */
body.bottom-sheet-focus .hl-extraction.hl-focus-actif {
  background-color: color-mix(in srgb, var(--statut-commente) 18%, transparent) !important;
  border-bottom: 2px solid var(--statut-commente) !important;
}

/* =========================================================================
   LOT T8 — LES OVERLAYS
   Modale d'alignement, toasts WebSocket, contenu du drawer d'analyses,
   bottom sheet. Tous portaient des `white` et des couleurs Tailwind
   ecrites en dur, invisibles au remappage de tokens — et fatals en
   theme sombre. On surcharge ici, hypostasia.css reste intacte.
   / Overlays: hard-coded whites and Tailwind colors, fatal in dark
   mode. Overridden here; the old sheet is left untouched.
   ========================================================================= */

/* ---- La modale d'alignement (hypostasia.css:1031-1210) : cinq fonds
   blancs, dont trois sticky, plus deux cellules colorees et un onglet
   actif bleu. ---- */
/* ATTENTION aux noms : `.tableau-alignement` est la classe de
   l'ETALON (front/static/front/maquettes/corpus.html:408). Le gabarit Django, lui,
   emploie `.alignement-table` et des cellules `.alignement-th-page`
   (sticky haut), `.alignement-th-corner` (coin) et
   `.alignement-td-hypostase` (colonne sticky gauche), dont le
   `background: white` est ecrit en dur dans hypostasia.css:1068,1088,
   1101. Viser les classes de la maquette ne matchait RIEN : 35 fonds
   blancs survivaient, jusqu'a 1,45:1 en sombre (verification T8).
   / The mockup's class names are not the app's; target the real ones. */
.alignement-modale, .alignement-header, .alignement-footer,
.alignement-table thead th, .alignement-table tbody th,
.alignement-th-page, .alignement-th-corner, .alignement-td-hypostase {
  background: var(--papier) !important;
  color: var(--encre);
}
.alignement-modale { border: 1px solid var(--filet); }
.alignement-table th, .alignement-table td,
.alignement-th-page, .alignement-th-corner, .alignement-td-hypostase {
  border-color: var(--filet) !important;
}
.alignement-th-page, .alignement-th-corner {
  font-family: ui-monospace, Menlo, monospace;
  color: var(--encre-douce) !important;
}
/* Les deux provenances typographiques (hypostasia.css:74-75) n'etaient
   remappees nulle part : 2,40:1 en sombre pour tout .typo-machine. */
:root { --typo-machine-color: var(--encre-douce); --typo-citation-color: var(--encre); }
/* Les deux cellules de difference : le sens (retire / ajoute) est porte
   par la teinte ET par le filet gauche, jamais par la teinte seule. */
.alignement-cell-supprime {
  background: color-mix(in srgb, var(--danger) 10%, var(--papier)) !important;
  border-color: color-mix(in srgb, var(--danger) 40%, var(--filet)) !important;
}
.alignement-cell-ajoute {
  background: color-mix(in srgb, var(--succes) 10%, var(--papier)) !important;
  border-color: color-mix(in srgb, var(--succes) 40%, var(--filet)) !important;
}
.onglet-comparaison {
  font-family: ui-monospace, Menlo, monospace;
  color: var(--encre-douce);
  background: var(--papier-creux);
  border-color: var(--filet-controle);
}
.onglet-comparaison:hover { color: var(--encre); background: var(--revelation); }
.onglet-comparaison.active {
  color: var(--encre) !important;
  background: var(--papier) !important;
  border-color: var(--filet-statut-commente) !important;
  font-weight: 700;
}
.alignement-backdrop, #alignement-modale-backdrop { background: rgba(0, 0, 0, .45) !important; }

/* ---- Les TOASTS. Constat du lot T8, a verser au lot T9 : les 150
   lignes de `.ws-toast*` (hypostasia.css:1668-1810) sont du CSS MORT —
   aucune occurrence de `ws-toast` dans un .js, un .html ou un .py du
   depot. Les deux systemes de toasts annonces par le cahier des
   charges (§ 1.6 b) n'en font plus qu'un : SweetAlert2. C'est donc lui
   qu'on habille. / The `.ws-toast` block is dead CSS; only SweetAlert
   remains. ---- */
.swal2-popup {
  background: var(--papier) !important;
  color: var(--encre) !important;
  border: 1px solid var(--filet);
  border-radius: 6px;
  font-family: ui-monospace, Menlo, monospace;
}
.swal2-title, .swal2-html-container { color: var(--encre) !important; }

/* LE TOAST DOIT SE DETACHER, PAS SEULEMENT ETRE LISIBLE
   (mesure au navigateur du 12 aout, mode sombre)

     texte du toast   rgb(236, 234, 228)
     fond du toast    rgb(22, 21, 26)
     fond de la page  rgb(22, 21, 26)   <- le MEME
     bordure          0px none
     ombre            noire a 18 %, invisible sur un fond noir

   Le contraste du TEXTE etait excellent — 14,8:1 — et c'est ce qui a
   trompe : rien ne signalait de defaut. Mais un rectangle sans contour,
   pose sur un fond identique au sien, n'est pas percu comme un objet.
   On lisait son message superpose au panneau sans voir que c'en etait
   un. En clair, l'ombre portee suffisait a le detacher ; en sombre, une
   ombre noire sur du noir ne separe rien.

   C'EST LE CONTOUR QUI FAIT LE TRAVAIL. Le fond passe de `papier` a
   `creux`, mais soyons exact : en sombre, #1e1d23 contre #16151a ne
   donne que 1,09:1 — un fond qu'on ne voit pas. Il ecarte la
   coincidence exacte des deux couleurs, rien de plus. Ce qui detache
   reellement le toast, c'est le contour au filet RENFORCE : `--filet`
   tombe a 1,29:1 sur le fond sombre, la ou WCAG 1.4.11 demande 3:1
   quand le contour est le seul indice de la limite. Contours mesures :
   4,1:1 en sombre, 3,3:1 en clair. L'ombre, plus dense, aide en clair
   et reste secondaire en sombre.

   `.swal2-popus` porte deja un fond et un contour, mais SweetAlert
   redeclare les siens pour la variante toast : sans `!important` ils
   l'emportent.
   / Excellent text contrast hid the real defect: an unbordered box on an
   identical background does not read as an object. Distinct fill,
   reinforced border, denser shadow. */
.swal2-toast {
  background: var(--papier-creux) !important;
  border: 1px solid var(--filet-controle) !important;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .35) !important;
}
.swal2-actions .swal2-confirm {
  background: var(--encre) !important; color: var(--papier) !important;
  border-radius: 4px !important;
}
.swal2-actions .swal2-cancel {
  background: var(--papier) !important; color: var(--encre) !important;
  border: 1px solid var(--filet-controle) !important; border-radius: 4px !important;
}
.swal2-actions .swal2-deny {
  background: var(--danger) !important; color: var(--papier) !important;
  border-radius: 4px !important;
}
.swal2-container.swal2-backdrop-show { background: rgba(0, 0, 0, .45) !important; }
/* En sombre, le bouton principal « encre sur papier » s'inverse. */
:root[data-theme="dark"] .swal2-actions .swal2-confirm {
  background: var(--info) !important; color: var(--papier) !important;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .swal2-actions .swal2-confirm {
    background: var(--info) !important; color: var(--papier) !important;
  }
}

/* ---- Le contenu du drawer d'analyses : les badges d'hypostase.
   ECART ASSUME (cahier des charges § 2.5, E2) : l'etalon les veut en
   CONTOUR (« trois badges cote a cote ne doivent pas peser plus que la
   phrase qu'ils qualifient »), le produit les remplit depuis
   hypostasia.css:47-70. Le remplissage est conserve — il est cite en
   style= inline dans _card_body.html, hors de portee — mais les fonds
   pastel sont RAMENES a la palette : teinte de la famille a 14 % du
   papier, texte en teinte de famille (elle-meme basculee en sombre).
   / Family badges keep their fill (inline style), but the pastel
   backgrounds are re-derived from the family tint. ---- */
:root {
  /* Quatre familles etaient DEJA limites sur du papier nu (objet 4,64,
     speculatif 4,92) : sur un fond derive de leur propre teinte elles
     passaient sous 4,5:1 (verification T8). On les fonce d'un cran —
     le gain vaut partout ou ces tokens servent de texte, pas seulement
     dans les badges. / Four family tints were already borderline. */
  --hypostase-objet-text:#4b5563;      /* etait #64748b */
  --hypostase-speculatif-text:#92400e;  /* etait #b45309 */
  --hypostase-mode-text:#155e75;        /* etait #0e7490 */
  --hypostase-empirique-text:#065f46;   /* etait #047857 */

  --hypostase-epistemique-bg: color-mix(in srgb, var(--hypostase-epistemique-text) 14%, var(--papier));
  --hypostase-empirique-bg: color-mix(in srgb, var(--hypostase-empirique-text) 14%, var(--papier));
  --hypostase-speculatif-bg: color-mix(in srgb, var(--hypostase-speculatif-text) 14%, var(--papier));
  --hypostase-structurel-bg: color-mix(in srgb, var(--hypostase-structurel-text) 14%, var(--papier));
  --hypostase-normatif-bg: color-mix(in srgb, var(--hypostase-normatif-text) 14%, var(--papier));
  --hypostase-problematique-bg: color-mix(in srgb, var(--hypostase-problematique-text) 14%, var(--papier));
  --hypostase-mode-bg: color-mix(in srgb, var(--hypostase-mode-text) 14%, var(--papier));
  --hypostase-objet-bg: color-mix(in srgb, var(--hypostase-objet-text) 14%, var(--papier));
}
/* En sombre, un texte clair sur un fond a 14 % de lui-meme reste
   lisible ; c'est la meme formule, les tokens *-text ayant bascule. */

/* ---- Le bottom sheet et sa poignee. ---- */
#bottom-sheet-backdrop, #drawer-backdrop {
  background: rgba(0, 0, 0, .45) !important;
}
#bottom-sheet .poignee, .bottom-sheet-poignee {
  background: var(--filet-controle) !important;
}
#bottom-sheet-contenu { color: var(--encre); }

/* =========================================================================
   LE FIL D'ARIANE (lot « architecture de page »)
   Source : front/static/front/maquettes/corpus.html:134-163 et :530-541.
   Il vit HORS de .zone-corpus — il coiffe l'ecran, il n'en fait pas
   partie — donc ses regles sont ici et non dans le design system scope.
   Il est colle sous la barre : le conteneur defilant est #zone-lecture,
   pas la fenetre, donc `top:0` s'y refere.
   ATTENTION (commentaire explicite de l'etalon) : overflow doit rester
   VISIBLE, sinon le menu de bascule est invisible.
   / The breadcrumb sits above the zone, not inside it. It sticks to the
   top of #zone-lecture, the real scrolling container. Overflow must
   stay visible or the switcher menu is clipped.
   ========================================================================= */
.fil-ariane {
  position: sticky; top: 0; z-index: 66;
  display: flex; align-items: center; gap: .35rem; flex-wrap: wrap;
  min-height: 2rem; margin: -2rem -2rem 1rem; padding: .35rem 1.5rem;
  background: var(--papier-creux); border-bottom: 1px solid var(--filet);
  font-family: ui-monospace, Menlo, monospace; font-size: .65rem;
  color: var(--encre-douce);
  overflow: visible;
}
.fil-ariane a, .fil-ariane .lien {
  color: inherit; text-decoration: none; cursor: pointer; background: none;
  border: none; font: inherit; border-radius: 3px;
  /* Cible elargie sans decaler la ligne (WCAG 2.5.8). */
  padding: .3rem .25rem; margin: -.3rem 0;
}
.fil-ariane a:hover, .fil-ariane .lien:hover {
  color: var(--encre); background: var(--papier); text-decoration: underline;
}
.fil-ariane .courant { color: var(--encre); font-weight: 600; }
/* Le chevron etait a 2,01:1 (opacite .5). Il est aria-hidden, donc
   invisible aux lecteurs d'ecran, mais il structure la lecture a l'oeil :
   on le laisse discret sans le rendre illisible.
   / The separator was at 2.01:1; discreet is not the same as unreadable. */
.fil-ariane .sep { opacity: .75; }
.bascule-carnet { position: relative; }
.menu-bascule {
  position: absolute; top: calc(100% + .25rem); left: 0; z-index: 95;
  min-width: 15rem; max-height: 60vh; overflow-y: auto;
  background: var(--papier); border: 1px solid var(--filet); border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .16); padding: .3rem; display: none;
}
.menu-bascule.est-visible { display: block; }
.menu-bascule button {
  display: flex; width: 100%; align-items: center; gap: .4rem; text-align: left;
  background: none; border: none; padding: .35rem .45rem; border-radius: 4px;
  cursor: pointer; font-family: ui-monospace, Menlo, monospace; font-size: .65rem;
  color: var(--encre);
}
.menu-bascule button:hover { background: var(--papier-creux); }
/* Quatre carnets s'appellent « Mes imports » : le proprietaire les
   separe, a l'oeil comme au lecteur d'ecran.
   / Four notebooks share a name; the owner tells them apart. */
.menu-bascule .proprietaire-du-carnet { color: var(--encre-douce); font-size: .58rem; }
/* Le carnet courant n'est pas signale par la seule graisse : une
   marque le nomme aussi. / Not by weight alone: a mark names it. */
.menu-bascule button[aria-current="true"] { font-weight: 700; }
.menu-bascule button[aria-current="true"]::after {
  content: "· courant"; margin-left: auto; font-weight: 400;
  color: var(--encre-douce); font-size: .58rem;
}
/* Dans la zone de lecture, le padding du conteneur est p-8 (2rem) :
   les marges negatives ci-dessus le compensent pour que le fil aille
   d'un bord a l'autre. Sur mobile ce padding tombe a 1rem. */
@media (max-width: 640px) {
  .fil-ariane { margin: -1rem -1rem 1rem; padding: .35rem 1rem; }
}
/* LE FIL DOIT COLLER AU HAUT DE LA ZONE. #zone-lecture porte `p-8` :
   un element sticky se cale sur la boite de CONTENU, donc le fil se
   figeait 32px plus bas et laissait defiler du texte dans la bande
   au-dessus de lui (verification : navTop 80 contre zoneTop 48).
   On retire ce padding haut UNIQUEMENT quand un fil est present, et le
   fil rend l'espace par sa marge basse. Sans :has(), on retombe sur
   l'ancien comportement — imparfait, jamais casse.
   / A sticky element sticks to the content box: the container's top
   padding pushed the breadcrumb 32px down. Drop it only when a
   breadcrumb is there. */
@supports selector(:has(*)) {
  #zone-lecture:has(> .fil-ariane) { padding-top: 0; }
  #zone-lecture:has(> .fil-ariane) > .fil-ariane {
    margin-top: 0; margin-bottom: 1.5rem;
  }
}

/* Un tableau large (transcription audio, donnees) faisait deborder la
   zone de lecture a 390px : il defile DANS sa boite plutot que de
   pousser la page. Constat de la verification « architecture de page »,
   defaut pre-existant. / Wide tables scroll inside their own box. */
#readability-content table, .lecture-article table {
  display: block; max-width: 100%; overflow-x: auto;
}
#zone-lecture .pilules-locuteurs, #zone-lecture #filtre-locuteurs {
  display: flex; flex-wrap: wrap; max-width: 100%;
}
/* Le vrai coupable du debordement a 390px n'etait pas le tableau mais
   le formulaire « + Ajouter a… » du bloc carnets d'une note : un flex
   sans wrap dont le <select> porte les noms de tous les carnets
   disponibles (« … — PUBLIC : la note y sera visible par tous »).
   Il poussait #zone-lecture a 558px de large.
   / The overflow came from the "add to notebook" form: a nowrap flex
   whose select holds every notebook name. */
[data-testid="corpus-bloc-ajouter-form"],
[data-testid="corpus-base-ajouter-form"] { flex-wrap: wrap; }
[data-testid="corpus-bloc-ajouter-select"],
[data-testid="corpus-base-ajouter-select"] { max-width: 100%; }
@media (max-width: 640px) {
  #zone-lecture form.flex { flex-wrap: wrap; }
  #zone-lecture select { max-width: 100%; }
}

/* ---- Divers chrome. ---- */
#zone-lecture { background: var(--papier); }
#bottom-sheet { background: var(--papier-panneau) !important; }
/* Son bouton de fermeture etait a 4,49:1 — sous le seuil d'un cheveu
   (verification T4). --encre-douce le porte a 5,2:1. */
.btn-fermer-bottom-sheet { color: var(--encre-douce) !important; }
input, select, textarea {
  font-family: inherit;
  background: var(--papier);
  border-color: var(--filet-controle);
  color: var(--encre);
}
::selection { background: color-mix(in srgb, var(--statut-commente) 30%, transparent); }

/* ---- Le LIEN D'EVITEMENT : hors de l'ecran tant qu'il n'a pas le
   focus, puis pose par-dessus la barre. Il n'est jamais display:none —
   un lien masque ainsi n'est pas focusable, donc inutile.
   / Skip link: off-screen until focused, never display:none. ---- */
.lien-evitement {
  position: fixed; top: 0; left: 0; z-index: 200;
  transform: translateY(-120%);
  background: var(--papier); color: var(--encre);
  border: 1px solid var(--filet-controle); border-radius: 0 0 4px 0;
  padding: .5rem .9rem; text-decoration: none;
  font-family: ui-monospace, Menlo, monospace; font-size: .7rem;
}
.lien-evitement:focus { transform: translateY(0); }

/* ---- :has() n'existe pas avant Safari 15.4 / Firefox 121. La puce de
   filtre cochee y perdrait TOUTE marque visible : on lui redonne le
   contour natif de la case. / Fallback for engines without :has(). ---- */
@supports not selector(:has(*)) {
  .zone-corpus .puce-categorie input {
    position: static; opacity: 1; pointer-events: auto;
    accent-color: var(--encre);
  }
  .zone-corpus .puce-categorie .point { display: none; }
}

/* ---- Focus visible : info sur papier, PAPIER sur la barre sombre
   (audit a11y B3 : 2,18:1 -> 16,79:1). ---- */
:focus-visible { outline: 2px solid var(--info); outline-offset: 1px; }
nav.h-12 :focus-visible { outline-color: var(--barre-texte); }

/* ---- Mouvements : neutralises pour qui le demande (audit a11y I3 —
   46 transitions de hypostasia.css comprises). ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* =========================================================================
   U1 — LE MODE STRUCTURE : boutons d'operations d'element
   Les groupes .actions-element (dans chaque bloc) et les placeholders
   .element-masque sont RENDUS pour qui peut ecrire, mais INVISIBLES tant
   que #zone-lecture ne porte pas .mode-structure (bouton a bascule).
   display:none les retire aussi de l'arbre d'accessibilite : un lecteur
   d'ecran ne traverse pas trente boutons au fil du texte en mode lecture.
   / U1 structure mode: per-block action buttons and masked-element
   placeholders, hidden (display:none, out of the a11y tree too) until
   #zone-lecture carries .mode-structure.
   ========================================================================= */

.actions-element { display: none; }
#zone-lecture.mode-structure .actions-element {
  display: inline-flex; gap: .3rem; flex-wrap: wrap;
  margin-left: .5rem; vertical-align: middle;
}
/* Les boutons : petits, monospace, neutres — la grammaire des controles
   discrets de l'etalon (.bouton-icone). Le filet de contour est
   --filet-controle : le --filet nu fait 1,28:1 la ou WCAG 1.4.11 veut
   3:1 (lecon T5). Taille de police FIXE : le groupe vit aussi dans des
   h2/h3. / Small mono neutral buttons; --filet-controle for 3:1. */
.actions-element button {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .68rem; line-height: 1; font-weight: 400;
  color: var(--encre-douce); background: var(--papier);
  border: 1px solid var(--filet-controle); border-radius: 4px;
  /* min-height 24px : cible WCAG 2.5.8 (les ~22 px mesures en
     verification U1 passaient dessous). / 24px target size. */
  padding: .32rem .55rem; min-height: 24px; cursor: pointer;
}
.actions-element button:hover {
  color: var(--encre); background: var(--papier-creux);
}
.actions-element button.danger:hover {
  color: var(--danger); border-color: var(--danger);
}

/* Le placeholder d'un element masque : visible en mode structure
   seulement, en retrait assume — filet pointille, encre douce (4,6:1
   sur papier clair), JAMAIS un fond semantique plein sous du texte
   fige. / Masked-element placeholder, structure mode only. */
.element-masque { display: none; }
#zone-lecture.mode-structure .element-masque {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  border: 1px dashed var(--filet-controle); border-radius: 5px;
  background: var(--papier-creux); color: var(--encre-douce);
  padding: .45rem .7rem; margin: .6rem 0; font-size: .85rem;
  list-style: none;
}
.element-masque .etiquette {
  font-family: ui-monospace, Menlo, monospace; font-size: .62rem;
  text-transform: uppercase; letter-spacing: .07em;
}
.element-masque .extrait { font-style: italic; }
.element-masque button {
  font-family: ui-monospace, Menlo, monospace; font-size: .68rem;
  color: var(--encre); background: var(--papier);
  border: 1px solid var(--filet-controle); border-radius: 4px;
  padding: .28rem .5rem; cursor: pointer;
}
.element-masque button:hover { background: var(--papier-creux); }

/* Le dialogue des formulaires d'element (corriger, couper en deux).
   <dialog> natif : voile, focus et Echap viennent du navigateur ; ici,
   seulement l'habillage aux tokens du theme. / Element form dialog:
   native <dialog>, theme tokens only. */
.dialogue-element {
  /* margin:auto — le preflight Tailwind (*{margin:0}) ecrase le
     centrage NATIF de <dialog> : sans cette ligne il se colle en haut
     a gauche (verification navigateur U1, defaut D1). Bordure en
     --filet-controle + ombre : en sombre, --filet contre la page
     voilee faisait 1,53:1 — la surface du modal ne se detachait pas
     (defaut D2). / margin:auto restores native centering against the
     Tailwind preflight; controle-grade border + shadow so the modal
     separates from the dimmed page in dark mode. */
  margin: auto;
  border: 1px solid var(--filet-controle); border-radius: 8px;
  background: var(--papier); color: var(--encre);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .35);
  padding: 1.1rem 1.3rem; max-width: 34rem; width: calc(100% - 2rem);
}
.dialogue-element::backdrop { background: rgba(0, 0, 0, .45); }
/* La zone d'erreur du dialogue : la grammaire des etats de ce design
   system — un filet GAUCHE danger, jamais un fond semantique plein
   sous du texte fige (defaut D3 de la verification U1).
   / The dialog's error zone follows the left-rule state grammar. */
.dialogue-element .erreur-du-dialogue {
  border-left: 3px solid var(--danger);
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  border-radius: 0 5px 5px 0;
  padding: .45rem .7rem; margin: .5rem 0 0;
  font-size: .85rem;
}
.dialogue-element h2 {
  font-size: 1.05rem; font-weight: 600; margin: 0 0 .8rem;
}
.dialogue-element label { font-size: .85rem; }
.dialogue-element textarea {
  width: 100%; margin: .4rem 0; padding: .5rem .6rem;
  font-family: Georgia, serif; font-size: .95rem; line-height: 1.5;
  color: var(--encre); background: var(--papier-panneau);
  border: 1px solid var(--filet-controle); border-radius: 5px;
}
.dialogue-element .aide {
  font-size: .78rem; color: var(--encre-douce); margin: .2rem 0 .6rem;
}
.dialogue-element .pied-du-dialogue {
  display: flex; justify-content: flex-end; gap: .5rem; margin-top: .6rem;
}
.dialogue-element .bouton-du-dialogue {
  font-family: ui-monospace, Menlo, monospace; font-size: .75rem;
  color: var(--encre); background: var(--papier);
  border: 1px solid var(--filet-controle); border-radius: 5px;
  padding: .4rem .8rem; cursor: pointer;
}
.dialogue-element .bouton-du-dialogue:hover { background: var(--papier-creux); }
/* Le bouton principal : encre sur papier INVERSES — le couple le plus
   contraste du systeme, dans les deux themes (jamais de white fige,
   lecon T10/F4-F10). / Primary: inverted ink/paper, never fixed white. */
.dialogue-element .bouton-du-dialogue.principal {
  background: var(--encre); color: var(--papier); border-color: var(--encre);
}

/* Relecture U1 (defaut H3) : l'include des boutons vit aussi dans des
   <pre> ou white-space:pre rend chaque retour a la ligne VISIBLE — les
   blancs internes au groupe redeviennent ordinaires ici.
   / Inside <pre>, internal whitespace must collapse again. */
.actions-element { white-space: normal; }

/* La puce d'etat de l'ingestion (U2) : discrete en marche normale,
   grammaire du filet GAUCHE danger en echec. Le voyant est decoratif
   (aria-hidden), l'etat est dans le TEXTE. / Ingestion status chip. */
.etat-ingestion {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  font-family: ui-monospace, Menlo, monospace; font-size: .78rem;
  color: var(--encre-douce); background: var(--papier-panneau);
  border: 1px solid var(--filet); border-radius: 5px;
  padding: .45rem .7rem; margin: 0 0 1rem;
}
.etat-ingestion .voyant {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--encre-douce); align-self: center;
  animation: voyant-ingestion 1.6s ease-in-out infinite;
}
@keyframes voyant-ingestion {
  0%, 100% { opacity: .35; } 50% { opacity: 1; }
}
.etat-ingestion.echec {
  border-left: 3px solid var(--danger); border-radius: 0 5px 5px 0;
  color: var(--encre);
}
.etat-ingestion.echec .voyant { background: var(--danger); animation: none; }
.etat-ingestion .detail { font-style: italic; color: var(--encre-douce); }
.etat-ingestion button {
  font-family: ui-monospace, Menlo, monospace; font-size: .7rem;
  color: var(--encre); background: var(--papier);
  border: 1px solid var(--filet-controle); border-radius: 4px;
  padding: .32rem .55rem; min-height: 24px; cursor: pointer;
}
.etat-ingestion button:hover { background: var(--papier-creux); }

/* ------------------------------------------------------------------ */
/* AVERTISSEMENT DE PLAFOND — le nom et la forme sont ceux de          */
/* l'etalon (maquette.html § 8, `.avertissement-plafond`).             */
/*                                                                      */
/* Il avait d'abord ete recree sous un autre nom et un autre style :    */
/* la maquette PREVOYAIT deja ce cas, et deux vocabulaires pour la      */
/* meme chose est exactement l'ecart qu'on cherche a supprimer.         */
/*                                                                      */
/* Ce qu'on ne reprend PAS de l'etalon, c'est son TEXTE : il affiche    */
/* « 623 marques : au-dela de 400, le texte est rendu nu               */
/* (rendu_elements.py:215) ». C'est un message de diagnostic pour qui   */
/* developpe la maquette, pas une phrase adressee a un lecteur.        */
/* / Same name and shape as the mock; not its developer-facing text.   */
.avertissement-plafond {
  margin: .4rem 0 .6rem;
  font-family: ui-monospace, Menlo, monospace; font-size: .68rem;
  color: var(--encre); border-left: 2px solid var(--danger);
  padding-left: .5rem;
}

/* Etiquette « ancre detachee » sur une carte du panneau (10 aout).    */
/* Reprise de l'etalon (maquette.html l. 1671-1682) : petite capitale  */
/* monospace, discrete mais lisible. Elle doit se voir sans crier :    */
/* c'est un etat a trancher, pas une erreur.                           */
/* / A state to be resolved, not an error to shout about.              */
.carte-ancre-detachee {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .55rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--encre-douce); margin-bottom: .3rem;
}

/* Ancre estompee par le filtre par contributeur (recablage du 10 aout). */
/* Le filtre estompait les pastilles en marge ; celles-ci ayant disparu  */
/* avec l'ancien moteur, il ne trouvait plus un noeud et etait INERTE.   */
/* Il agit maintenant sur l'ancre inline elle-meme. On BAISSE l'ancre    */
/* hors filtre au lieu de rehausser celles qui restent : le texte garde  */
/* son contraste de lecture, seul le surlignage s'efface.                */
/* / Dim the anchor, never the text: reading contrast is preserved.      */
#readability-content .hl-extraction.ancre-hors-filtre {
  background: transparent;
  box-shadow: none;
  opacity: 1;
}

/* ================================================================== */
/* LA GOUTTIERE (etalon maquette.html § .bloc, § 7)                   */
/*                                                                     */
/* Chaque bloc de lecture est une grille « gouttiere | corps », avec   */
/* un filet d'etat vertical entre les deux. La gouttiere porte le      */
/* reperage du passage.                                                */
/*                                                                     */
/* CE QUI SE VOIT, ET CE QUI NE SE VOIT PAS                            */
/* Un lecteur n'a rien a faire de « SECTION_HEADER #6 » ni d'une       */
/* empreinte : label, numero et empreinte ne paraissent qu'en mode     */
/* structure. Le COMPTEUR D'IDEES, lui, s'adresse au lecteur — il      */
/* reste visible, c'est le seul habitant permanent de la gouttiere.    */
/* / Only the idea count speaks to a reader; the rest is diagnostics.  */
/* ================================================================== */
#readability-content {
  --gouttiere: 46px;
  /* LA GOUTTIERE S'AJOUTE A LA LARGEUR DE LECTURE, elle ne s'y prend
     pas. Sans cette compensation, poser la grille retire 70px a la
     colonne de texte (mesure au navigateur : 648 -> 578px) : on aurait
     paye la gouttiere en confort de lecture, ce que l'etalon ne fait
     pas — il donne 672px au corps au meme viewport.
     / The gutter adds to the reading width instead of eating it. */
  max-width: calc(100% + var(--gouttiere) + 3rem);
  /* La compensation ne peut pas depasser la place REELLEMENT libre a
     gauche, sinon elle pousse le texte hors de l'ecran. Mesure : la
     marge disponible n'atteint 70px qu'a ~845px de viewport, et 120px
     (gouttiere audio) qu'a ~945px — entre 769px et ces seuils, la
     lecture etait rognee. `min()` sur la moitie de la marge du
     conteneur borne le debordement au lieu de le supposer.
     / Clamp the pull to the space that actually exists. */
  /* Ce sont les marges NEGATIVES qui elargissent la boite, pas le
     max-width : en flux normal, un bloc prend la largeur de son parent
     moins ses marges, et `max-width` ne fait qu'autoriser sans pousser.
     A gauche on rend la place prise par la gouttiere ; a droite on
     rejoint les 672px de corps de l'etalon.
     / Negative margins widen the box; max-width alone would not. */
  margin-left: min(0px, calc(-1 * (var(--gouttiere) + 1.5rem)));
  margin-right: -1.5rem;
}
#readability-content.mode-structure,
#zone-lecture.mode-structure #readability-content {
  --gouttiere: 124px;
}

/* UN AUDIO A UNE GOUTTIERE PLUS LARGE (etalon l. 77-78).
   Elle porte davantage : le nom du locuteur et le minutage du tour de
   parole, la ou un document ecrit n'a qu'un label et un numero. 96px
   en lecture, 150px en mode structure — les memes valeurs que
   l'etalon, qui les a calees sur « Marie-Christine 00:14:32 ».
   / An audio gutter carries a speaker and a timecode: it is wider. */
#readability-content:has([data-media="audio"]),
#readability-content [data-testid="blocs-elements"][data-media="audio"] {
  --gouttiere: 96px;
}
#zone-lecture.mode-structure
  #readability-content:has([data-media="audio"]),
#zone-lecture.mode-structure
  #readability-content [data-testid="blocs-elements"][data-media="audio"] {
  --gouttiere: 150px;
}

/* Repli pour les navigateurs sans `:has()` — la variable est alors
   posee par l'attribut du conteneur de blocs, qui suffit.
   / Fallback: the blocks container carries the attribute too. */
@supports not selector(:has(*)) {
  #readability-content [data-testid="blocs-elements"][data-media="audio"] {
    --gouttiere: 96px;
  }
}

#readability-content .bloc {
  display: grid;
  grid-template-columns: var(--gouttiere) 1fr;
  gap: 0 1.5rem;
  position: relative;
}

/* UN CONTENU LARGE DEFILE DANS SA BOITE, IL N'EMPORTE PAS LA PAGE
   (mesure du 12 aout, PDF etalon, fenetre de 1600px)

   Les tableaux sortent du service en `<pre>` (rendu_elements.py:87-94,
   ecart n°6 de l'etalon, non encore comble). Un `<pre>` ne se replie
   pas : celui du PDF etalon mesurait 3 162px et poussait la zone de
   lecture a 3 419px pour 1 586 de large. Resultat : une barre de
   defilement horizontale sous un texte de lecture, et un texte qui
   glissait lateralement des qu'on la touchait.

   Deux declarations, pas une. `overflow-x` seul ne suffit PAS : dans
   une grille, une piste `1fr` a un `min-width: auto` implicite qui la
   laisse grandir a la taille de son contenu — la boite du corps aurait
   grandi avec le `<pre>`, et le defilement n'aurait jamais eu lieu.
   `min-width: 0` rend a la piste le droit de se retrecir.
   / Wide content scrolls inside its own box. `min-width: 0` is required:
   a `1fr` track has implicit `min-width: auto` and would grow instead. */
#readability-content .bloc > .corps {
  min-width: 0;
  /* `min-width: 0` rend la piste retrecissable ; encore faut-il que son
     contenu accepte de se couper. Un mot insecable long — une URL dans
     une note web — pousserait la boite exactement comme le `<pre>`, par
     un autre chemin et sans etre couvert par `overflow-x` ci-dessous.
     / Shrinkable is not enough: a long unbreakable word would push the
     box just as the <pre> did, by another route. */
  overflow-wrap: anywhere;
}
#readability-content .bloc > .corps pre {
  overflow-x: auto;
  max-width: 100%;
}

#readability-content .gouttiere {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .62rem; line-height: 1.45;
  color: var(--encre-douce); text-align: right;
  padding-top: .5rem;
  display: flex; flex-direction: column; align-items: flex-end; gap: .2rem;
  user-select: none;
}

/* Diagnostic : reserve au mode structure. / Debug only. */
#readability-content .etiquette-label,
#readability-content .numero-element,
#readability-content .empreinte { display: none; }
#zone-lecture.mode-structure #readability-content .etiquette-label,
#zone-lecture.mode-structure #readability-content .numero-element,
#zone-lecture.mode-structure #readability-content .empreinte {
  display: inline-block;
}
#readability-content .etiquette-label {
  font-size: .53rem; text-transform: uppercase; letter-spacing: .07em;
  padding: .04rem .28rem;
  border: 1px solid var(--filet); border-radius: 2px;
}
#readability-content .numero-element,
#readability-content .empreinte { opacity: .55; }

/* Le filet d'etat : ambre des qu'une idee du passage est commentee.  */
/* La COULEUR ne porte pas seule l'information — le compteur d'idees  */
/* et le surlignage disent la meme chose autrement (daltonien-safe).  */
/* / Colour never carries the meaning alone.                          */
#readability-content .filet-etat {
  position: absolute;
  left: calc(var(--gouttiere) + .55rem);
  top: .5rem; bottom: .1rem;
  width: 2px; border-radius: 1px;
  background: transparent;
}
#readability-content .bloc[data-etat="debattu"] .filet-etat {
  background: var(--filet-statut-commente);
}
#zone-lecture.mode-structure #readability-content .filet-etat {
  background: var(--filet);
}
#zone-lecture.mode-structure
  #readability-content .bloc[data-etat="debattu"] .filet-etat {
  background: var(--filet-statut-commente);
}

/* Le compteur d'idees. Cible tactile de 24px (WCAG 2.5.8) : l'etalon */
/* s'en tient a 1,05rem, trop petit pour un doigt.                    */
/* / 24px touch target; the mock's 1.05rem is too small for a finger. */
#readability-content .compteur-idees {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 24px; padding: 0 .3rem;
  border: 1px solid var(--filet-controle); border-radius: 999px;
  font-size: .62rem; font-family: ui-monospace, Menlo, monospace;
  background: none; color: var(--encre); cursor: pointer;
  transition: border-color .12s, color .12s;
}
/* Au survol, seul le CONTOUR prend la teinte d'etat : le texte, lui,
   garde l'encre pleine. Mesure : `--filet-statut-commente` en TEXTE
   tombe a 3,82:1 sur le papier clair, sous les 4,5:1 exiges. Un
   contour n'a besoin que de 3:1 (WCAG 1.4.11), il passe.
   / On hover only the border takes the state colour; text stays ink. */
#readability-content .compteur-idees:hover {
  border-color: var(--filet-statut-commente);
  color: var(--encre);
}
#readability-content .compteur-idees:focus-visible {
  outline: 2px solid var(--encre); outline-offset: 2px;
}

/* Sous 768px la gouttiere mange la largeur de lecture : elle passe   */
/* au-dessus du bloc, en ligne. / Below 768px the gutter goes inline. */
@media (max-width: 960px) {
  /* ON ANNULE LA COMPENSATION DE GOUTTIERE.
     Elle deborde le conteneur vers la gauche pour rendre au texte la
     place que la grille lui prend. En mobile il n'y a plus de colonne
     de gouttiere — elle passe au-dessus du bloc — donc plus rien a
     compenser : la laisser rognait la lecture de 54px hors ecran,
     lettres coupees et aucun scroll pour aller les chercher.

     LE SEUIL EST A 960px, PAS 768. Entre les deux, la marge du
     conteneur ne suffit pas encore a absorber la compensation (70px
     pour un document, 120px pour un audio dont la gouttiere est plus
     large) : la bande 769-945px etait rognee elle aussi.
     / 960px, not 768: the margin only covers the pull past ~945px. */
  #readability-content { max-width: 100%; margin-left: 0; margin-right: 0; }
  #readability-content .bloc { grid-template-columns: 1fr; gap: 0; }
  #readability-content .gouttiere {
    flex-direction: row; align-items: center; justify-content: flex-end;
    gap: .4rem; padding-top: .2rem;
  }
  #readability-content .filet-etat { left: -.6rem; }
}

/* ================================================================== */
/* TYPOGRAPHIE DE LECTURE — alignee sur l'etalon (maquette.html § 5-7)*/
/*                                                                     */
/* Mesure de l'ecart avant alignement : l'application lisait en 18px / */
/* interligne 1,85 (Tailwind `prose-lg`) contre 17px / 1,7 pour        */
/* l'etalon, avec des marges de paragraphe de 20px la ou l'etalon en   */
/* met ZERO.                                                           */
/*                                                                     */
/* LE POINT QUI CHANGE TOUT : QUI PORTE L'ESPACEMENT                   */
/* Dans l'etalon, `.corps > * { margin: 0 }` — un paragraphe n'a pas   */
/* de marge. C'est le BLOC qui espace, et sa valeur depend du label :  */
/* un titre respire plus qu'une puce. L'espacement devient donc une    */
/* propriete de la STRUCTURE du document, pas de la balise rendue —    */
/* ce qui est exactement ce que le moteur ELEMENT sait dire.           */
/* / The block spaces, not the paragraph: spacing follows structure.   */
/* ================================================================== */
#readability-content {
  font-size: 17px;
  line-height: 1.7;
}

#readability-content .bloc {
  scroll-margin-top: 7rem;
  margin-bottom: 1.15rem;
}
#readability-content .bloc[data-label="title"] { margin-bottom: 1.6rem; }
#readability-content .bloc[data-label="section_header"] {
  margin-top: 2.6rem; margin-bottom: 1rem;
}
#readability-content .bloc[data-label="list_item"] { margin-bottom: .15rem; }
#readability-content .bloc[data-label="picture"] { margin: 2rem 0; }
#readability-content .bloc[data-label="table"] { margin: 1.8rem 0; }

/* Le corps ne porte AUCUNE marge : elle appartient au bloc. */
/* / The body carries no margin; the block owns it. */
#readability-content .corps > * { margin: 0; }
#readability-content .corps h2 {
  font-size: 1.42rem; line-height: 1.3; letter-spacing: -.012em;
}
#readability-content .corps h3 {
  font-size: 1.1rem; line-height: 1.35; letter-spacing: -.008em;
}
#readability-content .corps ul { margin: 0; padding-left: 1.15rem; }
#readability-content .corps li { margin: 0; }

/* Numero de page et bouton « voir la source » (etalon § 7).          */
/* Le bouton se revele au survol du bloc : en lecture il ne doit pas  */
/* encombrer la gouttiere, mais rester a portee. En mode structure il */
/* prend son libelle complet, la gouttiere y est assez large.         */
/* / Revealed on block hover; full label in structure mode.           */
/* PAS D'OPACITE SUR UN TEXTE QU'ON DOIT LIRE.
   `--encre-douce` a .75 tombe a ~3,4:1 sur le papier, sous les 4,5:1
   d'un texte de 10px. L'etalon met .75 parce que sa gouttiere est un
   decor ; ici le numero de page se lit. Pleine encre douce : 5,4:1.
   / Opacity dropped this to 3.4:1; it is text, not decoration. */
#readability-content .numero-page {
  font-variant-numeric: tabular-nums;
}
#readability-content .bouton-voir-source {
  border: none; background: none; padding: 0; cursor: pointer;
  font-size: .62rem; color: var(--encre-douce);
  opacity: 0; transition: opacity .12s ease; white-space: nowrap;
  min-height: 24px;
}
#readability-content .bouton-voir-source .libelle-source { display: none; }
#readability-content .bloc:hover .bouton-voir-source,
#readability-content .bouton-voir-source:focus-visible { opacity: 1; }
#readability-content .bouton-voir-source:focus-visible {
  outline: 2px solid var(--encre); outline-offset: 2px;
}
#zone-lecture.mode-structure #readability-content .bouton-voir-source {
  opacity: 1; font-size: .58rem;
  text-decoration: underline; text-underline-offset: 2px;
}
#zone-lecture.mode-structure
  #readability-content .bouton-voir-source .libelle-source { display: inline; }
#zone-lecture.mode-structure
  #readability-content .bouton-voir-source .icone-source { display: none; }

/* Glissiere d'un AUDIO : le locuteur, puis le minutage (etalon § 7).  */
/* Le locuteur porte une pastille de couleur — la meme d'un bloc a     */
/* l'autre pour une meme voix — parce que suivre un debat, c'est       */
/* d'abord suivre qui parle. La couleur ne porte pas l'information     */
/* seule : le NOM est ecrit a cote.                                    */
/* / Colour never carries it alone: the name is written beside it.     */
#readability-content .etiquette-locuteur {
  color: var(--encre); font-weight: 600;
  display: inline-flex; align-items: center; gap: .28rem;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
#readability-content .etiquette-locuteur::before {
  content: ""; display: inline-block; flex: none;
  width: .46rem; height: .46rem; border-radius: 50%;
  background: var(--couleur-locuteur, var(--encre-douce));
}
#readability-content .ligne-minutage {
  display: flex; align-items: center; gap: .28rem;
  font-variant-numeric: tabular-nums;
}
/* LE BOUTON D'ECOUTE DE LA GOUTTIERE (etalon § 7, l. 455-460).

   Il s'EFFACE jusqu'a ce qu'on en ait besoin : une colonne de fleches ▶
   le long du texte ferait de la marge une barre d'outils verticale,
   alors qu'elle est une marge de REPERES. Il parait au survol du bloc,
   et pendant la lecture du tour — la ou l'oeil est deja.

   ECART ASSUME AVEC L'ETALON : sa couleur est `--encre-douce`, non le
   violet `--cible`. Ce violet marque dans l'etalon ce qui n'existe PAS
   ENCORE ; le garder maintenant que le bouton fonctionne mentirait.
   / It fades away until needed: a column of ▶ would turn the margin
   into a vertical toolbar. Colour is --encre-douce, not the mock's
   "unbuilt" violet. */
#readability-content .bouton-ecouter {
  appearance: none; background: none; border: none;
  padding: 0; margin: 0; line-height: 1;
  font-size: .66rem; color: var(--encre-douce);
  cursor: pointer; opacity: 0; transition: opacity .12s ease;
}
#readability-content .bloc:hover .bouton-ecouter,
#readability-content .bloc.bloc-en-lecture .bouton-ecouter { opacity: 1; }
#readability-content .bouton-ecouter:hover { color: var(--encre); }

/* AU CLAVIER, IL DOIT SE MONTRER. L'etalon ne revele le bouton qu'au
   SURVOL — un geste que le clavier ne fait pas. Sans cette regle, on
   tabule sur un bouton invisible : le focus est quelque part, et rien
   a l'ecran ne dit ou. C'est le meme defaut qu'un `outline: none`.
   / The mock reveals it on hover only — a gesture the keyboard cannot
   make. Without this, focus lands on an invisible control. */
#readability-content .bouton-ecouter:focus-visible {
  opacity: 1; color: var(--encre);
  outline: 2px solid var(--info); outline-offset: 2px; border-radius: 2px;
}

/* LE TOUR EN COURS D'ECOUTE (etalon l. 559). Un fond ambre a 7 % sur
   TOUT le bloc — la teinte de `--statut-commente`, deja celle du filet
   d'etat, donc rien de neuf a apprendre.

   Une premiere version posait un liseré dans la gouttiere, par crainte
   qu'un fond n'abaisse le contraste du texte. MESURE AU NAVIGATEUR, le
   13 aout, sur la note 8 :

     · clair  : 16,79:1 -> 14,37:1  (perte de 2,42 points)
     · sombre : 15,10:1 -> 15,30:1  (gain de 0,20)

   La perte existe donc bien en clair, et elle n'est pas negligeable en
   valeur absolue — mais 14,37:1 reste trois fois le seuil AA de 4,5:1.
   La crainte etait fondee sur le principe, sans consequence sur ce
   texte-la, qui part de tres haut.
   / An earlier version used a gutter rule, fearing contrast loss.
   Measured: 16.79 -> 14.37 in light (real, but still 3x the AA
   threshold), 15.10 -> 15.30 in dark. */
#readability-content .bloc.bloc-en-lecture {
  background: color-mix(in srgb, var(--statut-commente) 7%, transparent);
}

/* ------------------------------------------------------------------ */
/* EDITEUR EN PLACE (etalon § 11)                                      */
/*                                                                     */
/* Le corps du bloc s'efface, le champ prend sa place — meme largeur,  */
/* meme typographie, meme interligne. Corriger ne doit pas donner      */
/* l'impression de changer d'endroit : c'est le meme texte, ecrit      */
/* autrement. Le contour ambre est le seul signal qu'on edite.         */
/* / The body gives way to the field: same width, same type, same      */
/* place. Correcting is not going somewhere else.                      */
/* ------------------------------------------------------------------ */
#readability-content .editeur { display: none; }
#readability-content .bloc.est-en-edition .corps > :not(.editeur) {
  display: none;
}
#readability-content .bloc.est-en-edition .editeur { display: block; }
#readability-content .editeur textarea {
  width: 100%; font: inherit; line-height: 1.7;
  color: var(--encre); background: var(--papier);
  /* Le token FILET, pas l'ambre brut : celui-ci fait 2,2:1 sur le
     papier clair, sous les 3:1 d'un contour de controle (1.4.11).
     C'est la convention de ce fichier depuis le lot T10.
     / The filet token: raw amber is 2.2:1 on light paper. */
  border: 1px solid var(--filet-statut-commente); border-radius: 4px;
  padding: .5rem .6rem; resize: vertical; min-height: 4.5rem;
}
#readability-content .editeur-barre {
  display: flex; align-items: center; gap: .45rem; margin-top: .45rem;
  font-family: ui-monospace, Menlo, monospace; font-size: .64rem;
  flex-wrap: wrap;
}
#readability-content .editeur-aide {
  margin-left: auto; color: var(--encre-douce);
}
/* L'etiquette du champ est lue par les lecteurs d'ecran, pas montree : */
/* le contexte visuel (le bloc s'efface, le champ apparait a sa place)  */
/* dit deja ce qu'on edite. / Read aloud, not shown: context says it.   */
#readability-content .editeur .etiquette-invisible {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
#readability-content .erreur-de-l-editeur {
  margin: .4rem 0 0; padding-left: .5rem;
  border-left: 3px solid var(--danger);
  color: var(--encre); font-size: .78rem;
}

/* ================================================================== */
/* LE SURLIGNAGE SE REVELE, IL NE S'IMPOSE PAS (etalon § 9)           */
/*                                                                     */
/* L'etalon le dit dans son propre commentaire : « Au repos le texte  */
/* est nu. Il se revele au survol du bloc, et s'allume au choix        */
/* explicite. » Trois niveaux, donc, et l'application n'en avait       */
/* qu'un : tout surligne, tout le temps.                               */
/*                                                                     */
/* CE QUE CE CHANGEMENT DIT DU PRODUIT                                 */
/* Un texte entierement surligne n'est plus un texte : c'est une       */
/* liste de citations. Le parti pris de la maquette est l'inverse —    */
/* on lit d'abord, on voit les idees quand on s'approche, on en        */
/* choisit une quand on la veut. Le document reste le sujet.           */
/* / Bare at rest, revealed on block hover, lit on explicit choice.    */
/* ================================================================== */

/* 1. AU REPOS : nu. Le statut ne pose qu'une VARIABLE, aucun fond.   */
#readability-content mark.hl-extraction {
  background: none;
  border-bottom: none;
  color: inherit;
  cursor: pointer;
  border-radius: 2px;
  padding: .04em 0;
  transition: background-color .14s ease, box-shadow .14s ease;
}
#readability-content .hl-extraction[data-statut="nouveau"] {
  --couleur-statut: var(--filet-statut-nouveau);
}
#readability-content .hl-extraction[data-statut="commente"],
#readability-content .hl-extraction[data-statut="discutable"],
#readability-content .hl-extraction[data-statut="discute"] {
  --couleur-statut: var(--filet-statut-commente);
}
#readability-content .hl-extraction[data-statut="consensuel"] {
  --couleur-statut: var(--succes);
}
#readability-content .hl-extraction[data-statut="controverse"] {
  --couleur-statut: var(--danger);
}

/* 2. AU SURVOL DU BLOC : les idees de CE passage se revelent, dans un */
/*    gris neutre — on montre OU elles sont, pas encore lesquelles.    */
#readability-content .bloc:hover mark.hl-extraction {
  background-color: var(--revelation);
}

/* 3. AU SURVOL DE L'ANCRE puis AU CHOIX : la teinte du statut, puis   */
/*    le liseré. Le liseré porte l'etat sans la couleur seule.         */
/* La regle du BLOC survole (1,3,1) l'emporterait sur `mark:hover`
   (1,2,1) : mesure au navigateur, l'ancre survolee restait grise. On
   remet le survol de l'ancre au-dessus en le qualifiant du bloc.
   / Measured: the block rule outranked the anchor's own hover. */
#readability-content .bloc:hover mark.hl-extraction:hover,
#readability-content mark.hl-extraction:hover {
  background-color: color-mix(
    in srgb, var(--couleur-statut, var(--encre-douce)) 14%, transparent
  );
}
#readability-content .bloc:hover mark.hl-extraction.ancre-active,
#readability-content mark.hl-extraction.ancre-active {
  background-color: color-mix(
    in srgb, var(--couleur-statut, var(--encre-douce)) 22%, transparent
  );
  box-shadow: inset 0 -2px 0 0 var(--couleur-statut, var(--encre-douce));
}
#readability-content mark.hl-extraction:focus-visible {
  outline: 2px solid var(--encre); outline-offset: 2px;
}

/* Les marques IMBRIQUEES gardent leurs liserés lisibles l'un sous     */
/* l'autre — sinon deux idees sur le meme passage n'en font qu'une.    */
#readability-content mark.hl-extraction[data-superposition="2"] {
  padding-bottom: .14em;
}
#readability-content mark.hl-extraction[data-superposition="3"],
#readability-content mark.hl-extraction[data-superposition="4"] {
  padding-bottom: .24em;
}

/* CLAVIER ET TACTILE : sans survol, la revelation n'arriverait        */
/* jamais. Un bloc qui contient le focus se revele comme au survol.    */
/* / Without hover there is no reveal: focus does it too.              */
#readability-content .bloc:focus-within mark.hl-extraction {
  background-color: var(--revelation);
}
@media (hover: none) {
  #readability-content mark.hl-extraction {
    background-color: var(--revelation);
  }
}

/* ================================================================== */
/* LE PANNEAU VIT DANS LE CORPS DE LA NOTE (etalon § 5 et § 13)       */
/*                                                                     */
/* Decision du mainteneur, 12 aout : « Elle doit vraiment etre         */
/* integree dans la note, et non pas ailleurs. »                       */
/*                                                                     */
/* CE QUI A ETE ESSAYE AVANT, ET POURQUOI CA NE SUFFISAIT PAS          */
/* Une premiere version gardait le panneau `fixed` et lui RENDAIT sa   */
/* place par un `padding-right` sur la zone de lecture. A l'oeil, sur  */
/* une note ouverte, le resultat etait le bon — d'ou l'argument, ecrit */
/* ici le 9 aout, qu'on evitait ainsi de toucher au gabarit global.    */
/*                                                                     */
/* Sauf qu'un element hors flux n'appartient a aucun ecran. Il RESTAIT */
/* quand on quittait la note pour les carnets ou les bases, avec son   */
/* bouton dans la barre, alors qu'il ne parle que d'une note. Et sa    */
/* place devait etre recalculee a la main a chaque changement de       */
/* largeur, au lieu d'etre repartie par une grille.                    */
/*                                                                     */
/* Le panneau est donc passe DANS le plateau (base.html), et cette     */
/* couche ne fait plus que decrire la grille.                          */
/* / An out-of-flow element belongs to no screen: it survived leaving  */
/* the note. It now lives inside the plateau grid.                     */
/*                                                                     */
/* SOUS 1400px, RIEN NE CHANGE : le panneau redevient le tiroir        */
/* superpose de 36rem, avec son voile. Ceder 368px au panneau sur un   */
/* ecran etroit rendrait le texte illisible.                           */
/* ================================================================== */

/* Une seule colonne par defaut : c'est le regime du tiroir.
   / One column by default: that is the drawer regime. */
.plateau {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 0;
}

@media (min-width: 1400px) {
  /* Pas de voile : le texte reste lisible et cliquable a gauche. */
  #drawer-backdrop { display: none !important; }

  /* La grille repartit la place. Plus de `padding-right` compensatoire :
     c'est la colonne qui occupe l'espace, pas un vide menage pour elle.
     / The grid allocates the space; no compensating padding. */
  body.panneau-integre .plateau {
    grid-template-columns: 1fr var(--panneau);
  }

  /* Le panneau REJOINT le flux. Chaque `!important` bat une classe
     Tailwind ecrite en dur dans le gabarit ou son style inline — le
     build Tailwind est fige, on ne peut pas les retirer la-bas sans
     casser le mode tiroir, qui s'en sert encore sous 1400px.
     / Each !important overrides a hardcoded Tailwind class still needed
     by the drawer regime below 1400px. */
  body.panneau-integre #drawer-overlay {
    position: static !important;   /* .fixed */
    width: auto !important;        /* style inline min(36rem, 100vw) */
    transform: none !important;    /* .translate-x-full */
    pointer-events: auto !important;  /* .pointer-events-none */
    z-index: auto !important;      /* style inline 70 */
    height: 100%;
    min-height: 0;

    /* Integre, il n'a plus l'ombre portee d'un objet flottant : un
       filet suffit a l'en separer. / A rule replaces the drop shadow. */
    box-shadow: none;
    border-left: 1px solid var(--filet);
  }

  /* La zone de lecture garde une respiration a droite, pas une place
     reservee. / Breathing room, not reserved space. */
  body.panneau-integre #zone-lecture {
    padding-right: 2rem;
  }

  /* Le menu des taches deborde sur la colonne de droite et s'y faisait
     tronquer. Mesure faite sur le menu « Dashboard », retire depuis :
     240px perdus sur 352. Le menu des taches a la meme geometrie.
     / A toolbar dropdown must outrank what it overlaps. */
  .taches-dropdown-wrapper {
    z-index: 71;
  }
}

/* ------------------------------------------------------------------ */
/* LA CARTE DU PANNEAU : SOBRE PAR DEFAUT (etalon § 14)                */
/*                                                                     */
/* La citation ne s'affiche QUE sur la carte choisie. Une liste ou     */
/* chaque carte deroule son extrait n'est plus une liste : c'est un    */
/* mur de texte qui redit ce que la page dit deja a cote. L'etalon     */
/* garde le titre et le statut, et ouvre la citation au clic.          */
/* / The quote opens on the chosen card; a wall of quotes is not a     */
/* list. */
#drawer-contenu .drawer-carte-compacte .typo-citation {
  display: none;
}
#drawer-contenu .drawer-carte-compacte.drawer-carte-active .typo-citation {
  display: block;
}

/* LE PASSAGE QUI ENTOURE LA CITATION, sur la carte ACTIVE elle aussi.
   C'est le meme geste que sur la fiche de preuve : on clique, et le
   document s'ouvre autour de ce qu'on cite. Une citation sortie de son
   paragraphe se lit mal — et parfois faux.

   IL EST GRIS, ET PLUS PETIT QUE LA CITATION. La carte existe pour
   montrer ce qui est CITE ; le passage l'eclaire, il ne le concurrence
   pas. Contraste de `--encre-douce` sur le fond de la carte : 4,74:1 en
   clair et 6,55:1 en sombre — au-dessus des 4,5:1 d'un texte courant,
   alors qu'il n'est ici que du decor.

   `pre-line` REND LE SAUT DE LIGNE que le service pose a la jonction de
   deux elements. Sans lui, le paragraphe precedent et la citation se
   liraient comme un seul, et le passage mentirait sur la structure du
   document. / The newline at the element join is load-bearing. */
#drawer-contenu .contexte-de-la-citation {
  display: block;
  color: var(--encre-douce);
  font-size: .92em;
  white-space: pre-line;
}
/* La citation reste la seule a pleine encre : c'est elle qu'on cite.
   / The quote alone keeps full ink: it is the thing being cited. */
#drawer-contenu .citation-de-la-carte {
  display: block;
  color: var(--typo-citation-color);
  margin: .15rem 0;
}

/* ================================================================== */
/* LA COLONNE DE LECTURE EST CENTREE (etalon § 5 « DISPOSITION »)     */
/*                                                                     */
/* Mesure du 12 aout, fenetre de 1600px : le bloc faisait bien 742px  */
/* — la largeur de l'etalon — mais son centre tombait 51px A GAUCHE   */
/* du centre de l'espace disponible. L'etalon, lui, tient dans 13px.  */
/*                                                                     */
/* LES DEUX FACONS D'ALIGNER LE TITRE SUR LE TEXTE                     */
/* Le corps du texte commence apres la gouttiere. Pour que le titre    */
/* tombe a la meme abscisse, il y a deux chemins :                     */
/*                                                                     */
/*   · l'etalon DECALE LE TITRE vers la droite                         */
/*     (`.entete-page { padding-left: calc(gouttiere + 1.5rem) }`) ;   */
/*   · le produit TIRAIT LE CORPS vers la gauche, par des marges       */
/*     negatives sur #readability-content.                             */
/*                                                                     */
/* Les deux alignent titre et texte. Mais le second deplace la boite   */
/* entiere hors de son axe : la moitie de l'elargissement (70px a      */
/* gauche, 24 a droite, plus les 56 de padding) part d'un seul cote.   */
/* D'ou les 51px. On adopte donc le chemin de l'etalon.                */
/* / Both align title and body; only offsetting the header keeps the   */
/* whole box on its axis.                                              */
/* ================================================================== */
.lecture-zone-conteneur {
  /* La gouttiere s'AJOUTE a la largeur de lecture, elle ne s'y prend
     pas : 40rem de texte + 46px de gouttiere + 3.5rem de gap et de
     respiration = les 742px de l'etalon.
     / The gutter adds to the reading width instead of eating it. */
  --gouttiere: 46px;
  max-width: calc(var(--largeur-lecture) + var(--gouttiere) + 3.5rem);
  padding-right: 0;
  margin-left: auto;
  margin-right: auto;
}
/* Un audio a une gouttiere plus large : elle porte le locuteur et le
   minutage. La boite s'elargit d'autant, et le titre se decale
   d'autant — sinon il se desaligne du texte des qu'on ouvre un audio.
   / An audio gutter is wider; the box and the header follow. */
.lecture-zone-conteneur:has([data-media="audio"]) {
  --gouttiere: 96px;
}

/* Le corps reprend sa place : plus de marges negatives. */
#readability-content {
  max-width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* L'EN-TETE se decale, lui. Tous les enfants directs du conteneur SAUF
   le corps du document : titre, ligne de source, carnets, versions,
   bandeaux. C'est ce decalage qui aligne le titre sur le texte.
   / Everything but the document body shifts right by one gutter. */
.lecture-zone-conteneur > *:not(#readability-content) {
  padding-left: calc(var(--gouttiere) + 1.5rem);
}
/* La barre de progression traverse toute la boite : elle n'est pas du
   texte, elle ne s'aligne sur rien. / The progress bar spans the box. */
.lecture-zone-conteneur > #progress-analyse {
  padding-left: 0;
}

/* Sous 768px il n'y a plus de place pour ce decalage : la gouttiere
   se replie et l'en-tete revient au bord.
   / Below 768px the offset is dropped along with the gutter. */
@media (max-width: 768px) {
  .lecture-zone-conteneur > *:not(#readability-content) {
    padding-left: 0;
  }
}

/* ================================================================== */
/* LE PIED DU PANNEAU : l'etat du debat et la synthese                */
/*                                                                     */
/* Recueille ce que le dashboard de la barre d'outils portait, retire  */
/* le 12 aout a la demande du mainteneur. Le bouton de synthese y      */
/* avait son SEUL point d'entree.                                      */
/*                                                                     */
/* Il se detache du flux des cartes par un filet et un fond de         */
/* panneau : c'est un pied, pas une carte de plus.                     */
/* / Recovers what the removed toolbar dashboard carried.              */
/* ================================================================== */
.pied-de-panneau {
  margin-top: 1rem;
  padding: .8rem .65rem 1rem;
  border-top: 1px solid var(--filet);
}
.pied-de-panneau .etat-du-debat {
  margin: 0 0 .45rem;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .68rem;
  color: var(--encre-douce);
}
.pied-de-panneau .etat-du-debat strong { color: var(--encre); font-weight: 700; }
.pied-de-panneau .part-du-debat { opacity: .65; }

.jauge-du-debat {
  height: 4px;
  border-radius: 999px;
  background: var(--papier-creux);
  overflow: hidden;
  margin-bottom: .7rem;
}
.jauge-du-debat .jauge-remplie {
  height: 100%;
  background: var(--statut-commente);
  transition: width .5s ease;
}

.pied-de-panneau .btn-synthese {
  width: 100%;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .68rem;
  cursor: pointer;
  background: var(--encre);
  color: var(--papier);
  border: 1px solid var(--encre);
  border-radius: 4px;
  padding: .4rem .6rem;
}
.pied-de-panneau .btn-synthese:hover {
  background: color-mix(in srgb, var(--encre) 88%, var(--papier));
}

/* ================================================================== */
/* LE PANNEAU N'EXISTE QUE LA OU IL A QUELQUE CHOSE A MONTRER         */
/*                                                                     */
/* Le panneau vit dans `base.html`, donc dans le DOM de tous les       */
/* ecrans. Mais il ne parle que de DEUX choses : les analyses d'une    */
/* note, les preuves d'un article. Sur les carnets ou les bases, il    */
/* n'a rien a montrer.                                                 */
/*                                                                     */
/* POURQUOI EN CSS ET NON PAR UN `{% if %}`                            */
/* La navigation est HTMX : elle ne remplace que `#zone-lecture`,      */
/* jamais `base.html`. Un `{% if page %}` autour du panneau ne serait  */
/* evalue qu'au chargement complet — passer d'un carnet a une note ne  */
/* le ferait jamais apparaitre. La presence du conteneur de lecture    */
/* dans la zone, elle, suit chaque swap.                               */
/* / HTMX only swaps #zone-lecture, so a template condition would      */
/* never re-evaluate. The reading container's presence does.           */
/* ================================================================== */
.plateau:not(:has([data-testid="lecture-zone-principale"],
                  [data-testid="synthese-article"])) > #drawer-overlay {
  display: none;
}
/* Et la grille redevient une seule colonne : sans cela, elle          */
/* reserverait 23rem a un panneau invisible.                           */
/* / Otherwise the grid keeps 23rem for a hidden panel.                */
@media (min-width: 1400px) {
  body.panneau-integre
    .plateau:not(:has([data-testid="lecture-zone-principale"],
                      [data-testid="synthese-article"])) {
    grid-template-columns: 1fr;
  }
}

/* ================================================================== */
/* LE MEME TIROIR PORTE LES PREUVES D'UN ARTICLE                      */
/*                                                                     */
/* Les preuves etaient une colonne de la grille INTERNE de l'article.  */
/* Cette grille est centree sous un `max-width` : mesure du 20 aout,   */
/* fenetre de 1600px, le bord droit du panneau tombait a 1508 — 92px   */
/* avant le bord de la fenetre, la ou le panneau d'une note tombe a    */
/* 1600 pile. Un panneau qui flotte au milieu de la page ne se lit pas */
/* comme un panneau.                                                   */
/*                                                                     */
/* Les preuves passent donc par `#drawer-overlay`, qui est une colonne */
/* de `.plateau` : le bord droit devient exact par construction, et il */
/* n'y a plus qu'un panneau, un seuil et un controle a tenir.          */
/* / The evidence sat in the article's own centred grid and stopped    */
/* 92px short of the window edge. It now uses the plateau's column.    */
/* ================================================================== */

/* AUCUN VOILE, A AUCUNE LARGEUR, SUR AUCUN DES DEUX PANNEAUX.        */
/*                                                                     */
/* Le motif est le meme des deux cotes : le panneau existe pour qu'on  */
/* lise le texte ET ce qui s'y rapporte EN MEME TEMPS. Une preuve se   */
/* compare a l'affirmation qu'elle porte ; une extraction se compare   */
/* au passage dont elle est tiree. Griser le texte pendant qu'on lit   */
/* la carte retire precisement ce qu'on est venu comparer.             */
/*                                                                     */
/* La regle ne visait que l'article : les analyses gardaient leur      */
/* voile sous 1400px, alors que les deux panneaux partagent desormais  */
/* une place, un interrupteur et une largeur. Deux comportements pour  */
/* un meme objet, c'est un objet qu'on n'apprend pas.                  */
/* / Same reason on both sides: the panel exists so that text and what */
/* refers to it can be read together. The rule targeted only articles, */
/* while both panels now share a place, a toggle and a width.          */
#drawer-backdrop {
  display: none !important;
}

/* ================================================================== */
/* EN REGIME TIROIR, LE PANNEAU SE POSE SOUS LA BANDE DE CONTEXTE     */
/*                                                                     */
/* Le tiroir est `fixed top-0 bottom-0` a z-index 70 ; la bande, elle, */
/* est a 66. Ouvert sous le seuil, il RECOUVRAIT donc la bande — et    */
/* avec elle son propre interrupteur. Mesure du 20 aout, 1279px :      */
/* le clic sur le bouton etait intercepte par une fiche de preuve.     */
/* Sans voile a cliquer, le panneau ne se refermait plus qu'au clavier.*/
/*                                                                     */
/* Il commence donc ou commence le plateau, et les deux regimes se     */
/* posent au meme endroit. Le calcul est celui de `.plateau` plus bas, */
/* et pour la meme raison : la bande n'existe pas sur tous les ecrans. */
/*                                                                     */
/* LE VOILE SUIT LA MEME REGLE. Il est `fixed inset-0` a z-index 68 :  */
/* il grisait la barre d'outils et la bande, donc l'interrupteur avec. */
/* Il ne recouvre plus que ce qu'il a a griser — le texte.             */
/* / Fixed at z-index 70 over a strip at 66, the open drawer covered   */
/* its own toggle. Both it and its veil now start where the plateau    */
/* starts.                                                             */
/* ================================================================== */
#drawer-overlay,
#drawer-backdrop { top: var(--barre-outils); }
body:has(.bande-de-contexte .fil-ariane) #drawer-overlay,
body:has(.bande-de-contexte .fil-ariane) #drawer-backdrop {
  top: calc(var(--barre-outils) + var(--barre-fil, 2rem));
}

/* LE PANNEAU DES PREUVES REMPLIT LE TIROIR. C'est SON corps qui       */
/* defile (`_style_maquette.html`), parce que le defilement vers une   */
/* fiche ne doit deplacer que la liste. Laisser au tiroir son          */
/* rembourrage et son propre defilement ferait un cadre dans le cadre  */
/* et deux barres de defilement imbriquees.                            */
/* / The panel's own body scrolls, so bringing a card up moves only    */
/* the list.                                                           */
#drawer-contenu:has(.panneau-preuve) {
  padding: 0;
  overflow: hidden;
}
/* La pastille de statut garde la taille de la FICHE (.72rem) et non   */
/* celle de la carte d'analyse (.78rem) : la regle `#drawer-overlay    */
/* .indicateur-statut` l'emporterait sinon par son identifiant.        */
/* / The card's own dot size, which the id-based rule would outrank.   */
#drawer-overlay .panneau-preuve .indicateur-statut {
  width: .72rem;
  height: .72rem;
}

/* ================================================================== */
/* LA GOUVERNANCE D'UN CARNET, SUR SA PROPRE PAGE                     */
/*                                                                     */
/* Renommer, changer la visibilite, partager, supprimer, quitter un   */
/* partage : ces gestes vivaient dans le menu contextuel du tiroir    */
/* lateral, retire le 12 aout 2026. Ils sont relogés sur              */
/* `/carnets/<id>/` (carnet_detail.html).                              */
/*                                                                     */
/* POURQUOI ICI ET NON EN CLASSES TAILWIND                             */
/* Le build Tailwind du projet est FIGE : toute classe arbitraire     */
/* (`z-[70]`, `min-w-[160px]`) est inerte, et l'echelle standard ne   */
/* couvre pas ce dont ce bloc a besoin. On etend donc le design       */
/* system du corpus (`.zone-corpus`, _style_maquette.html) depuis la  */
/* feuille globale, chargee en dernier.                                */
/* / The Tailwind build is frozen; arbitrary classes are inert. We    */
/* extend the corpus design system from the global sheet instead.     */
/* ================================================================== */

/* Le bloc est REPLIE : on vient sur un carnet pour le lire, pas pour */
/* l'administrer. Le triangle natif de <summary> suffit a dire que ca */
/* s'ouvre. / Collapsed: this page is for reading, not admin.         */
.zone-corpus .gouvernance-carnet {
  margin: .8rem 0 0;
  border: 1px solid var(--filet);
  border-radius: 6px;
  background: var(--papier-panneau);
  padding: .5rem .7rem;
}
.zone-corpus .gouvernance-carnet > summary {
  cursor: pointer;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--encre-douce);
  /* Cible elargie sans decaler la ligne (WCAG 2.5.8).                */
  padding: .2rem 0;
}
.zone-corpus .gouvernance-carnet > summary:hover { color: var(--encre); }
/* Le contour de focus doit rester visible sur fond panneau : sans    */
/* cette regle, le contour par defaut du navigateur se confond avec   */
/* le filet du bloc. / Keep the focus ring visible on the panel.      */
.zone-corpus .gouvernance-carnet > summary:focus-visible {
  outline: 2px solid var(--cible);
  outline-offset: 2px;
}
/* Les rangees du bloc respirent : quatre controles colles les uns    */
/* aux autres se lisent comme un seul objet.                          */
.zone-corpus .gouvernance-carnet .rangee-champ { margin: .5rem 0; }
.zone-corpus .gouvernance-carnet .rangee-champ .champ-texte {
  min-width: 14rem;
}

/* L'ETAT ACTIF de la visibilite passe par [aria-pressed], donc par   */
/* la MEME source que ce qu'annonce un lecteur d'ecran. Un `.actif`   */
/* pose a la main aurait pu diverger de l'attribut.                    */
/* / The active state keys off [aria-pressed]: same source as what a  */
/* screen reader announces, so the two cannot drift apart.            */
.zone-corpus .bouton-plat[aria-pressed="true"] {
  background: var(--encre);
  color: var(--papier);
  border-color: var(--encre);
}

/* LE DANGER se dit par la couleur ET par le mot : « Supprimer le     */
/* carnet », « Quitter ce partage » nomment deja l'acte. La teinte    */
/* n'est donc qu'un renfort, jamais le seul porteur (§ 8.3).          */
/* --danger est defini en clair ET en sombre dans le :root de cette   */
/* feuille : le bouton suit le theme sans regle supplementaire.        */
/* / Colour reinforces a label that already names the act; --danger   */
/* is themed at :root, so no dark-mode rule is needed here.           */
.zone-corpus .bouton-plat.danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 55%, var(--filet));
}
.zone-corpus .bouton-plat.danger:hover {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, var(--papier));
}

/* LES ACTIONS D'UNE LIGNE DE NOTE, groupees dans la troisieme colonne. */
/* `.ligne-note` est une grille `auto 1fr auto` : la suppression, ajoutee */
/* le 12 aout 2026 a cote des fleches d'ordre, faisait un QUATRIEME      */
/* enfant, qui se repliait sur un nouveau rang sous la note et n'avait   */
/* plus l'air de lui appartenir (verifie au navigateur).                 */
/* / .ligne-note is a three-column grid; the delete button added beside  */
/* the ordering arrows made a fourth child that wrapped onto its own row.*/
.zone-corpus .ligne-note .actions-de-la-note {
  display: flex;
  align-items: flex-start;
  gap: .35rem;
}

/* ================================================================== */
/* L'ONGLET « BASES DE CONNAISSANCES » DE L'ACCUEIL                   */
/*                                                                     */
/* Ces deux sections ont vecu sur l'accueil, en onglet, du 12 au       */
/* 21 aout 2026. Elles sont depuis dans `/bases/` — l'onglet montrait  */
/* une SECONDE vue des bases, qui avait deja diverge de celle-ci.      */
/*                                                                     */
/* Deux sections, pas une liste triee. Ce que j'ai cree, j'y reviens  */
/* — espace de travail ; ce que d'autres publient, je l'explore —     */
/* catalogue. Le titre de section porte cette distinction, et le      */
/* sous-titre la dit en toutes lettres : la place seule ne suffit pas */
/* a faire comprendre pourquoi une base est d'un cote plutot que de   */
/* l'autre.                                                            */
/* / Two sections, not one sorted list; the subtitle says why.        */
/* ================================================================== */
/* ELLE N'A PLUS DE LARGEUR NI DE GOUTTIERE A ELLE. Ces sections
   vivaient sur l'accueil, en enfants directs de la zone de lecture :
   elles portaient donc `max-width:64rem`, `margin:auto` et un padding
   lateral. Depuis le 21 aout 2026 elles sont DANS `.zone-corpus`
   (bases_liste.html), qui porte exactement les memes — les deux
   s'ajoutaient, et le titre de section commencait 25px a droite du
   titre de la page. Mesure du 21 aout : h1 a x=212, section a x=237.
   / They used to be direct children of the reading zone and carried
   their own width and gutters; inside .zone-corpus the two added up. */
.section-de-bases { margin: 0 0 2.5rem; }
.section-de-bases:last-child { margin-bottom: 0; }

.titre-section-bases {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--encre-douce);
  margin: 0 0 .15rem;
}
.sous-titre-section-bases {
  margin: 0 0 1rem;
  font-size: .82rem;
  color: var(--encre-douce);
}
.etat-vide-bases {
  margin: 0;
  padding: 1.2rem 0;
  font-size: .82rem;
  font-style: italic;
  color: var(--encre-douce);
}

/* ================================================================== */
/* L'EN-TETE DU PANNEAU : UNE ETIQUETTE, PAS UNE BARRE DE TITRE       */
/*                                                                     */
/* Etalon § 13. Le panneau est dans le flux depuis le 12 aout, mais    */
/* son en-tete parlait encore la langue d'un tiroir : barre de 48px,   */
/* titre gras de 14px, croix de 32px. Un en-tete de colonne se lit     */
/* comme l'etiquette d'une section — il ne rivalise pas avec le titre  */
/* du document a cote.                                                 */
/* / A column header reads as a section label; it must not compete     */
/* with the document title beside it.                                  */
/* ================================================================== */
#drawer-overlay .panneau-entete {
  padding: .7rem .8rem .55rem;
  border-bottom: 1px solid var(--filet);
  flex: none;
}
#drawer-overlay .panneau-titre {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .66rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--encre-douce);
}
#drawer-overlay .panneau-titre .fermer {
  margin-left: auto;
  text-transform: none;
  letter-spacing: 0;
  font-size: .8rem;
  line-height: 1;
  padding: .15rem .3rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--encre-douce);
  cursor: pointer;
}
#drawer-overlay .panneau-titre .fermer:hover {
  border-color: var(--filet-controle);
  color: var(--encre);
}

/* ================================================================== */
/* LA BANDE DE CONTEXTE : UNE SEULE DIV POUR LES DEUX COLONNES        */
/*                                                                     */
/* Arbitrage du mainteneur, 12 aout. Le fil d'Ariane faisait 32px,     */
/* l'en-tete du panneau 40,6 : deux bandes cote a cote, separees par   */
/* un decrochement de 8px que rien ne justifiait.                      */
/*                                                                     */
/* Les aligner aurait voulu dire tenir DEUX regles egales a la main.   */
/* Une seule div rend l'alignement vrai par construction : il n'y a    */
/* plus qu'une hauteur.                                                */
/*                                                                     */
/* ECART ASSUME avec l'etalon, qui garde deux bandes distinctes        */
/* (le fil traverse, le panneau porte son propre en-tete dessous).     */
/* / One div, one height. Deliberate departure from the mockup.        */
/* ================================================================== */
.bande-de-contexte {
  display: flex;
  align-items: stretch;
  min-height: var(--barre-fil, 2rem);
  background: var(--papier-creux);
  border-bottom: 1px solid var(--filet);
  font-family: ui-monospace, Menlo, monospace;
  font-size: .65rem;
  color: var(--encre-douce);
}

/* Le fil occupe la colonne de gauche, exactement la largeur de la
   colonne de lecture : la separation verticale des deux colonnes se
   prolonge donc dans la bande, au lieu de s'y interrompre.
   / The strip's split continues the columns' own split. */
.bande-de-contexte .fil-ariane-conteneur {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  padding: 0 .9rem;
  /* OVERFLOW VISIBLE, IMPERATIVEMENT — c'est deja ce qu'exige le
     commentaire de `.fil-ariane` plus haut dans ce fichier. Le menu de
     bascule est un `position: absolute` ancre sous le fil : un
     `overflow: hidden` pose ICI le rogne sur la hauteur de la bande
     (2rem), et le menu devient invisible bien qu'ouvert. Ce qu'il
     protegeait — un long titre qui pousse la colonne — est desormais
     traite sur le segment lui-meme, juste dessous.
     / The switcher menu is absolutely positioned below the strip:
     clipping here hides it. Long titles are truncated per-segment. */
  overflow: visible;
}

/* Le fil lui-meme perd les marges negatives qui le faisaient deborder
   de la zone de lecture : il n'y est plus. / No more negative margins:
   the breadcrumb no longer lives inside the reading zone. */
.bande-de-contexte .fil-ariane {
  min-height: 0;
  margin: 0;
  padding: 0;
  border-bottom: none;
  background: none;
  width: 100%;
  /* Une seule ligne : le fil mesure la hauteur de la bande, et la
     variable `--barre-fil` sur laquelle se calcule la hauteur du
     plateau reste juste. En `wrap`, un chemin long ferait deux lignes
     et le bas du plateau passerait sous le pli.
     / One line only, or the plateau height computation goes stale. */
  flex-wrap: nowrap;
}

/* LA TRONCATURE VIT ICI, sur le dernier segment — le titre de la note,
   seul element de longueur libre. Il cede en premier et s'abrege ; les
   maillons cliquables en amont gardent leur cible entiere.
   / Truncate the free-length trailing segment, not the whole strip. */
.bande-de-contexte .fil-ariane > .courant {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bande-de-contexte .bande-titre-panneau {
  display: none;
  align-items: center;
  gap: .5rem;
  padding: 0 .8rem;
  border-left: 1px solid var(--filet);
  text-transform: uppercase;
  letter-spacing: .08em;
}
.bande-de-contexte .bande-titre-panneau .fermer {
  margin-left: auto;
  text-transform: none;
  letter-spacing: 0;
  font-size: .8rem;
  line-height: 1;
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--encre-douce);
  cursor: pointer;

  /* 24 x 24 AU MINIMUM (WCAG 2.5.8, niveau AA). Mesure au navigateur :
     ce bouton faisait **18 x 18**, et l'exception « cible prise dans une
     phrase » ne le couvre pas — il vit seul dans une bande.
     C'est le SEUL moyen de rouvrir le panneau une fois ferme : le rater
     au doigt, c'est perdre l'acces aux preuves ou aux analyses.
     / Measured at 18 x 18; the inline exception does not apply, and this
     is the only way to reopen the panel once closed. */
  min-width: 1.5rem;
  min-height: 1.5rem;
  padding: .2rem .35rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.bande-de-contexte .bande-titre-panneau .fermer:hover {
  border-color: var(--filet-controle);
  color: var(--encre);
}

/* La bande ne parait que la ou elle a un contexte a porter : sur un
   ecran sans fil d'Ariane elle serait une barre vide.
   / No breadcrumb, no strip: it would be an empty bar. */
.bande-de-contexte:not(:has(.fil-ariane)) {
  display: none;
}

/* LE CONTROLE DU PANNEAU EST TOUJOURS LA OU UN PANNEAU EXISTE.

   Il ne paraissait qu'avec `body.panneau-integre`, c'est-a-dire panneau
   OUVERT : le refermer effacait le seul bouton capable de le rouvrir.
   Il parait donc des que l'ecran a un panneau — une note ou un article
   — ouvert ou ferme, et a toutes les largeurs : sous le seuil, le
   tiroir n'avait aucun controle du tout sur un article.

   `body:has(...)` et non un combinateur : la bande PRECEDE le plateau
   dans le document, et CSS n'a pas de « frere precedent ».

   ET UN DESCENDANT, JAMAIS UN `:has()` DANS UN `:has()` : la
   specification l'interdit, et un selecteur invalide fait tomber TOUTE
   la regle — en silence. Ecrit `.plateau:has([data-testid=...])`, ce
   bloc n'a jamais rien affiche (mesure du 20 aout : `display: none` a
   1600px, panneau ouvert).
   / The control only appeared while the panel was open, so closing it
   removed the only way to reopen. The strip precedes the plateau, hence
   :has() on the body — and a descendant inside it, because :has() may
   not nest and an invalid selector drops the whole rule silently. */
body:has(.plateau [data-testid="lecture-zone-principale"])
  .bande-de-contexte .bande-titre-panneau,
body:has(.plateau [data-testid="synthese-article"])
  .bande-de-contexte .bande-titre-panneau {
  display: flex;
}

/* OUVERT ET INTEGRE, l'en-tete coiffe la colonne : meme largeur, donc
   la separation verticale des deux colonnes se prolonge dans la bande.
   Ferme, ou en regime tiroir, il se reduit a son bouton et le fil
   reprend toute la place. / Open and integrated, the header caps the
   column; closed, it shrinks to its button. */
@media (min-width: 1400px) {
  body.panneau-integre .bande-de-contexte .bande-titre-panneau {
    width: var(--panneau);
    flex: none;
  }
}

/* LE GLYPHE DIT CE QUE LE CLIC FERA, et il le lit dans l'etat porte par
   le corps : la croix retire le panneau, le chevron le ramene. Aucun
   JavaScript ne recopie cet etat, donc les deux ne peuvent pas
   diverger. / The glyph reads the state class; nothing copies it. */
.bande-titre-panneau .fermer .quand-ferme { display: none; }
body:not(.panneau-integre) .bande-titre-panneau .fermer .quand-ouvert {
  display: none;
}
body:not(.panneau-integre) .bande-titre-panneau .fermer .quand-ferme {
  display: inline;
}

/* La hauteur du plateau se calcule a partir de CE QUI LE SURPLOMBE : la
   barre d'outils, et la bande de contexte quand elle parait. Elle etait
   ecrite en style inline (`calc(100vh - 3rem)`), ce qui ne pouvait pas
   suivre l'apparition d'une seconde bande — le bas du panneau serait
   passe sous le pli.
   / The plateau's height derives from what sits above it; an inline
   calc could not account for a second strip appearing. */
.plateau {
  height: calc(100vh - var(--barre-outils));
}
body:has(.bande-de-contexte .fil-ariane) .plateau {
  height: calc(100vh - var(--barre-outils) - var(--barre-fil, 2rem));
}

/* L'aide sous un champ : elle dit ce que le champ accepte et ce qui se
   passe sans lui. Un « facultatif » qui ne dit pas ce qui remplace le
   vide laisse choisir a l'aveugle.
   / Field help says what happens when the field stays empty. */
.zone-corpus .aide-champ {
  margin: -.2rem 0 .4rem;
  font-size: .68rem;
  color: var(--encre-douce);
}

/* ================================================================== */
/* LES FORMES PAR LABEL (etalon § 8) — ecart n°6, comble le 12 aout   */
/*                                                                     */
/* `blockquote`, `formula` et `picture` tombaient dans le `<p>` par    */
/* defaut, faute de figurer dans BALISE_PAR_LABEL. Une citation avait  */
/* la forme du texte qui la commente ; une formule se coupait au       */
/* milieu et se justifiait comme de la prose ; une image perdait le    */
/* lien avec sa legende.                                               */
/* / Three labels fell into the default <p>: a quote read as prose, a  */
/* formula broke mid-line, an image lost its caption tie.              */
/* ================================================================== */
#readability-content .bloc > .corps blockquote {
  margin: 1.1rem 0;
  padding: .1rem 0 .1rem 1.05rem;
  border-left: 3px solid var(--filet);
  font-style: italic;
  color: var(--encre-douce);
}

#readability-content .bloc > .corps figure {
  margin: 0;
}
/* Une formule se lit d'un bloc : centree, entre deux filets, jamais
   justifiee. / A formula reads as one block: centred, never justified. */
#readability-content .bloc > .corps figure.formule {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .84rem;
  text-align: center;
  padding: .9rem 0;
  margin: .5rem 0;
  border-top: 1px solid var(--filet);
  border-bottom: 1px solid var(--filet);
}
#readability-content .bloc > .corps figure.illustration {
  margin: 1.1rem 0;
  font-size: .77rem;
  line-height: 1.5;
  color: var(--encre-douce);
  text-align: center;
  font-style: italic;
}

/* LE TABLEAU (etalon § 8) — il remplace le `<pre>` de l'ecart n°6.
   Le cadre le fait defiler DANS SA BOITE quand il depasse la colonne :
   celui du PDF etalon mesurait 3 162px et emportait toute la page.
   / The wrapper scrolls the table inside its own box. */
#readability-content .bloc > .corps .cadre-tableau {
  overflow-x: auto;
  max-width: 100%;
  margin: 1.1rem 0;
}
#readability-content .bloc > .corps table {
  border-collapse: collapse;
  width: 100%;
  font-size: .85rem;
  font-family: ui-monospace, Menlo, monospace;
  line-height: 1.5;
}
#readability-content .bloc > .corps th,
#readability-content .bloc > .corps td {
  padding: .28rem .6rem;
  text-align: right;
  border-bottom: 1px solid var(--filet);
}
/* La premiere colonne porte le LIBELLE, les suivantes des nombres :
   l'une se lit a gauche, les autres s'alignent a droite pour se
   comparer d'un coup d'oeil.
   / First column labels, the rest numbers: different alignments. */
#readability-content .bloc > .corps th:first-child,
#readability-content .bloc > .corps td:first-child { text-align: left; }
#readability-content .bloc > .corps thead th {
  color: var(--encre-douce);
  font-weight: 600;
  font-size: .75rem;
}

/* ================================================================== */
/* LE LECTEUR AUDIO — etalon § 19                                     */
/* ================================================================== */
/* ECART N°2, le dernier ouvert. Mesure du 12 aout sur la note 8 :
   « 0 balise <audio>, aucune tete de lecture, aucun rail ». Le produit
   avait, depuis PHASE-15, une « barre de progression de lecture » dont
   le nom trompait : elle suivait le DEFILEMENT, pas le son.
   / Gap n°2: the old "playback progress bar" tracked scrolling, not
   audio. */

.lecteur {
  position: fixed; left: 0; right: 0; bottom: 0;
  /* Au-dessus du panneau (z-70) : la barre parle du document entier,
     le panneau d'une de ses idees. Celle qui porte le plus large
     passe devant. / Above the panel: the bar speaks for the whole
     document, the panel for one of its ideas. */
  z-index: 82;
  height: 62px;
  display: flex; align-items: center; gap: .9rem;
  padding: 0 1rem;
  background: var(--papier);
  /* L'etalon pose ici `2px solid var(--cible)` — le violet qui marque
     ce qui n'existe PAS ENCORE dans le produit. Maintenant que le
     lecteur existe, garder la marque de cible mentirait dans l'autre
     sens. Le filet ordinaire suffit a detacher la barre du texte.
     / The mock's violet border marks unbuilt things; the player is
     built now, so it takes the ordinary rule. */
  border-top: 1px solid var(--filet);
}

/* LA RESERVE DE PLACE. `.lecteur` est `fixed`, donc hors du flux :
   sans elle, la barre recouvrirait les derniers tours de parole.
   Le token n'est releve que lorsque la barre est reellement rendue —
   `:has()` le dit en une regle, sans que le serveur ait a poser un
   attribut sur <body> qu'il faudrait ensuite tenir a jour.
   / .lecteur is fixed, so reserve its height — only when present. */
body:has(.lecteur) { --barre-audio: 62px; }
#zone-lecture { padding-bottom: calc(2rem + var(--barre-audio)); }

/* LES REGLAGES DE STENOTYPIE, a droite de la barre (§ 6.3).
   Discrets : ils ne doivent pas concurrencer la piste, qui est ce
   qu'on regarde. Meme grammaire de controle discret que le mode
   structure — mono, petit, --filet-controle pour tenir les 3:1 de
   WCAG 1.4.11, et 24px de cible (WCAG 2.5.8).
   / The § 6.3 settings: discreet controls at the bar's right end. */
.reglages-de-lecture {
  display: flex; align-items: center; gap: .35rem;
  margin-left: auto;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .72rem;
  color: var(--encre-douce);
  white-space: nowrap;
}
.reglages-de-lecture .bouton-reglage {
  min-width: 24px; min-height: 24px;
  line-height: 1;
  color: var(--encre-douce);
  background: var(--papier);
  border: 1px solid var(--filet-controle);
  border-radius: 4px;
  cursor: pointer;
}
.reglages-de-lecture .bouton-reglage:hover {
  color: var(--encre); background: var(--papier-creux);
}
/* La vitesse est un CHIFFRE QUI CHANGE : elle a besoin d'une largeur
   stable, sinon les boutons sautent a chaque palier.
   / A changing number needs a stable width, or the buttons jump. */
#vitesse-de-lecture {
  min-width: 3.2em; text-align: center;
}
.reglages-de-lecture .etiquette-reglage { margin-left: .5rem; }
.reglages-de-lecture select {
  font-family: inherit; font-size: inherit;
  color: var(--encre-douce);
  background: var(--papier);
  border: 1px solid var(--filet-controle);
  border-radius: 4px;
  min-height: 24px;
  padding: 0 .2rem;
}

.bouton-lecture {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  border: none; background: var(--encre); color: var(--papier);
  cursor: pointer; display: grid; place-items: center;
  font-size: .72rem; line-height: 1;
}
.bouton-lecture:focus-visible {
  outline: 2px solid var(--info); outline-offset: 2px;
}

.piste { flex: 1; min-width: 0; }
.piste-entete {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: .5rem;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .62rem; color: var(--encre-douce);
  margin-bottom: .25rem;
}
/* Le titre de piste nomme le tour courant — il peut etre long, et il
   ne doit jamais pousser le minutage hors de la barre.
   / The track title may be long; it must never push the timecode out. */
#piste-titre {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#minutage { flex: none; }

.rail {
  height: 8px; border-radius: 4px; background: var(--papier-creux);
  position: relative; cursor: pointer; display: flex; overflow: hidden;
}
.rail:focus-visible { outline: 2px solid var(--info); outline-offset: 3px; }

/* UN SEGMENT PAR TOUR DE PAROLE, a la couleur de sa voix. C'est ce qui
   distingue ce rail d'une barre de progression : il donne la FORME du
   debat — qui parle, combien de temps — avant qu'on l'ecoute.
   Un tour qui porte des idees est plus opaque : le rail devient une
   carte de ce qui a ete extrait.
   / One segment per turn, coloured by voice: the rail shows the shape
   of the debate. Turns bearing ideas are more opaque. */
/* POSITIONNES EN ABSOLU, ET NON POUSSES LES UNS DERRIERE LES AUTRES.
   ECART ASSUME AVEC L'ETALON, qui empile les segments dans un `flex`
   en ne leur donnant qu'une largeur. Cela suppose que les tours de
   parole se touchent — or ils ne se touchent pas : sur la note 8, le
   premier finit a 0,5s et le second commence a 0,7s. En flex, ces
   silences disparaissent et CHAQUE SEGMENT DERIVE de la somme des
   blancs qui le precedent : la tete de lecture ne tombe plus sur le
   tour qu'on entend. En absolu, chacun est a sa vraie place, et les
   silences se lisent en creux — un blanc dans un debat est une
   information.
   / Deliberate departure: the mock flex-stacks segments by width,
   which assumes turns are contiguous. They are not — silences would
   vanish and every segment would drift by the sum of the gaps before
   it. Absolute positioning puts each at its true time. */
.segment-locuteur { position: absolute; top: 0; height: 100%; opacity: .45; }
.segment-locuteur.a-des-idees { opacity: .85; }

.tete-de-lecture {
  position: absolute; top: -3px; left: 0;
  width: 2px; height: 14px; background: var(--encre);
  /* La tete ne doit pas intercepter le clic destine au rail : c'est le
     rail qu'on vise pour se deplacer, et elle est juste dessus.
     / The playhead must not steal the rail's click. */
  pointer-events: none;
}

/* Le fond du tour en cours d'ecoute est defini plus haut, avec le
   bouton d'ecoute de la gouttiere — les deux forment le meme geste.
   / The playing turn's wash is defined above, with the gutter button. */

/* ================================================================== */
/* LE FILTRE PAR LOCUTEUR — masquage des tours des autres voix        */
/* ================================================================== */
/* Corrige le 14 aout 2026, en meme temps que le JS.
   `hypostasia.css:167` ne masquait que `.speaker-block`, c'est-a-dire
   le HTML diarise fige que les pages ELEMENT ne rendent plus. Le filtre
   rebranche sur les blocs reels serait reste sans effet VISIBLE : la
   classe etait bien posee, aucune regle ne la lisait.
   / The masking rule only covered the frozen diarised HTML: rewiring
   the filter without this would set a class no rule ever read. */
#readability-content .bloc.masque-par-filtre { display: none; }

/* ==========================================================================
   LE MESSAGE D'ACCUEIL — la modale qui explique les quatre entrees du menu.
   Gabarit : front/templates/front/includes/message_d_accueil.html
   / The welcome modal explaining the four menu entries.

   TOUT EST EN TOKENS, AUCUNE COULEUR EN DUR. `--papier`, `--encre`,
   `--filet` basculent deja en sombre plus haut dans ce fichier : ecrire
   `#fff` ici donnerait une modale blanche sur une application sombre,
   sans qu'aucune erreur ne le signale.
   / Tokens only: a hardcoded #fff would stay white in dark mode.

   Z-INDEX 80. Au-dessus du panneau lateral (70) et du voile du tiroir
   (68) : ce message est le premier ecran qu'on voit, rien ne doit passer
   devant. / Above the side panel (70) and the drawer veil (68).
   ========================================================================== */

.voile-message-accueil {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(0, 0, 0, .45);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
}

.carte-message-accueil {
  background: var(--papier); color: var(--encre);
  border: 1px solid var(--filet); border-radius: 10px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .35);
  width: min(34rem, 100%);
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  padding: 1.4rem 1.5rem 1.2rem;
}

.entete-message-accueil {
  display: flex; align-items: flex-start; gap: .75rem;
  margin-bottom: .6rem;
}
.titre-message-accueil {
  flex: 1; min-width: 0;
  font-size: 1.25rem; line-height: 1.25; margin: 0;
  letter-spacing: -.015em; font-weight: 700;
}
.fermer-message-accueil {
  flex-shrink: 0;
  width: 2rem; height: 2rem; line-height: 1;
  border: none; background: transparent; cursor: pointer;
  color: var(--encre-douce); font-size: 1rem; border-radius: 5px;
}
.fermer-message-accueil:hover {
  background: var(--papier-creux); color: var(--encre);
}

.intro-message-accueil {
  font-size: .875rem; line-height: 1.55;
  color: var(--encre-douce); margin: 0 0 1rem;
}

/* Les quatre entrees. Le nom est en monospace, comme tout ce que la
   machine nomme ailleurs dans le produit (cotes, identifiants) : ici il
   designe un bouton EXISTANT de la barre, pas une idee.
   / Monospace names: they point at real buttons, not at concepts. */
.entrees-message-accueil {
  list-style: none; margin: 0 0 1.2rem; padding: 0;
  display: flex; flex-direction: column; gap: .55rem;
}
.entrees-message-accueil li {
  display: grid; grid-template-columns: 6.5rem 1fr; gap: .1rem .8rem;
  align-items: baseline;
  padding: .55rem .7rem;
  border: 1px solid var(--filet); border-radius: 6px;
  background: var(--papier-creux);
}
.entrees-message-accueil .nom-entree {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .8rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
}
.entrees-message-accueil .role-entree {
  font-size: .8125rem; line-height: 1.5; color: var(--encre-douce);
}

.pied-message-accueil {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  border-top: 1px solid var(--filet); padding-top: .9rem;
}
.case-message-accueil {
  display: flex; align-items: center; gap: .5rem;
  font-size: .8125rem; color: var(--encre-douce); cursor: pointer;
}
.case-message-accueil input { width: 1rem; height: 1rem; cursor: pointer; }

.bouton-message-accueil {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .8rem; font-weight: 700;
  padding: .5rem 1.1rem; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--barre);
  background: var(--barre); color: var(--barre-texte);
}
.bouton-message-accueil:hover { opacity: .88; }

/* Sur telephone, les deux colonnes de chaque entree s'empilent : une
   colonne de 6,5rem sur un ecran de 360px ne laisse plus de quoi lire
   le role. / On phones the two columns stack. */
@media (max-width: 560px) {
  .carte-message-accueil { padding: 1.1rem 1.1rem 1rem; }
  .entrees-message-accueil li { grid-template-columns: 1fr; }
  .pied-message-accueil { justify-content: stretch; }
  .bouton-message-accueil { flex: 1; }
}

/* ==========================================================================
   LES ENTREES DE NAVIGATION DE LA BARRE — quatre depuis le 21 aout 2026.
   / The toolbar navigation entries — four since 21 August 2026.

   ELLES DOIVENT TENIR SUR UN TELEPHONE. Leur conteneur porte
   `overflow-hidden` : un lien qui deborde n'est pas replie, il est
   ROGNE, et aucune erreur ne le dit. Le mot « Hypostasia » s'efface deja
   sous 768px (`.titre-app-desktop`), ce qui libere la place des deux
   entrees ajoutees ; en dessous de 480px on resserre encore.
   / The container is overflow-hidden: an overflowing link is clipped in
   silence, not wrapped. */
.lien-nav-barre {
  font-size: .875rem; text-decoration: none; flex-shrink: 0;
  padding: .15rem .1rem;
}
.entrees-de-navigation {
  display: flex; align-items: center; gap: .5rem;
  flex-shrink: 0; min-width: 0;
}

/* SOUS 560px, LES ENTREES PRENNENT UN RANG A ELLES.
   / Under 560px the entries take a row of their own.

   Ce n'est pas un choix d'esthetique, c'est une mesure. En 390px, les
   controles de droite occupent 243px des 366px utiles ; les quatre
   entrees en reclament 237. Elles ne tiennent pas, et l'ancienne forme
   les ROGNAIT en silence. Les abreger ou les changer en pictogrammes
   contredirait le retrait du burger (« trois pictogrammes empiles ne
   disent pas ou l'on va ») : elles descendent d'un rang.

   `--barre-outils` EST REDEFINI AVEC, ET C'EST INDISPENSABLE. Quatre
   autres regles s'en servent — le voile du tiroir, le haut du panneau,
   deux hauteurs de colonne. Grandir la barre sans bouger le token
   ferait passer le tiroir SOUS elle.
   / A measurement, not a taste: 243 + 237 > 366. The height token is
   redefined with it — four other rules read it. */
@media (max-width: 560px) {
  :root { --barre-outils: 78px; }

  .barre-outils { flex-wrap: wrap; align-content: center; }

  .entrees-de-navigation {
    order: 3;
    flex-basis: 100%;
    justify-content: flex-start;
    gap: .9rem;
    padding-top: .1rem;
    border-top: 1px solid var(--filet);
    margin-top: .1rem;
  }
  .lien-nav-barre { font-size: .8125rem; }
}
/* AUCUNE REGLE ICI POUR `#titre-toolbar`. La place que les deux entrees
   ajoutees reclament est deja liberee plus haut dans ce fichier, par une
   regle du 20 aout qui rend le titre au fil d'Ariane SOUS CONDITION :
   `body:has(#fil-ariane) … { display: none }`. La condition n'est pas un
   detail — une note qui n'appartient a aucun carnet n'a PAS de fil, et
   un masquage inconditionnel la laisserait sans titre nulle part.
   Mesure du 21 aout : un `#titre-toolbar { display: none }` pose ici a
   fait tomber `test_navbar_le_titre_sort_de_la_barre_et_le_fil_le_porte`.
   / No rule for #titre-toolbar here: the 20 August rule already frees
   that space, and only where a breadcrumb exists to carry the title. */

/* ================================================================== */
/* LE MODE D'EDITION PAR BLOCS (SPEC-edition-par-blocs § 2)           */
/*                                                                     */
/* « Le mode doit se voir et s'annoncer. Un mode invisible qui capture */
/* le clavier est une source de confusion. » Tout ce qui suit passe    */
/* par les TOKENS : le build Tailwind est fige, une classe arbitraire  */
/* y serait inerte — et ces tokens suivent deja le theme clair/sombre. */
/* / The mode must be visible; everything goes through the tokens.     */
/* ================================================================== */

/* LE PIEGE DE `plaintext-only`, ET SA PARADE.                        */
/*                                                                     */
/* `contenteditable="plaintext-only"` applique `white-space: pre-wrap` */
/* a tout le champ. Or `.corps` contient les BLANCS D'INDENTATION du   */
/* gabarit Django : ils cessent d'etre collapses et deviennent de      */
/* vraies lignes vides. Mesure du 30 aout : un bloc passait de 24 px a */
/* 197 px, et la note de 17 259 px a 154 062 px — le mode etait        */
/* inutilisable, sans la moindre erreur.                               */
/*                                                                     */
/* On rend donc `.corps` a `normal` — ses blancs se recollapsent — et  */
/* on garde `pre-wrap` sur l'ELEMENT INTERNE, celui qui porte le texte.*/
/* CE N'EST PAS QU'UNE REPARATION : c'est ce qui rend enfin VISIBLE le */
/* saut de ligne insere par Entree (§ 4.2 de la spec, question ouverte */
/* depuis le 23 aout — « un geste qui ne se voit pas est pire que pas  */
/* de geste »). En mode edition, il se voit.                            */
/* / plaintext-only turns the template's indentation into blank lines. */
/* IL N'Y A PAS DE PARADE EN CSS, ET C'EST MESURE.                    */
/* `white-space: normal !important` pose EN LIGNE sur `.corps` reste   */
/* calcule `pre-wrap` : le navigateur impose la valeur dans un champ   */
/* `plaintext-only`, et aucune regle d'auteur ne la contredit.         */
/* La cure est donc dans le DOM — `mode_edition.js` retire les noeuds  */
/* texte purement blancs a l'ouverture du mode. Ils ne portent rien.   */
/* / No CSS cure: the browser forces the value. The fix is in the DOM. */

/* Le champ se distingue du papier par son FILET, pas par son fond :  */
/* mesure du 30 aout, `--papier-creux` sur `--papier` ne donne que    */
/* 1,08:1 — invisible. Le filet, lui, tient 5:1 dans les deux themes. */
/* / The rule carries the signal; the ground alone is invisible.      */
#zone-lecture.mode-edition #readability-content [data-testid="blocs-elements"] {
  background: var(--papier-creux);
  border-left: 4px solid var(--encre-douce);
  border-radius: 4px;
  padding: .75rem .5rem .75rem .75rem;
  outline: none;
}

/* ET LE MODE SE NOMME. « Le mode doit se voir et s'annoncer » (§ 2). */
/* Un filet seul demande de savoir ce qu'il veut dire ; une phrase,   */
/* non. / A rule alone must be learned; a sentence need not be.       */
#zone-lecture.mode-edition #readability-content
  [data-testid="blocs-elements"]::before {
  /* LE TEXTE VIENT DE LA TABLE DES RACCOURCIS, pas d'ici. Ecrit en
     dur, il aurait menti au premier changement de touche — et il ne
     pouvait pas dire les touches de son, qui n'existent que sur une
     note qui en a. `mode_edition.js` pose `data-bandeau`.
     / The text comes from the shortcut table, never from here. */
  content: attr(data-bandeau);
  display: block;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--encre-douce);
  border-bottom: 1px solid var(--filet);
  padding-bottom: .4rem;
  margin-bottom: .6rem;
}

/* LES BOUTONS D'ACTION N'ONT RIEN A FAIRE DANS LE CHAMP.
   Ils appartiennent au mode STRUCTURE. Les laisser paraitre ici
   mettrait « corriger couper recoller masquer » au milieu du texte
   qu'on edite. Ils restent dans le DOM — la serialisation les retire
   — mais ils ne se voient pas.
   / They belong to structure mode; hidden here, still in the DOM. */
#zone-lecture.mode-edition .actions-element { display: none; }

/* LA GOUTTIERE PORTE LES FRONTIERES, et c'est tout ce qu'il faut :
   elles sont deja materialisees, bloc par bloc. On ne perd que le
   chrome d'edition, jamais le repere. En mode edition on rend le
   NUMERO visible — sans lui, un champ de 210 blocs n'a plus d'echelle.
   / The gutter already shows the borders; the number comes back. */
#zone-lecture.mode-edition .numero-element { display: inline-block; }

/* UN BLOC VIDE DOIT LE DIRE.
   `:empty` ne marche PAS ici — l'element interne garde le <span> des
   boutons d'action, meme cache : il n'est donc jamais vide au sens CSS.
   Mesure du 30 aout : la mention ne s'affichait JAMAIS. C'est le JS qui
   pose `data-vide`, et le CSS le lit.
   / :empty fails: the hidden buttons keep the element non-empty. */
#zone-lecture.mode-edition .bloc[data-vide="oui"] > .corps > [data-element-id]::after,
#zone-lecture.mode-edition .bloc[data-vide="oui"] > .corps > ul > li[data-element-id]::after {
  content: "(vidé — sera masqué à l’enregistrement)";
  color: var(--encre-douce);
  font-style: italic;
  font-size: .85em;
}

/* L'ETAT « des modifications ne sont pas enregistrees », discret mais
   present : le filet passe a l'encre pleine.
   / Unsaved changes: the rule turns to full ink. */
#zone-lecture.mode-edition.a-des-modifications
  #readability-content [data-testid="blocs-elements"] {
  border-left-color: var(--encre);
}

/* Pendant l'envoi, le champ ne se touche plus — le lot part avec le
   texte tel qu'il etait au Ctrl+S.
   / While saving, the field is not editable. */
#zone-lecture.mode-edition.enregistrement-en-cours
  #readability-content [data-testid="blocs-elements"] {
  opacity: .6;
  pointer-events: none;
}

/* Le compte rendu du lot. */
#compte-rendu-du-lot .compte-rendu-du-lot {
  background: var(--papier-panneau);
  border: 1px solid var(--filet);
  border-radius: 4px;
  padding: .6rem .8rem;
  margin: .5rem 0;
  font-size: .85rem;
}
#compte-rendu-du-lot .chiffres-du-lot,
#compte-rendu-du-lot .refus-du-lot { margin: 0; padding-left: 1.1rem; }
#compte-rendu-du-lot .refus-du-lot { margin-top: .4rem; color: var(--encre-douce); }
#compte-rendu-du-lot .refus-du-lot code { font-size: .8em; }

/* LE PANNEAU DES PASSAGES MASQUES — § 5.3, cas 4.
   Il n'existe QUE dans le mode d'edition : en lecture, il ferait
   doublon avec le placeholder du mode structure, qui dit deja la meme
   chose a l'endroit du texte. Ici, l'endroit du texte est justement ce
   qu'on ne veut pas encombrer — d'ou une commande, repliee par defaut.
   / Only in edit mode: elsewhere the structure-mode placeholder says it
   at the text's own place. */
#panneau-des-passages-masques { display: none; }
#zone-lecture.mode-edition #panneau-des-passages-masques {
  display: block;
  background: var(--papier-panneau);
  border: 1px dashed var(--filet-controle);
  border-radius: 5px;
  padding: .4rem .7rem;
  margin: .5rem 0;
  font-size: .85rem;
  color: var(--encre-douce);
}
#panneau-des-passages-masques > summary {
  cursor: pointer;
  font-family: ui-monospace, Menlo, monospace;
  font-size: .72rem;
  letter-spacing: .03em;
  /* La cible du pliage fait au moins 24 px de haut : WCAG 2.5.8, la
     meme regle que les boutons du mode structure.
     / At least a 24px target, like the structure-mode buttons. */
  min-height: 24px;
  display: flex;
  align-items: center;
}
#panneau-des-passages-masques ul {
  list-style: none;
  margin: .4rem 0 .2rem;
  padding: 0;
}
#panneau-des-passages-masques li {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
  padding: .25rem 0;
  border-top: 1px solid var(--filet);
}
#panneau-des-passages-masques .extrait { font-style: italic; }
/* Le bouton reprend la grammaire des controles discrets (.bouton-icone
   et .actions-element) : --filet-controle tient les 3:1 de WCAG 1.4.11,
   la ou le --filet nu ne fait que 1,28:1.
   / Same discreet-control grammar; --filet-controle holds 3:1. */
#panneau-des-passages-masques button {
  font-family: ui-monospace, Menlo, monospace;
  font-size: .68rem;
  line-height: 1;
  color: var(--encre-douce);
  background: var(--papier);
  border: 1px solid var(--filet-controle);
  border-radius: 4px;
  padding: .32rem .55rem;
  min-height: 24px;
  cursor: pointer;
}
#panneau-des-passages-masques button:hover {
  color: var(--encre);
  background: var(--papier-creux);
}

/* ================================================================== */
/* EDITER, C'EST NE VOIR QUE LE TEXTE                                 */
/*                                                                     */
/* Le mode d'edition retire tout ce qui parle DES extractions pour ne  */
/* laisser que ce dont elles parlent. Le panneau et le drawer vivent   */
/* HORS de `#zone-lecture` : c'est le `body` qui porte la classe.      */
/* / Editing means seeing the text alone.                              */
/* ================================================================== */

body.en-mode-edition #sidebar-right,
body.en-mode-edition #drawer-overlay,
body.en-mode-edition #bottom-sheet {
  display: none !important;
}

/* LES SURLIGNAGES S'EFFACENT, MAIS LE TEXTE RESTE.
   On ne RETIRE pas les <mark> du DOM : ils portent le texte, et les
   retirer le decouperait. On les rend transparents — le texte qu'ils
   contiennent est intact, et la serialisation ne voit pas la
   difference. / The marks stay in the DOM (they carry the text); only
   their appearance goes. */
#zone-lecture.mode-edition mark.hl-extraction,
#zone-lecture.mode-edition mark.portion {
  background: transparent !important;
  box-shadow: none !important;
  border: 0 !important;
  color: inherit !important;
  cursor: text;
}

/* CE QUI MENE AILLEURS N'A RIEN A FAIRE ICI. Le compteur d'idees, le
   bouton « voir la source » et le bouton d'ecoute ouvrent un panneau,
   un document ou le son : autant de sorties du texte, au milieu d'une
   session de correction.
   / What leads elsewhere has no place here. */
#zone-lecture.mode-edition .compteur-idees,
#zone-lecture.mode-edition .bouton-voir-source,
#zone-lecture.mode-edition .avertissement-plafond {
  display: none !important;
}

/* UN PASSAGE REFUSE SE VOIT LA OU IL EST.
   Le compte rendu le NOMME par son identifiant — mais un identifiant ne
   dit pas ou regarder, et le compte rendu s'affiche en tete de la note.
   / An id does not say WHERE to look. */
#zone-lecture.mode-edition .bloc.a-ete-refuse > .filet-etat {
  background: var(--danger);
  width: 3px;
}
#zone-lecture.mode-edition .bloc.a-ete-refuse > .corps > [data-element-id],
#zone-lecture.mode-edition .bloc.a-ete-refuse > .corps > ul > li[data-element-id] {
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  border-radius: 3px;
}
#zone-lecture.mode-edition .bloc.a-ete-refuse > .gouttiere::after {
  content: "refusé";
  color: var(--danger);
  font-size: .6rem;
  font-weight: 700;
  text-transform: uppercase;
}

/* UN BLOC EN LECTURE SEULE LE DIT.
   Le tableau ne se relit pas : son `texte` est du markdown « pipe », et
   ce qui s'affiche est un <table> construit apres coup. Le laisser
   paraitre modifiable ferait croire qu'on le corrige — et le travail
   serait perdu au premier Ctrl+S.
   / A read-only block says so: a table cannot be read back. */
#zone-lecture.mode-edition .bloc[data-lecture-seule] > .corps {
  opacity: .72;
  cursor: not-allowed;
}
#zone-lecture.mode-edition .bloc[data-lecture-seule] > .gouttiere::after {
  content: "lecture seule";
  color: var(--encre-douce);
  font-size: .55rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  line-height: 1.1;
}
