/* ==========================================================================
   trad-saas -- socle visuel
   Application de travail interne (pas un site vitrine) : sobre, dense sans
   etre serree, lisible longtemps. Palette chaude et discrete, accent cuir.
   Aucune dependance externe, aucune police distante : tout fonctionne hors
   ligne (htmx est deja servi localement, cette feuille suit le meme principe).
   ========================================================================== */

/* -------------------------------------------------------------------------
   1. Jetons
   ------------------------------------------------------------------------- */
:root {
  /* Palette chaude et discrete, esprit maison (fond creme, pas blanc pur) */
  --fond: #faf6f1;
  --surface: #ffffff;
  --bordure: #e6ddd2;
  --texte: #3a332c;
  /* Assombri le 22/07 (relecture) : #8a7f72 ne faisait que 3,64:1 sur --fond (seuil
     WCAG texte courant = 4,5:1). #6b6153 conserve la meme teinte chaude (brun-gris)
     et atteint 5,64:1 sur --fond / 6,07:1 sur --surface -- calcule via la formule
     de luminance relative WCAG 2.x (sRGB -> lineaire -> 0.2126R+0.7152G+0.0722B),
     ratio = (L_claire+0.05)/(L_sombre+0.05). */
  --texte-attenue: #6b6153;

  /* Accent cuir (brun chaud) -- jamais de bleu generique */
  --accent: #8a5a34;
  --accent-hover: #714829;
  --accent-fond: #f3e8dd;

  /* Quatre couleurs d'etat, chacune avec son fond assorti tres clair */
  --succes: #2f6f4e;
  --succes-fond: #e7f2ec;
  --attente: #8a6d1d;
  --attente-fond: #f7efdc;
  --alerte: #a15c00;
  --alerte-fond: #fdf0e0;
  --erreur: #a13a3a;
  --erreur-fond: #fbe9e9;

  /* Surface secondaire (fond de barre d'en-tete de section) + bordure appuyee (bouton de sortie) --
     maquette validee, fiche produit (tache 10 bis). */
  --surface-2: #fbf8f4;
  --bordure-forte: #d8ccbc;

  /* Le remplissage NEUTRE (pas l'accent) d'une position active « jamais / exclu / par defaut » de
     l'interrupteur a positions (chantier interrupteur, 22/08, maquette validee) -- deux
     remplissages distincts pour deux sens opposes, jamais la meme couleur pour "oui" et "non".
     Valeurs sombres redefinies dans le bloc "9. THEME SOMBRE" plus bas, meme patron que --accent. */
  --neutre-actif: #57503f;
  --neutre-actif-texte: #ffffff;

  /* Bleu ardoise discret cote FR (source) / cuir-orange discret cote ES (cible) -- maquette
     validee. Valeurs sombres redefinies dans le bloc "9. THEME SOMBRE" plus bas. */
  --fr-accent: #35507a;
  --fr-fond: #eef2f8;
  --cible-accent: #a3532a;
  --cible-fond: #fbefe3;

  /* Rayons, ombres, echelle d'espacement */
  --rayon: 8px;
  --rayon-sm: 5px;
  --ombre: 0 1px 3px rgba(58, 51, 44, 0.08), 0 1px 2px rgba(58, 51, 44, 0.06);

  --espace-1: 0.25rem;
  --espace-2: 0.5rem;
  --espace-3: 0.75rem;
  --espace-4: 1rem;
  --espace-5: 1.5rem;
  --espace-6: 2rem;

  /* 7-313 -- LES DEUX SEULS JETONS DE TAILLE DE POLICE DU FICHIER, ajoutes pour sortir
     views/revue.njk de l'inline style (charte-ux.html, ecart n8 : « le seul ecran a coder
     font-size:2.5rem directement dans la page »). Le reste de l'application code sa taille de
     police PAR REGLE (voir h1/h2/h3 juste plus bas, ou n'importe quel ".xxx { font-size: ... }"
     de ce fichier) -- ce patron n'est PAS etendu au reste du fichier par ce lot, seul l'ecran
     nomme par la charte est corrige. */
  --taille-chiffre-cle: 2.5rem;
  --taille-chiffre-secondaire: 1.4rem;

  --police: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Police a chasse fixe -- mode "Code" des champs riches (bascule Rendu/Code, tache rendu-code). */
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* -------------------------------------------------------------------------
   2. Base
   ------------------------------------------------------------------------- */
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: var(--police);
  background: var(--fond);
  color: var(--texte);
  max-width: 1100px;
  margin: 2rem auto;
  padding: 0 var(--espace-4);
  line-height: 1.5;
}

h1, h2, h3 {
  font-weight: 600;
  color: var(--texte);
  margin-top: var(--espace-6);
  margin-bottom: var(--espace-3);
}

h1 { font-size: 1.4rem; }
h2 { font-size: 1.1rem; }
h3 { font-size: 1rem; }

a {
  color: var(--accent);
  text-decoration: none;
}

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

.muted {
  color: var(--texte-attenue);
  font-style: italic;
}

/* 🔴 CLASSE FANTÔME COMBLÉE (22/08, relecture) : `.lien-discret` était employée par
   `emails.njk`, `inventaire-famille.njk`, `univers-detail.njk` et `_jauge-passages.njk` SANS
   jamais être définie dans cette feuille -- un lien « Effacer »/« Suivre ce passage » y héritait
   donc la couleur d'accent PLEINE d'un lien normal, jamais le ton discret que son nom promet.
   Atténué comme `.muted`, souligné au survol comme tout lien (`a:hover`, ci-dessus) -- rien de plus. */
.lien-discret {
  color: var(--texte-attenue);
}

/* Indicateur htmx ("... en cours") : masque par defaut, visible SEULEMENT pendant la requete --
   c'est htmx qui pose/retire la classe .htmx-request sur l'element cible pendant l'appel (voir
   hx-indicator dans les vues). Sans cette regle (absente jusqu'ici), l'indicateur restait affiche en
   PERMANENCE -- invisible sur un bouton isole en bas de fiche, mais elargissait a tort la colonne
   d'actions une fois deplacee dans l'en-tete (tache 10 revue d'exactitude, trouve par capture d'ecran
   comparee a la maquette). */
.htmx-indicator {
  display: none;
}

.htmx-request.htmx-indicator,
.htmx-request .htmx-indicator {
  display: inline;
}

/* Roue d'attente (demande utilisateur, 25/08) : un texte "... en cours" fige, sans mouvement,
   ressemble a un blocage plutot qu'a une execution en cours -- cette classe ajoute une petite roue
   animee AVANT le texte des spans htmx-indicator (fiche produit : boutons d'en-tete et bouton par
   champ). `border-top-color: transparent` sur `currentColor` : la roue herite TOUJOURS de la
   couleur du texte qui la porte (ici `.muted` -> var(--texte-attenue), deja correct en theme sombre,
   ligne ~1710) -- jamais une couleur ecrite en dur (jeton de charte, docs/design/charte-ux.html). */
/* 🆕 27/08 -- L'INDICATEUR DE CHARGEMENT DE TOUT LE SaaS (option 1, validee par l'utilisateur) :
   la zone qui va changer s'estompe, et une pastille apparait en bas a droite. Pose par
   public/js/indicateur-chargement.js sur les evenements htmx -- aucun gabarit ne le sait.
   `pointer-events` reste ACTIF a dessein (voir le commentaire du script). */
.zone-en-chargement {
  opacity: 0.45;
  transition: opacity 0.15s ease;
}

.pastille-chargement {
  position: fixed;
  right: var(--espace-5);
  bottom: var(--espace-5);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: var(--espace-2) var(--espace-4);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte-attenue);
  font-size: 0.82rem;
  box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.pastille-chargement--visible {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .zone-en-chargement,
  .pastille-chargement {
    transition: none;
  }
}

.indicateur-attente::before {
  content: '';
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-right: 0.35em;
  vertical-align: -0.15em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: roue-attente 0.8s linear infinite;
}

@keyframes roue-attente {
  to { transform: rotate(360deg); }
}

/* Le texte ("... en cours") porte deja l'information : sous mouvement reduit, la roue n'ajoute
   rien qu'un mouvement -- on la retire plutot que de laisser un anneau immobile et incomplet. */
@media (prefers-reduced-motion: reduce) {
  .indicateur-attente::before {
    animation: none;
    display: none;
  }
}

/* En-tete de page (gabarit de base -- nom du produit + deconnexion) */
.entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--espace-3);
  margin-bottom: var(--espace-4);
  border-bottom: 1px solid var(--bordure);
  font-size: 0.95rem;
}

/* La marque du produit dans l'en-tete commun (refonte UX, 07/08) : une pastille decorative et le
   nom en toutes lettres. Jetons seulement -- le mode sombre suit.
   7-285 (10/09) : c'est desormais un LIEN (<a>) vers l'accueil -- sans ce reset, la regle globale
   `a { color: var(--accent) }` reteindrait tout le texte de l'en-tete et le soulignerait au survol,
   comme un lien de texte ordinaire. Une marque cliquable n'est pas un lien de prose. */
.entete__marque {
  display: inline-flex; align-items: center; gap: var(--espace-2);
  color: inherit; text-decoration: none;
}
.entete__marque:hover, .entete__marque:focus-visible { text-decoration: none; }
.entete__pastille {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem;
  border-radius: var(--rayon-sm);
  background: var(--accent);
  color: var(--surface);
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em;
}

.entete__deco {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin: 0;
}

/* Le formulaire de deconnexion, DANS l'en-tete commun (.entete__deco ci-dessus) : depuis le lot
   "changer le mot de passe" (18/08) il partage cette boite avec le lien "Mon compte" (et, jusqu'au
   17/09, un libelle "Connecte" retire a la demande de l'utilisateur -- il repetait ce que la seule
   presence de ces deux commandes disait deja) --
   sans cette regle il ouvrirait sa PROPRE boite flex, et ses jetons d'espacement (gap) dedoubleraient
   ceux du parent. `display: contents` le retire de la mise en page sans toucher a son enfant (le
   bouton), qui reste un membre direct du flex parent. */
.entete__deco-form {
  display: contents;
}

/* -------------------------------------------------------------------------
   3. Composants
   ------------------------------------------------------------------------- */

/* Carte */
.card {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--espace-4);
}

/* `.card--cadrage` et `.card--fabrication` ONT DISPARU LE 07/08 : les deux blocs d'entree de
   l'accueil sont passes aux VIGNETTES (voir `.grille-vignettes` en fin de feuille), qui portent le
   meme liesere d'accent. Plus aucun gabarit ne demandait ces deux classes -- les garder aurait
   laisse croire qu'un ecran s'en sert encore. */

/* L'INSTRUCTION MONTREE TELLE QUELLE (lot E3b). Un prompt est du texte a chasse fixe dont les sauts
   de ligne PORTENT DU SENS : les ecraser ferait lire a l'utilisateur autre chose que ce que le
   modele recoit. Il defile dans son propre cadre -- jamais la page (regle des contenus larges). */
.apercu-prompt {
  margin: var(--espace-2) 0;
  padding: var(--espace-3);
  background: var(--fond);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure);
  border-radius: var(--rayon-sm);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre-wrap;
  /* 7-309 -- `anywhere` COUPAIT DES MOTS ENTIERS AU MILIEU (le vrai coupeur de mots, contrairement
     a `word-break: break-word` qui se comporte pareil sur les navigateurs modernes). `break-word` ne
     coupe qu'en DERNIER RECOURS -- une chaine sans espace (URL, identifiant tres long) casse encore
     si besoin, mais un mot ordinaire qui tiendrait sur la ligne suivante n'est plus tranche en deux. */
  overflow-wrap: break-word;
}

.apercu-prompt--long {
  max-height: 32rem;
  overflow-y: auto;
}

/* Une phrase de fabrication et son formulaire. Le verrouille se lit d'un coup d'oeil sans qu'on
   ait besoin de chercher le cadenas. */
.regle-bloc {
  padding: var(--espace-3) 0;
  border-top: 1px solid var(--bordure);
}

.regle-bloc--verrouille {
  opacity: 0.85;
}

/* Bouton */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  padding: var(--espace-2) var(--espace-4);
  border-radius: var(--rayon-sm);
  border: 1px solid var(--bordure);
  background: var(--surface);
  color: var(--texte);
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

/* Icone SVG inline dans un bouton (boutons de tete de fiche, tache 10 visuel) -- aucune
   dependance externe, l'icone est du balisage inline dans la vue. */
.btn svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.btn:hover {
  background: var(--fond);
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.btn--primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--accent);
}

.btn--ghost:hover {
  background: var(--accent-fond);
}

.btn--danger {
  background: var(--erreur-fond);
  border-color: var(--erreur);
  color: var(--erreur);
}

.btn--danger:hover {
  background: var(--erreur);
  color: #fff;
}

/* Bouton "de sortie" (maquette validee, fiche produit) : jamais primaire -- reserve aux actions qui
   font sortir/publier un contenu ("Ecrire sur la boutique"), par opposition a l'action principale
   ("Traduire toute la fiche", .btn--primary). */
.btn--sortie {
  background: var(--surface);
  color: var(--texte);
  border-color: var(--bordure-forte);
}

.btn--sortie:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.btn--sm {
  font-size: 0.8rem;
  padding: var(--espace-1) var(--espace-2);
}

/* Badge (etat court, liste dense) -- pastille de couleur devant le libelle (maquette validee) :
   ::before dessine un petit rond plein en currentColor, donc il suit automatiquement la couleur
   de chaque modificateur (--succes/--attente/--alerte/--neutre) sans variable supplementaire. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  font-weight: 500;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.9;
  flex: none;
}

.badge--succes {
  background: var(--succes-fond);
  color: var(--succes);
}

.badge--attente {
  background: var(--attente-fond);
  color: var(--attente);
}

.badge--alerte {
  background: var(--alerte-fond);
  color: var(--alerte);
}

.badge--neutre {
  background: var(--fond);
  color: var(--texte-attenue);
  border: 1px solid var(--bordure);
}

/* 🆕 .badge--erreur (lot Q, 10/09) -- MANQUAIT a cette famille : les trois autres etats (succes/
   attente/alerte) avaient leur badge, pas l'erreur -- alors que `--erreur`/`--erreur-fond` existent
   deja (charte : 4 couleurs d'etat) et servent deja `.alert--err`/`.err` ailleurs dans ce fichier.
   Premier appelant : l'ecran « Ce qui a echoue » (.ecran-echecs-ecriture), pour la nature "attend un
   geste" -- jamais une 5e couleur inventee, seulement le modificateur qui manquait a celle-ci. */
.badge--erreur {
  background: var(--erreur-fond);
  color: var(--erreur);
}

/* --- L'INTERRUPTEUR A POSITIONS (`_interrupteur-decision.njk`, chantier interrupteur, 22/08) ----
   Composant PARTAGE par tous les ecrans de decision -- une seule feuille de style, jamais une copie
   par ecran (defaut n°1 du depot). Capsule "pill" (coins 999px), segments separes par un filet fin.
   La position ACTIVE est un `<span>` (INERTE, pas un bouton) rempli d'un jeton d'accent -- jamais
   une couleur en dur -- et porte la coche dessinee (`icone('coche')`) : la couleur ne porte jamais
   l'information seule. Sans decision, la capsule passe en bordure pointillee (`--indecis`) : aucun
   segment n'est rempli, toutes les positions restent des boutons cliquables. */
.interrupteur-decision-form {
  display: inline-flex;
  /* 3-059 (07/09) : la capsule peut desormais etre suivie d'un champ « jusqu'au ». `wrap` +
     `align-items` evitent qu'une colonne etroite pousse la date hors de la cellule -- aucune
     largeur en dur, aucune couleur en dur (charte, invariant : le sombre reste gratuit). */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin: 0;
}

/* 3-059 -- LE CHAMP « JUSQU'AU » d'un regime ouvert pour un lot (facultatif : la macro ne le rend
   que si l'ecran le demande). Discret par construction : c'est une precision sur la decision, pas
   une decision de plus. */
.interrupteur-decision__date {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: 0.85em;
  font-style: normal;
}

.interrupteur-decision__date input {
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
  padding: 0.1rem 0.25rem;
  font: inherit;
}

.interrupteur-decision {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--bordure-forte);
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface);
}

.interrupteur-decision--indecis {
  border-style: dashed;
}

.interrupteur-decision__segment {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.2;
  border: none;
  border-left: 1px solid var(--bordure);
  background: transparent;
  color: var(--texte-attenue);
  white-space: nowrap;
}

.interrupteur-decision__segment:first-child {
  border-left: none;
}

/* Seules les positions INACTIVES sont de vrais boutons -- le curseur et le survol ne s'appliquent
   qu'a `<button>`, jamais au `<span>` inerte de la position active. */
button.interrupteur-decision__segment {
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}

button.interrupteur-decision__segment:hover {
  background: var(--accent-fond);
  color: var(--accent);
}

.interrupteur-decision__segment .icone {
  width: 0.85em;
  height: 0.85em;
  flex: none;
}

/* Le remplissage "oui" (jeton d'accent) et le remplissage "non/neutre" (jeton neutre fonce) sont
   DEUX couleurs distinctes -- jamais la meme pour deux sens opposes (maquette validee). */
.interrupteur-decision__segment--actif {
  background: var(--accent);
  color: #fff;
}

.interrupteur-decision__segment--actif.interrupteur-decision__segment--neutre {
  background: var(--neutre-actif);
  color: var(--neutre-actif-texte);
}

/* Tableau */
/* Par defaut, l'enveloppe NE rogne PAS (pas de overflow) -- un ancetre en overflow
   coupe aussi ses descendants positionnes : l'infobulle .aide ancree sur un <th>,
   placee au-dessus de la ligne d'en-tete, sortirait de la boite et serait rognee.
   Pour les rares tableaux larges qui ont vraiment besoin de defiler horizontalement,
   ajouter le modificateur .table__wrap--scroll ci-dessous.
   ATTENTION -- LES DEUX NE SE COMBINENT PAS : specifier overflow-x en non-"visible"
   force le navigateur a calculer overflow-y a "auto" (regle du spec CSS), donc un
   tableau qui defile rogne aussi verticalement -- une bulle au-dessus de l'en-tete
   serait rognee meme si on la mettait "en dessous" a la place. Un tableau qui
   defile ne peut donc pas porter d'infobulle telle qu'elle est faite : ne poser
   data-aide que sur un tableau SANS ce modificateur (ex. la liste principale,
   4 colonnes, qui ne defile pas). */
.table__wrap {
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.table__wrap--scroll {
  overflow-x: auto;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.table th, .table td {
  text-align: left;
  padding: var(--espace-2) var(--espace-3);
  border-bottom: 1px solid var(--bordure);
}

.table th {
  color: var(--texte-attenue);
  font-weight: 500;
  background: var(--fond);
}

.table tbody tr:last-child td {
  border-bottom: none;
}

.table tbody tr:hover {
  background: var(--fond);
}

/* La table des reglages de module (20/08, soin visuel) : la derniere colonne ne porte que le
   groupe de boutons .module-choix -- sans cette regle, la colonne Motif (une phrase) revendique
   toute la largeur disponible et comprime les boutons jusqu'a leur faire perdre une ligne.
   `width: 1%` est le detournement standard qui force le navigateur a ne donner a cette colonne QUE
   l'espace requis par son contenu (deja borne par white-space: nowrap), et a pousser le reste vers
   les colonnes de texte -- qui, elles, continuent de revenir a la ligne normalement. */
.table--reglages td:last-child,
.table--reglages th:last-child {
  /* ⚠️ PAS de white-space: nowrap sur la CELLULE (mesure du 20/08 sur la vraie page : combine aux
     cles techniques insecables de la 1re colonne, il forcait la table au-dela de sa carte et la
     colonne des gestes se retrouvait PEINTE HORS CADRE -- le debordement de .table__wrap n'est pas
     rogne). Le nowrap vit sur les BOUTONS (.module-choix .btn) : un bouton ne casse jamais son
     libelle, mais les boutons peuvent s'empiler dans la cellule. */
  width: 1%;
}

/* Les cles techniques (COLISSIMO_ACCOUNT_CONTRACT_NUMBER...) sont INSECABLES par nature : sans
   autorisation de coupure, leur largeur minimale pousse la table hors de sa carte.
   🔴 CLASSE EXPLICITE, PAS :first-child (soin visuel, 21/08) -- le tiroir « sans module reconnu »
   a gagne une colonne de case a cocher DEVANT la cle (edition en masse) ; un selecteur positionnel
   aurait alors protege la case (rien a proteger) et laisse la vraie colonne des cles nue, exactement
   le defaut que cette regle existe pour eviter. La classe vit sur le <td> lui-meme
   (_ligne-reglage.njk), donc les TROIS ecrans qui partagent ce fragment restent couverts.
   🔴 EXCEPTION NOMMEE DU LOT 7-309 (12/09) -- `anywhere` reste ICI, volontairement, et SEULEMENT
   ici : une cle technique est une chaine SANS AUCUN ESPACE, `break-word` (utilise partout ailleurs
   depuis ce lot) attendrait un point de coupure qui n'existe jamais et laisserait la cellule
   deborder de la carte -- exactement le defaut que cette regle existe deja pour eviter. */
.table--reglages td.td--cle-reglage {
  overflow-wrap: anywhere;
}

/* La colonne de case a cocher (soin visuel, 21/08, edition en masse du tiroir) -- meme
   detournement `width: 1%` que la derniere colonne des gestes : ne revendique que l'espace requis
   par la case, jamais une part de la largeur disponible. */
.table--reglages td.td--case-reglage,
.table--reglages th.th--case-reglage {
  width: 1%;
}

/* 7-031, 13/09 : UNE SEULE ZONE CLIQUABLE PAR LIGNE, decision de l'utilisateur du 11/09 --
   REMPLACE la decision "tache 9" (21/08) qui posait un <a> identique DANS CHAQUE cellule d'une
   ligne (six par ligne sur les categories, sept sur les produits), faute de pouvoir entourer le
   <tr> lui-meme par un <a> (illegal en HTML). Meme contrainte, autre remede -- meme patron que la
   carte de langue de l'accueil ("la carte entiere est un lien englobant", 7-291, univers.njk),
   adapte a un <tr> qui ne peut jamais etre lui-meme la balise <a> : un SEUL <a class=
   "table-row-link"> vit desormais dans la PREMIERE cellule de donnees de la ligne (categories :
   colonne ID ; produits : colonne ID, apres la case a cocher), et son ::after l'etend a toute la
   ligne par un positionnement absolu (inset:0) cale sur le <tr> -- voir la regle
   "position:relative" partagee entre les deux ecrans, plus bas dans cette feuille. Les AUTRES
   cellules de la ligne ne portent plus de <a> vers la meme destination : leur texte reste du
   texte normal, rendu cliquable uniquement par cet overlay. */
.table-row-link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.table-row-link::after {
  content: '';
  position: absolute;
  inset: 0;
  /* z-index EXPLICITE (pas juste "positionne") : la case de selection de la liste produits doit
     pouvoir se hisser au-dessus de CET overlay precis avec un z-index superieur -- voir
     .ecran-produits td.td--case-selection, plus bas. Sans une valeur explicite ici, la comparaison
     "z-index: 1 > z-index: auto" resterait vraie mais moins lisible a l'entretien. */
  z-index: 0;
}

.table-row-link:hover,
.table-row-link:focus-visible {
  text-decoration: underline;
}

/* Le focus clavier doit se voir sur TOUTE LA LIGNE (un seul arret de tabulation par ligne, exige
   du 11/09) -- pas seulement sur le petit texte porteur du <a> (l'identifiant, en premiere
   colonne) : l'anneau par defaut du navigateur se dessinerait sur ce texte seul, invisible a
   l'echelle de la ligne. On le retire donc du <a> et on le repose sur son ::after, qui couvre la
   ligne entiere -- memes jetons que le reste de la charte (--accent, 2px, cf. .champ input:focus
   plus haut et .connexion-carte__champ:focus-visible plus bas). */
.table-row-link:focus-visible {
  outline: none;
}

.table-row-link:focus-visible::after {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Referentiel de positionnement du ::after ci-dessus (7-031) : sans position:relative ICI, sur le
   <tr>, inset:0 se calerait sur le PROCHAIN ancetre positionne rencontre en remontant le DOM --
   potentiellement bien plus grand que la ligne (voire la page entiere). Regle PARTAGEE entre les
   DEUX tableaux (produits ET categories) -- un seul selecteur, jamais deux copies : meme
   mecanisme, memes jetons, decision de l'utilisateur explicitement "les deux tableaux ensemble". */
.ecran-produits .table tbody tr,
.ecran-categories .table tbody tr {
  position: relative;
}

/* Alerte (bandeau de statut) */
.alert {
  padding: var(--espace-3) var(--espace-4);
  border-radius: var(--rayon);
  margin-bottom: var(--espace-4);
  border: 1px solid transparent;
}

.alert--ok {
  background: var(--succes-fond);
  color: var(--succes);
  border-color: var(--succes-fond);
}

.alert--warn {
  background: var(--alerte-fond);
  color: var(--alerte);
  border-color: var(--alerte-fond);
}

.alert--err {
  background: var(--erreur-fond);
  color: var(--erreur);
  border-color: var(--erreur-fond);
}

/* Informationnel (ex. signalement du relecteur : "vrai mais non couvert par une regle declaree",
   n'empeche jamais la publication -- il informe). Aucune des quatre couleurs d'etat (succes/attente/
   alerte/erreur) ne convient a un message ni positif ni negatif : on reprend donc l'idiome deja posé
   pour .badge--neutre (fond/texte-attenue/bordure) plutot que d'inventer une cinquieme couleur. */
.alert--info {
  background: var(--fond);
  color: var(--texte-attenue);
  border-color: var(--bordure);
}

/* Fil d'Ariane */
.fil {
  font-size: 0.85rem;
  color: var(--texte-attenue);
  margin-bottom: var(--espace-4);
}

.fil a {
  color: var(--texte-attenue);
}

.fil a:hover, .fil a:focus-visible {
  color: var(--accent);
}

.fil__separateur {
  margin: 0 var(--espace-2);
  color: var(--bordure);
}

.fil__courant {
  color: var(--texte);
  font-weight: 500;
}

/* Bouton "Retour" (navigation ARRIERE de l'historique, sous le fil d'Ariane -- retouche UX du
   22/08). Repose sur .btn--ghost/.btn--sm : aucune couleur en dur, le mode sombre suit tout seul.
   `display: inline-flex` (herite de .btn) l'empeche de s'etirer sur toute la largeur -- il reste
   aligne a gauche sans regle supplementaire. */
.bouton-retour {
  margin-bottom: 0;
}

/* 🔴 L'ÉCART « Retour » → titre (décision utilisateur, 22/08 : « espace normal »). Cause mesurée :
   `.bouton-retour` portait `margin-bottom: var(--espace-4)` (1rem) EN PLUS du `margin-top:
   var(--espace-6)` (2rem) du premier `h1, h2, h3` générique -- environ 3rem cumulés. Le
   `margin-bottom` ci-dessus passe à 0 ; les règles suivantes réduisent le `margin-top` du PREMIER
   titre qui suit le bouton, sur les TROIS structures réelles du dépôt (h1 frère direct du bouton ;
   h1 premier enfant d'un conteneur frère -- `views/univers-detail.njk` enveloppe tout son corps
   dans `<div class="ecran-langue">` ; et le cas de la fiche/catégorie, ci-dessous) -- jamais une
   règle générique qui toucherait un h1 sans rapport avec ce bouton. */
.bouton-retour + h1,
.bouton-retour + * > h1:first-child {
  margin-top: var(--espace-3);
}

/* 🔴 CORRECTIF DE RELECTURE (22/08) : LA TROISIÈME STRUCTURE, RATÉE PAR LES DEUX RÈGLES CI-DESSUS.
   `views/fiche.njk` et `views/categorie.njk` enveloppent leur corps dans `<div class="fiche">`,
   dont le PREMIER ENFANT est `<header class="fiche-entete">` -- pas un h1 : le h1 réel vit encore
   un niveau plus bas (`.fiche-entete > div > h1`), hors de portée d'un sélecteur `> h1:first-child`.
   Ni `.fiche` ni `.fiche-entete` ne portent de `margin-top` (`.fiche-entete` n'a qu'un
   `margin-bottom`) : sans cette règle, `margin-bottom: 0` sur `.bouton-retour` COLLAIT le bouton à
   l'en-tête de fiche (mesuré en réel sur `/fr-es/produit/100` : 0px, contre 1rem avant ce lot).
   Cible directement le conteneur `.fiche`, jamais son h1 interne -- même écart que les deux autres
   structures. */
.bouton-retour + .fiche {
  margin-top: var(--espace-3);
}

/* Champ de formulaire */
.champ {
  display: block;
  margin-bottom: var(--espace-4);
}

.champ label {
  display: block;
  margin-bottom: var(--espace-1);
  font-size: 0.85rem;
  color: var(--texte-attenue);
}

.champ input, .champ textarea, .champ select {
  font-family: inherit;
  font-size: 0.9rem;
  width: 100%;
  padding: var(--espace-2) var(--espace-3);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
}

.champ input:focus, .champ textarea:focus, .champ select:focus {
  outline: 2px solid var(--accent-fond);
  border-color: var(--accent);
}

/* Barre de filtres (carte des emplacements + couverture par bloc) : sans cette regle,
   recherche/marque/etat s'affichaient en flux brut, empiles verticalement pleine largeur
   (correctif de relecture, point 6). Un simple flex reutilisant les jetons d'espacement existants
   suffit -- pas une refonte. Placee APRES .champ (meme specificite sur input/select : l'ordre du
   fichier tranche) pour que "width: auto" gagne bien face a ".champ input/select { width: 100% }".
   🔴 LA LISTE PRODUITS N'EN FAIT PLUS PARTIE DEPUIS LE 20/08 : elle porte desormais sa PROPRE
   barre, `.filtre-produits` (labels au-dessus des champs, voir plus bas) -- le constat de
   l'utilisateur etait que cette barre-ci (recherche/marque/etat en ligne nue, sans etiquette) ne
   s'alignait pas avec le tableau. `.filtres-emplacements`/`.filtre-couverture` gardent CETTE
   forme, inchangee : la retoucher ici aurait deplace le defaut sur deux ecrans qui n'ont rien
   demande. */
/* Tache B du lot D2 (04/08) : la carte des emplacements reprend EXACTEMENT la meme barre que la
   couverture par bloc -- meme flex, memes jetons. Ajouter le selecteur ici plutot que recopier le
   bloc : deux copies divergent au premier reglage applique a une seule des deux. */
.filtres-emplacements,
/* Lot UX-2 (couverture par bloc) : meme barre, meme jetons -- selecteur ajoute ici plutot que
   recopie, comme le lot D2 l'a deja fait pour .filtres-emplacements. */
.filtre-couverture {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-3);
  margin-bottom: var(--espace-4);
}

.filtres-emplacements input,
.filtres-emplacements select,
.filtre-couverture select {
  width: auto;
}

.filtres-emplacements input[type="search"] {
  flex: 2 1 220px;
}

.filtres-emplacements select,
.filtre-couverture select {
  flex: 1 1 160px;
}

/* 🆕 LA BARRE DE FILTRES DE LA LISTE PRODUITS (20/08, constat de l'utilisateur par capture d'ecran :
   recherche/marque/etat ne s'alignaient pas avec le tableau). Meme patron que .filtre-modules
   (deja en charte, cadrage-modules.njk/modules.njk) : chaque champ porte son LIBELLE au-dessus,
   ce qui lui donne la meme hauteur de ligne et le meme alignement vertical que ses voisins --
   contrairement a la barre nue ci-dessus, ou un <input> et un <select> n'ont jamais exactement la
   meme hauteur intrinseque selon le navigateur. Scopee sous .ecran-produits (charte-ux.html, la
   classe de portee) : ces regles n'atteignent aucun des 30+ autres ecrans.
   🆕 .ecran-categories AJOUTE (chantier liste des categories, 21/08) : la liste des categories
   reutilise LA MEME classe .filtre-produits telle quelle (meme gabarit, meme HTML) -- deux
   ecrans partagent donc ce selecteur, comme .filtres-emplacements/.filtre-couverture plus haut
   dans cette feuille.
   🆕 .ecran-reglages-sans-module AJOUTE (soin visuel du tiroir des reglages, 21/08) : TROISIEME
   ecran a reutiliser ce meme gabarit telle quelle (recherche + un select `etat`, meme HTML). */
.ecran-produits .filtre-produits,
.ecran-categories .filtre-produits,
.ecran-reglages-sans-module .filtre-produits {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--espace-3);
  margin: var(--espace-4) 0;
}
.ecran-produits .filtre-produits label,
.ecran-categories .filtre-produits label,
.ecran-reglages-sans-module .filtre-produits label {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  font-size: 0.78rem;
  color: var(--texte-attenue);
  flex: 1 1 160px;
}
.ecran-produits .filtre-produits__recherche,
.ecran-categories .filtre-produits__recherche,
.ecran-reglages-sans-module .filtre-produits__recherche { flex: 2 1 220px; }
.ecran-produits .filtre-produits input,
.ecran-produits .filtre-produits select,
.ecran-categories .filtre-produits input,
.ecran-categories .filtre-produits select,
.ecran-reglages-sans-module .filtre-produits input,
.ecran-reglages-sans-module .filtre-produits select {
  font: inherit;
  font-size: 0.88rem;
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
  width: auto;
}

/* La case « Adresse protégée uniquement » (lot « URL protégée, ne pas mentir », 25/08) : seule
   case de .filtre-produits (les autres labels du bloc portent un texte AU-DESSUS d'un input, voir
   la règle flex-direction:column ci-dessus) -- ici la case et son texte doivent tenir côte à côte
   sur UNE ligne, jamais empilés. Un sélecteur PLUS SPÉCIFIQUE (deux classes) l'emporte sur la règle
   générale sans avoir besoin de !important. Scopée .ecran-produits seulement : aucun autre écran
   ne porte cette case aujourd'hui. */
.ecran-produits .filtre-produits label.filtre-produits__case {
  flex-direction: row;
  align-items: center;
  gap: var(--espace-2);
  flex: 0 0 auto;
  white-space: nowrap;
}
.ecran-produits .filtre-produits__case input[type="checkbox"] {
  width: auto;
}

/* La barre « Appliquer à la sélection » du tiroir des réglages (soin visuel, 21/08, édition en
   masse) -- un simple flex, même jetons que les barres de filtre ci-dessus. Scopée : n'atteint
   aucun autre écran.
   🆕 .ecran-produits AJOUTÉ (lot « sélection en masse », 24/08) : DEUXIÈME écran à réutiliser ce
   même gabarit (barre + case à cocher + attribut `form`) tel quel -- voir produits.njk. */
.ecran-reglages-sans-module .barre-selection,
.ecran-produits .barre-selection {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-3);
  margin-bottom: var(--espace-3);
}
/* 🆕 MASQUAGE PAR SELECTION (21/08 soir) -- `[hidden]` seul NE SUFFIT PAS : la regle `display:flex`
   ci-dessus est une regle AUTEUR, elle bat toujours la feuille UA `[hidden]{display:none}` a
   specificite egale (l'origine de la regle depasse la specificite dans la cascade). Sans cette
   ligne, poser l'attribut `hidden` en JS ne cacherait RIEN -- la barre resterait un flex visible.
   Meme piege pour `.groupe-geste` (barre-selection-groupe) plus bas dans ce fichier. */
.ecran-reglages-sans-module .barre-selection[hidden],
.ecran-produits .barre-selection[hidden] {
  display: none;
}

/* La colonne de case à cocher de la liste produits (lot « sélection en masse », 24/08) -- même
   détournement `width: 1%` que celle du tiroir des réglages (`.th--case-reglage` ci-dessus) : ne
   revendique que l'espace requis par la case, jamais une part de la largeur disponible. Classes
   GÉNÉRIQUES (pas -reglage, cet écran n'a pas de « réglage ») : réutilisables telles quelles par un
   futur écran de liste (catégories, e-mails...) qui gagnerait la même sélection en masse. */
.ecran-produits td.td--case-selection,
.ecran-produits th.th--case-selection {
  width: 1%;
  /* 🆕 (sélection de plage au Maj+clic, 24/08) : sans elle, un Maj+clic sur une checkbox déclenche
     la sélection de TEXTE du navigateur (comportement natif du Maj+clic hors de tout champ) --
     visible comme un surlignage bleu qui traverse les lignes du tableau. Scopée à la cellule de la
     case, jamais au tableau entier (le texte des autres colonnes reste sélectionnable normalement). */
  user-select: none;
}

/* 7-031 (13/09) : la case de selection reste AU-DESSUS de l'overlay du lien de ligne
   (.table-row-link::after, plus haut) -- sans cette elevation, cliquer sur la case naviguerait
   vers la fiche au lieu de la (de)cocher : l'overlay, absolument positionne sur toute la ligne
   (inset:0 sur le <tr>), passerait par-dessus une case restee statique. `position: relative` cree
   ici un contexte pour porter un z-index ; 1 > 0 (le z-index explicite de l'overlay) suffit a
   gagner l'empilement, quel que soit l'ordre du DOM entre les deux cellules. Scopee au <td> SEUL
   (jamais au <th> : l'en-tete n'a pas de ligne "en trop" a proteger, aucun overlay n'y vit). */
.ecran-produits td.td--case-selection {
  position: relative;
  z-index: 1;
}
/* 🔴 `#tableau-produits` VIT DANS LE MEME `<form>` QUE LES CHAMPS (voir produits.njk) -- donc DANS
   ce conteneur `display:flex`, ce qui en fait un ELEMENT FLEX comme les autres. Sans cette regle,
   un flex-item se dimensionne par defaut sur son CONTENU (shrink-to-fit), pas sur la largeur du
   conteneur : le tableau poussait alors le `<form>` bien au-dela de sa carte (mesure a l'ecran, 375
   px : 677 px de large) au lieu de passer sous les champs et de prendre toute la largeur -- le
   `max-width:100%` pose sur `.table__wrap` plus bas ne pouvait rien y faire, son parent etait deja
   trop large. `flex: 1 1 100%` force sa PROPRE ligne (flex-wrap la fait déjà passer dessous) sur
   toute la largeur disponible. `#tableau-categories` : MEME besoin, meme remede (id different,
   memes categories vivent dans le meme <form> que ses champs de filtre). */
.ecran-produits #tableau-produits,
.ecran-categories #tableau-categories { flex: 1 1 100%; min-width: 0; }

/* 🔴 MESURE A L'ECRAN (20/08, 375 px) : le tableau des produits est passe de 5 a 7 colonnes (les
   deux statuts) -- au-dela de la carte sur un telephone, il forcait un DEFILEMENT HORIZONTAL DE LA
   PAGE ENTIERE (694 px de contenu dans un viewport de 375 px). `.table__wrap` reste volontairement
   SANS `--scroll` par defaut (le commentaire au-dessus de sa regle l'explique : ca rognerait les
   infobulles `data-aide`) -- mais une infobulle au survol n'a de toute facon aucun sens sur un
   ecran tactile. ⇒ le defilement horizontal est active UNIQUEMENT sous 620 px (meme seuil que les
   autres bascules mobiles de cette feuille), scope a CET ecran : le reste du tableau (desktop,
   ou l'infobulle sert vraiment) est inchange. La liste des categories (6 colonnes) porte le meme
   risque -- meme remede, meme seuil. */
@media (max-width: 620px) {
  /* `max-width: 100%` EST NECESSAIRE, PAS DECORATIF : sans elle, `.table__wrap` grandit a la
     largeur de son CONTENU (le tableau, ~680 px) au lieu de celle de son PARENT -- rien ne
     "deborde" alors de sa propre boite, et `overflow-x: auto` ne se declenche jamais (mesure a
     l'ecran : la regle seule ne suffisait pas). */
  .ecran-produits .table__wrap,
  .ecran-categories .table__wrap { max-width: 100%; overflow-x: auto; }
}

/* La case a cocher et son texte sur une seule ligne (sinon .champ les empile). */
.champ--case {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--espace-2);
}

/* Le marqueur « votre décision manque » : une pastille suivie de sa phrase, sous le nom de
   l'emplacement. C'est la seule chose de cet ecran qui dise a l'utilisateur qu'on l'attend --
   elle doit respirer, pas se fondre dans la ligne. */
.attente-decision {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-top: var(--espace-2);
  max-width: 34rem;
}

/* Pagination (liste produits, tache 9) : meme correctif, memes jetons. */
.pagination-produits {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  padding-top: var(--espace-3);
}

/* -------------------------------------------------------------------------
   4. Infobulle (.aide) -- CSS pur, survol ET focus clavier
   ------------------------------------------------------------------------- */
/* Exigence : le focus clavier (:focus-visible) ne se declenche QUE si l'element
   porteur de data-aide est focusable. Un <th> ordinaire n'est PAS focusable :
   il faut lui ajouter tabindex="0" (ex. <th data-aide="..." tabindex="0">).
   Sans ce tabindex, l'infobulle marche au survol mais l'exigence clavier
   disparait silencieusement -- rien ne le signale a l'usage.
   🆕 7-010 (10/09) : SECOND CAS, un <th data-aide="..."> SANS tabindex mais qui CONTIENT un
   element deja focusable (un <a>, _tableau-produits.njk) -- poser tabindex="0" EN PLUS creerait
   une DEUXIEME cible clavier pour le meme en-tete (defaut mesure le 02/09). `:focus-within`
   couvre ce cas (elle se declenche aussi quand un DESCENDANT recoit le focus), SANS rien retirer
   au premier cas : sur un <th tabindex="0"> sans enfant focusable, un element est toujours
   « focus-within » de lui-meme des qu'il est focus. */
[data-aide] {
  position: relative;
  cursor: help;
  border-bottom: 1px dotted var(--texte-attenue);
}

[data-aide]:focus {
  outline: none;
}

[data-aide]::after {
  content: attr(data-aide);
  position: absolute;
  bottom: 125%;
  left: 50%;
  transform: translateX(-50%);
  background: var(--texte);
  color: var(--surface);
  font-size: 0.75rem;
  font-style: normal;
  font-weight: 400;
  white-space: normal;
  width: max-content;
  max-width: 220px;
  text-align: left;
  padding: var(--espace-1) var(--espace-2);
  border-radius: var(--rayon-sm);
  box-shadow: var(--ombre);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 10;
}

[data-aide]:hover::after,
[data-aide]:focus-visible::after,
[data-aide]:focus-within::after {
  opacity: 1;
  visibility: visible;
}

/* Derniere colonne d'un tableau, pres du bord droit : une bulle centree deborderait
   a droite. On ancre plutot son bord droit sur le bord droit de l'element porteur. */
.table th:last-child[data-aide]::after {
  left: auto;
  right: 0;
  transform: none;
}

/* -------------------------------------------------------------------------
   5. Grille de cartes (responsive)
   ------------------------------------------------------------------------- */
.grille-cartes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--espace-4);
}

/* -------------------------------------------------------------------------
   6. Fiche produit -- sections empilees, deux colonnes FR/ES au MEME format
   (maquette validee, tache 10 bis : docs/design/maquette-fiche-validee.html).
   Les corps de regle ci-dessous sont copies VERBATIM depuis cette maquette
   (memes valeurs numeriques), avec les selecteurs adaptes au prefixe
   `fiche-` pour ne jamais entrer en collision avec des classes courtes deja
   partagees par d'autres ecrans (.champ = ligne de formulaire du glossaire
   et de la barre de filtres, section 3 ; un `.section`/`.col`/`.boite` bruts
   n'existaient pas encore mais restent reserves a d'autres composants futurs).
   ------------------------------------------------------------------------- */

/* Portee locale : la maquette utilise des rayons plus genereux (10px/7px) que
   le reste de l'appli (8px/5px, section 1). Redefinir les jetons DANS le
   perimetre de la fiche (cascade des variables CSS) les applique a tout ce qui
   les consomme deja ici (.fiche-groupe, .fiche-champ__contenu, les
   <textarea>, les boutons) SANS toucher au reste de l'application (listes,
   glossaire, connexion...). */
.fiche {
  --rayon: 10px;
  --rayon-sm: 7px;
}

/* --- En-tete de fiche (titre + actions), maquette ".entete" ------------------------------------ */
.fiche-entete {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 1.75rem;
}

.fiche-entete h1 {
  font-size: 1.45rem;
  font-weight: 650;
  margin: 0 0 0.3rem;
  text-wrap: balance;
  letter-spacing: -0.01em;
}

.entete__sous {
  font-size: 0.85rem;
  color: var(--texte-attenue);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.paire {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-weight: 550;
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bordure);
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
}

/* --- Actions de tete (maquette ".actions"/".actions__ligne"/".actions__aide") : "Traduire toute la
   fiche" / "Ecrire sur la boutique", DANS l'en-tete (spec §7.4 : jamais enfouies au milieu des
   champs) -- ciblent #fiche-corps par ID, donc restent actives et persistent au re-rendu du
   fragment meme places hors de lui (invariant fonctionnel, voir _fiche-corps.njk). */
.fiche-entete-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}

.fiche-entete-actions__ligne {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.fiche-entete-actions__aide {
  margin: 0;
  font-size: 0.78rem;
  color: var(--texte-attenue);
  max-width: 340px;
  text-align: right;
}

.fiche-entete-actions__aide b {
  color: var(--texte);
  font-weight: 600;
}

/* --- Section (maquette ".section"/".section__tete"/".section__corps") : une carte blanche avec une
   barre d'en-tete distincte (libelle en MAJUSCULES, sur fond --surface-2) et un corps rembourre. Un
   groupe PrestaShop, une categorie d'entite partagee (caracteristiques/couleurs/marque) ou la
   relecture -- toutes empilees, jamais d'onglets (decision de cadrage, voir CLAUDE.md). */
.fiche-groupe {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-bottom: 1.15rem;
  overflow: hidden;
}

.fiche-groupe__tete {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.85rem 1.15rem;
  border-bottom: 1px solid var(--bordure);
  background: var(--surface-2);
}

.fiche-groupe__tete h2 {
  font-size: 0.82rem;
  font-weight: 650;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--texte-attenue);
}

.fiche-groupe__corps {
  padding: 0.5rem 1.15rem 1.15rem;
  display: flex;
  flex-direction: column;
}

/* --- Champ (maquette ".champ"/".champ__tete"/".champ__nom"/".champ__partage") : une ligne complete
   (entete + deux colonnes + annotations), separee de la suivante par un filet POINTILLE -- pas un
   nouveau <table> par champ (compact, mais jamais tronque). */
.fiche-champ {
  padding: 0.95rem 0;
  border-bottom: 1px dashed var(--bordure);
}

.fiche-champ:first-child {
  padding-top: 0;
}

.fiche-champ:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

/* Nom a GAUCHE, badge d'etat pousse a DROITE (point 2 de la revue -- avant : le badge suivait le nom
   dans le flux, sans les separer). */
.fiche-champ__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.55rem;
}

/* Le nom TECHNIQUE d'un champ (table.colonne), a cote du nom back-office -- harmonisation du
   20/08 : les DEUX noms, sur toutes les fiches. Jetons seulement, lisible dans les deux themes. */
.fiche-champ__technique {
  font-family: ui-monospace, 'Cascadia Code', Consolas, monospace;
  font-size: 0.72rem;
  color: var(--texte-attenue);
  background: none;
  padding: 0;
}

.fiche-champ__nom {
  font-weight: 600;
  font-size: 0.95rem;
}

.fiche-champ__partage {
  font-size: 0.76rem;
  color: var(--texte-attenue);
  font-weight: 400;
}

/* --- Deux colonnes FR/ES au MEME format (maquette ".duo") -- repliees en une seule colonne sur un
   ecran etroit (spec §7.4 : "chaque champ en DEUX colonnes", pas un tableau a 4 colonnes). */
.fiche-champ__colonnes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

@media (max-width: 620px) {
  .fiche-champ__colonnes {
    grid-template-columns: 1fr;
  }
}

.fiche-champ__col {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

/* Titre de colonne (maquette ".col__tete") : la regle DE BASE reste neutre -- partagee avec le
   glossaire des conditions commerciales (_liste-conditions.njk), qui n'a pas besoin du traitement
   MAJUSCULES/couleur/drapeau ci-dessous. Ce traitement vit UNIQUEMENT dans les modificateurs
   --fr/--cible (point 3 de la revue), poses seulement par la fiche produit. */
.fiche-champ__col-titre {
  display: block;
  margin-bottom: var(--espace-1);
  font-size: 0.8rem;
  color: var(--texte-attenue);
}

/* Drapeau = un petit SVG (jamais un emoji : Windows n'a pas les glyphes de drapeaux). Dimensionne en
   em pour suivre la taille du libelle a cote (titre de colonne ~0.72rem, pastille de paire ~0.85rem),
   ratio 3:2, coins legerement arrondis, fin liseré qui suit le theme via --bordure. flex:none pour ne
   pas se laisser ecraser dans le conteneur inline-flex du titre de colonne. */
.drapeau {
  display: inline-block;
  width: 1.35em;
  height: 0.9em;
  border-radius: 2px;
  border: 1px solid var(--bordure);
  vertical-align: -0.12em;
  flex: none;
  overflow: hidden;
}

/* EN MAJUSCULES, letter-spacing, drapeau devant, couleur sobre par cote (bleu ardoise discret cote
   FR/source, cuir-orange discret cote ES/cible) -- reserve a la fiche produit (maquette validee). */
.fiche-champ__col-titre--fr,
.fiche-champ__col-titre--cible {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.fiche-champ__col-titre--fr { color: var(--fr-accent); }
.fiche-champ__col-titre--cible { color: var(--cible-accent); }

/* --- Boites (maquette ".boite"/".boite--fr"/".boite--cible") : MEME arrondi, MEME style des deux
   cotes -- plus de "carre annees 90". Contenu FR (lecture seule) ou ES verrouille (non editable) :
   jamais rogne -- pas de max-height ni d'overflow cache, le texte le plus long (description riche)
   reste entierement present et revient a la ligne naturellement (exigence explicite, tache 10 --
   c'est le SEUL ajout par rapport a la maquette, necessaire ici car ce sont de vrais <div>, pas la
   demo courte de la maquette). */
.fiche-champ__contenu {
  white-space: pre-wrap;
  /* 7-309 -- harmonise sur la propriete standard (`word-break: break-word` se comporte comme
     `overflow-wrap: break-word` sur les navigateurs modernes, mais n'est pas la propriete pensee
     pour ca -- meme correctif que .apercu-prompt plus haut). */
  overflow-wrap: break-word;
  border-radius: var(--rayon-sm);
  padding: 0.6rem 0.7rem;
  font-size: 0.92rem;
  line-height: 1.5;
  min-height: 2.6rem;
  border: 1px solid var(--bordure);
}

.fiche-champ__contenu--fr {
  background: var(--fr-fond);
  border-color: color-mix(in srgb, var(--fr-accent) 22%, var(--bordure));
  color: var(--texte);
}

.fiche-champ__contenu--cible {
  background: var(--surface);
  border-color: color-mix(in srgb, var(--cible-accent) 30%, var(--bordure));
  color: var(--texte);
}

/* Zone d'edition ES (proposition non verrouillee) : MEME forme que .fiche-champ__contenu ("meme
   format arrondi" de la maquette validee), avec un contour qui s'accentue au focus. Le contenu
   COMPLET vit deja dans la valeur du champ ; `rows` n'est qu'un confort visuel, jamais une
   troncature (un textarea trop plein defile en interne, il ne perd aucun caractere). Reste un
   <textarea> fonctionnel (jamais contenteditable -- invariant fonctionnel).
   🆕 19/09 (constat fiche 100, « le champ de saisie qui fait toute la page ») -- le plancher est
   descendu de 8rem a 2.6rem (~une ligne + le rembourrage) : c'est desormais `rows` (calcule par
   `rowsPourTexte`, presentation/rows-texte.ts, borne [2, 12] sur le texte REEL, jamais une
   constante) qui porte la hauteur reelle, ce plancher n'etant qu'un minimum de confort au clavier.
   Un ancien appelant qui pose encore un `rows` fixe (8 pour un champ riche non termine, voir la
   branche "non termine" de _fiche-corps.njk) depasse largement ce plancher -- rien ne change pour
   lui. */
.fiche-champ__col textarea {
  display: block;
  width: 100%;
  min-height: 2.6rem;
  font: inherit;
  border-radius: var(--rayon-sm);
  padding: 0.6rem 0.7rem;
  font-size: 0.92rem;
  line-height: 1.5;
  border: 1px solid color-mix(in srgb, var(--cible-accent) 30%, var(--bordure));
  background: var(--surface);
  color: var(--texte);
  outline: none;
  resize: vertical;
}

.fiche-champ__col textarea:focus {
  border-color: var(--cible-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cible-accent) 18%, transparent);
}

/* 🆕 Constat du 18/09 (fiche 3554) -- classe historique des champs COURTS (nom, balise titre, meta
   description, URL simplifiee, legende d'image, libelle de personnalisation...). Le plancher est
   desormais partage avec la regle generale ci-dessus (19/09) : cette classe ne fait plus qu'AFFICHER
   qu'un champ est court (un test la pince), elle ne porte plus une valeur differente. */
.fiche-champ__col textarea.fiche-champ__contenu--court {
  min-height: 2.6rem;
}

/* Pied de colonne (maquette ".col__pied") : les actions propres a la cible ES (Valider / Re-traduire
   ce champ), alignees de part et d'autre sous la boite. */
.fiche-champ__pied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  min-height: 1.5rem;
  margin-top: 0.35rem;
}

/* Lien discret "Re-traduire ce champ" (maquette ".lien-mini") -- PAS un bouton .btn--ghost : une
   action secondaire, toujours disponible (point 7 de la revue), qui ne doit pas concurrencer
   visuellement le bouton Valider. */
.fiche-lien-mini {
  font: inherit;
  font-size: 0.76rem;
  color: var(--texte-attenue);
  background: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.1rem;
}

.fiche-lien-mini:hover {
  color: var(--accent);
}

.fiche-lien-mini svg {
  width: 13px;
  height: 13px;
  flex: none;
}

.fiche-champ__annotations {
  margin-top: var(--espace-2);
  font-size: 0.85rem;
}

/* Texte d'alerte inline (ex. "a re-traduire") -- distinct de .alert (bandeau bloc) : ici on reste
   dans le fil du texte d'une annotation de champ. */
.texte-alerte {
  color: var(--alerte);
}

/* -------------------------------------------------------------------------
   6 bis. Bascule Rendu / Code (champs riches -- description/description_short)
   Maquette validee : voir scratchpad/maquette-rendu-code.html. Segment CSS pur (deux radios +
   labels), pilotage d'affichage par le selecteur :has() -- AUCUN JavaScript maison (invariant 5).
   ------------------------------------------------------------------------- */

/* Regroupe la bascule et le badge d'etat a DROITE de l'entete de champ (maquette ".champ__droite") --
   seul le badge y vivait avant ; la bascule s'y ajoute UNIQUEMENT pour un champ riche. */
.fiche-champ__entete-droite {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

/* Segment "Rendu / Code" : deux radios masques + deux <label> cliquables, l'option cochee recoit
   le fond accent. Meme forme que le reste de l'app (rayon plein, bordure --bordure). */
.bascule {
  display: inline-flex;
  border: 1px solid var(--bordure);
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface);
  flex: none;
}

.bascule input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.bascule label {
  font-size: 0.74rem;
  font-weight: 600;
  padding: 0.22rem 0.7rem;
  cursor: pointer;
  color: var(--texte-attenue);
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  user-select: none;
}

.bascule label svg {
  width: 13px;
  height: 13px;
  flex: none;
}

.bascule input:checked + label {
  background: var(--accent);
  color: #fff;
}

/* Pilotage CSS pur : par defaut Rendu est visible (.vue-code masque). Quand le radio ".b-code" de
   CE champ est coche, on inverse -- le selecteur :has() porte sur .fiche-champ (l'ancetre commun a
   la bascule ET aux deux vues), donc chaque champ riche bascule independamment des autres.
   ⚠️ On masque le code via `.fiche-champ:has(.b-rendu:checked) .vue-code` et NON via un simple
   `.vue-code { display:none }` : cote ES, le champ de saisie porte AUSSI la regle
   `.fiche-champ__col textarea { display:block }` (specificite 0,1,1) qui battait `.vue-code`
   (0,1,0) -- le code espagnol restait donc visible en mode Rendu (bug constate). Le selecteur
   :has() ci-dessous (specificite 0,4,0) passe devant. Le <pre> FR n'avait pas ce probleme
   (aucune regle textarea ne le concerne), d'ou l'asymetrie observee. */
.fiche-champ:has(.b-rendu:checked) .vue-code {
  display: none;
}

.fiche-champ:has(.b-code:checked) .vue-rendu {
  display: none;
}

.fiche-champ:has(.b-code:checked) .vue-code {
  display: block;
}

/* 🔴 LOT 5.C (31/08) -- le HTML de la boutique ne s'affiche plus DANS cette page. Le filtre
   `htmlRendu` (nunjucks-env.ts) rend desormais un <iframe sandbox> dont le contenu vit dans un
   document ISOLE (voir src/presentation/cadre-rendu.ts) : aucun octet du marchand n'est plus jamais
   interprete ICI, quel que soit son contenu (leçon n°12 du CLAUDE.md -- une garde de securite ne
   juge JAMAIS du langage naturel ; on ne filtre plus, on isole). Consequence directe : les regles
   qui stylaient un <p>/<ul>/<strong> interprete DIRECTEMENT dans `.fiche-champ__contenu` ont
   disparu d'ici -- un <p> etc. du marchand n'atterrit plus jamais dans CETTE feuille de style.
   Leurs equivalentes vivent maintenant DANS le document du cadre (cadre-rendu.ts) : un srcdoc
   n'herite d'AUCUNE regle posee ici, il faut les y re-ecrire -- c'est fait, et la maquette
   (docs/design/maquette-cadre-rendu.html) prouve l'equivalence visuelle. */
.cadre-rendu {
  display: block;
  width: 100%;
  border: 0;
  background: transparent;
  /* Repli SANS JavaScript (public/js/cadre-rendu.js absent ou en erreur) : hauteur minimale et
     defilement interne -- jamais un contenu coupe, seulement moins confortable a lire d'un coup.
     Une fois mesure, le script remplace cette hauteur par celle du contenu et retire le
     defilement -- la page s'allonge alors comme n'importe quel autre champ. */
  height: 12rem;
  overflow-y: auto;
}

/* Code brut (HTML echappe, jamais interprete -- voir invariant 3) : chasse fixe, jamais tronque
   (retour a la ligne naturel, meme sur un mot tres long). */
.fiche-champ__code {
  font-family: var(--mono);
  font-size: 0.82rem;
  white-space: pre-wrap;
  /* 7-309 -- meme harmonisation que .fiche-champ__contenu plus haut. */
  overflow-wrap: break-word;
  margin: 0;
}

/* -------------------------------------------------------------------------
   6 ter. LA FICHE ALLÉGÉE -- maquette validée le 18/09
   (docs/design/maquette-fiche-produit-2026-09-18.html). Classe de portée `.ecran-fiche-produit`
   (charte, section D) posée sur `.fiche` (views/fiche.njk) : la fiche n'en portait aucune -- ces
   règles n'atteignent donc aucun autre écran.
   ------------------------------------------------------------------------- */

/* --- Décision 6 : la barre d'actions reste visible au défilement --------------------------------
   `position: sticky` sur la rangée de boutons (Traduire / Relancer la relecture / Écrire), pas sur
   tout l'en-tête (le titre défile normalement). Fond --surface + l'ombre de la charte (--ombre,
   seul jeton d'ombre déclaré) : sans fond opaque, le texte qui défile dessous transparaîtrait. */
.ecran-fiche-produit .fiche-entete-actions__ligne {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  padding: var(--espace-2) 0;
  box-shadow: var(--ombre);
}

/* --- La barre d'avancement (décision du 18-19/09 : « je ne vois pas pourquoi je vais me balader
   sur cette page ») ------------------------------------------------------------------------------
   Sous la barre d'actions collante, au-dessus du corps de la fiche -- un simple paragraphe, jamais
   une carte de plus : elle ne concerne qu'un passage en cours ou tout juste terminé, pas un état
   permanent de l'écran. */
.ecran-fiche-produit .avancement-passage {
  margin: var(--espace-2) 0;
}

/* --- Décision 1 : ne déplier que ce qui a un geste -----------------------------------------------
   MÊME peau et MÊME affordance de pli que `.ecran-modules .groupe-modules` / `.ecran-gabarits
   .groupe-gabarits` (réutiliser avant d'inventer, charte section C) -- un chevron en CSS pur
   (bordure pivotée), jamais une icône de plus. */
.ecran-fiche-produit .fiche-groupe--repliable {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-bottom: 1.15rem;
  overflow: hidden;
}

.ecran-fiche-produit .fiche-groupe--repliable > summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: 0.85rem 1.15rem;
  cursor: pointer;
  list-style: none;
  background: var(--surface-2);
  font-weight: 620;
  font-size: 0.92rem;
}

.ecran-fiche-produit .fiche-groupe--repliable > summary::-webkit-details-marker { display: none; }

.ecran-fiche-produit .fiche-groupe--repliable > summary::after {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto;
  flex: none;
}

.ecran-fiche-produit .fiche-groupe--repliable[open] > summary::after { transform: rotate(45deg); }

.ecran-fiche-produit .fiche-groupe--repliable > summary:hover { color: var(--accent); }

.ecran-fiche-produit .fiche-groupe--repliable .fiche-groupe__corps-repli {
  padding: 0.5rem 1.15rem 1.15rem;
  display: flex;
  flex-direction: column;
}

/* --- Décision 2 : « Ce qui manque » devient un tableau de bord -----------------------------------
   MÊME grammaire visuelle que `.ecran-bloc .mesures`/`.mesure` (une rangée de cartes qui s'adapte
   sans point de rupture écrit à la main, charte A6) -- réutilisée plutôt que réinventée. */
.ecran-fiche-produit .fiche-tableau-manque {
  background: var(--attente-fond);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  padding: var(--espace-4);
  margin-bottom: 1.15rem;
}

.ecran-fiche-produit .fiche-tableau-manque--partage {
  background: var(--surface-2);
}

.ecran-fiche-produit .fiche-tableau-manque__titre {
  margin: 0 0 var(--espace-3);
  font-weight: 650;
  font-size: 0.9rem;
  color: var(--attente);
}

.ecran-fiche-produit .fiche-tm-cartes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: var(--espace-3);
}

.ecran-fiche-produit .fiche-tm-carte {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-sm);
  padding: 0.65rem 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.ecran-fiche-produit .fiche-tm-carte__label {
  font-size: 0.85rem;
  font-weight: 600;
}

.ecran-fiche-produit .fiche-tm-carte__n {
  font-size: 1.15rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.ecran-fiche-produit .fiche-tm-carte__n small {
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--texte-attenue);
}

/* Le détail par champ reste ATTEIGNABLE (rien ne se perd), replié dans la carte -- même liste
   qu'avant ce lot, seulement plus discrète. */
.ecran-fiche-produit .fiche-tm-carte__detail {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.82rem;
}

/* --- Décision 2 (suite) : le menu secondaire replié « Autres gestes » ----------------------------
   Les deux boutons « Valider » (autorité humaine) restent dans leur <form> d'origine (inchangé) --
   seul cet écrin de repli est nouveau. `position: relative` sur le <details> lui-même : le menu
   ouvert se pose devant le champ suivant sans pousser la mise en page. */
.ecran-fiche-produit details.menu-mini {
  display: inline-block;
  position: relative;
}

.ecran-fiche-produit details.menu-mini > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: var(--rayon-sm);
  border: 1px solid var(--bordure-forte);
  color: var(--texte-attenue);
  cursor: pointer;
  background: var(--surface);
  list-style: none;
}

.ecran-fiche-produit details.menu-mini > summary::-webkit-details-marker { display: none; }

.ecran-fiche-produit details.menu-mini > summary:hover {
  background: var(--accent-fond);
  color: var(--accent);
}

.ecran-fiche-produit details.menu-mini > summary .icone {
  width: 1.05em;
  height: 1.05em;
}

.ecran-fiche-produit .menu-mini__corps {
  position: absolute;
  right: 0;
  top: calc(100% + 0.25rem);
  z-index: 3;
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-sm);
  box-shadow: var(--ombre);
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 15rem;
}

.ecran-fiche-produit .menu-mini__corps .btn {
  width: 100%;
  justify-content: flex-start;
}

/* --- 19/09 après-midi (constat fiche 21235, « pourquoi ouvrir quelque chose en dessous pour
   modifier ? c'est incompréhensible ») : LA BOÎTE CIBLE EST LA ZONE DE SAISIE, TOUJOURS
   -----------------------------------------------------------------------------------------------
   Remplace l'ancien `.fiche-champ__corriger` (le <details> posé LE MATIN MÊME, dont le <summary>
   était la boîte -- retiré ce jour, quelques heures plus tard : un pli à ouvrir pour taper dans une
   case est resté un détour, pas une amélioration). Les deux « Valider » (règle n°3, autorité
   humaine) ne sont plus dans un pied permanent -- au repos, RIEN ne distingue plus un champ dont on
   peut corriger le texte d'une simple boîte de lecture (même fond, même bordure, voir
   `.fiche-champ__contenu--cible` plus haut) : seul le pied les révèle, et seulement quand on est en
   train d'écrire dans la boîte (`:focus-within`) ou qu'on vient juste de le faire (classe
   `fiche-champ__col--modifie`, posée par public/js/champ-modifie.js -- sans lui, le pied se
   referme dès qu'on clique ailleurs, mais reste joignable en recliquant dans la boîte). */
.ecran-fiche-produit .fiche-champ__pied--valider {
  display: none;
}

.ecran-fiche-produit .fiche-champ__col--cible:focus-within .fiche-champ__pied--valider,
.ecran-fiche-produit .fiche-champ__col--cible.fiche-champ__col--modifie .fiche-champ__pied--valider {
  display: flex;
}

/* Le <textarea> cible ne vit plus forcément sous `.fiche-champ__col` a la meme profondeur
   qu'un champ "non termine" (règle ancestrale `.fiche-champ__col textarea` plus haut, qui s'applique
   quand meme par descendance) : cette regle, par sa propre classe, donne le meme confort (largeur,
   redimensionnement, anneau au focus) où qu'il vive -- la boîte elle-même (rayon, marge, couleur de
   bordure) vient déjà des classes partagées `.fiche-champ__contenu`/`--cible`/`--court`. */
.ecran-fiche-produit textarea.fiche-champ__contenu--cible {
  display: block;
  width: 100%;
  font: inherit;
  outline: none;
  resize: vertical;
}

.ecran-fiche-produit textarea.fiche-champ__contenu--cible:focus {
  border-color: var(--cible-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cible-accent) 18%, transparent);
}

/* --- Décision 3 : le lien vers le cadrage, en pied de fiche --------------------------------------- */
.ecran-fiche-produit .composition-lien {
  margin-top: var(--espace-4);
  padding-top: var(--espace-3);
  border-top: 1px solid var(--bordure);
  font-size: 0.85rem;
  color: var(--texte-attenue);
}

/* --- Décision 5 : la cible ne redit pas le français -----------------------------------------------
   Zone hachurée (CSS pur, pas d'image) à la place du texte français recopié, quand rien n'est
   encore traduit -- même arrondi/bordure que `.fiche-champ__contenu`. */
.ecran-fiche-produit .fiche-champ__contenu--vide {
  border-radius: var(--rayon-sm);
  padding: 0.6rem 0.7rem;
  min-height: 2.6rem;
  display: flex;
  align-items: center;
  border: 1px dashed var(--bordure-forte);
  background: repeating-linear-gradient(
    135deg, var(--surface-2), var(--surface-2) 7px, var(--surface) 7px, var(--surface) 14px
  );
}

/* -------------------------------------------------------------------------
   7. Glossaire -- regles en cartes (tache 11)
   ------------------------------------------------------------------------- */

/* En-tete de carte : nom de la regle + badge active/inactive, sur la meme ligne. */
.glossaire-carte__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-2);
  margin-bottom: var(--espace-2);
}

.glossaire-carte__entete h3 {
  margin: 0;
}

/* Activer/desactiver + supprimer, sur la meme ligne, jamais empiles verticalement. */
.glossaire-carte__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  margin-top: var(--espace-3);
}

/* Formulaire de modification, revele par <details> (HTML natif, aucun JavaScript maison). */
.glossaire-carte__edition {
  margin-top: var(--espace-3);
  border-top: 1px solid var(--bordure);
  padding-top: var(--espace-3);
}

.glossaire-carte__edition summary {
  cursor: pointer;
  list-style: none;
}

.glossaire-carte__edition summary::-webkit-details-marker {
  display: none;
}

.glossaire-carte__edition[open] summary {
  margin-bottom: var(--espace-3);
}

/* -------------------------------------------------------------------------
   7 bis. Glossaire -- cartes de regle "toujours traduire" / "prompt" (tache 8)
   Trois blocs de la page /:pair/glossaire (tache 7) : la zone d'import CSV
   (.regle-import), les deux listes de regles (.regle-carte, une par-tournure
   ou par-instruction) et le formulaire d'ajout en pied de section
   (.regle-formulaire-ajout). Aucun nouveau jeton de couleur : les pastilles
   FR/ES reprennent EXACTEMENT --fr-accent/--fr-fond et --cible-accent/--cible-fond
   deja poses par la fiche produit (section 6, .fiche-champ__col-titre--fr/--cible)
   -- meme paire de couleurs pour "source francaise" / "cible espagnole" sur
   TOUTE l'application, clair comme sombre (les jetons portent deja les deux
   themes, rien a redefinir ici).
   ------------------------------------------------------------------------- */

/* Compteur discret a cote du titre de section ("Toujours traduire — 12 regles"). */
.regle-compteur {
  font-weight: 400;
  font-size: 0.8rem;
}

/* Zone d'import CSV (.card deja posee par le gabarit) : le contenu colle est
   du CSV brut -- chasse fixe, comme le reste du contenu "code" de l'appli
   (section 6 bis, .fiche-champ__code), pour que les virgules/colonnes restent
   alignees a l'oeil pendant la relecture avant import. */
.regle-import textarea {
  font-family: var(--mono);
  font-size: 0.85rem;
}

/* Carte de regle : meme rayon que le reste de l'appli (le jeton racine, PAS
   celui reduit localement par .fiche) -- explicite ici plutot que laisse a la
   seule heritage de .card, pour que la garde de non-regression (test) porte
   sur CETTE classe. Legere reaction au survol, coherente avec .table tbody tr:hover. */
.regle-carte {
  border-radius: var(--rayon);
  transition: border-color 0.12s ease;
}

.regle-carte:hover {
  border-color: var(--bordure-forte);
}

/* Carte "prompt" (une seule pastille FR, pas de cible ES imposee) : un filet
   neutre a gauche (meme jeton que le bouton "de sortie", section 3 -- ni
   accent ni couleur de colonne, pour rester sobre) suffit a la distinguer
   visuellement d'une carte "toujours traduire" (source ET cible), sans
   dupliquer de mise en page. */
.regle-carte--prompt {
  border-left: 3px solid var(--bordure-forte);
}

.regle-carte__entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--espace-3);
  flex-wrap: wrap;
  margin-bottom: var(--espace-2);
}

/* La tournure FR -> ES (ou l'instruction FR seule) : boucle sur plusieurs
   lignes plutot que d'etaler la carte -- une regle longue (mention legale,
   phrase de reassurance) ne doit jamais forcer un defilement horizontal de
   la page (invariant tenu ailleurs sur .table__wrap, section 3). */
.regle-carte__tournure {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  flex-wrap: wrap;
  min-width: 0;
}

.regle-carte__fleche {
  color: var(--texte-attenue);
  flex: none;
}

.regle-carte__badges {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  flex-wrap: wrap;
  flex: none;
}

.regle-carte__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  margin-top: var(--espace-3);
}

/* Revelation SANS JavaScript maison (<details>/<summary>, meme patron que
   .glossaire-carte__edition ci-dessus pour les conditions commerciales). */
.regle-carte__edition {
  margin-top: var(--espace-3);
  border-top: 1px solid var(--bordure);
  padding-top: var(--espace-3);
}

.regle-carte__edition summary {
  cursor: pointer;
  list-style: none;
}

.regle-carte__edition summary::-webkit-details-marker {
  display: none;
}

.regle-carte__edition[open] summary {
  margin-bottom: var(--espace-3);
}

/* Apercu de la liste « Toujours traduire » (38 regles). Les cartes restent TOUJOURS
   dans le document : on borne la hauteur et on estompe le bas, pour qu'on VOIE qu'il
   existe des regles (un repli total ne montrait plus rien -- retour utilisateur du 24/07).
   Le <details> ne porte aucun contenu, il sert d'interrupteur sans JavaScript maison ;
   `:has()` remonte son etat au conteneur.
   18rem = une rangee de cartes entiere + la moitie de la suivante : c'est LE seul nombre
   a retoucher si la hauteur des cartes change. */
.regle-repli--apercu:not(:has(.regle-repli__bascule[open])) .grille-cartes {
  max-height: 18rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
}

.regle-repli__bascule > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) 0;
  color: var(--texte-attenue);
  font-size: 0.9rem;
}

.regle-repli__bascule > summary::-webkit-details-marker {
  display: none;
}

/* Le libelle dit ce que le clic VA faire, pas l'etat courant : deux libelles dans le
   document, celui qui ne s'applique pas est retire. Pas de texte en CSS (non selectionnable). */
.regle-repli__bascule:not([open]) .regle-repli__libelle--moins,
.regle-repli__bascule[open] .regle-repli__libelle--plus {
  display: none;
}

.regle-repli__chevron {
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  flex: none;
}

.regle-repli__bascule[open] > summary .regle-repli__chevron {
  transform: rotate(45deg);
}

.regle-carte__casse {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: 0.85rem;
  color: var(--texte-attenue);
  margin: var(--espace-3) 0;
}

/* Pastille FR/ES : la meme paire de couleurs que les colonnes de la fiche
   produit (section 6), en pilule pleine plutot qu'en simple libelle colore --
   ici il n'y a pas de boite de contenu autour, la pastille porte donc seule le
   signal "source francaise" / "cible espagnole". max-width + break-word :
   une tournure tres longue reste dans la carte, jamais hors cadre. */
.pastille {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.85rem;
  line-height: 1.3;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid transparent;
  max-width: 100%;
  /* 7-309 -- meme harmonisation que .fiche-champ__contenu plus haut. */
  overflow-wrap: break-word;
}

.pastille-fr {
  background: var(--fr-fond);
  color: var(--fr-accent);
  border-color: color-mix(in srgb, var(--fr-accent) 25%, transparent);
}

.pastille-cible {
  background: var(--cible-fond);
  color: var(--cible-accent);
  border-color: color-mix(in srgb, var(--cible-accent) 25%, transparent);
}

/* Formulaire d'ajout, en pied de chaque section (tournure/instruction/import) :
   separe des cartes par un filet, memes jetons d'espacement que le reste. */
.regle-formulaire-ajout {
  margin-top: var(--espace-4);
  padding-top: var(--espace-4);
  border-top: 1px solid var(--bordure);
}

.regle-formulaire-ajout h3 {
  margin-top: 0;
  font-size: 0.9rem;
}

/* =========================================================================
   8. BLOC DE COMPATIBILITE -- ecrans PAS ENCORE migres vers le nouveau systeme
   =========================================================================
   Portee, la tache 6 n'a touche QUE le gabarit de base : plusieurs vues de
   contenu deja existantes (rapport d'ingestion, a-retraduire) utilisent
   encore les anciens noms de classe herites de l'ancien <style> inline :
   .statut/.ok/.warn/.err pour les bandeaux, et des <table> nues sans classe.
   Sans ce bloc, ces ecrans perdent tout signal visuel d'erreur/avertissement
   (mesure : ~30 bandeaux sur 8 vues, 7 tableaux nus) -- le texte reste, mais
   un refus d'ecriture deviendrait du texte plat.
   Ce bloc reprend l'apparence des composants neufs (.alert--*) via les memes
   jetons, SANS dupliquer de valeur en dur. A RETIRER quand chaque ecran
   liste ci-dessus aura ete migre vers .alert/.table__wrap/.table (tache par
   tache, brique "refonte UX") -- ne pas ajouter de nouvel usage ici.
   Fiche produit et liste produits deja migres (taches 9 et 10) : retires de
   la liste ci-dessus. Le glossaire (tache 11) ne l'est PAS encore -- il pose
   toujours .fiche-section (compat ci-dessous) en attendant sa propre refonte.
   L'ecran de connexion (login.njk) migre le 18/08 vers .alert/.alert--err --
   retire de la liste, garde ci-dessous SEULEMENT pour la reponse 401 servie
   en HTML brut (hors gabarit) par le garde d'authentification quand une
   session htmx expire (`.statut err` inline, `src/app.ts`).
   #8 lui-meme reste indispensable : ce bloc n'est plus utilise que par les
   ecrans encore lites (voir tests app-css.test.ts, qui verifient sa presence
   continue). */
.statut {
  padding: var(--espace-3) var(--espace-4);
  border-radius: var(--rayon);
  margin-bottom: var(--espace-4);
}

.ok { background: var(--succes-fond); color: var(--succes); }
.warn { background: var(--alerte-fond); color: var(--alerte); }
.err { background: var(--erreur-fond); color: var(--erreur); }

/* .fiche-section : encore posee par views/glossaire.njk ("Ajouter une condition"), pas encore migre
   vers .fiche-groupe (tache 11, glossaire refondu). Sans cette regle, son <h2> retomberait sur la
   marge par defaut (section 2, 2rem) au lieu d'etre colle a la carte. A retirer avec le reste de ce
   bloc quand glossaire.njk migrera. */
.fiche-section h2 {
  margin-top: 0;
}

table:not(.table) {
  border-collapse: collapse;
  width: 100%;
}

table:not(.table) th,
table:not(.table) td {
  text-align: left;
  padding: var(--espace-2) var(--espace-3);
  border-bottom: 1px solid var(--bordure);
}

/* =========================================================================
   9. THEME SOMBRE -- memes jetons, valeurs redefinies (tache 10 visuel,
   maquette validee). Comme presque tout le reste de la feuille consomme deja
   les variables ci-dessous (var(--fond), var(--surface)...), redefinir leur
   valeur ici suffit a rendre l'app entiere lisible en sombre, sans reprendre
   composant par composant. Aucun bouton de bascule n'existe dans l'app : le
   theme suit uniquement la preference systeme du navigateur.
   ========================================================================= */
@media (prefers-color-scheme: dark) {
  :root {
    --fond: #1c1815;
    --surface: #262019;
    --bordure: #3b342a;
    --texte: #ece4d9;
    --texte-attenue: #b3a794;
    --accent: #cf9165;
    --accent-hover: #dea376;
    --accent-fond: #3a2c20;
    --succes: #7dc3a0;
    --succes-fond: #223129;
    --attente: #d8b968;
    --attente-fond: #332c1a;
    --alerte: #e0a05a;
    --alerte-fond: #362a1c;
    --erreur: #e08a8a;
    --erreur-fond: #351f1f;
    --surface-2: #2c251d;
    --bordure-forte: #4a4133;
    --fr-accent: #9db6e0;
    --fr-fond: #232a36;
    --cible-accent: #e0a274;
    --cible-fond: #34271d;
    --ombre: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 8px rgba(0, 0, 0, 0.25);
    --neutre-actif: #d9cebb;
    --neutre-actif-texte: #221d16;
  }
}

/* La section "10. FICHE PRODUIT" (habillage FR/ES + en-tete) a ete repliee dans la section 6
   ci-dessus lors de la mise en conformite avec la maquette validee (tache 10, revue d'exactitude) :
   .drapeau, .fiche-champ__col-titre--fr/--cible, .fiche-champ__contenu--fr/--cible, .fiche-entete (ex.
   .entete-fiche), .entete__sous et .paire y vivent desormais, avec les memes selecteurs. Rien n'a
   disparu -- seul le doublon (et le bug d'ordre de cascade qu'il entrainait sur le theme sombre,
   les jetons --fr-accent/--cible-accent etaient redefinis ICI en clair APRES le bloc sombre) a ete
   retire. Section supprimee volontairement pour ne garder qu'UNE source de verite par regle. */

/* --- Le classement des pages CMS (lot « pages CMS administrables », 05/08) --------------------
   Styles DEDIES au formulaire de classement : reutiliser .champ ici pousserait chaque case a
   l'extremite de la cellule (.champ input { width: 100% }) et empilerait onze formulaires geants
   -- defaut vu en regardant la vraie page, pas dans les tests. */
.classement-page { display: flex; flex-direction: column; gap: 4px; min-width: 18rem; }
.classement-page label { display: flex; align-items: flex-start; gap: 6px; margin: 0; font-size: 0.9rem; }
.classement-page input[type="radio"], .classement-page input[type="checkbox"] { width: auto; margin: 3px 0 0; flex: none; }
.classement-page__sous { margin-left: 1.4rem; display: flex; flex-direction: column; gap: 2px; }
.classement-page button { align-self: flex-start; margin-top: 6px; }

/* --- Les pages, bloc par bloc (lot A de l'analyse visuelle, 06/08) ----------------------------
   Le rendu schematique VERTICAL : les blocs empiles dans l'ordre de la page, groupes sous les
   trois regions, chacun colore par son ETAT via une bordure gauche epaisse + un fond assorti --
   les quatre jetons d'etat existants, jamais une couleur inventee. « A rattacher » est un trait
   POINTILLE en plus de la couleur d'etat : les deux informations sont independantes. */
.page-schema { display: flex; flex-direction: column; gap: var(--espace-4); margin: var(--espace-4) 0; }
.page-schema__region { border: 1px solid var(--bordure-forte); border-radius: var(--rayon); padding: var(--espace-3); background: var(--surface-2); }
.page-schema__region-titre { margin: 0 0 var(--espace-2); font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--texte-attenue); }

/* Le panneau « ce qui devrait s'afficher ici » (preuve formelle, lot 2, 23/08) -- meme habillage
   de conteneur que .page-schema__region : un encart distinct, jamais confondu avec le schema
   vertical qui le suit. */
.panneau-identite { border: 1px solid var(--bordure-forte); border-radius: var(--rayon); padding: var(--espace-3); background: var(--surface-2); margin: var(--espace-4) 0; }
.panneau-identite__selecteur { display: flex; flex-wrap: wrap; gap: 0.2em 0.5em; }
.bloc-schema { border: 1px solid var(--bordure); border-left-width: 6px; border-radius: var(--rayon-sm); background: var(--surface); margin-bottom: var(--espace-2); }
.bloc-schema:last-child { margin-bottom: 0; }
.bloc-schema__resume { display: flex; flex-wrap: wrap; gap: var(--espace-2); align-items: baseline; padding: var(--espace-2) var(--espace-3); cursor: pointer; }
.bloc-schema__nom { font-weight: 600; }
.bloc-schema__etat { font-size: 0.85rem; color: var(--texte-attenue); }
.bloc-schema__meta { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; font-size: 0.85rem; color: var(--texte-attenue); margin-left: auto; }
.bloc-schema__corps { padding: 0 var(--espace-3) var(--espace-3); border-top: 1px dashed var(--bordure); }
.bloc-schema--traduit, .bloc-schema--decide { border-left-color: var(--succes); }
.bloc-schema--couvert { border-left-color: var(--attente); background: var(--attente-fond); }
.bloc-schema--connu_non_couvert { border-left-color: var(--alerte); background: var(--alerte-fond); }
.bloc-schema--inconnu { border-left-color: var(--erreur); background: var(--erreur-fond); }
.bloc-schema--sans_texte, .bloc-schema--non_jugeable, .bloc-schema--sans_verdict { border-left-color: var(--bordure-forte); }
.bloc-schema--a-rattacher { border-style: dashed; border-left-style: solid; }

/* Les decomptes d'un bloc, en PASTILLES (retouche UX du 22/08) -- meme correspondance etat -> jeton
   que .bloc-schema--* juste au-dessus (une seule table de verite), jamais une couleur inventee.
   Neutre par defaut : les etats qui ne changent rien (sans_texte/non_jugeable/sans_verdict) restent
   sur le fond de base plutot que d'inventer une cinquieme couleur (meme choix que .badge--neutre). */
.pastille-etat {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3em;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  white-space: nowrap;
}
.pastille-etat--traduit, .pastille-etat--decide { border-color: var(--succes); color: var(--succes); }
.pastille-etat--couvert { border-color: var(--attente); color: var(--attente); background: var(--attente-fond); }
.pastille-etat--connu_non_couvert { border-color: var(--alerte); color: var(--alerte); background: var(--alerte-fond); }
.pastille-etat--inconnu { border-color: var(--erreur); color: var(--erreur); background: var(--erreur-fond); }

/* Le panneau « ce qui devrait s'afficher ici » (preuve formelle, lot 2, 23/08) -- PROPRE prefixe
   (`pastille-preuve--*`, jamais `pastille-etat--*`) : ses dix familles n'ont rien a voir avec la
   taxonomie de la colonne « Etat » du tableau par bloc (spec § 7 : jamais le meme espace de noms).
   Meme base `.pastille-etat`, memes quatre jetons de couleur -- aucun jeton invente. */
.pastille-preuve--trouve_dans_le_html { border-color: var(--succes); color: var(--succes); }
.pastille-preuve--identique_a_la_source { border-color: var(--attente); color: var(--attente); background: var(--attente-fond); }
.pastille-preuve--present_mais_masque { border-color: var(--alerte); color: var(--alerte); background: var(--alerte-fond); }
.pastille-preuve--tronque_probable { border-color: var(--alerte); color: var(--alerte); background: var(--alerte-fond); }
.pastille-preuve--absent_a_elucider { border-color: var(--erreur); color: var(--erreur); background: var(--erreur-fond); }
.pastille-preuve--pas_encore_traduit { border-color: var(--attente); color: var(--attente); background: var(--attente-fond); }
.pastille-preuve--trop_court_pour_inclusion,
.pastille-preuve--invisible_par_construction,
.pastille-preuve--non_verifiable_ecrit_apres_releve,
.pastille-preuve--hors_perimetre_du_panneau { border-color: var(--bordure-forte); color: var(--texte-attenue); }

/* 🆕 LOT 4 DE 8-051 (15/09), VAGUE 2 -- « D'OU VIENT CE TEXTE ? » (presentation/origine-texte.ts,
   sept familles de la cascade). PROPRE prefixe (`pastille-origine--*`), memes quatre jetons de
   couleur que `.pastille-etat`/`.pastille-preuve` -- aucune cinquieme couleur inventee. Degrade
   voulu : vert = une cle precise VERIFIEE (une traduction connue porte ce texte) ; ambre = une
   cle precise mais avec une limite assumee (le coeur PrestaShop n'est pas encore lu par le module,
   ou le texte est ecrit en dur dans un gabarit) ; orange = un module est en vitrine mais AUCUNE
   etiquette ne reconnait ce texte precis (merite un regard) ; neutre = rien de nouveau a expliquer
   (identique a la source) ; rouge = origine inconnue, le pire cas (meme jeton que
   `.pastille-etat--inconnu`). */
.pastille-origine--champ_contenu,
.pastille-origine--etiquette_connue { border-color: var(--succes); color: var(--succes); }
.pastille-origine--etiquette_du_coeur,
.pastille-origine--texte_en_dur { border-color: var(--attente); color: var(--attente); background: var(--attente-fond); }
.pastille-origine--module_sans_etiquette { border-color: var(--alerte); color: var(--alerte); background: var(--alerte-fond); }
.pastille-origine--repris_tel_quel { border-color: var(--bordure-forte); color: var(--texte-attenue); }
.pastille-origine--inconnu { border-color: var(--erreur); color: var(--erreur); background: var(--erreur-fond); }

/* --- LES ICONES D'ECRAN (refonte UX de l'accueil, 07/08) --------------------------------------
   Dessinees dans views/_icones.njk. Dimensionnees en `em` et colorees par `currentColor` : une
   icone suit la taille et la couleur du texte qui l'entoure, donc le mode sombre la suit aussi
   sans une seule regle en plus. Meme principe que .drapeau. */
.icone {
  display: inline-block;
  width: 1.35em;
  height: 1.35em;
  vertical-align: -0.22em;
  flex: none;
}

/* --- LE BANDEAU D'ETAT (refonte UX de l'accueil, 07/08) ---------------------------------------
   Une ligne par sujet, le detail derriere un dépli. 🔴 Le repli est un CONFORT DU CAS NORMAL : le
   gabarit ouvre le <details> des que l'etat est anormal (spec § 4.2). Aucune regle ici ne masque
   quoi que ce soit -- si cette feuille ne se chargeait pas, tout le detail resterait lisible. */
.bandeau-etat {
  display: block;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  padding: var(--espace-3);
  margin: var(--espace-3) 0;
  font-size: 0.92rem;
}
/* 🔴 L'AFFORDANCE DU PLI (relecture 07/08) -- MEME PATRON QUE LES TROIS AUTRES <details> DE CETTE
   FEUILLE (.glossaire-carte__edition, .regle-carte__edition, .regle-repli__bascule) : on retire le
   marqueur natif EXPLICITEMENT, et on pose son remplacant dans le meme geste.
   Pourquoi c'est obligatoire ici : passer `display` a `flex` sur un <summary> SUPPRIME deja le
   triangle de depli natif (Blink, WebKit, Firefox). Sans remplacant il ne restait que le curseur --
   invisible au repos, donc un pli qu'on ne voit pas etre un pli. Sur un ecran dont le risque nomme
   est « une information disparait sans que personne ne s'en apercoive », c'est le risque deplace du
   serveur vers l'oeil.
   Pourquoi un ::after et pas un <span> comme .regle-repli__chevron : le chevron est purement
   decoratif (le titre a cote porte le sens, il est deja `aria-hidden` la-bas) -- en pseudo-element
   il ne touche PAS au HTML (aucun gabarit a modifier, aucune garde de contenu a rejouer) et il
   reste hors de l'arbre d'accessibilite. Forme GEOMETRIQUE, jamais un caractere typographique ni un
   emoji (regle du lot) : geometrie et rotation reprises telles quelles de .regle-repli__chevron. */
.bandeau-etat summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  cursor: pointer;
  list-style: none;
}

.bandeau-etat summary::-webkit-details-marker { display: none; }

.bandeau-etat summary::after {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto;
  margin-right: 0.2rem;
  flex: none;
}

/* Replie : le chevron pointe a droite (« il y a la suite »). Deplie : il pointe en bas. */
.bandeau-etat details[open] > summary::after { transform: rotate(45deg); }

/* 🔴 UN BANDEAU SANS DEPLI (a-retraduire) EST UNE RANGEE, LUI AUSSI (relecture 07/08) : sans cette
   regle, `.bandeau-etat__titre { flex: 1 }` n'a aucun parent flex et reste inerte, et la pastille
   n'est qu'un <span> inline dans un parent en `display: block`. `:has()` sert deja d'interrupteur
   ailleurs dans cette feuille (.regle-repli--apercu) ; s'il n'etait pas compris, on retombe sur
   pastille en inline-block + titre inline -- decale, jamais masque. */
.bandeau-etat:not(:has(details)) {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
}

/* 🔴 LA LIGNE D'ÉTAT SŒUR DU <details> (revue finale UX3, 19/08) : le hub de cadrage
   (views/blocs.njk) sort la pastille + les compteurs du <summary> pour qu'ils restent visibles
   même quand le pli se referme (état vert) -- le <summary>, lui, ne porte plus qu'un libellé
   propre (« Ce que ce compte couvre — et ne couvre pas »). Même mise en page flex que le patron
   `:not(:has(details))` ci-dessus, appliquée explicitement ici puisque ce bandeau-ci a bel et
   bien un <details> comme second enfant. */
.bandeau-etat__ligne {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin-bottom: var(--espace-2);
}

.bandeau-etat__titre { flex: 1; min-width: 0; }

/* 🔴 `display` EXPLICITE (relecture 07/08). Sans lui, la pastille du bandeau a-retraduire est un
   <span> INLINE NON REMPLACE : `width` et `height` y sont IGNORES et le fond ne peint rien -- le
   seul bandeau porteur d'une couleur d'attente n'avait pas son point. `inline-block` vaut dans les
   DEUX contextes : dans un <summary> en flex il est blockifie, le rendu ne bouge pas d'un pixel. */
.bandeau-etat__pastille {
  display: inline-block;
  width: 0.55rem; height: 0.55rem; border-radius: 50%;
  background: var(--bordure-forte); flex: none;
  vertical-align: middle;
}
.bandeau-etat__detail { margin-top: var(--espace-3); color: var(--texte-attenue); }
.bandeau-etat__detail > span { display: block; margin-top: var(--espace-2); }
.bandeau-etat--ok { border-left-color: var(--succes); }
.bandeau-etat--ok .bandeau-etat__pastille { background: var(--succes); }
.bandeau-etat--attente { border-left-color: var(--attente); background: var(--attente-fond); }
.bandeau-etat--attente .bandeau-etat__pastille { background: var(--attente); }
.bandeau-etat--alerte { border-left-color: var(--alerte); background: var(--alerte-fond); }
.bandeau-etat--alerte .bandeau-etat__pastille { background: var(--alerte); }
/* « info » : un bandeau qui INFORME sans porter d'etat (cadrage-theme). Il reutilise l'ACCENT --
   jamais une 5e couleur d'etat (invariant A3). Classe designee par views/cadrage-theme.njk depuis
   le 08/08 et definie NULLE PART jusqu'a la nuit du 19-20/08 : le bandeau se rendait sans couleur,
   exactement l'ecart n°5 de la charte (un nom inexistant ne provoque aucune erreur). */
.bandeau-etat--info { border-left-color: var(--accent); background: var(--accent-fond); }
.bandeau-etat--info .bandeau-etat__pastille { background: var(--accent); }

/* 🔴 LA DATE HUMAINE ET SON AFFORDANCE (refonte UX, 07/08) -- l'accueil affichait
   « 2026-08-06T13:35:42.403Z », du format machine sur l'ecran d'une lectrice non developpeuse. Ce
   qui se lit est desormais une phrase ; la valeur EXACTE de la base n'a pas disparu pour autant :
   elle vit dans le `title` du <time>, et ce pointille est ce qui le fait savoir. Sans lui, une
   information present dans la page serait invisible -- exactement le silence que le principe
   cardinal interdit.
   `currentColor` : aucune couleur ecrite en dur, le pointille suit le texte du bandeau (donc les
   deux themes, clair et sombre, sans une regle de plus). Si cette feuille ne se chargeait pas, la
   phrase et son `title` resteraient l'un et l'autre intacts -- rien ici ne porte de sens. */
.date-humaine {
  text-decoration: underline dotted currentColor;
  text-underline-offset: 0.2em;
  cursor: help;
}

/* --- LES VIGNETTES D'ENTREE (refonte UX de l'accueil, 07/08) ----------------------------------
   Les blocs cote a cote demandes par l'utilisateur : une icone dessinee, un titre, une phrase.
   `auto-fit` + `minmax` : deux colonnes sur un ecran large, empilees en dessous de ~30rem, sans
   media query et sans point de rupture ecrit a la main. Meme mecanique que .grille-cartes. */
.grille-vignettes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  gap: var(--espace-4);
  margin: var(--espace-4) 0;
}
.vignette {
  display: block;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--accent);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--espace-4);
  color: inherit;
  text-decoration: none;
}
a.vignette:hover { border-color: var(--accent); }
.vignette h2 { margin: var(--espace-3) 0 var(--espace-2); font-size: 1.05rem; }
.vignette p { margin: 0; }
.vignette__icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.4rem; height: 2.4rem;
  border-radius: var(--rayon);
  background: var(--accent-fond);
  color: var(--accent);
}
/* `.vignette--cadrage` et `.vignette--prompts` ne portent VOLONTAIREMENT aucune regle : les deux
   vignettes se ressemblent, et c'est voulu (elles vivent au meme niveau). Ces deux classes sont des
   reperes d'IDENTITE pour les gardes, qui verifient ainsi que les DEUX blocs sont bien la -- un
   compte anonyme aurait laisse passer une vignette rendue deux fois (lecon § Dettes C). */

/* --- L'ÉTAT DES LIEUX D'UNE BOUTIQUE (accueil, 01/09 -- REFONDU EN TUILES le 01/09, second tour) --
   L'utilisateur a vu le PREMIER jet (un tableau) en vrai et l'a rejeté : « ça part en vertical sur
   la partie gauche, c'est pas propre ». Cause mesurée : le tableau vivait dans une carte de
   DEMI-LARGEUR (`.cadrage-carte`, elle-même à la place de la vignette dans `.grille-vignettes` à
   deux colonnes) -- sa colonne « dans la boutique » (une phrase longue) s'écrasait à un mot par
   ligne. Ce n'était pas le contenu qui posait problème, c'était le contenant : QUATRE TUILES PLEINE
   LARGEUR, hors de la grille étroite -- voir `.etat-lieux-bande` dans `views/univers.njk`, une
   section À PART sous la grille des vignettes, pas un remplacement au même endroit. */

/* La clé de lecture (« Pourquoi trois nombres »), au-dessus des tuiles -- même idiome que `.note`
   de la charte (fond accent, filet gauche accent) : un encart qui EXPLIQUE, pas qui alerte, donc ni
   succès ni attente ni alerte. */
.etat-lieux-cle {
  background: var(--accent-fond);
  border-left: 3px solid var(--accent);
  border-radius: var(--rayon);
  padding: var(--espace-4);
  /* La cle de lecture est SOUS les tuiles (demande utilisateur, 02/09) : la marge est donc
     EN HAUT, jamais en bas -- sinon elle se colle a la grille et laisse un vide inutile
     avant la bande suivante. */
  margin: var(--espace-4) 0 0;
  font-size: 0.92rem;
}
.etat-lieux-cle p { margin: 0 0 var(--espace-2); }
.etat-lieux-cle p:last-child { margin-bottom: 0; }
/* L'échelle emboîtée : trois lignes, chacune plus indentée que la précédente -- ça MONTRE
   l'emboîtement (total ⊃ boutique ⊃ actifs) sans qu'il faille le lire deux fois. Nombres alignés en
   chasse fixe : ils se comparent d'un coup d'œil. */
.etat-lieux-echelle {
  font-family: var(--mono); font-size: 0.85rem; line-height: 1.8;
  font-variant-numeric: tabular-nums; margin-top: var(--espace-3);
}
.etat-lieux-echelle > div:nth-child(2) { padding-left: 1.4rem; }
.etat-lieux-echelle > div:nth-child(3) { padding-left: 2.8rem; }

/* La bande des tuiles -- PLEINE LARGEUR de la section « socle commun », hors de `.grille-vignettes`
   (qui reste à deux colonnes pour les portes de cadrage et de prompts). */
.etat-lieux-bande { margin: var(--espace-5) 0; }
.etat-lieux-bande__titre {
  font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 650;
  color: var(--texte-attenue); margin: 0 0 var(--espace-3);
}
.etat-lieux-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(245px, 1fr));
  gap: var(--espace-4);
}
.etat-lieux-tuile {
  display: flex; flex-direction: column; gap: var(--espace-3);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--espace-4);
}
/* Même vocabulaire de couleur que `.regle--*` (charte) et `.badge--*` -- jamais une 5ᵉ couleur. */
.etat-lieux-tuile--succes { border-left-color: var(--succes); }
.etat-lieux-tuile--attente { border-left-color: var(--attente); }
.etat-lieux-tuile--alerte { border-left-color: var(--alerte); }
.etat-lieux-tuile__entete { display: flex; align-items: center; justify-content: space-between; gap: var(--espace-2); }
/* 🆕 CORRECTIF P37 (02/09) -- jusqu'à DEUX pastilles côte à côte (« à lire » ET « à retirer » --
   une soustraction de totaux les aurait fondues en un seul nombre trompeur, voir
   presentation/etat-lieux-boutique.ts::voyants). `flex-wrap` : sur une tuile étroite, la seconde
   pastille passe à la ligne plutôt que de déborder. */
.etat-lieux-tuile__voyants { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.35rem; }
.etat-lieux-tuile__voyant { display: flex; align-items: center; gap: 0.3rem; }
.etat-lieux-tuile__etiquette {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 650;
  color: var(--texte-attenue);
}
.etat-lieux-tuile__chiffre { font-size: 1.9rem; font-weight: 660; line-height: 1.15; font-variant-numeric: tabular-nums; }
.etat-lieux-tuile__chiffre-legende { font-size: 0.78rem; color: var(--texte-attenue); margin-top: 0.1rem; }
.etat-lieux-tuile__lignes { display: grid; gap: 0.3rem; font-size: 0.85rem; }
.etat-lieux-tuile__ligne { display: flex; align-items: baseline; justify-content: space-between; gap: var(--espace-2); }
/* La ligne « dont … » SEULE porte le filet -- elle montre qu'elle est un SOUS-ENSEMBLE de la ligne
   du dessus, jamais un troisième nombre parallèle (demande explicite de l'utilisateur). */
.etat-lieux-tuile__ligne--imbriquee { padding-left: 0.7rem; border-left: 2px solid var(--bordure-forte); }
.etat-lieux-tuile__valeur { font-variant-numeric: tabular-nums; font-weight: 600; flex: none; }
.etat-lieux-tuile__note { font-size: 0.8rem; color: var(--texte-attenue); font-style: italic; }
.etat-lieux-tuile__panne { font-size: 0.85rem; color: var(--erreur); }
.etat-lieux-tuile__pied {
  display: flex; align-items: center; justify-content: space-between; gap: var(--espace-2);
  padding-top: var(--espace-2); border-top: 1px solid var(--bordure);
  font-size: 0.8rem; color: var(--texte-attenue);
}
.etat-lieux-tuile__pied .lien-discret { flex: none; }
/* 🆕 02/09 -- le bouton « Synchroniser » et sa trace (le geste MANUEL, distinct du pied juste
   au-dessus qui dit le CONTENU lu). Repose sur .btn/.btn--ghost/.btn--sm (existants) : aucune
   couleur en dur, le mode sombre suit tout seul. */
.etat-lieux-tuile__geste {
  display: flex; align-items: center; justify-content: space-between; gap: var(--espace-2);
  flex-wrap: wrap;
  font-size: 0.78rem; color: var(--texte-attenue);
}
.etat-lieux-tuile__geste form { flex: none; }

/* --- L'EN-TETE D'UNE CARTE DE LANGUE (refonte UX de l'accueil, 07/08) -------------------------
   Les deux drapeaux, la fleche decorative, puis le nom de la langue. Le libelle complet vit en
   dessous, en attenue : c'est lui qui dit d'ou l'on part, et un univers sans drapeau dessine
   n'y perd rien. */
.carte-langue__entete { display: flex; align-items: center; gap: var(--espace-2); }
.carte-langue__entete h3 { margin: 0; }
.carte-langue__paire { margin: var(--espace-1) 0 var(--espace-3); font-size: 0.88rem; }

/* --- LES CARTES DE MODULE (cadrage des modules, 07/08) ---------------------------------------
   L'ecran etait une liste nue ; l'utilisateur : « ce n'est pas au niveau de l'UX des autres pages,
   il faut que ce soit aere, espace, propre ». Il reprend donc .card et .grille-cartes, comme le
   reste de l'application -- aucune mise en page inventee pour lui seul. Jetons uniquement : le mode
   sombre suit sans une regle de plus. */
.module-carte { display: flex; flex-direction: column; gap: var(--espace-3); }
.module-carte__entete {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--espace-2); flex-wrap: wrap;
}
/* 7-309 -- meme harmonisation que .fiche-champ__contenu plus haut. */
.module-carte__entete h3 { margin: 0; font-size: 1rem; overflow-wrap: break-word; }
.module-carte__mesure { margin: 0; font-size: 0.88rem; }
/* Un lisere par etat : la meme convention que les bandeaux, jamais une couleur nouvelle. */
.module-carte--succes { border-left: 3px solid var(--succes); }
.module-carte--attente { border-left: 3px solid var(--attente); }
.module-carte--neutre { border-left: 3px solid var(--bordure-forte); }
/* Les trois boutons passent a la ligne plutot que de deborder la carte sur un ecran etroit. */
.module-choix { display: flex; flex-wrap: wrap; gap: var(--espace-2); margin: 0; }
/* Dans un groupe .module-choix, c'est le GROUPE qui passe a la ligne quand la place manque (regle
   ci-dessus), jamais le MOT a l'interieur d'un bouton (soin visuel, 20/08 -- la colonne « gestes »
   des reglages de module compressait chaque bouton jusqu'a casser son libelle en deux lignes). */
.module-choix .btn { white-space: nowrap; }
.retour-cadrage { margin-top: var(--espace-6); }

/* --- L'ECRAN DE DECISION DES MODULES (09/08) ------------------------------------------------
   152 decisions a prendre : la mise en page doit rendre le balayage possible. L'etat se lit dans
   la FORME (fond teinte, opacite) autant que dans le mot, pour qu'on voie ou on en est sans lire.
   🔴 CORRIGE LE 19/08 : ce bloc a ete ecrit avec le vocabulaire de jetons d'une AUTRE feuille
   (--border, --muted, --survol, --bg-ok, --border-strong n'existent pas ici) -- sans repli, une
   telle declaration est simplement IGNOREE par le navigateur, en silence (aucune erreur console).
   Invariant de la feuille : UN SEUL vocabulaire de jetons ; un nom qui n'existe pas ne casse rien
   au chargement, il fait juste disparaitre la regle. */
.avancement {
  background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--rayon);
  padding: 1rem 1.25rem; margin: 0 0 1.25rem; display: grid; gap: .75rem;
}
.avancement__chiffres { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.avancement__chiffres p { margin: 0; }
.avancement__gros { font-size: 1.35rem; font-weight: 500; font-variant-numeric: tabular-nums; }
.avancement__gros span { font-size: .95rem; font-weight: 400; color: var(--texte-attenue); }
.avancement__reserve { font-size: .85rem; margin: 0; }
.jauge { height: 5px; border-radius: 3px; background: var(--bordure); overflow: hidden; }
.jauge > i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }

.liste-modules { list-style: none; margin: 0; padding: 0; border: 1px solid var(--bordure); border-radius: var(--rayon); overflow: hidden; background: var(--surface); }
.ligne-module {
  display: flex; align-items: center; justify-content: space-between; gap: 1.25rem;
  padding: .9rem 1.15rem; border-bottom: 1px solid var(--bordure);
}
.ligne-module:last-child { border-bottom: 0; }

/* 🔴 SUIVRE UNE LIGNE DE GAUCHE A DROITE (correctif du 09/08, defaut signale par l'utilisateur :
   « les lignes ne sont pas clairement visibles, pas de separation, de gauche a droite pas toujours
   facile de bien voir »). Sur 150 lignes, le nom est a gauche et les boutons a droite : sans repere
   continu, l'oeil saute d'une ligne a l'autre en traversant.
   Trois moyens, du plus discret au plus fort -- aucun ne colore l'information, ils ne servent qu'a
   TENIR LE REGARD :
     1. un fond alterne (zebra), le remede classique du tableau long ;
     2. un surlignage au survol, qui donne un repere ACTIF sous le curseur ;
     3. une colonne d'actions de largeur fixe, pour que les boutons forment une verticale nette.
   ⚠️ Le zebra ne s'applique QU'AUX lignes sans etat : sur une ligne retenue ou ecartee, la couleur
   PORTE une information, et un fond alterne par-dessus la brouillerait. */
.ligne-module:nth-child(even):not(.ligne-module--retenu):not(.ligne-module--ecarte) {
  background: var(--surface-2);
}
/* Aucun jeton de survol n'existe dans la charte -- litteral assumé, pas un jeton fantome oublie.
   Sa contrepartie sombre est deja couverte plus bas (§ theme sombre de ce bloc, ~30 lignes). */
.ligne-module:hover { background: rgba(0, 0, 0, .045); }
.ligne-module--retenu:hover { filter: brightness(.98); }

/* L'etat dans la forme : un module retenu se voit sans qu'on lise son etiquette. */
.ligne-module--retenu { background: var(--succes-fond); box-shadow: inset 3px 0 0 var(--accent); }
.ligne-module--ecarte .ligne-module__corps { opacity: .5; }
.ligne-module__corps { min-width: 0; }
.ligne-module__entete { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: 0; }
.ligne-module__nom { font-weight: 500; }
.ligne-module__mesure { margin: .2rem 0 0; font-size: .875rem; font-variant-numeric: tabular-nums; }
/* Largeur fixe : les boutons de toutes les lignes s'alignent sur une meme verticale, ce qui donne
   au regard un second rail apres celui du nom. `justify-content: flex-end` les colle a droite meme
   quand l'etiquette d'etat les precede.
   🔴 CORRECTIF DU CHEVAUCHEMENT (chantier « orchestrateur module », 22/08 -- capture utilisateur,
   racine mesuree via getBoundingClientRect() dans un navigateur reel, pas devinee). La phrase « Votre
   choix ici surcharge... » (_ligne-module-univers.njk) partage cette classe avec l'ecran de decision
   (_ligne-module.njk, interrupteur seul, court) : sans `max-width`, un `<p>` de phrase entiere en
   `flex-shrink:0` gonflait ce bloc a ~713px de contenu -- sur une liste de 152 modules, un nom TRES
   long et non secable (ex. `facebookconversiontrackingplus`, 31 caracteres, aucun espace) laissait
   alors `.ligne-module__corps` (min-width:0, flex-shrink:1) s'ecraser a 0px de large : son texte
   restait rendu (overflow:visible) mais peignait par-dessus `.ligne-module__actions`, juste a cote.
   `max-width` BORNE le bloc quel que soit `flex-shrink:0` (il l'emporte toujours sur le
   dimensionnement flex, cf. spec CSS) ; `flex-wrap: wrap` laisse la phrase (mise en pleine ligne
   juste apres) et les boutons se replier proprement en dessous plutot que de deborder a l'horizontale.
   Sans effet observable sur _ligne-module.njk (interrupteur + un bouton, deja bien plus etroit que
   la nouvelle limite). */
.ligne-module__actions {
  display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: .5rem;
  flex-shrink: 0; min-width: 12.5rem; max-width: 18rem;
}
.ligne-module__actions .module-choix { margin: 0; }
.ligne-module__actions > p.muted { flex-basis: 100%; margin: 0 0 .35rem; text-align: right; }

/* La proportion deja faite par PrestaShop, lisible d'un coup d'oeil. */
.part { display: flex; height: 4px; border-radius: 2px; overflow: hidden; margin: .45rem 0 0; max-width: 320px; background: var(--bordure); }
.part > i { display: block; height: 100%; }
.part__fait { background: var(--accent); }
.part__reste { background: var(--bordure-forte); }

@media (max-width: 640px) {
  .ligne-module { flex-direction: column; align-items: flex-start; }
}

/* Le fond du survol et de l'alternance, en clair comme en sombre : une valeur unique ne peut pas
   convenir aux deux -- sur fond sombre il faut ECLAIRCIR la ou on assombrit sur fond clair. */
@media (prefers-color-scheme: dark) {
  .ligne-module:nth-child(even):not(.ligne-module--retenu):not(.ligne-module--ecarte) {
    background: rgba(255, 255, 255, .028);
  }
  .ligne-module:hover { background: rgba(255, 255, 255, .055); }
}
:root[data-theme="dark"] .ligne-module:nth-child(even):not(.ligne-module--retenu):not(.ligne-module--ecarte) {
  background: rgba(255, 255, 255, .028);
}
:root[data-theme="dark"] .ligne-module:hover { background: rgba(255, 255, 255, .055); }

/* -------------------------------------------------------------------------
   11. L'EN-TÊTE D'UNE LANGUE ET SA BARRE D'AVANCEMENT (lot UX-1, tâches 2/5, 17/08)
   Maquette validée par l'utilisateur : docs/design/maquette-ux-tableau-de-bord.html.
   🔴 REPREND LES JETONS EXISTANTS (--surface/--bordure/--rayon/--ombre/--succes/--texte-attenue/
   --alerte/--bordure-forte/--espace-*), PAS la palette propre à la maquette (--vert/--ambre) :
   c'est le sens de la tâche 5 -- ce langage doit RESSERVIR aux 36 autres écrans de l'application,
   donc rester sur UNE seule palette partagée, jamais deux qui divergent en clair comme en sombre.
   Comme tout le reste de cette feuille consomme déjà ces jetons, les DEUX THÈMES sont couverts
   SANS UNE SEULE règle de plus ici (aucun bloc `@media (prefers-color-scheme: dark)` ni
   `[data-theme]` propre à cette section -- voir la règle du CLAUDE.md : « une couleur ne doit
   jamais être définie uniquement dans un bloc de thème »).
   ------------------------------------------------------------------------- */
.univers-entete {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--espace-4) var(--espace-5);
  margin: var(--espace-4) 0 var(--espace-5);
}

.univers-entete__haut {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  flex-wrap: wrap;
}

/* Même forme que .paire (fiche produit, section 6) -- une pastille discrète, jamais un nouveau
   patron de badge pour dire la même chose (« un couple boutique/langue »). */
.univers-entete__puce {
  display: inline-flex;
  align-items: center;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.2rem 0.65rem;
  border-radius: 999px;
  background: var(--fond);
  color: var(--texte-attenue);
  border: 1px solid var(--bordure);
}

/* La barre : TROIS segments empilés horizontalement, largeur posée en `style="width:...%"` par le
   gabarit (valeurs déjà calculées et bornées par `construireBarreAvancement`, jamais une largeur
   inventée en CSS) -- même patron que `.jauge > i` de l'écran de décision des modules
   (cadrage-modules.njk), en pourcentage plutôt qu'en flex-grow. `flex: none` est OBLIGATOIRE : sans
   lui, le flex-basis auto d'un flex-item peut re-distribuer l'espace et ignorer la largeur explicite
   posée par le gabarit -- exactement le bug qui romprait le bornage du cas du dépassement (§1.2). */
.univers-entete__barre {
  display: flex;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--fond);
  margin: var(--espace-3) 0 var(--espace-2);
}

.univers-entete__segment {
  display: block;
  height: 100%;
  flex: none;
}

/* Une seule couleur (--succes) pour les deux premiers segments, distingués par l'opacité -- « déjà
   en ligne » est un sous-ensemble plein de « traduit » (voir avancementDesChamps : enLigne ⊆
   traduits). Le troisième segment reste TRANSPARENT (le fond --fond de la piste suffit à le
   montrer) : « sans traduction » n'est pas un échec à colorer en alerte, c'est le reste neutre du
   périmètre (§1.3 du plan -- ne jamais le présenter comme un défaut). */
.univers-entete__segment--en-ligne { background: var(--succes); }
.univers-entete__segment--traduits { background: var(--succes); opacity: 0.45; }
.univers-entete__segment--sans-traduction { background: transparent; }

/* La légende : TOUJOURS rendue, jamais optionnelle -- c'est elle qui porte les trois nombres pour
   quiconque ne peut pas (ou ne veut pas) lire la couleur (§ accessibilité de la tâche 2). */
.univers-entete__legende {
  display: flex;
  gap: var(--espace-4);
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--texte-attenue);
  margin: 0 0 var(--espace-2);
}

.univers-entete__legende strong {
  color: var(--texte);
  font-variant-numeric: tabular-nums;
}

.univers-entete__pastille {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 3px;
  margin-right: 0.4rem;
  vertical-align: baseline;
}

.univers-entete__pastille--en-ligne { background: var(--succes); }
.univers-entete__pastille--traduits { background: var(--succes); opacity: 0.45; }
.univers-entete__pastille--sans-traduction { background: var(--bordure-forte); }

.univers-entete__limite {
  font-size: 0.82rem;
  color: var(--texte-attenue);
  margin: var(--espace-2) 0 0;
}

.univers-entete__limite a {
  color: var(--texte-attenue);
}

.univers-entete__alerte {
  font-size: 0.82rem;
  color: var(--alerte);
  margin: var(--espace-2) 0 0;
}

.univers-entete__vide {
  font-size: 0.92rem;
  color: var(--texte-attenue);
  margin: var(--espace-2) 0 0;
}

/* --- ÉCHELLE DE TITRES DE SECTION, PARTAGÉE ENTRE ÉCRANS -- mise en commun le 19/08 --------------
   h1 d'écran (27px/-.025em/660) et h2 de rubrique (étiquette discrète, petites capitales) : la
   même paire de règles vivait en TROIS exemplaires -- `.ecran-langue h1` + `.ecran-accueil h1`
   (§ 12 ci-dessous), `.ecran-cadrage h1` (§ 17) ; et `.ecran-langue h2`, `.ecran-accueil > h2`
   (§ 13), `.ecran-cadrage > h2` (§ 17). Un quatrième écran refondu en aurait fait une quatrième
   copie -- valeurs vérifiées identiques dans les trois avant fusion.
   ⚠️ LES SÉLECTEURS DE h2 NE SONT PAS UNIFORMES, ET C'EST VOLONTAIRE, PAS UN OUBLI : `.ecran-langue`
   n'imbrique aucun h2 propre (sélecteur DESCENDANT, sans risque) ; `.ecran-accueil` et
   `.ecran-cadrage` imbriquent chacun des <h2> propres à leurs vignettes/portes, qu'un sélecteur
   descendant rétrécirait par erreur en étiquette de rubrique -- d'où l'enfant DIRECT (`>`) sur ces
   deux-là. La liste ci-dessous reprend donc les trois sélecteurs tels quels.
   🔴 TOUT ÉCRAN REFONDU AJOUTE SON SÉLECTEUR ICI, JAMAIS UNE QUATRIÈME COPIE. */
.ecran-langue h1,
.ecran-accueil h1,
.ecran-cadrage h1 {
  font-size: 27px;
  letter-spacing: -0.025em;
  font-weight: 660;
}

.ecran-langue h2,
.ecran-accueil > h2,
.ecran-cadrage > h2 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 650;
  color: var(--texte-attenue);
  margin: 2.6rem 0 0.875rem;
}

/* -------------------------------------------------------------------------
   12. CHARTE VISUELLE DE LA MAQUETTE, APPLIQUÉE À `/:pair` SEULEMENT (18/08)
   Constat de l'utilisateur : la maquette validée (section 11 ci-dessus, .univers-entete) n'avait
   habillé QUE l'en-tête -- le reste de l'écran (titres de section, cartes de bloc, grille)
   gardait les jetons génériques de l'appli, pas l'échelle typographique ni l'anatomie de carte
   de la maquette (docs/design/maquette-ux-tableau-de-bord.html).
   🔴 DÉCISION DE L'UTILISATEUR (18/08) : ON GARDE LA PALETTE ACTUELLE (beige-brun chaud), on
   N'ADOPTE PAS le gris neutre froid de la maquette -- le CSS est partagé par 37 écrans, changer
   une couleur les repeindrait tous d'un coup, ce qui contredit sa règle « montrer et valider
   écran par écran ». Ce bloc ne reprend donc de la maquette que la STRUCTURE, l'ÉCHELLE
   TYPOGRAPHIQUE, l'ANATOMIE DES CARTES et la DENSITÉ -- avec les jetons de couleur EXISTANTS
   (--surface/--bordure/--texte-attenue/--rayon/--ombre/--espace-*), jamais une couleur littérale.
   🔴 PORTÉE : chaque règle est préfixée `.ecran-langue` (posée par `views/univers-detail.njk`,
   qui enveloppe tout son `{% block body %}` dans <div class="ecran-langue">, et NULLE PART
   ailleurs dans les gabarits) -- aucune de ces règles ne peut donc atteindre `/`,
   `/:pair/produits`, `/passages` ou n'importe lequel des 36 autres écrans. Même garantie que la
   section 11 : AUCUNE règle CSS existante n'est modifiée ici, uniquement des ajouts.
   🔴 DEUX THÈMES SANS RIEN ÉCRIRE DE PLUS : comme la section 11, ce bloc ne consomme que des
   jetons déjà redéfinis en sombre (section 9) -- aucun `@media`/`[data-theme]` propre n'est
   nécessaire ici, et c'est volontaire (CLAUDE.md : « jamais de couleur définie dans un seul
   bloc de thème »).
   ------------------------------------------------------------------------- */

/* --- Échelle typographique (h1/h2 de section) -- MISE EN COMMUN LE 19/08, déplacée dans sa
   propre section juste avant celle-ci (§ 12) : `.ecran-langue`/`.ecran-accueil`/`.ecran-cadrage`
   y sont listés ensemble plutôt que redupliqués trois fois. Tout écran refondu s'ajoute LÀ-BAS. */

/* Tous les nombres de cet écran suivent la même graphie que la maquette (chiffres alignés,
   jamais de largeur de glyphe qui saute d'un nombre à l'autre) -- un réglage de graisse de
   chiffre, sans effet sur le texte non numérique : sans risque à poser sur tout l'écran plutôt
   que de traquer chaque nombre un par un. */
.ecran-langue strong,
.ecran-accueil strong {
  font-variant-numeric: tabular-nums;
}

/* --- La grille des blocs -- densité de la maquette (324px vs 280px ailleurs dans l'appli) ----- */
.ecran-langue .grille-cartes,
.ecran-accueil .grille-cartes {
  grid-template-columns: repeat(auto-fill, minmax(324px, 1fr));
}

/* --- L'anatomie d'une carte de bloc (maquette : .tete/.nom, .chiffres/.ch, .mesures/.cout) --- */

/* Le titre d'une carte ("Fiches produit", "Catégories"...) -- poids et taille de la maquette
   (.nom), et marge remise à zéro : le `h1,h2,h3{margin-top:var(--espace-6)}` générique (section 2)
   est pensé pour un titre de PAGE, pas pour le premier élément d'une carte déjà espacée par la
   grille (section 5) -- sans ce reset, chaque carte ouvrait sur 32px de vide avant son nom. */
.ecran-langue .grille-cartes .card h3,
.ecran-accueil .grille-cartes .card h3 {
  font-size: 16.5px;
  font-weight: 650;
  letter-spacing: -0.012em;
  margin: 0 0 0.5rem;
}

/* Le bandeau de chiffres (maquette : .chiffres/.ch) -- remplace la phrase unique
   "3 846 contenu(s) · 9 champ(s) traduit(s) · 0 en ligne" par TROIS colonnes hiérarchisées
   (gros nombre, libellé dessous), bordées haut/bas : c'est ce qui rend une carte lisible d'un
   coup d'œil. Les TROIS mêmes nombres et les TROIS mêmes libellés qu'avant (univers-detail.njk,
   boucle des vignettes) -- rien de retiré, seulement réparti sur trois colonnes au lieu d'une
   phrase à parser.
   🔴 UX-2 (18/08) -- `.ecran-accueil` REJOINT CE PATRON (docs/design/maquette-ux2-accueil.html,
   sélecteurs `.chiffres`/`.ch`) : la légende de la carte de langue de l'accueil (jusque-là
   `.univers-entete__legende`, une ligne de 13.6px identique à son libellé, ÉCRASÉE de surcroît par
   la règle `.card > p` juste au-dessus qui la ramenait à 12.8px) devient CE MÊME bandeau -- les
   TROIS mêmes nombres (`langue.barre.segments`, `construireBarreAvancement`, INCHANGÉ), seul le
   balisage change (`views/univers.njk`). Un seul patron pour les deux écrans plutôt qu'un doublon
   : c'est la carte de bloc du tableau de bord (`/:pair`) ET la carte de langue de l'accueil (`/`). */
.ecran-langue .carte-bloc__chiffres,
.ecran-accueil .carte-bloc__chiffres {
  display: flex;
  gap: var(--espace-4);
  flex-wrap: wrap;
  padding: 0.5rem 0;
  margin: 0 0 var(--espace-2);
  border-top: 1px solid var(--bordure);
  border-bottom: 1px solid var(--bordure);
}

.ecran-langue .carte-bloc__chiffre,
.ecran-accueil .carte-bloc__chiffre {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.ecran-langue .carte-bloc__chiffre strong,
.ecran-accueil .carte-bloc__chiffre strong {
  font-size: 19px;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--texte);
}

.ecran-langue .carte-bloc__chiffre span,
.ecran-accueil .carte-bloc__chiffre span {
  font-size: 11.5px;
  color: var(--texte-attenue);
}

/* Les phrases secondaires d'une carte (le total du catalogue, "Caractéristiques : déjà
   traduites...", les badges d'état, "reste à traduire"...) -- discrètes et compactes, jamais un
   paragraphe qui pèse visuellement autant que le bandeau de chiffres au-dessus (maquette :
   .mesures/.cout/.phrase, 12.8px). Ne cible QUE les <p>/<ul> DIRECTEMENT enfants d'une `.card`
   sous cette grille -- pas les alertes de tête de page ni les autres `<p>` de l'écran, qui
   gardent leur taille habituelle. Aucune information n'est retirée : chaque phrase déjà rendue
   par le gabarit reste affichée, seule sa taille change.
   🔴 `.ecran-langue` RETIRÉ D'ICI LE 20/08 (refonte « aération ») : aucune carte de bloc n'a plus de
   <p>/<ul> DIRECTEMENT enfant de `.card` -- tout le narratif vit désormais sous `.carte-bloc__detail`
   (le pli « Le détail »), qui a sa propre règle de taille juste plus bas. `.ecran-accueil` n'est PAS
   concerné par ce lot (ses cartes de langue n'ont pas de pli) : sa moitié de la règle reste telle
   quelle. */
.ecran-accueil .grille-cartes .card > p,
.ecran-accueil .grille-cartes .card > ul {
  font-size: 12.8px;
  margin: 0.35rem 0;
}

.ecran-accueil .grille-cartes .card > p:first-child {
  margin-top: 0;
}

/* La même discrétion, à l'intérieur du pli replié d'une carte de bloc (§ refonte « aération »
   ci-dessous) -- même taille, même marge que l'ancienne règle directe, appliquée maintenant au
   contenu du <details> plutôt qu'aux enfants directs de `.card`. */
.ecran-langue .carte-bloc__detail-corps > p,
.ecran-langue .carte-bloc__detail-corps > ul {
  font-size: 12.8px;
  margin: 0.35rem 0;
}

.ecran-langue .carte-bloc__detail-corps > p:first-child {
  margin-top: 0;
}

/* --- Écart 1 (18/08, mesuré dans le navigateur) -- la carte devient une COLONNE FLEX (maquette :
   .carte, `display:flex;flex-direction:column`) pour que le geste de bas de carte s'aligne d'une
   carte à l'autre. Sans ça, `.actions` reste à la place que lui donne le flux normal : sur une
   rangée de cartes de MÊME hauteur (la grille CSS égalise les lignes), une carte au texte court
   laisse ses boutons flotter très haut, une carte au texte long les pousse tout en bas -- mesuré :
   70px / 134px / 17px du bas de carte sur trois cartes de 438px. `margin-top:auto` sur `.actions`
   (le dernier enfant de la carte) l'ancre en bas de la colonne, quel que soit ce qui le précède --
   exactement le patron de la maquette. Pas de `gap` ajouté ici : les `<p>` ci-dessus portent déjà
   leurs propres marges (0.35rem) -- un `gap` sur `.card` s'ajouterait à ces marges au lieu de les
   remplacer, doublant l'espacement entre chaque ligne (consigne explicite : ajuster, pas empiler).
   Les cartes SANS `.actions` (glossaire, faits, couverture...) restent inchangées à l'œil : une
   colonne flex sans gap qui n'étire aucun enfant se comporte comme le flux bloc qu'elles avaient. */
.ecran-langue .grille-cartes .card,
.ecran-accueil .grille-cartes .card {
  display: flex;
  flex-direction: column;
}

/* 7-291 (10/09) : la carte d'univers de l'accueil est redevenue un LIEN (<a class="card">, voir
   views/univers.njk) -- sans ce reset, la règle globale `a { color: var(--accent) }` reteindrait
   TOUT le texte de la carte (titre, chiffres, phrases) dans la couleur d'accent, et le survol le
   soulignerait en bloc. Scopé à `.ecran-accueil` seul : les autres `.card` de l'application restent
   des `<div>` ordinaires, cette règle ne les touche pas. */
.ecran-accueil .grille-cartes a.card {
  color: inherit;
  text-decoration: none;
}
.ecran-accueil .grille-cartes a.card:hover,
.ecran-accueil .grille-cartes a.card:focus-visible {
  text-decoration: none;
}

/* 🔴 `.ecran-langue` RETIRÉ D'ICI LE 20/08 -- `.actions` n'est plus l'enfant DIRECT de `.card` sur
   une carte de bloc (il vit sous `.carte-bloc__pied`, voir § refonte « aération » plus bas, qui
   porte désormais ce même `margin-top: auto`). `.ecran-accueil` n'a pas de pied, sa règle reste
   inchangée. */
.ecran-accueil .grille-cartes .card > .actions {
  margin-top: auto;
}

/* 🆕 17/09 -- LES ANNEAUX D'AVANCEMENT DE LA CARTE DE LANGUE (recette de l'accueil : « au lieu de
   mettre ce texte en entier, des petits cercles pour montrer le taux d'avancement, écrit en
   petit »). Remplace les CINQ LIGNES EN TEXTE posées le 15/09 (`.carte-langue__comptes`, retirée
   avec ce lot) -- MÊMES données (voir routes/univers.ts, presentation/anneau-avancement.ts),
   présentation neuve. Maquette validée, reproduite : docs/design/maquette-accueil-anneaux-
   2026-09-17.html -- même géométrie (rayon 26, `stroke-dasharray`/`stroke-dashoffset` en SVG,
   jamais recalculée ici), mêmes jetons de la charte.
   🔴 QUATRE COLONNES DE MÊME TAILLE (décidé) -- passent à DEUX sous 480px de largeur d'écran, un
   palier propre à cette grille (les rings ont une taille minimale lisible, contrairement au texte
   qu'ils remplacent). `.reste-du-site` (un anneau plus petit, une phrase) est un bloc À PART, sous
   la grille des quatre -- jamais un cinquième dedans (la maquette le sépare aussi, sous un filet). */
.ecran-accueil .anneaux-langue {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--espace-3);
  margin: var(--espace-2) 0;
}
@media (max-width: 480px) {
  .ecran-accueil .anneaux-langue {
    grid-template-columns: repeat(2, 1fr);
  }
}

.ecran-accueil .anneau {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.3rem;
}

/* L'anneau SVG lui-même : piste neutre (`--bordure`, même jeton que le cadre d'une carte), valeur en
   accent -- JAMAIS le vert avant 100 % pile (`.anneau--complet`, plus bas), règle explicite de la
   maquette. `transform: rotate(-90deg)` fait partir le remplissage du haut, pas de la droite (le
   point de départ par défaut d'un cercle SVG). */
.ecran-accueil .anneau svg {
  width: 60px;
  height: 60px;
  transform: rotate(-90deg);
}
.ecran-accueil .anneau__piste {
  fill: none;
  stroke: var(--bordure);
  stroke-width: 6;
}
.ecran-accueil .anneau__valeur {
  fill: none;
  stroke: var(--accent);
  stroke-width: 6;
  stroke-linecap: round;
  /* Le remplissage animé (~1 s, demande de l'utilisateur) -- public/js/anneaux-avancement.js pose
     l'attribut `stroke-dashoffset` définitif au chargement, cette transition fait le reste. */
  transition: stroke-dashoffset 1s cubic-bezier(0.22, 0.8, 0.3, 1);
}
.ecran-accueil .anneau--complet .anneau__valeur {
  stroke: var(--succes);
}

/* 🆕 17/09 -- LE TAUX, EN GROS, AU CENTRE GÉOMÉTRIQUE DE L'ANNEAU -- CORRIGÉ (défaut vu par
   l'utilisateur sur capture : « les chiffres dans les anneaux ne sont pas centrés », assis dans la
   moitié basse du cercle). L'ANCIEN PATRON (`margin: -38px 0 0`, retiré) remontait le `<p>` d'un
   nombre magique CALIBRÉ SUR LA TAILLE DU GRAND ANNEAU SEUL -- juste ici, faux dès que la taille
   change (le petit anneau du « reste du site », plus bas, portait déjà SA PROPRE rustine à -32px).
   ⇒ CENTRE GÉOMÉTRIQUE, SANS NOMBRE MAGIQUE : `.anneau__cadran` (le conteneur qui enveloppe le
   `<svg>`, posé dans le gabarit) est `position: relative`, dimensionné par son SEUL enfant visible
   (le `<svg>`) ; `.anneau__centre` devient `position: absolute; inset: 0` PAR-DESSUS lui, centré
   par flexbox -- il occupe donc TOUJOURS exactement le carré du `<svg>`, quelle que soit sa taille
   (60px ici, 44px sur le petit anneau : UNE SEULE règle pour les deux, plus de rustine par taille).
   Choix `<p>` absolu plutôt que `<text>` DANS le SVG (l'autre option envisagée) : la police, la
   graisse et la couleur du thème (clair/sombre, `var(--texte)`) suivent alors les mêmes jetons CSS
   que le reste de la page sans dupliquer de style en SVG, et `&nbsp;%` reste du HTML ordinaire. */
.ecran-accueil .anneau__cadran {
  position: relative;
  display: inline-flex;
}
.ecran-accueil .anneau__centre {
  position: absolute;
  inset: 0;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--texte);
}
.ecran-accueil .anneau--complet .anneau__centre {
  color: var(--succes);
}

.ecran-accueil .anneau__libelle {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.2;
}
.ecran-accueil .anneau__detail {
  margin: 0;
  font-size: 0.72rem;
  color: var(--texte-attenue);
  line-height: 1.25;
}
/* La pastille « N pas encore publiées » -- réutilise `.badge`/`.badge--attente` (déjà posés pour
   toute l'application, jamais une 5ᵉ couleur inventée), ABSENTE quand `pasEncorePubliees` vaut zéro
   (le gabarit ne la rend pas du tout dans ce cas). `white-space: normal` SURCHARGE `.badge` (qui
   l'interdit partout ailleurs) : une colonne d'anneau (≈60-70px) est trop étroite pour la phrase
   complète sur une seule ligne, mesurée à l'écran avec « 104 pas encore publiées ». */
.ecran-accueil .anneau__pastille {
  white-space: normal;
  text-align: center;
}

/* La mesure en échec : jamais un anneau à 0 % (règle E1 de la charte -- une mesure ratée n'est pas
   un zéro), la même place dans la grille que les trois autres, un texte à la place du dessin. */
.ecran-accueil .anneau--vide {
  justify-content: center;
  min-height: 60px;
}
.ecran-accueil .anneau--vide .anneau__cause {
  margin: 0;
  font-size: 0.78rem;
}

/* Le cinquième anneau, PLUS PETIT (décidé sur la maquette) -- « le reste du site », toujours sous la
   grille des quatre, jamais dedans. */
.ecran-accueil .reste-du-site {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding-top: var(--espace-2);
  border-top: 1px solid var(--bordure);
}
.ecran-accueil .reste-du-site .anneau--petit svg {
  width: 44px;
  height: 44px;
}
/* 🆕 17/09 -- PLUS DE RUSTINE DE CENTRAGE ICI (`.anneau__cadran`/`.anneau__centre` ci-dessus
   centrent déjà les DEUX tailles par la même règle géométrique) : seule la taille de police reste
   propre au petit anneau. */
.ecran-accueil .reste-du-site .anneau--petit .anneau__centre {
  font-size: 0.76rem;
}
.ecran-accueil .reste-du-site__texte {
  margin: 0;
  font-size: 0.86rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.ecran-accueil .reste-du-site__texte strong {
  font-size: 0.88rem;
}
/* La ligne muette « l'habillage, le panier, le compte » -- ancienne sous-ligne imbriquée du 15/09,
   conservée telle quelle sous le bloc « reste du site » (même discrétion, jamais perdue). */
.ecran-accueil .carte-langue__reste-detail {
  margin: 0.2rem 0 0;
  font-size: 0.8rem;
}

@media (prefers-reduced-motion: reduce) {
  .ecran-accueil .anneau__valeur {
    transition: none;
  }
}

/* --- Écart 2 (18/08, mesuré dans le navigateur) -- le texte secondaire d'une carte (`.muted`)
   calculait `font-style: italic`, hérité de la règle GLOBALE (section 1, `.muted`) qui sert aussi
   les 36 autres écrans -- ON NE LA TOUCHE PAS. La maquette (`.mesures`/`.phrase`) est en romain :
   trois à quatre lignes d'italique par carte alourdissent la lecture pour rien. Surcharge scopée à
   la fois à cet écran ET aux cartes seulement (jamais la phrase d'introduction en tête de page,
   hors carte, sans équivalent dans la maquette) -- la règle globale reste donc intacte partout
   ailleurs, y compris pour ce même écran hors grille de cartes. */
.ecran-langue .grille-cartes .card .muted,
.ecran-accueil .grille-cartes .card .muted {
  font-style: normal;
}

/* --- LA REFONTE « AÉRATION » DE LA CARTE DE BLOC (20/08) ---------------------------------------
   Constat de l'utilisateur, mot pour mot : « les boutons sont trop collés au texte, ce n'est pas
   assez aéré [...] tu es bombardé de texte ». Mesuré avant ce lot : 1 326 mots à l'ouverture de
   `/:pair`, dont 878 dans la seule section « Les blocs de contenu » -- une phrase de catalogue
   (« ce bloc ne s'énumère pas fiche par fiche... ») y était répétée DIX fois, une par carte non
   énumérable.
   🔴 LE PRINCIPE CARDINAL INTERDIT DE SUPPRIMER : rien ne disparaît, tout se replie. Chaque carte
   garde trois zones -- un EN-TÊTE (le titre + un seul compteur de synthèse + un badge SEULEMENT si
   une décision attend, patron A3 de la charte), une JAUGE purement visuelle (mêmes segments que
   `.univers-entete__barre`, section 11 -- on ne redessine pas une deuxième barre), et un PIED
   TOUJOURS VISIBLE (les gestes, les mesures). Tout le reste -- le détail des trois chiffres, les
   phrases de catalogue, la cause d'un blocage, les notes propres à un bloc -- part dans un
   `<details>` replié : présent dans le HTML donc dans tous les tests existants (aucun ne distingue
   visible de replié), absent de ce que l'œil lit à l'ouverture. Portée : `.ecran-langue` seulement,
   comme le reste de cette section 12. */
.ecran-langue .carte-bloc__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espace-2);
  flex-wrap: wrap;
}
/* 🆕 17/09 (LOT T2-bis, addendum « Apparence » de la maquette validée
   docs/design/maquette-cartes-boutons-2026-09-17.html, `.carte__tete`/`.carte__icone`) -- L'ICÔNE
   ET LE TITRE, REGROUPÉS : sans ce conteneur, l'icône et le `<h3>` se seraient partagé l'espace
   avec le résumé de droite via le `justify-content: space-between` de `.carte-bloc__entete`
   (ci-dessus), au lieu de rester COLLÉS l'un à l'autre à gauche. */
.ecran-langue .carte-bloc__titre {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-width: 0;
}
/* La pastille crème (34px, comme la maquette) : fond `--accent-fond`, icône `--accent`, mêmes
   jetons que les pastilles déjà posées ailleurs (vignettes du cadrage, section suivante) --
   jamais une cinquième couleur inventée. `flex: none` : elle ne rétrécit jamais quand le titre est
   long. */
.ecran-langue .carte-bloc__icone {
  width: 34px;
  height: 34px;
  border-radius: var(--rayon-sm);
  background: var(--accent-fond);
  color: var(--accent);
  display: grid;
  place-items: center;
  flex: none;
}
.ecran-langue .carte-bloc__icone svg {
  width: 18px;
  height: 18px;
}
.ecran-langue .carte-bloc__entete h3 {
  margin: 0;
}
.ecran-langue .carte-bloc__resume {
  font-size: 0.78rem;
  color: var(--texte-attenue);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* 🆕 MAQUETTE B (27/08, validee par l'utilisateur) -- LE GRAND CHIFFRE ET LE RESTE VENTILE de la
   carte « Fiches produit ». Sa demande : « trop de vide pour rien », et « ce qui nous interesse,
   c'est plus les donnees macro sur l'etat d'avancement de la traduction ». Aucune couleur en dur :
   uniquement les jetons, le mode sombre suit tout seul. */
.ecran-langue .carte-bloc__grand-chiffre {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin: 0.6rem 0 0;
}
.ecran-langue .carte-bloc__grand-chiffre strong {
  font-size: 1.5rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.ecran-langue .carte-bloc__grand-chiffre span {
  font-size: 0.85rem;
  color: var(--texte-attenue);
}
.ecran-langue .carte-bloc__reste {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin: 0.7rem 0 0;
  font-size: 0.8rem;
  color: var(--texte-attenue);
}
.ecran-langue .carte-bloc__reste-ligne {
  display: flex;
  justify-content: space-between;
  gap: var(--espace-2);
  font-variant-numeric: tabular-nums;
}
.ecran-langue .carte-bloc__reste-ligne strong {
  font-weight: 500;
  color: var(--texte);
}
.ecran-langue .carte-bloc__reste-ligne--fille > span:first-child {
  padding-left: 0.6rem;
}

.ecran-langue .carte-bloc__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  margin: 0.5rem 0 0;
}

/* La jauge : MÊME MÉCANIQUE que la barre d'en-tête (`.univers-entete__barre`/`__segment`, section 11)
   -- on la réutilise telle quelle (mêmes classes de segment, donc les mêmes jetons de couleur), on la
   rétrécit seulement dans le contexte d'une carte. Deux segments colorés (en ligne / traduit non
   publié) : le troisième ("ce qui reste") est le fond transparent de la piste, comme en section 11 --
   jamais une troisième couleur à inventer pour ce qui n'est pas encore traduit. */
.ecran-langue .carte-bloc__barre {
  height: 5px;
  margin: 0.6rem 0 0;
}

/* Le pli -- même affordance de chevron géométrique que `.bandeau-etat`/`.regle-repli__bascule`
   (jamais un emoji, jamais un triangle typographique, invariant A5) : on retire le marqueur natif du
   navigateur et on pose son remplaçant dans le même geste. */
.ecran-langue .carte-bloc__detail {
  margin-top: var(--espace-3);
  border-top: 1px solid var(--bordure);
  padding-top: var(--espace-2);
}
.ecran-langue .carte-bloc__detail summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  cursor: pointer;
  list-style: none;
  font-size: 0.78rem;
  color: var(--texte-attenue);
}
.ecran-langue .carte-bloc__detail summary::-webkit-details-marker {
  display: none;
}
.ecran-langue .carte-bloc__detail summary::after {
  content: '';
  width: 0.4rem;
  height: 0.4rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  flex: none;
}
.ecran-langue .carte-bloc__detail[open] summary::after {
  transform: rotate(45deg);
}
.ecran-langue .carte-bloc__detail[open] summary {
  margin-bottom: var(--espace-2);
}

/* Le pied : gestes ET mesures toujours visibles, mais séparés du corps par un filet -- c'est ce qui
   répond au constat « boutons trop collés au texte ». Avant ce lot, AUCUNE règle ne donnait
   d'espacement à `.actions` sur cette carte : les boutons s'empilaient directement au fil du texte
   qui précédait. `.carte-bloc__pied` remplace `.actions` comme dernier enfant direct de `.card`
   (voir le correctif de la règle `margin-top: auto`, juste avant l'Écart 1 ci-dessus) : c'est LUI
   qui s'ancre en bas de la colonne flex de la carte, désormais.
   🆕 17/09 (mesuré par la coordination SUR LE SERVEUR : le haut de la rangée de boutons variait de
   96 à 160 px dans une même rangée) -- `margin-top: auto` PASSE DE CE PIED À LA RANGÉE DE BOUTONS
   ELLE-MÊME (`.carte-bloc__actions`, plus bas) : avant ce correctif, TOUT le pied (mesures + liens
   tertiaires + rangée de boutons) était poussé en bloc vers le bas, donc la hauteur de ce qui suit
   les boutons (1 à 5 liens tertiaires selon la carte) décalait la rangée elle-même VERS LE HAUT.
   Ce pied devient donc un CONTENEUR FLEX qui grandit pour remplir l'espace restant de la carte
   (`flex: 1`) : les mesures et les liens tertiaires flottent en haut, poussés par leur propre
   contenu, et c'est la rangée de boutons -- SEULE -- qui porte `margin-top: auto` pour s'ancrer au
   bas de CE conteneur, donc au bas de la carte, IDENTIQUE d'une carte à l'autre dans une même
   rangée puisque les cartes d'une rangée font déjà la même hauteur (grille en `align-items: stretch`
   implicite). */
.ecran-langue .grille-cartes .card > .carte-bloc__pied {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding-top: var(--espace-3);
  border-top: 1px solid var(--bordure);
}
.ecran-langue .carte-bloc__pied .mesures-bloc {
  margin: 0 0 var(--espace-2);
}
/* 🆕 17/09 (LOT T2, MAQUETTE VALIDÉE « parfait » -- docs/design/maquette-cartes-boutons-2026-09-17
   .html) -- TROIS COLONNES FIXES : Synchroniser · Voir · Traduire, toujours dans cet ordre, le même
   bouton à la même place sur les 13 cartes (avant ce lot : `display: flex` sans largeur posée, les
   boutons flottaient à des places différentes selon ce qu'une carte avait ou non à proposer).
   🆕 17/09 (renommée depuis `.actions` -- voir le commentaire du pied ci-dessus) : c'est ELLE qui
   porte désormais `margin-top: auto`, jamais le pied entier. */
.ecran-langue .carte-bloc__pied .carte-bloc__actions {
  display: grid;
  margin-top: auto;
  /* `minmax(0, 1fr)`, PAS `1fr` seul -- un `1fr` nu garde un plancher `auto` (le contenu minimal
     du plus large bouton, ici « Traduire… »), donc les trois colonnes se calaient sur SA largeur
     au lieu de se partager l'espace à parts égales (mesuré à l'écran : 143px/85px/124px pour trois
     colonnes censées faire le même tiers chacune). `minmax(0, …)` autorise une colonne à descendre
     sous son contenu -- le texte du bouton passe alors sur deux lignes plutôt que de déborder. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--espace-2);
}
.ecran-langue .carte-bloc__pied .carte-bloc__actions .btn {
  width: 100%;
  justify-content: center;
  padding: 0.5rem 0.5rem;
  font-size: 0.84rem;
  white-space: normal;
  text-align: center;
}
/* Le `<form>` du geste « Synchroniser » est lui-même l'ITEM de la grille (le `<button>` est son
   enfant) -- sans ce reset, le bouton ne remplirait pas sa colonne comme les deux `<a class="btn">`
   voisines, qui SONT l'item directement. */
.ecran-langue .carte-bloc__pied .carte-bloc__actions form {
  display: flex;
}
/* Un geste qui n'existe pas pour cette carte GARDE SA PLACE, en gris, `cursor: not-allowed` -- sa
   raison vit dans `title` (réutilisée telle quelle depuis la phrase déjà calculée côté
   présentation, jamais une seconde formulation). Variante AJOUTÉE au composant `.btn` COMMUN de la
   charte -- jamais redéfini (`.btn`/`.btn--primary`/`.btn--ghost` restent inchangés), scopée à
   cette carte comme le reste de cette section. */
.ecran-langue .btn--absent {
  background: var(--fond);
  border-color: var(--bordure);
  color: var(--texte-attenue);
  cursor: not-allowed;
}
.ecran-langue .btn--absent:hover {
  background: var(--fond);
}


/* -------------------------------------------------------------------------
   13. L'ACCUEIL REFONDU (lot UX-2, 18/08)
   Maquette validée par l'utilisateur le matin même : docs/design/maquette-ux2-accueil.html.
   `.ecran-accueil` enveloppe tout le `{% block body %}` de `views/univers.njk` (même patron que
   `.ecran-langue` ci-dessus) -- aucune règle d'ici ne peut atteindre un autre écran. La section 12
   ci-dessus a déjà été ÉLARGIE (pas dupliquée) pour tout ce que les deux écrans partagent
   réellement (h1, `strong`, densité de grille, titre/texte/actions d'une carte, et depuis ce
   correctif le bandeau de chiffres `.carte-bloc__chiffres`) ; ce qui suit est ce qui est
   SPÉCIFIQUE à l'accueil : le titre de section (qui ne doit pas confondre avec les h2 imbriqués
   des vignettes de cadrage), les couleurs propres de SA jauge (2ᵉ segment ambre, 3ᵉ segment en
   piste visible -- le tableau de bord garde SA propre jauge, deux nuances de --succes, intacte),
   la ligne calme de synchronisation, et la carte en pointillés « Déclarer une langue ». Mêmes
   garanties que section 12 : jetons existants uniquement, aucun bloc de thème propre nécessaire
   (tout ce qui suit consomme des jetons déjà redéfinis en sombre, section 9).
   ------------------------------------------------------------------------- */

/* Le titre de section (« Synchronisation quotidienne », « Les langues », « Le socle commun… »).
   🔴 ENFANT DIRECT SEULEMENT (`>`) DANS LA RÈGLE PARTAGÉE (§ 12, avant § 12) : l'accueil imbrique
   des <h2> propres à CHAQUE vignette de cadrage (« Cadrage du site — la boutique France », un par
   boutique déclarée) sous ce même `.ecran-accueil` -- un sélecteur descendant les aurait attrapés
   aussi et rétrécis en étiquette de rubrique par erreur ; c'est pourquoi `.ecran-accueil > h2`
   garde son `>` dans la liste partagée plutôt que d'en sortir. Règle déplacée là-bas le 19/08,
   mise en commun avec `.ecran-langue h2` et `.ecran-cadrage > h2`. */

/* --- La jauge des cartes de langue -- SA PROPRE palette (maquette : .jauge/.j-ok/.j-mi) ---------
   🔴 LA MÉCANIQUE RESTE `.univers-entete__barre`/`__segment` (section 11, partagée avec le
   tableau de bord de la langue, `univers-detail.njk`) -- même markup, même trois largeurs posées
   par `construireBarreAvancement` : on ne redessine PAS la barre, on n'en écrit PAS une seconde.
   Seule SA COULEUR change, et seulement ICI : la maquette de l'accueil distingue « en ligne » (vert)
   de « traduits, pas publiés » (ambre, `--attente`) -- deux états bien différents pour qui décide
   quoi publier -- là où le tableau de bord les nuance en deux opacités du même vert (section 11,
   décision distincte, jamais touchée). Le 3ᵉ segment (« sans traduction ») reste transparent comme
   en section 11 : c'est le FOND du conteneur, changé ici en piste visible (`--surface-2` bordée,
   comme la maquette), qui le rend visible -- aucune règle de segment à ajouter pour lui. Prouvé par
   capture le 18/08 : `/fr-es` (qui ne porte pas `.ecran-accueil`) rend sa barre PIXEL POUR PIXEL
   comme avant ce bloc. */
.ecran-accueil .univers-entete__barre {
  background: var(--surface-2);
  border: 1px solid var(--bordure);
}

.ecran-accueil .univers-entete__segment--traduits {
  background: var(--attente);
  opacity: 1;
}

/* --- La synchronisation quotidienne -- une ligne calme (maquette : .synchro/.ligne/.pastille) ---
   🔴 LA MECANIQUE RESTE `.bandeau-etat` (partagée par plusieurs écrans, § dédiée plus haut dans
   cette feuille) -- on ne la redessine PAS depuis zéro : cette règle ne fait qu'ASSOUPLIR sa
   silhouette, pour se rapprocher de la "ligne calme" de la maquette sans dupliquer son mécanisme de
   repli/couleur d'état (déjà prouvé par test/accueil-synchro.test.ts, qu'on ne veut SURTOUT pas
   devoir re-prouver).
   🆕 15/09 (lot « l'accueil mangé par la nuit », correctif défaut n°9) -- `#trace-synchro` a
   DÉMÉNAGÉ de l'accueil (`.ecran-accueil`) vers sa propre page (`.ecran-synchro`, `/synchro`,
   `views/synchro.njk`) : le sélecteur suit, sinon cette règle ne visait plus rien (le fragment se
   rendait à sa taille par défaut, charte non respectée). L'accueil, lui, ne porte plus
   `#trace-synchro` du tout -- son bandeau compact (`#tri-synchro`) n'a besoin d'aucun ajustement de
   taille. */
.ecran-synchro #trace-synchro {
  font-size: 13.5px;
}

/* La phrase « connu ≠ traduit » (aRetraduire), fondue dans la même ligne que le passage de
   synchro (voir le commentaire du gabarit) : sa PROPRE couleur d'état, indépendante du bandeau
   qui l'entoure -- `--attente` seulement quand des champs ont réellement changé ou que la mesure a
   échoué, jamais par défaut (le cas zéro reste dans le texte neutre du bandeau, `--texte-attenue`
   déjà hérité de `.bandeau-etat__titre`). */
.bandeau-etat__retraduire {
  display: block;
  margin-top: 0.3rem;
}

.bandeau-etat__retraduire--attente {
  color: var(--attente);
}

/* --- La carte en pointillés « Déclarer une langue » (maquette : .carte-plus) -------------------
   Seule vraie nouveauté visuelle de ce lot : aucun autre écran n'a de carte "en creux" qui invite
   à EN CRÉER une -- rien à partager depuis la section 12. Centrée, bordure en tirets, jamais
   d'ombre (elle ne doit pas rivaliser avec les cartes de langue, pleines, à côté d'elle). */
.carte-langue--nouvelle {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-height: 11rem;
  text-align: center;
  color: inherit;
  text-decoration: none;
  background: transparent;
  border: 1px dashed var(--bordure-forte);
  box-shadow: none;
}

.carte-langue--nouvelle:hover {
  border-color: var(--accent);
}

.carte-langue--nouvelle__plus {
  font-size: 1.6rem;
  line-height: 1;
  color: var(--texte-attenue);
}

/* =========================================================================
   14. ECRAN DE CONNEXION -- portee .ecran-connexion (refonte UX, 18/08)
   =========================================================================
   Ajouts purs : rien au-dessus de cette section n'est touche. Seul ecran
   affiche AVANT toute session -- `layout.njk` masque son en-tete applicatif
   ici (`pageLogin`), donc la marque (pastille + nom) est reprise DANS la
   carte : meme classe `.entete__pastille` que l'en-tete commun (section 1),
   pas une couleur ou une taille dupliquee. Jetons seulement, valables dans
   les deux themes sans regle `@media`/`[data-theme]` propre. */
.ecran-connexion {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Le corps commun pose deja 2rem de marge haute et basse (section 2) : on
     centre dans le reste de la fenetre plutot que de retoucher `body`,
     partage par tous les ecrans. */
  min-height: calc(100vh - 4rem);
}

.connexion-carte {
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: var(--espace-6) var(--espace-5);
}

.connexion-carte__marque {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin-bottom: var(--espace-3);
}

.connexion-carte__marque strong {
  font-size: 1.1rem;
}

.connexion-carte .muted {
  margin-top: 0;
  margin-bottom: var(--espace-5);
}

.connexion-carte .alert--err {
  margin-bottom: var(--espace-4);
}

.connexion-carte label {
  display: block;
  font-size: 0.85rem;
  font-weight: 500;
  margin-bottom: var(--espace-2);
}

.connexion-carte__champ {
  display: block;
  width: 100%;
  padding: var(--espace-3);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  margin-bottom: var(--espace-4);
}

/* Focus visible obligatoire (accessibilite) : le champ n'a plus aucun style en
   ligne -- avant cette section, il n'avait aucun style du tout, focus compris. */
.connexion-carte__champ:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.connexion-carte__bouton {
  width: 100%;
  justify-content: center;
}

.connexion-carte__bouton:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Lien "Mot de passe oublie ?" (lot "mot de passe oublie", 18/08) -- discret, sous le bouton :
   une porte de secours, pas l'action principale de la carte. */
.connexion-carte__lien-secondaire {
  margin: var(--espace-4) 0 0;
  text-align: center;
  font-size: 0.85rem;
}

/* =========================================================================
   15. ECRAN « MON COMPTE » -- portee .ecran-compte (lot "changer le mot de
   passe", 18/08)
   =========================================================================
   Ajouts purs : rien au-dessus de cette section n'est touche. Un ecran
   d'application NORMAL (en-tete commun, fil d'Ariane) -- a la difference de
   .ecran-connexion (section 14), qui centre une carte SEULE avant toute
   session. Ici on reutilise .fiche-groupe/.champ tels quels (memes jetons
   que le reste de l'app) et on borne seulement la largeur : un formulaire de
   trois champs de mot de passe etale sur les 1100px du corps commun (body,
   section 1) serait illisible. */
.ecran-compte {
  max-width: 32rem;
}

/* ------------------------------------------------------------------------------------------------
   SECTION 16 — L'OEIL D'AFFICHAGE DU MOT DE PASSE (18/08)
   Un bouton oeil dans le coin droit de chaque champ mot de passe (connexion, « Mon compte »,
   reinitialisation). Le balisage est pose par public/js/afficher-mot-de-passe.js -- ici seulement
   l'habillage, aux jetons existants, valide dans les deux themes.
   ------------------------------------------------------------------------------------------------ */
.champ-mdp {
  position: relative;
  display: block;
}
.champ-mdp input {
  width: 100%;
  /* Place pour l'oeil : le texte ne passe jamais dessous. */
  padding-right: 2.6rem;
}
.champ-mdp__oeil {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  border-radius: var(--rayon-sm);
  background: transparent;
  color: var(--texte-attenue);
  cursor: pointer;
}
.champ-mdp__oeil:hover {
  color: var(--texte);
}
.champ-mdp__oeil:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* =========================================================================
   17. LE HUB DU CADRAGE -- portee .ecran-cadrage (chantier UX3, 19/08)
   =========================================================================
   Maquette validée : docs/design/maquette-ux3-cadrage.html. `.ecran-cadrage` enveloppe tout le
   `{% block body %}` de `views/blocs.njk` -- même patron que `.ecran-accueil`/`.ecran-langue`
   (§ 12/13) : aucune règle d'ici ne peut atteindre un autre écran. Le mécanisme (`.bandeau-etat`,
   `.grille-vignettes`/`.vignette`, `.badge`) est PARTAGÉ, prouvé ailleurs -- on ne le redessine
   PAS ici, on l'ASSOUPLIT dans ce seul contexte, comme § 13 le fait déjà pour l'accueil.
   🔴 h1/h2 DE SECTION : MÊME RÈGLE PARTAGÉE QUE `.ecran-langue`/`.ecran-accueil` (mise en commun le
   19/08, section dédiée juste avant § 12) -- ce hub imbrique lui aussi des <h2> propres à CHAQUE
   porte (le titre de la vignette, « Fiches produit », « Modules »…) sous ce même `.ecran-cadrage`,
   d'où `.ecran-cadrage > h2` (enfant DIRECT) dans la liste partagée, jamais en descendant.
   ------------------------------------------------------------------------- */

/* --- Les dix portes -- l'icône, le titre et le badge sur une même ligne ------------------------
   `.vignette__entete` est un ajout scopé : `.vignette` (§ commune) empile ses enfants en colonne
   (icône seule au-dessus du titre, comme "Vérifier" plus bas sur ce même écran), ce hub est le
   premier écran à vouloir icône + titre + badge sur UNE SEULE ligne. La marge native de
   `.vignette h2` (§ commune) est retirée ICI SEULEMENT -- ailleurs elle reste intacte : c'est
   `.vignette__entete h2`, pas `.vignette h2`, qui est retouché. */
.ecran-cadrage .vignette__entete {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
}
/* 🔴 RETOUR DE L'UTILISATEUR SUR LES CAPTURES (19/08) : « pourquoi sur la maquette il y a des
   petites icônes pour chaque bloc et non ici ? » -- `.vignette__icone` (§ commune, dimensionné en
   2.4rem) rejoint désormais l'entête, à gauche du titre. `flex: none` EMPÊCHE le rétrécissement
   flex par défaut (`flex-shrink: 1`) de le compresser quand la ligne est serrée (titre long +
   badge) : sans lui, l'icône perdrait sa taille carrée sur les portes au libellé le plus long
   (« Caractéristiques & déclinaisons »). */
.ecran-cadrage .vignette__entete .vignette__icone {
  flex: none;
}
.ecran-cadrage .vignette__entete h2 {
  margin: 0;
  flex: 1;
  min-width: 0;
}
/* Le premier paragraphe (la description) touchait l'entête sans cette marge -- `.vignette p` (§
   commune) part à zéro pour TOUTE vignette, y compris celles de "Vérifier" (h2 seul, pas d'entête)
   qui gardent la marge native de `.vignette h2` juste au-dessus et n'ont donc pas besoin de ce
   correctif. */
.ecran-cadrage .vignette__entete + p {
  margin-top: var(--espace-2);
}
.ecran-cadrage .vignette__badge {
  flex: none;
  margin-left: var(--espace-2);
}
/* Un second texte, plus discret, sous la description (ex. la taille du tiroir des réglages sans
   module reconnu) -- jamais la même taille que la description, sinon les deux se confondent. */
.ecran-cadrage .vignette__sousligne {
  font-size: 12.3px;
  color: var(--texte-attenue);
  margin-top: 0.3rem;
}

/* --- L'ALLEGEMENT DE LA RESERVE (19/08, demande utilisateur : « ce gros bloc de texte est
   indigeste ») -----------------------------------------------------------------------------
   🔴 TOUT EST SOUS `.ecran-cadrage` : aucune de ces regles ne peut atteindre un autre ecran, meme
   patron que le reste de cette section (§ 17). C'est ce qui permet de reformer CE bloc-ci sans
   repeindre les 34 autres.
   🔴 AUCUNE COULEUR ECRITE EN DUR (invariant n°6 de la charte) : tout passe par les jetons, donc
   le theme sombre suit sans une regle de plus. */
.ecran-cadrage .reserve {
  margin: 0 0 var(--espace-2);
  line-height: 1.5;
}
.ecran-cadrage .reserve__titre {
  margin: var(--espace-3) 0 var(--espace-2);
}
/* Les trois reserves chiffrees : une puce chacune, jamais trois paragraphes. La puce est un tiret
   cadratin en ::marker plutot qu'un disque -- meme famille visuelle que le reste de l'ecran. */
.ecran-cadrage .reserve-liste {
  margin: var(--espace-2) 0 0;
  padding-left: 1.1rem;
  list-style: none;
}
.ecran-cadrage .reserve-liste li {
  position: relative;
  margin-top: 0.3rem;
}
.ecran-cadrage .reserve-liste li::before {
  content: '—';
  position: absolute;
  left: -1.1rem;
  color: var(--texte-attenue);
}
/* Le tableau de l'etat des lieux. Il herite du `table:not(.table)` generique (§ 8) pour les bordures
   et le rembourrage ; on ne redefinit ici que ce qui doit changer DANS un bandeau : une taille plus
   petite, des en-tetes discrets, et des chiffres alignes.
   `overflow-x: auto` sur l'enveloppe : cinq colonnes dans un bandeau, sur un ecran etroit le tableau
   defile TOUT SEUL plutot que de pousser la page entiere en travers. */
.ecran-cadrage .tableau-reserve {
  font-size: 0.94em;
  margin-top: var(--espace-2);
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}
.ecran-cadrage .tableau-reserve th,
.ecran-cadrage .tableau-reserve td {
  padding: 0.35rem var(--espace-3) 0.35rem 0;
  border-bottom: 1px solid var(--bordure);
}
.ecran-cadrage .tableau-reserve thead th {
  font-size: 0.86em;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--texte-attenue);
}
.ecran-cadrage .tableau-reserve tbody th {
  font-weight: 600;
  color: var(--texte);
}
.ecran-cadrage .tableau-reserve tr:last-child th,
.ecran-cadrage .tableau-reserve tr:last-child td {
  border-bottom: 0;
}
/* Des chiffres qui se comparent en colonne s'alignent a droite et prennent des chasses egales --
   sans quoi « 33 097 » et « 6 » ne se lisent pas comme une meme grandeur. */
.ecran-cadrage .tableau-reserve .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* La fraicheur dans le <summary> : presente mais jamais aussi forte que le libelle du pli. Elle
   s'efface sur un ecran etroit -- la date est un CONFORT (le detail la reporte), le libelle non. */
.ecran-cadrage .bandeau-etat__fraicheur {
  color: var(--texte-attenue);
  font-weight: 400;
}
@media (max-width: 620px) {
  .ecran-cadrage .bandeau-etat__fraicheur { display: none; }
}

/* =========================================================================
   18. LA PAGE D'UN BLOC DE CADRAGE -- portee .ecran-bloc (19/08)

   Maquette validee : docs/design/maquette-page-de-bloc.html. Ne parle QUE de son bloc : ses 15
   champs, ses propres compteurs, et ce que la fiche affiche aussi. Toute regle d'ici est sous
   `.ecran-bloc` -- meme mecanique de portee que les sections 12, 13 et 17 : aucune ne peut
   atteindre les autres ecrans.

   🔴 AUCUNE COULEUR ECRITE EN DUR (invariant A1/A4 de la charte) : tout passe par les jetons, donc
   le theme sombre suit sans une regle de plus.
   🔴 QUATRE COULEURS D'ETAT, PAS UNE DE PLUS (A3) : « attend votre confirmation » prend `--alerte`
   (quelque chose ne tourne pas rond), JAMAIS `--erreur` (c'est casse / c'est refuse) -- le champ
   n'est pas casse, il attend une signature.
   🔴 AUCUNE LARGEUR DE BASCULE ECRITE A LA MAIN (A6) : la rangee de mesures se reorganise seule.
   ========================================================================= */
.ecran-bloc h1 {
  font-size: 27px;
  letter-spacing: -0.025em;
  font-weight: 660;
}
.ecran-bloc > h2 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 650;
  color: var(--texte-attenue);
  margin: 2.6rem 0 0.875rem;
}

/* Une phrase de cadrage de SECTION, pas d'ecran : meme forme, ton plus discret -- deux bandeaux a
   l'accent cuir sur la meme page se disputeraient l'oeil. Aucune 5e couleur : surface secondaire
   et bordure appuyee, deux jetons existants. */
.ecran-bloc .alert--neutre {
  background: var(--surface-2);
  border-left-color: var(--bordure-forte);
}

/* --- LA RANGEE DE MESURES -- reprise du patron `.avancement__chiffres` du tableau de bord ------ */
.ecran-bloc .mesures {
  display: flex;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  overflow: hidden;
  margin-bottom: var(--espace-4);
}
.ecran-bloc .mesure {
  flex: 1 1 9rem;
  padding: var(--espace-3) var(--espace-4);
  border-left: 1px solid var(--bordure);
}
.ecran-bloc .mesure:first-child { border-left: none; }
.ecran-bloc .mesure__gros {
  font-size: 1.35rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.ecran-bloc .mesure__libelle {
  font-size: 0.78rem;
  color: var(--texte-attenue);
  margin-top: var(--espace-1);
}
.ecran-bloc .mesure--alerte .mesure__gros { color: var(--alerte); }
.ecran-bloc .mesure--succes .mesure__gros { color: var(--succes); }

/* --- LE CADRE DU TABLEAU ---------------------------------------------------------------------- */
.ecran-bloc .cadre {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  overflow: hidden;
}
.ecran-bloc .cadre__pied {
  padding: var(--espace-3) var(--espace-4);
  background: var(--surface-2);
  border-top: 1px solid var(--bordure);
  font-size: 0.82rem;
  color: var(--texte-attenue);
}
.ecran-bloc .apres {
  margin-top: var(--espace-4);
  font-size: 0.85rem;
  color: var(--texte-attenue);
}

/* --- LE TABLEAU DES CHAMPS -------------------------------------------------------------------- */
.ecran-bloc .tableau-bloc { font-size: 0.88rem; }
.ecran-bloc .tableau-bloc th,
.ecran-bloc .tableau-bloc td {
  padding: var(--espace-3) var(--espace-4);
  vertical-align: middle;
}
.ecran-bloc .tableau-bloc thead th {
  border-top: none;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 650;
  color: var(--texte-attenue);
  background: var(--surface-2);
}
.ecran-bloc .tableau-bloc .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* La barre de separation d'un paquet, sur la page du pool : ranger agit sur TOUTE la table, la
   rangee de titre le montre. */
.ecran-bloc .tableau-bloc .ligne-table th {
  background: var(--surface-2);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--texte-attenue);
  padding: var(--espace-2) var(--espace-4);
}
.ecran-bloc .tableau-bloc .ligne-table code {
  font-family: var(--mono);
  color: var(--texte);
}
/* 🔴 PARTAGÉ AVEC LA CARTE COMPLÈTE (.ecran-carte, 21/08) -- le nom en clair d'un champ suivi de
   son nom technique est le MÊME composant sur les deux écrans (règle de l'utilisateur du 20/08 :
   un champ = UN nom back-office + le nom technique, partout). Deux jeux de règles identiques
   auraient dérivé au premier réglage ; on ajoute donc le sélecteur, on ne recopie pas le bloc. */
.ecran-bloc .champ__nom,
.ecran-carte .champ__nom { display: block; font-weight: 620; }
.ecran-bloc .champ__nom--technique,
.ecran-carte .champ__nom--technique { font-family: var(--mono); font-size: 0.84rem; font-weight: 500; }
.ecran-bloc .champ__technique,
.ecran-carte .champ__technique {
  display: block;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--texte-attenue);
}
.ecran-bloc .champ__alerte {
  display: block;
  font-size: 0.76rem;
  color: var(--alerte);
  margin-top: var(--espace-1);
  max-width: 46ch;
}
.ecran-bloc .tableau-bloc tr.hors-portee { background: var(--alerte-fond); }
.ecran-bloc .qui { font-size: 0.79rem; color: var(--texte-attenue); }
.ecran-bloc .qui strong { color: var(--accent); font-weight: 600; }

/* Les deux formulaires d'une ligne. Le regime COURANT se lit avant les boutons : sans lui, deux
   boutons cote a cote ne disent pas lequel est l'etat actuel. */
.ecran-bloc .ligne-bloc__formulaire {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin: 0;
}
.ecran-bloc .ligne-bloc__gestes { display: flex; gap: var(--espace-1); }
.ecran-bloc .regime {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--texte-attenue);
  white-space: nowrap;
}
.ecran-bloc .regime--traduire { color: var(--succes); }
.ecran-bloc .regime--jamais { color: var(--texte-attenue); }
.ecran-bloc .regime--a_decider { color: var(--alerte); }
/* Lot 4 « ré-adresser » (24/08, correctif S3 de la relecture) : sans cette règle, le jeton
   `.regime--readresser` sort SANS COULEUR (aucune erreur -- le piège CSS connu, CLAUDE.md § ÉTAT
   MESURÉ / SaaS). Ni succès (ce n'est pas une traduction) ni alerte (ce n'est pas en attente) :
   l'accent, réservé à une décision qui lui est propre. */
.ecran-bloc .regime--readresser { color: var(--accent); }
.ecran-bloc select {
  font: inherit;
  font-size: 0.82rem;
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
  max-width: 12rem;
}

/* L'effet de bord de « deplacer », dit AVANT le clic (19/08). Discret : c'est une precision, pas
   une alerte -- le geste est legitime, c'est sa PORTEE qui doit se voir. */
.ecran-bloc .ligne-bloc__entraine {
  display: block;
  width: 100%;
  font-size: 0.74rem;
  color: var(--texte-attenue);
  margin-top: var(--espace-1);
}
.ecran-bloc .ligne-bloc__entraine code { font-family: var(--mono); }

/* Le reste de la boutique (hub, 19/08) : une vignette-LISTE, pas une porte -- elle n'est pas
   cliquable en bloc, chaque famille a son lien. Meme peau que les portes, sans le curseur. */
.ecran-cadrage .vignette--reste { cursor: default; }
.ecran-cadrage .vignette__liste-blocs {
  margin: var(--espace-2) 0 0;
  padding-left: 1.1rem;
  font-size: 0.86rem;
}
.ecran-cadrage .vignette__liste-blocs li { margin-top: var(--espace-1); }

/* Le pont d'un ecran de decision vers la page des champs de son bloc (19/08). */
.pont-champs { font-size: 0.88rem; }

/* --- L'ECRAN DES MODULES, GROUPE PAR ETAT (19/08) ---------------------------------------------
   Constat de l'utilisateur : « c'est une liste tres longue -- trier par ordre alphabetique ».
   Quatre groupes en <details>, ce qui demande une action ouvert d'abord. Meme affordance de pli
   que .bandeau-etat (chevron geometrique, jamais un caractere typographique). */
/* 🔴 DÉSCOPÉ (20/08, soin visuel) : ce composant (une recherche + un bouton) est repris tel quel
   sur cinq formulaires de quatre écrans différents (modules.njk, cadrage-modules.njk, revue.njk,
   module-detail.njk -- deux fois) -- SEUL `modules.njk`/`cadrage-modules.njk` portent la classe de
   portée `.ecran-modules` : sur les deux autres écrans, la règle ne s'appliquait jamais et le
   formulaire retombait au style par défaut du navigateur, en silence (même famille que les jetons
   fantômes du 19/08 -- un sélecteur qui ne correspond à rien ne casse rien au chargement, il
   disparaît juste). Un composant voulu IDENTIQUE partout n'a pas besoin d'une classe de portée. */
.filtre-modules {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--espace-3);
  margin: var(--espace-4) 0;
}
.filtre-modules label {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  font-size: 0.78rem;
  color: var(--texte-attenue);
}
.filtre-modules input[type="search"],
.filtre-modules select {
  font: inherit;
  font-size: 0.88rem;
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  color: var(--texte);
}
.ecran-modules .groupe-modules {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-top: var(--espace-3);
}
.ecran-modules .groupe-modules--a_decider { border-left-color: var(--attente); }
.ecran-modules .groupe-modules--retenus,
.ecran-modules .groupe-modules--ecartes,
.ecran-modules .groupe-modules--rien_a_faire { border-left-color: var(--succes); }
.ecran-modules .groupe-modules--illisible { border-left-color: var(--alerte); }
.ecran-modules .groupe-modules > summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
  font-weight: 620;
}
.ecran-modules .groupe-modules > summary::-webkit-details-marker { display: none; }
.ecran-modules .groupe-modules > summary::after {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto;
  margin-right: 0.2rem;
  flex: none;
}
.ecran-modules .groupe-modules[open] > summary::after { transform: rotate(45deg); }
.ecran-modules .groupe-modules .liste-modules { margin: 0; padding: 0 var(--espace-4) var(--espace-3); }
.ecran-modules .groupe-modules__filtre { font-size: 0.78rem; font-weight: 400; }
.ecran-modules .groupe-modules__vide { padding: 0 var(--espace-4) var(--espace-3); margin: 0; }

/* --- L'ECRAN DES GABARITS, GROUPE PAR PROPRIETAIRE (lot 1 « gabarits du theme », 13/09) --------
   Classe de portee `.ecran-gabarits` (charte, section D). MEME peau et MEME affordance de pli que
   `.ecran-modules .groupe-modules` juste au-dessus -- un patron, pas un de plus (la garde de la
   charte demande de reutiliser un composant existant avant d'en inventer un). */
.ecran-gabarits .groupe-gabarits {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-top: var(--espace-3);
}
.ecran-gabarits .groupe-gabarits > summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
  font-weight: 620;
}
.ecran-gabarits .groupe-gabarits > summary::-webkit-details-marker { display: none; }
.ecran-gabarits .groupe-gabarits > summary::after {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto;
  margin-right: 0.2rem;
  flex: none;
}
.ecran-gabarits .groupe-gabarits[open] > summary::after { transform: rotate(45deg); }
.ecran-gabarits .groupe-gabarits .table__wrap { padding: 0 var(--espace-4) var(--espace-3); }

/* Une icone DANS le texte (le cadenas de « verrouille », passe charte A5 du 19/08) : elle prend la
   taille et la couleur du mot qu'elle accompagne -- c'est tout l'interet du dessin sur l'emoji. */
.icone--texte-boite { display: inline-flex; vertical-align: -0.12em; }
.icone--texte-boite .icone { width: 0.95em; height: 0.95em; }

/* Les mesures d'un bloc sur sa carte (lot UX-2, 19/08) : une ligne discrete de LIENS -- chaque
   chiffre mene a l'ecran deja filtre sur ce bloc, regle E3 de la charte (« un chiffre mene la ou
   on peut agir »). Ambre : ce sont des choses a regarder, jamais des succes. */
.mesures-bloc {
  font-size: 0.8rem;
  margin: var(--espace-2) 0 0;
}
.mesures-bloc a { color: var(--attente); font-weight: 600; }
.mesures-bloc__sep { color: var(--texte-attenue); margin: 0 var(--espace-1); }

/* Les groupes de l'ecran e-mails (20/08) : meme peau et meme affordance de pli que les groupes de
   l'ecran des modules -- un patron, pas deux. */
.groupe-emails {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-top: var(--espace-3);
}
.groupe-emails > summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
  font-weight: 620;
}
.groupe-emails > summary::-webkit-details-marker { display: none; }
.groupe-emails > summary::after {
  content: '';
  width: 0.5rem; height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto; margin-right: 0.2rem;
  flex: none;
}
.groupe-emails[open] > summary::after { transform: rotate(45deg); }
.groupe-emails .table__wrap { padding: 0 var(--espace-4) var(--espace-3); }

/* La barre de geste de groupe, a l'interieur d'un `.groupe-emails` deplie -- UNSCOPED et PARTAGEE
   (soin visuel des reglages, 21/08) : meme affordance que `.ecran-emails .decision-email__groupe`,
   mais celle-ci est empruntable par n'importe quel ecran, jamais une classe scopee empruntee a un
   seul (piege connu, CLAUDE.md -- un nom de classe emprunte a une autre portee rend un formulaire
   SANS style, en silence). */
.groupe-geste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin: var(--espace-2) 0;
  padding-bottom: var(--espace-2);
  border-bottom: 1px solid var(--bordure);
}
/* 🆕 MASQUAGE PAR SELECTION (21/08 soir) -- SCOPE A `.barre-selection-groupe` seul, jamais a
   `.groupe-geste` nu : ce dernier porte AUSSI la barre « Tout ce module » (reglages-a-trier.njk),
   qui doit rester visible en permanence. Meme piege de specificite que `.barre-selection` plus haut
   dans ce fichier -- `[hidden]` seul perdrait contre `display:flex` ci-dessus. */
.barre-selection-groupe[hidden] {
  display: none;
}

/* Le pli des reglages de module DEJA DECIDES (module-detail.njk, 20/08 -- soin visuel) : meme peau
   et meme chevron que les autres groupes depliables de l'application (.groupe-modules ci-dessus,
   .groupe-emails juste au-dessus) -- un patron, pas un de plus. Ce qui attend une decision reste
   dans une table ordinaire, toujours visible : seul ce qui est deja tranche se replie. */
.groupe-reglages {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-top: var(--espace-3);
}
.groupe-reglages > summary {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
  font-weight: 620;
}
.groupe-reglages > summary::-webkit-details-marker { display: none; }
.groupe-reglages > summary::after {
  content: '';
  width: 0.5rem; height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
  color: var(--texte-attenue);
  margin-left: auto; margin-right: 0.2rem;
  flex: none;
}
.groupe-reglages[open] > summary::after { transform: rotate(45deg); }
.groupe-reglages .table__wrap { margin-top: var(--espace-3); }

/* Les gestes de decision d'une ligne d'e-mail (lot E2b, 20/08). Sous la classe de portee
   `.ecran-emails` -- invariant de la charte (§ D) : moderniser un ecran ne repeint pas les autres.
   ⚠️ On ne REUTILISE PAS `.ligne-bloc__formulaire` : ses regles vivent sous `.ecran-bloc`, donc
   elles ne s'appliqueraient PAS ici -- un nom de classe emprunte a une autre portee rend un
   formulaire SANS style, en silence (meme famille que les jetons fantomes du 19/08). */
.ecran-emails .decision-email__formulaire {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
  margin: var(--espace-1) 0 0;
}
/* « décidé par vous, le ... » : sur sa propre ligne, sous le badge -- une date collee a un badge se
   lit comme une partie du badge. */
.ecran-emails .decision-email__qui { display: block; }

/* Le geste de GROUPE (21/08) : une ligne entre le repli et le tableau, meme famille visuelle que
   `.decision-email__formulaire` mais separee du contenu du groupe par un filet leger -- elle agit
   sur TOUT le groupe, pas sur une ligne. */
.ecran-emails .decision-email__groupe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin: var(--espace-2) 0;
  padding-bottom: var(--espace-2);
  border-bottom: 1px solid var(--bordure);
}


/* Le chapeau des portes du hub (passe de nuit 19-20/08) : l'espacement etait ecrit en dur dans le
   gabarit (style="margin-top:-0.5rem"), hors de l'echelle a six crans -- invariant A1. */
.ecran-cadrage .chapeau-portes { margin-top: calc(-1 * var(--espace-2)); }

/* L'atelier des modules a tables, replie sous la liste complete (relecture du 20/08) : le pli
   evite la double liste, le titre garde son rang visuel. */
.atelier-modules > summary { cursor: pointer; list-style: none; }
.atelier-modules > summary::-webkit-details-marker { display: none; }
.atelier-modules > summary h2 { display: inline-block; margin: 1.2rem 0 0.4rem; }
.atelier-modules > summary::before { content: '▸ '; color: var(--texte-attenue); }
.atelier-modules[open] > summary::before { content: '▾ '; }

/* -------------------------------------------------------------------------
   LA JAUGE PERSISTANTE (point 6, décision utilisateur 22/08) — «pour savoir où en est la
   traduction en cours il faut une jauge qui est persistante et visible tout le long de
   l'exécution ». Composant NON scopé (comme `.card`/`.badge`) : `views/_jauge-passages.njk`, le
   fragment que `/:pair` inclut au premier affichage et que `GET /:pair/passages-actifs` renvoie
   ensuite, tel quel, à chaque rafraîchissement htmx.
   Jetons EXISTANTS seulement : `--attente` pour le remplissage (même couleur que
   `.badge--attente` -- un passage « en cours » est une attente active, jamais une réussite ni une
   alerte), `--surface-2`/`--bordure` pour la piste et le cadre (mêmes jetons que `.card`/`.champ`).
   ------------------------------------------------------------------------- */
.jauge-passage {
  padding: var(--espace-3);
  margin: 0 0 var(--espace-2);
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-sm);
}

.jauge-passage + .jauge-passage {
  margin-top: var(--espace-2);
}

.jauge-passage__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-2);
  flex-wrap: wrap;
}

.jauge-passage__libelle {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--texte);
}

/* Piste fine (même hauteur que `.univers-entete__barre`, section 11) -- un remplissage unique,
   jamais plusieurs segments : cette jauge dit UN progrès, pas une répartition. */
.jauge-passage__barre {
  height: 8px;
  margin: var(--espace-2) 0 0;
  border-radius: 999px;
  overflow: hidden;
  background: var(--fond);
}

.jauge-passage__remplissage {
  display: block;
  height: 100%;
  background: var(--attente);
  transition: width 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
  .jauge-passage__remplissage {
    transition: none;
  }
}

.jauge-passage__compte {
  margin: var(--espace-1) 0 0;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   19. LA CONFIRMATION AVANT UNE TRADUCTION EN MASSE (lot « pop-up de confirmation », 27/08) --
   décision utilisateur : l'ancienne page d'annonce (« tu es incapable de faire des prévisions [...]
   c'est du pipo ») disparaît, remplacée par une confirmation COURTE -- une <dialog> native sur
   `views/produits.njk`, jamais une page à part. Composant NON scopé (même patron que
   `.jauge-passage`, juste au-dessus) : jetons EXISTANTS seulement (`.btn`/`.btn--primary` pour les
   actions) -- rien n'est réinventé. Le voile (`::backdrop`) reprend le meme noir translucide que
   `--ombre` (jetons "1. Jetons" plus haut) emploie déjà pour ses deux variantes, clair ET sombre :
   un assombrissement n'est pas une couleur de palette a inverser, invariant A4 (charte-ux.html).
   ------------------------------------------------------------------------- */
.dialogue-confirmation {
  max-width: 30rem;
  width: calc(100% - var(--espace-6));
  padding: var(--espace-5);
  background: var(--surface);
  color: var(--texte);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
}

.dialogue-confirmation::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.dialogue-confirmation h2 {
  margin-top: 0;
}

.dialogue-confirmation__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--espace-3);
  margin-top: var(--espace-5);
}

/* =========================================================================
   20. LES FICHES DISPARUES -- portee .ecran-fiches-disparues (02/09)

   Reorganisation d'AFFICHAGE seule (constat de l'utilisateur : « le bouton est tout en bas [...]
   il faut aller chercher deux fois en bas [...] beaucoup de texte, c'est tres touffu »). Aucune
   couleur inventee : `.decision` REPREND `.alert`/`.alert--warn`/`.alert--ok` tels quels (charte :
   reutiliser avant d'inventer) et n'ajoute que la mise en page (nombre en gros, encadre de seuil,
   formulaire) ; la rangee de chiffres REPREND le patron `.mesures`/`.mesure` deja en service sous
   `.ecran-bloc` (section 18) -- meme mecanique de portee, aucune des deux ne peut atteindre l'autre
   ecran.
   ========================================================================= */
.ecran-fiches-disparues .decision {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.ecran-fiches-disparues .decision p {
  margin: 0;
}
.ecran-fiches-disparues .decision__tete {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.ecran-fiches-disparues .decision__nombre {
  font-size: 1.85rem;
  font-weight: 650;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.ecran-fiches-disparues .decision__quoi {
  font-size: 1rem;
  font-weight: 600;
}
.ecran-fiches-disparues .decision__seuil {
  padding: var(--espace-3);
  border-radius: var(--rayon-sm);
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  color: var(--texte);
  font-size: 0.88rem;
}
.ecran-fiches-disparues .decision form {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  flex-wrap: wrap;
  margin: 0;
}

/* --- LA RANGEE DE CHIFFRES -- meme patron que `.ecran-bloc .mesures` (section 18) ------------- */
.ecran-fiches-disparues .mesures {
  display: flex;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  overflow: hidden;
  margin-bottom: var(--espace-4);
}
.ecran-fiches-disparues .mesure {
  flex: 1 1 9rem;
  padding: var(--espace-3) var(--espace-4);
  border-left: 1px solid var(--bordure);
}
.ecran-fiches-disparues .mesure:first-child {
  border-left: none;
}
.ecran-fiches-disparues .mesure__gros {
  font-size: 1.35rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.ecran-fiches-disparues .mesure__libelle {
  font-size: 0.78rem;
  color: var(--texte-attenue);
  margin-top: var(--espace-1);
}
.ecran-fiches-disparues .mesure__note {
  font-size: 0.72rem;
  color: var(--texte-attenue);
  margin-top: 0.15rem;
}

/* =========================================================================
   21. CE QUI A ÉCHOUÉ -- portée .ecran-echecs-ecriture (lot Q, 10/09, fiche 8-003)

   Maquette validée : docs/design/maquette-ce-qui-a-echoue.html. Réutilise `.card`/`.table`/
   `.table__wrap`/`.badge`/`.btn` tels quels (charte : réutiliser avant d'inventer) ; `.decision*`
   reprend le PATRON déjà posé pour .ecran-fiches-disparues (section 20, même raison : un nombre en
   gros suivi d'une phrase) -- copie propre à cet écran, comme .ecran-fiches-disparues l'est déjà de
   .ecran-bloc, pour ne jamais faire dépendre deux écrans l'un de l'autre. Seuls trois éléments sont
   vraiment nouveaux ici, absents de partout ailleurs dans ce fichier : la grille des CAUSES (clé de
   lecture de l'écran, l'utilisateur voit « pourquoi » avant « quoi »), les puces de FILTRE (liens
   simples, aria-pressed -- aucun JavaScript : le filtre est une navigation, pas un état client), et
   la pastille de TYPE de ressource (fiche/catégorie/page/module...).
   ========================================================================= */
.ecran-echecs-ecriture .decision {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: var(--espace-4);
}
.ecran-echecs-ecriture .decision__tete {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.ecran-echecs-ecriture .decision__nombre {
  font-size: 1.85rem;
  font-weight: 650;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.ecran-echecs-ecriture .decision__quoi {
  font-size: 1rem;
  font-weight: 600;
  color: var(--texte-attenue);
}
.ecran-echecs-ecriture .decision p {
  margin: 0;
  font-size: 0.9rem;
}

/* La grille des causes -- un accent de couleur à GAUCHE (border-left) plutôt qu'un badge plein sur
   chaque carte : à cette densité (jusqu'à une dizaine de causes), un aplat de couleur répété
   fatiguerait l'œil plus qu'il n'aiderait à lire. Deux natures SEULEMENT (le vrai vocabulaire de
   presentation/ce-qui-a-echoue.ts est binaire : `attend_geste`/`se_regle_seul`) -- la maquette en
   montrait quatre, à titre d'illustration sur des données FICTIVES ; ce dépôt n'en connaît que deux. */
.ecran-echecs-ecriture .grille-causes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--espace-3);
}
.ecran-echecs-ecriture .cause-carte {
  border: 1px solid var(--bordure);
  border-left-width: 3px;
  border-radius: var(--rayon-sm);
  padding: var(--espace-3);
  background: var(--surface-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ecran-echecs-ecriture .cause-carte b {
  font-size: 1.25rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.ecran-echecs-ecriture .cause-carte span {
  font-size: 0.8rem;
  color: var(--texte-attenue);
}
.ecran-echecs-ecriture .cause-carte--geste {
  border-left-color: var(--erreur);
}
.ecran-echecs-ecriture .cause-carte--geste b {
  color: var(--erreur);
}
.ecran-echecs-ecriture .cause-carte--seul {
  border-left-color: var(--attente);
}
.ecran-echecs-ecriture .cause-carte--seul b {
  color: var(--attente);
}

/* Les puces de filtre -- des LIENS (navigation par adresse, jamais un état JS), même famille
   visuelle que `.filtre-produits__case` (accent au survol) mais forme "pilule" complète, plus proche
   de la maquette validée. `[aria-pressed="true"]` porte le fond plein -- posé par le gabarit selon
   le filtre réellement actif dans l'URL, jamais deviné côté client. */
.ecran-echecs-ecriture .filtres {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin-bottom: var(--espace-4);
}
.ecran-echecs-ecriture .filtre {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid var(--bordure);
  background: var(--surface);
  color: var(--texte);
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
  font-size: 0.82rem;
  text-decoration: none;
}
.ecran-echecs-ecriture .filtre:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.ecran-echecs-ecriture .filtre[aria-pressed="true"] {
  background: var(--neutre-actif);
  color: var(--neutre-actif-texte);
  border-color: var(--neutre-actif);
}
.ecran-echecs-ecriture .filtre b {
  font-variant-numeric: tabular-nums;
  font-weight: 650;
}
.ecran-echecs-ecriture .filtre-sep {
  width: 1px;
  align-self: stretch;
  background: var(--bordure);
  margin: 0 var(--espace-1);
}

/* La pastille de TYPE (« Fiche », « Module »…) -- même vocabulaire de couleur que `.vignette__icone`/
   `.bandeau-etat--info` (accent-fond + accent, un signal INFORMATIONNEL, ni succès ni alerte). */
.ecran-echecs-ecriture .type-ressource {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 650;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-fond);
  color: var(--accent);
  white-space: nowrap;
}

.ecran-echecs-ecriture .pied {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--espace-3);
  flex-wrap: wrap;
  margin-top: var(--espace-3);
}

/* -------------------------------------------------------------------------
   19. L'ECRAN DE LA REVUE (.ecran-revue) -- LES CHIFFRES DE TETE DU JUGE
   ------------------------------------------------------------------------- */
/* 7-313 (12/09) -- SORTIS DE L'INLINE STYLE (`style="font-size:..."` dans views/revue.njk),
   charte-ux.html nommait cet ecran comme le seul a coder une taille de police en dur dans la page
   (ecart n8). MEME hierarchie visuelle qu'avant (rien d'autre ne change) : le grand chiffre
   (--taille-chiffre-cle, 2.5rem) reste le plus gros caractere de l'ecran, le chiffre secondaire
   (--taille-chiffre-secondaire, 1.4rem) reste sous lui, et la legende de chacun revient a la
   taille normale du texte (1rem, litteral comme h3 juste plus haut -- aucun jeton necessaire pour
   « la taille par defaut »).
   Scope sous .ecran-revue (7-300) : ces deux classes n'existent que sur cet ecran (verifie), la
   portee n'a donc aucun effet pratique aujourd'hui -- elle suit simplement la convention. */
.ecran-revue .juge-chiffre {
  font-size: var(--taille-chiffre-cle);
  font-weight: 700;
  margin: 0.2em 0;
}
.ecran-revue .juge-chiffre__legende {
  font-size: 1rem;
  font-weight: 400;
}
.ecran-revue .juge-connu-non-couvert {
  font-size: var(--taille-chiffre-secondaire);
  font-weight: 600;
  margin: 0.2em 0;
}
.ecran-revue .juge-connu-non-couvert__legende {
  font-size: 1rem;
  font-weight: 400;
}
