/* Charte FRIXY — reprise de la maquette docs/remixed-*.html + extensions
   (onglets QoS / Rentabilité, tables, login, admin). */

/* ═══════════════════════════════════════════════════════════════════════════
   `hidden` CACHE. Une bonne fois.

   ⚠ **Ce défaut s'est produit TROIS fois**, sur trois éléments sans rapport :
   `.lire-tout` (v1.20.0), `.form-ajout .champ` (v1.23.0), `.obs-detail`
   (v1.25.0). À chaque fois le même enchaînement : on pose un `display` sur une
   classe, et l'attribut `hidden` de cet élément cesse d'agir — parce que
   `[hidden]{display:none}` vient de la feuille du NAVIGATEUR, et que toute
   déclaration d'auteur la bat, quelle que soit sa spécificité.

   Le symptôme est traître : le DOM dit `hidden`, `aria-expanded` dit `false`,
   les tests qui lisent la propriété passent au vert — et l'élément est à
   l'écran. Deux fois sur trois, c'est une capture qui l'a montré, pas le banc.

   Six règles au cas par cas corrigeaient chaque occurrence après coup. Elles
   demandaient de se souvenir, à chaque nouvel élément masquable, d'une règle
   qu'on n'apprend qu'en se faisant avoir. Celle-ci n'a rien à retenir.

   ⛔ `!important` est ici le bon outil et non un aveu : un élément qui porte
   `hidden` ET veut s'afficher est une contradiction, pas un cas d'usage. Aucun
   `display` d'auteur n'a de raison légitime de gagner contre lui.
   ═══════════════════════════════════════════════════════════════════════════ */
[hidden] { display: none !important; }
:root {
  --green: #1a5c3a;
  --green-light: #2a7a4f;
  --green-pale: #e8f2ec;
  --green-mid: #c2dece;
  --red: #c0392b;
  --red-pale: #fdf0ee;
  --gold: #b8860b;
  --gold-pale: #fdf8e8;
  --text: #1a1a1a;
  --text-mid: #555;
  /* ⚠ #888 échouait AA (3,4:1) sur fond clair, et servait à une vingtaine
     d'endroits. Corrigé ICI plutôt que ligne par ligne : laisser le jeton
     fautif en place, c'est attendre que quelqu'un le réutilise de bonne foi. */
  --text-light: #6b7671;
  /* Une absence ÉCRITE porte de l'information — elle n'a pas droit au gris
     tertiaire. « sans échéance » doit se lire aussi bien qu'une date. */
  --text-absent: #5f6b65;
  --border: #e2e8e4;
  --bg: #f7faf8;
  --white: #ffffff;
  --shadow: 0 1px 3px rgba(26, 92, 58, 0.08), 0 4px 16px rgba(26, 92, 58, 0.06);
  --shadow-hover: 0 4px 12px rgba(26, 92, 58, 0.12), 0 8px 32px rgba(26, 92, 58, 0.08);

  /* ── Coque : nav latérale + en-tête de page (handoff design) ──
     Ces jetons REMPLACENT les précédents là où ils se recouvrent : le fond
     passe de #f7faf8 à un crème, l'encre et le rouge s'assombrissent. Tous
     les écrans changent donc d'aspect, pas seulement la coque — c'était le
     coût annoncé du handoff, pas un effet de bord. */
  --green-deep: #0f3d26;
  --green-soft: #e9f2ec;
  --green-line: #cfe2d6;
  --cream: #faf8f3;
  --ink: #152019;
  --ink-2: #4a5a51;
  --ink-3: #8a978f;
  --line: #e4eae6;
  --red-deep: #b42318;
  --red-soft: #fdeceb;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: "DM Sans", sans-serif;
  background: var(--cream);
  color: var(--ink);
  min-height: 100vh;
}

/* HEADER */
header {
  background: var(--white);
  border-bottom: 1px solid var(--border);
  padding: 20px 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 1px 0 var(--border);
}
.logo { display: flex; align-items: center; gap: 12px; }
.logo-icon {
  width: 36px; height: 36px; background: var(--green); border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
}
.logo-icon svg { width: 20px; height: 20px; fill: white; }
.logo-text {
  font-family: "DM Serif Display", serif; font-size: 22px;
  color: var(--green); letter-spacing: -0.3px;
}
.header-meta { display: flex; align-items: center; gap: 16px; }
.header-date { font-size: 13px; color: var(--text-light); }
.header-badge {
  background: var(--green-pale); color: var(--green); font-size: 11px;
  font-weight: 600; padding: 4px 10px; border-radius: 20px;
  letter-spacing: 0.5px; text-transform: uppercase;
}

/* Boutons */
.btn {
  font-family: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  padding: 8px 16px; border-radius: 20px; border: 1.5px solid var(--green);
  background: var(--green); color: #fff; transition: all 0.2s;
}
.btn:hover { background: var(--green-light); }
.btn.secondary { background: #fff; color: var(--green); }
.btn.secondary:hover { background: var(--green-pale); }
.btn:disabled { opacity: 0.5; cursor: progress; }

/* MAIN — la mise en page vit désormais sur `.page`, dans la grille de la
   coque. Cette règle ne garde qu'un défaut inoffensif pour les pages qui
   n'ont pas de coque (login, admin). */
main:not(.page) { max-width: 1300px; margin: 0 auto; padding: 28px 40px 0; }
.section-title {
  font-family: "DM Serif Display", serif; font-size: 13px; color: var(--text-light);
  text-transform: uppercase; letter-spacing: 2px; margin-bottom: 16px;
}

/* Onglets (niveau supérieur + périodes) */
.nav-tabs, .period-tabs {
  display: flex; gap: 8px; margin-bottom: 24px; flex-wrap: wrap;
}
.nav-tab, .period-tab {
  padding: 8px 20px; border-radius: 24px; border: 1.5px solid var(--border);
  background: var(--white); font-size: 13px; font-weight: 500;
  color: var(--text-mid); cursor: pointer; transition: all 0.2s;
}
.nav-tab.active, .period-tab.active {
  background: var(--green); border-color: var(--green); color: #fff;
}
.nav-tab:hover:not(.active), .period-tab:hover:not(.active) {
  border-color: var(--green); color: var(--green);
}

.view { display: none; }
.view.active { display: block; }

/* KPI GRID */
.kpi-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px;
}
.kpi-card {
  background: var(--white); border-radius: 16px; padding: 24px;
  border: 1px solid var(--border); box-shadow: var(--shadow);
  transition: box-shadow 0.2s, transform 0.2s; position: relative; overflow: hidden;
}
.kpi-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-1px); }
.kpi-card.highlight { background: var(--green); border-color: var(--green); }
.kpi-card.highlight .kpi-label, .kpi-card.highlight .kpi-period { color: rgba(255,255,255,0.7); }
.kpi-card.highlight .kpi-value { color: #fff; }
.kpi-card.highlight .kpi-evolution { background: rgba(255,255,255,0.15); }
.kpi-card.highlight .kpi-icon { background: rgba(255,255,255,0.2); }
.kpi-icon {
  width: 36px; height: 36px; border-radius: 10px; background: var(--green-pale);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px; font-size: 16px;
}
.kpi-label {
  font-size: 12px; font-weight: 500; color: var(--text-light);
  text-transform: uppercase; letter-spacing: 0.8px; margin-bottom: 8px;
}
.kpi-value {
  font-family: "DM Serif Display", serif; font-size: 32px; color: var(--green);
  line-height: 1; margin-bottom: 10px; letter-spacing: -1px;
}
.kpi-footer { display: flex; align-items: center; justify-content: space-between; }
.kpi-period { font-size: 12px; color: var(--text-light); }
.kpi-evolution {
  display: inline-flex; align-items: center; gap: 3px; font-size: 12px;
  font-weight: 600; padding: 3px 8px; border-radius: 12px;
}
.kpi-evolution.up { background: #e8f5e9; color: #2e7d32; }
.kpi-evolution.down { background: var(--red-pale); color: var(--red); }
.kpi-evolution.neutral { background: var(--gold-pale); color: var(--gold); }

/* Lignes de cartes */
.row-2 { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; margin-bottom: 28px; }

.chart-card, .canal-card, .panel-card {
  background: var(--white); border-radius: 16px; padding: 28px;
  border: 1px solid var(--border); box-shadow: var(--shadow);
}
.chart-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; }
.chart-title { font-family: "DM Serif Display", serif; font-size: 17px; color: var(--text); }
.chart-legend { display: flex; gap: 16px; }
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-light); }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; }

/* BAR CHART */
.bar-chart {
  display: flex; align-items: flex-end; gap: 8px; height: 170px;
  padding-bottom: 28px; position: relative;
}
.bar-chart::after {
  content: ""; position: absolute; bottom: 28px; left: 0; right: 0;
  height: 1px; background: var(--border);
}
.bar-group {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  gap: 4px; position: relative;
}
.bar-wrap { width: 100%; display: flex; align-items: flex-end; gap: 3px; height: 130px; }
.bar-col { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; }
.bar { flex: 1; border-radius: 4px 4px 0 0; transition: opacity 0.2s; cursor: pointer; position: relative; }
/* Dans la colonne CA, les barres respectent leur hauteur inline (pas de flex-grow
   vertical, sinon flex-basis:0% écrase la hauteur -> barre CA invisible). */
.bar-col > .bar, .bar-col > .bar-forecast { flex: none; width: 100%; }
.bar:hover { opacity: 0.85; }
.bar-ca { background: var(--green); }
.bar-cmd { background: var(--green-mid); }
/* Prévisionnel : segment hachuré au-dessus du réel. */
.bar-forecast {
  background: repeating-linear-gradient(45deg, var(--green-mid), var(--green-mid) 3px, transparent 3px, transparent 6px);
  border: 1.5px dashed var(--green); border-bottom: none; border-radius: 4px 4px 0 0; position: relative;
}
.bar-label {
  font-size: 10px; color: var(--text-light); text-align: center; position: absolute;
  bottom: -22px; left: 50%; transform: translateX(-50%); white-space: nowrap;
}
.bar-value {
  position: absolute; top: -16px; left: 50%; transform: translateX(-50%);
  font-size: 9px; font-weight: 600; color: var(--green); white-space: nowrap;
}
.bar-value-cmd { color: var(--text-light); }
.bar-value-forecast { color: var(--green-light); font-style: italic; }
/* Dates inclinées (vue quotidienne, 30 barres). */
.bar-chart.rotated-labels { padding-bottom: 40px; }
.bar-chart.rotated-labels .bar-label {
  transform: translateX(-50%) rotate(-45deg); transform-origin: top center;
  bottom: -30px; font-size: 9px;
}
.bar-chart.rotated-labels::after { bottom: 40px; }
/* Toggle prévisionnel dans l'en-tête de période. */
.period-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.forecast-toggle {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-mid);
  cursor: pointer; user-select: none; margin-bottom: 16px;
}
.forecast-toggle input { width: 16px; height: 16px; accent-color: var(--green); cursor: pointer; }
.forecast-note { font-size: 12px; color: var(--text-light); margin-left: 6px; }

/* DONUT CANAUX */
.canal-card { display: flex; flex-direction: column; }
.donut-container { display: flex; flex-direction: column; align-items: center; gap: 24px; flex: 1; justify-content: center; }
.donut-wrap { position: relative; width: 140px; height: 140px; }
.donut-wrap svg { transform: rotate(-90deg); width: 140px; height: 140px; }
.donut-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; }
.donut-center-val { font-family: "DM Serif Display", serif; font-size: 22px; color: var(--green); line-height: 1; }
.donut-center-label { font-size: 10px; color: var(--text-light); margin-top: 2px; }
.canal-list { width: 100%; display: flex; flex-direction: column; gap: 12px; }
.canal-item-wrap { display: flex; flex-direction: column; }
.canal-item { display: flex; align-items: center; gap: 10px; }
.canal-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.canal-name { font-size: 13px; color: var(--text-mid); flex: 1; }
.canal-pct { font-size: 13px; font-weight: 600; color: var(--text); }
.canal-bar-wrap { width: 100%; height: 4px; background: var(--border); border-radius: 2px; margin-top: 4px; }
.canal-bar-fill { height: 100%; border-radius: 2px; transition: width 1s ease; }

/* CUMUL */
.cumul-card {
  background: var(--green); border-radius: 16px; padding: 28px; border: none;
  box-shadow: 0 4px 20px rgba(26, 92, 58, 0.2); color: #fff; margin-bottom: 28px;
}
.cumul-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.5px; opacity: 0.7; margin-bottom: 8px; }
.cumul-value { font-family: "DM Serif Display", serif; font-size: 42px; line-height: 1; letter-spacing: -2px; }
.cumul-since { font-size: 12px; opacity: 0.6; margin-top: 4px; }
.cumul-stats { display: flex; gap: 32px; margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.2); }
.cumul-stat-val { font-family: "DM Serif Display", serif; font-size: 22px; }
.cumul-stat-label { font-size: 11px; opacity: 0.6; margin-top: 2px; }

/* TABLES (QoS / Rentabilité / Admin) */
table.data { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th, table.data td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); }
table.data th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-light); font-weight: 600; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data tr:hover td { background: var(--green-pale); }
.pill { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
.pill.good { background: #e8f5e9; color: #2e7d32; }
.pill.warn { background: var(--gold-pale); color: var(--gold); }
/* « N'a pas su conclure » — ni échec (rouge), ni geste attendu (orange) :
   le gris de l'indéterminé, la même famille que « à vérifier ». */
.pill.sans-verdict {
  background: transparent; color: var(--rv-gris, #8a8a8a);
  border: 1px dashed var(--rv-bord, #d0d0d0);
}
.pill.bad { background: var(--red-pale); color: var(--red); }
.muted { color: var(--text-light); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 28px; }

/* FOOTER */
.dashboard-footer {
  max-width: 1300px; margin: 8px auto 0; padding: 0 40px; font-size: 11px;
  color: var(--text-light); display: flex; gap: 16px; align-items: center;
}
.footer-dot { width: 6px; height: 6px; border-radius: 50%; background: #4caf50; animation: pulse 2s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

/* LOGIN */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login-card { background: #fff; border-radius: 16px; box-shadow: var(--shadow); padding: 40px; width: 100%; max-width: 380px; }
.login-card .logo { justify-content: center; margin-bottom: 24px; }
.login-card h1 { font-family: "DM Serif Display", serif; font-size: 22px; color: var(--text); text-align: center; margin-bottom: 4px; }
.login-card p.sub { font-size: 13px; color: var(--text-light); text-align: center; margin-bottom: 24px; }
.field { margin-bottom: 16px; }
.field label { display: block; font-size: 12px; font-weight: 500; color: var(--text-mid); margin-bottom: 6px; }
.field input {
  width: 100%; padding: 10px 12px; border: 1.5px solid var(--border); border-radius: 10px;
  font-family: inherit; font-size: 14px;
}
.field input:focus { outline: none; border-color: var(--green); }
.btn.block { width: 100%; padding: 12px; }
.error-msg { color: var(--red); font-size: 13px; margin-bottom: 12px; min-height: 18px; }
/* Confirmation d'écriture. Elle porte un fond, contrairement à `.error-msg` :
   un arbitrage réussi ne change RIEN d'autre à l'écran tant que le recalcul
   n'a pas tourné, donc cette ligne est le seul retour. En gris discret, elle
   passait inaperçue et l'opération réussie se lisait comme un échec. */
.ok-msg {
  color: var(--green); background: var(--green-pale); border: 1px solid var(--green-mid);
  font-size: 13px; font-weight: 600; padding: 10px 12px; border-radius: 6px; margin-bottom: 12px;
}

/* États de chargement */
.loading { color: var(--text-light); font-size: 13px; padding: 24px; text-align: center; }

@media (max-width: 900px) {
  main { padding: 20px; }
  header { padding: 16px 20px; flex-wrap: wrap; gap: 12px; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .row-2, .grid-2 { grid-template-columns: 1fr; }
}

/* ---------- Barre d'outils (période + exports) ---------- */
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 20px;
}
.toolbar-group {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.toolbar label {
  font-size: 13px;
  color: var(--text-light);
  display: flex;
  align-items: center;
  gap: 6px;
}
.toolbar input[type="date"] {
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font: inherit;
}

/* ---------- Impression / PDF ---------- */
@media print {
  header .btn,
  #logout-btn,
  #refresh-btn,
  #admin-link,
  .toolbar-group:last-child,
  .nav-tabs,
  .forecast-toggle {
    display: none !important;
  }
  /* En impression, on déplie toutes les vues pour un rapport complet. */
  .view { display: block !important; page-break-inside: avoid; }
  body { background: #fff; }
  .chart-card, .canal-card, .panel-card, .kpi-card, .cumul-card {
    box-shadow: none;
    border: 1px solid #ddd;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Registre — langage visuel de `docs/specs/maquettes.html` (section 1).
   
   ⚠ TOUT EST SCOPÉ SOUS `.reg-v2`, et ce n'est pas de la prudence décorative :
   la maquette et cette feuille emploient les MÊMES noms pour des choses
   différentes. `.pill` y est une bande de couleur de 9 px, ici c'est le badge
   de tout le tableau de bord ; `.bar` y est la barre de titre d'une fenêtre,
   ici c'est une barre de graphe. Reprendre les sélecteurs nus repeindrait
   quatre écrans qui n'ont rien demandé.
   
   Les noms de classes du BALISAGE restent verbatim (`b`, `fait`, `dt`, `lnks`,
   `m`, `act`, `ref`) : c'est le contrat visuel de la maquette. Seuls les
   sélecteurs sont préfixés.
   
   Valeurs hexadécimales recopiées de la maquette, jamais approchées.
   ═══════════════════════════════════════════════════════════════════════════ */

.reg-v2 {
  --rv-vert: #1B5E4A; --rv-vert-clr: #2E8B6F; --rv-accent: #E88B2A; --rv-rouge: #C0392B;
  --rv-encre: #1A1D1F; --rv-gris: #5A6368; --rv-gris-clr: #ECEFF0; --rv-bord: #DCE1E3;
  --rv-carte: #FFFFFF;

  /* ── Couche 2 : SÉVÉRITÉ des six badges d'état ──
     Quatre niveaux, pas six couleurs : la couleur porte la sévérité, le mot
     porte l'identité. Deux règles à ne pas « corriger » de bonne foi :

     ⚠ `a_arbitrer` et `a_revalider` PARTAGENT l'ambre. Même urgence, geste
       différent — c'est le mot qui les sépare. Deux teintes feraient croire à
       deux niveaux de gravité.
     ⚠ `declaratif` est un CONTOUR VERT, pas un badge bleu. Le bleu est
       réservé au travail en cours ; l'employer ici ferait lire « déclaré
       fait » comme un état transitoire. Contour vide = même sévérité que
       `fait`, preuve en moins. Un canal de plus sans un mot de plus. */
  --sev-defaut-fond: #F8DAD6;    --sev-defaut-encre: #A5291B;
  --sev-attention-fond: #FCEBD5; --sev-attention-encre: #8A4F08;
  --sev-afaire-fond: #ECEFF0;    --sev-afaire-encre: #4D5459;
  --sev-acquis-fond: #DCF0E7;    --sev-acquis-encre: #15694F;
  --sev-acquis-bord: #A9D5C2;

  /* ── Couche 3 : marque de vérification & méthode ──
     UN seul bleu : la distinction en file / prise passe par le glyphe, pas
     par une nuance ni par une vitesse — voir MARQUES_VERIFICATION. */
  --encours: #1C4F82;
  --echec: #C0392B;
  --modele: #8A4F08;
  --modele-bord: #D9A45F;
}
/* ⛔ **EN VEILLE, et ce n'est pas un abandon.** Ces couleurs étaient déclenchées
   par `prefers-color-scheme: dark` — mais SEUL `.reg-v2` en avait. Le reste de
   l'écran (50 variables, 75 couleurs en dur) n'a aucun traitement sombre, et
   `--bg: #f7faf8` reste crème quoi qu'il arrive. Un navigateur en préférence
   sombre affichait donc un ÎLOT noir dans une page claire : une tuile blanche
   à côté d'une tuile brune, une ligne de détail en bande noire sur du crème.
   Vérifié en capture, pas déduit.

   Un mode sombre à moitié est pire que pas de mode sombre : il ne se choisit
   pas, il s'impose à qui a réglé son système, et personne côté clair ne le voit
   jamais.

   Le travail de couleur est GARDÉ — il resservira tel quel — mais il attend
   maintenant un `data-theme="dark"` posé explicitement, que rien ne pose encore.
   Le jour où le reste de l'écran aura ses pas sombres, on rebranche la requête
   média ici, en une ligne. `tests/styles-sombre.test.ts` empêche l'îlot de
   revenir entre-temps. */
:root[data-theme="dark"] .reg-v2 {
  --rv-encre: #E8ECEE; --rv-gris: #9AA4AA; --rv-gris-clr: #232A2E; --rv-bord: #333C41;
  --rv-carte: #1B2124; --rv-vert: #5FBF9C; --rv-vert-clr: #3E9E7E;

  --sev-defaut-fond: #3D1C17;    --sev-defaut-encre: #F09585;
  --sev-attention-fond: #3B2A11; --sev-attention-encre: #E8B978;
  --sev-afaire-fond: #232A2E;    --sev-afaire-encre: #9AA4AA;
  --sev-acquis-fond: #16382C;    --sev-acquis-encre: #7FD9B4;
  --sev-acquis-bord: #2F6B54;
  --encours: #7FB2E8;
  --echec: #F09585;
  --modele: #E8B978;
  --modele-bord: #8A6A38;
}

/* ⚠ `fixed` — sans lui, les largeurs déclarées sur les `<th>` ne sont que des
   SUGGESTIONS : le navigateur les écrase au profit du contenu le plus large
   qui refuse de se couper. Mesuré à sept colonnes : « Échéance », en `nowrap`,
   se servait 174 px et la colonne Action tombait à 130 px — des titres sur six
   lignes, des lignes de 262 px de haut. Les pourcentages ne décidaient rien. */
/* ⚠ `min-width` MESURÉ, pas choisi : le bouton scindé « Vérifier ▾ » demande
   125 px avec ses gouttières, soit 13 % d'un tableau de 962 px. En dessous,
   il débordait de sa cellule — ce qui se voyait comme un rendu cassé. Le
   tableau défile alors dans `.scroll`, ce qui est le comportement honnête :
   il est réellement plus large que la fenêtre. Écraser les colonnes jusqu'à
   ce que les boutons sortent de leur case ne l'est pas. */
.reg-v2 table {
  width: 100%; min-width: 965px; table-layout: fixed;
  border-collapse: collapse; font-size: 13.5px;
}
.reg-v2 th {
  text-align: left; font-size: 11px; letter-spacing: .6px; text-transform: uppercase;
  color: var(--rv-gris); font-weight: 700; padding: 10px 11px;
  border-bottom: 1px solid var(--rv-bord);
}
/* 11 px et non 14 : à sept colonnes, les six gouttières coûtaient 36 px de
   contenu — pris sur les colonnes qui portent du texte. */
.reg-v2 td { padding: 11px 11px; border-bottom: 1px solid var(--rv-bord); vertical-align: top; }
.reg-v2 tr:last-child td { border-bottom: 0; }
.reg-v2 td.act { font-weight: 600; }
.reg-v2 td .ref {
  display: block; font-size: 11.5px; color: var(--rv-gris); font-weight: 400; margin-top: 2px;
}

/* Badges d'état. `.b` et non `.pill` : la maquette réserve `.pill` à autre chose. */
.reg-v2 .b {
  display: inline-block; font-size: 11.5px; font-weight: 700; padding: 3px 9px;
  border-radius: 5px; white-space: nowrap;
}
.reg-v2 .fait { background: var(--sev-acquis-fond); color: var(--sev-acquis-encre); }
/* ⚠ **L'ABSENCE d'état, pas un état.** Les six états portent une teinte de
   sévérité ; celui-ci n'en a pas et s'entoure d'un trait discontinu — la seule
   chose qui distingue encore « à vérifier » de « À faire », depuis que les deux
   se lisent pareil. Sans ça, un calcul jamais lancé se lirait comme un verdict
   rendu. */
.reg-v2 .sans-etat {
  background: transparent; color: var(--rv-gris);
  border: 1px dashed var(--rv-bord); font-weight: 600;
}

.reg-v2 .afaire {
  background: var(--sev-afaire-fond); color: var(--sev-afaire-encre);
  border: 1px solid var(--rv-bord);
}
/* « Corrigé — en attente de re-vérification » : l'intermédiaire entre le
   correctif appliqué et l'observation qui le constatera. Fond attention
   (orange doux), PAS le vert de « fait » — « fait » est réservé au
   re-vérifié ; le libellé peut wrapper, il est plus long qu'un badge. */
.reg-v2 .corrige-attente {
  background: var(--sev-attention-fond); color: var(--sev-attention-encre);
  white-space: normal; line-height: 1.3; cursor: help;
}
.reg-v2 .defait { background: var(--sev-defaut-fond); color: var(--sev-defaut-encre); }
.reg-v2 .arb { background: var(--sev-attention-fond); color: var(--sev-attention-encre); }
/* ⚠ Le SEUL badge sans aplat — donc le seul qu'une règle écrite pour cinq
   fonds oublie, et qui passe alors en blanc sur blanc. Il a son cas de test. */
.reg-v2 .decl {
  background: var(--rv-carte); color: var(--sev-acquis-encre);
  border: 1px solid var(--sev-acquis-bord);
}

/* ── Cellule ÉTAT : badge et marque CÔTE À CÔTE ──
   `nowrap` n'est pas cosmétique : une cellule qui gagne une ligne selon
   l'état désaligne tout le tableau, et la ligne la plus chargée est celle
   qu'on regarde le moins bien. */
/* ── Cellule ÉTAT : deux lignes, une question ──
   Ligne 1 : ce qui EST (badge) et ce qui bouge (marque), côte à côte, jamais
   repliés — une cellule qui gagne une ligne selon l'état désaligne tout le
   tableau. Ligne 2 : PAR QUOI on l'a établi. */
.reg-v2 .cel-etat { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; min-width: 0; }
.reg-v2 .cel-etat-l1 { display: flex; align-items: center; gap: 7px; flex-wrap: nowrap; min-width: 0; }
/* ⛔ Le CTA de ligne DÉBORDAIT sur la colonne voisine, et le plus important
   était le plus touché : « Lire la réponse à votre question » (31 caractères)
   se superposait à « aucune échéance ». Un appel à l'action qui se confond
   avec du texte gris ne se détache plus — il devient du bruit, et on passe à
   côté de la seule chose qui demandait un geste. Mesuré à l'image le 27/08.

   ⚠ Le repli est autorisé ICI SEULEMENT — la ligne du registre. `.cta-action`
   garde son `nowrap` partout ailleurs, et `.mini` n'est pas touché : les
   verbes courts (« Corriger », « Vérifier ») n'ont jamais eu besoin de se
   replier, et les faire wrapper allongerait des lignes pour rien. */
.reg-v2 .cel-etat-l1 .cta-action { white-space: normal; text-align: left; }

/* Méthode de vérification. Contour et JAMAIS aplat : un aplat annoncerait une
   sévérité, alors que la méthode qualifie la preuve. Seul l'ambre se détache —
   il isole ce qui vient d'un modèle. */
.reg-v2 .m {
  font-size: 11px; color: var(--rv-gris); border: 1px solid var(--rv-bord);
  border-radius: 4px; padding: 2px 7px; display: inline-block; white-space: nowrap;
  background: var(--rv-carte);
}
.reg-v2 .m.ia { border-color: var(--modele-bord); color: var(--modele); }

/* ── Cellule ÉCHÉANCE : un engagement pour l'avenir, rien d'autre ──
   Un état ACQUIS ne se peint jamais en retard : c'est `celluleEcheance` qui
   le décide, pas cette feuille. Voir son commentaire. */
.reg-v2 .ech { font-size: 12.5px; color: var(--rv-gris); white-space: nowrap; }
.reg-v2 .ech.late { color: var(--rv-rouge); font-weight: 600; }
/* Une absence écrite PORTE de l'information : pas de gris tertiaire ici. */
.reg-v2 .ech.none { font-style: italic; color: var(--text-absent); }

/* ── Cellule CONSTAT : la seule autorisée à dépasser une ligne ──
   C'est du texte libre — il ne se met pas en pastille et ne se tronque pas à
   mi-phrase. Trois lignes au maximum, puis coupure nette. */
.reg-v2 .constat {
  font-size: 13px; color: var(--rv-gris); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* ⛔ PAS de `nowrap` ici. La cellule est une `-webkit-box` coupée à trois
   lignes : un fragment insécable en fin de texte déborde HORIZONTALEMENT et se
   fait trancher en plein milieu — « 08 août 2026 » s'affichait « 08 août 2C »,
   c'est-à-dire une date fausse plutôt qu'une date coupée. */
.reg-v2 .constat .quand { color: var(--text-light); }
/* Démasqué par `app.js` UNIQUEMENT sur les constats réellement coupés : le
   proposer partout ferait dix-neuf liens dont dix-sept ne montrent rien de
   plus. */
.reg-v2 .lire-tout { font-size: 11.5px; margin-top: 3px; display: block; }

.reg-v2 .constat.none { font-style: italic; color: var(--text-absent); }

/* Dates portées par l'état — héritage, encore utilisé hors table des actions. */
.reg-v2 .dt { display: block; font-size: 11.5px; color: var(--rv-gris); margin-top: 4px; }
.reg-v2 .dt2 { display: block; font-size: 11px; color: var(--rv-gris); opacity: .8; margin-top: 1px; }
.reg-v2 .dt.late { color: var(--rv-rouge); font-weight: 600; }
.reg-v2 .dt.none { font-style: italic; opacity: .75; }
.reg-v2 tr.hi td { background: rgba(192, 57, 43, .055); }

/* Cible suivante, après une décision.

   ⚠ TRANSITOIRE, et retirée au bout de quatre secondes par `app.js`. Le
   surlignage répond à « laquelle des trois lignes visibles est celle sur
   laquelle on vient de m'amener ? » — un défilement seul laisse le regard
   chercher. Permanent, il deviendrait un état de plus dans un tableau qui en
   affiche déjà six, et on le lirait comme un verdict.

   La bordure gauche plutôt qu'un fond : `tr.hi` occupe déjà le fond pour dire
   « régression », et deux teintes superposées sur la même ligne ne se
   distingueraient plus l'une de l'autre. */
.reg-v2 tr.cible td { background: rgba(232, 139, 42, .10); transition: background .4s ease; }
.reg-v2 tr.cible td:first-child { box-shadow: inset 3px 0 0 var(--rv-accent); }

/* Liens profonds. `.dead` barre ce qui ne mène nulle part — sans le cacher. */
/* ⚠ **La taille est posée SUR LE CONTENEUR**, pas lien par lien. Le texte
   inerte « ◉ Voir la fiche · ↗ Admin » n'était pas un `<a>` : il échappait à
   la règle des liens et héritait des 13,5 px de la cellule, à côté de liens
   réels à 11,5. Deux tailles pour la même information selon qu'un produit est
   rattaché ou non. Le nom de produit suit la même taille que `.ref` — c'est un
   sous-titre, pas un titre. */
.reg-v2 .lnks { display: flex; gap: 4px 12px; flex-wrap: wrap; margin-top: 6px; font-weight: 400; font-size: 11.5px; }
/* Les liens d'une même ligne ne se coupent jamais en deux : le groupe passe à
   la ligne entier, ou pas du tout. */
.reg-v2 .lnks .grp { white-space: nowrap; display: inline-block; }
/* ⚠ **UNE seule règle de typographie pour les trois liens de la cellule.**
   « Voir le plan IA » est un `<button>` stylé en lien (il ouvre une modale, il
   n'emmène nulle part) : il héritait donc de `.lien-inline`, prévue pour du
   texte courant. Mesuré côte à côte dans la même cellule : 13,5 px souligné
   `#1a5c3a` contre 11,5 px sans soulignement `#2e8b6f` — trois écarts, pas un.
   Trois liens voisins qui font la même chose doivent se ressembler, sinon la
   différence de forme annonce une différence de nature qui n'existe pas. */
.reg-v2 .lnks a,
.reg-v2 td.act .lien-inline {
  font-family: inherit; font-size: inherit; font-weight: 400; font-style: normal;
  color: var(--rv-vert-clr); text-decoration: none;
  border-bottom: 1px solid transparent;
}
.reg-v2 td.act .lien-inline { padding: 0; background: none; border-left: 0; border-right: 0; border-top: 0; }
.reg-v2 td.act .lien-inline:hover { color: var(--rv-vert-clr); border-bottom-color: var(--rv-vert-clr); }
.reg-v2 .lnks a:hover { border-bottom-color: var(--rv-vert-clr); }
.reg-v2 .lnks a.hot { color: var(--rv-accent); font-weight: 600; }
.reg-v2 .lnks .dead {
  font-size: inherit; color: var(--rv-gris); opacity: .7; text-decoration: line-through;
}
.reg-v2 .lnks .dead:only-child { text-decoration: none; font-style: italic; }

/* Boutons d'intervention. */
.reg-v2 .btn {
  font-size: 13px; font-weight: 600; padding: 8px 16px; border-radius: 6px;
  border: 1px solid var(--rv-bord); background: var(--rv-carte); color: var(--rv-encre);
  cursor: pointer; font-family: inherit;
}
.reg-v2 .btn.p { background: var(--rv-vert); border-color: var(--rv-vert); color: #fff; }
.reg-v2 .btn.sm { font-size: 12px; padding: 5px 11px; white-space: nowrap; }
.reg-v2 .btn.ia { border-color: var(--rv-accent); color: var(--rv-accent); }
.reg-v2 .btn[disabled] { opacity: .55; cursor: not-allowed; }

/* Bouton scindé + menu déroulant. */
.reg-v2 .split { display: inline-flex; }
.reg-v2 .split .btn:first-child { border-radius: 6px 0 0 6px; }
.reg-v2 .split .caret { border-radius: 0 6px 6px 0; border-left: 0; padding: 5px 8px; }
.reg-v2 .menucell { position: relative; }
/* ⚠ **`absolute`, et ancré à DROITE.** Il était dans le flux : 206 px de large
   dans une cellule de 132, son bord droit tombait à 1410 px alors que le
   conteneur défilant s'arrête à 1325 — **85 px de libellés purement et
   simplement coupés** (« Vérifier mainten… », « Reporter l'échéa… »). Hors du
   flux et aligné sur le bord droit de sa cellule, il se déploie vers la
   GAUCHE, donc vers l'intérieur du tableau : rien à couper. */
/* ⚠ **`fixed`, et non `absolute` — sans quoi le menu est COUPÉ.**
   Il vit dans `.scroll`, qui porte `overflow-x: auto` ; or `overflow-x: auto`
   force le navigateur à clipper AUSSI en Y. Sur six entrées, une seule était
   visible — « Voir l'historique » et « Abandonner » tombaient sous la coupure,
   donc hors d'atteinte. Pire : le menu allongeait le contenu du conteneur, si
   bien que l'ouvrir faisait apparaître une barre de défilement verticale sur
   un tableau d'une seule ligne.

   Le piège est déjà documenté dix lignes plus bas, sur `.mq .bulle` : la bulle
   l'avait contourné en s'ouvrant SOUS la marque, dans la hauteur de la ligne.
   Un menu de six entrées ne tient pas dans une hauteur de ligne — il faut donc
   sortir du conteneur, et `fixed` est le seul positionnement qui échappe au
   clipping d'un ancêtre.

   ⚠ Les coordonnées viennent du JS (`placerMenu`), au moment de l'ouverture :
   `fixed` les compte depuis la fenêtre, elles ne peuvent donc pas être
   écrites ici. Le corollaire est que le menu ne suit plus la page — d'où sa
   fermeture au défilement. */
.reg-v2 .menu {
  position: fixed; z-index: 30;
  background: var(--rv-carte); border: 1px solid var(--rv-bord);
  border-radius: 8px; box-shadow: 0 8px 22px rgba(0, 0, 0, .16); padding: 5px;
  width: 206px; font-size: 12.5px;
}

.reg-v2 .mi { padding: 7px 9px; border-radius: 5px; color: var(--rv-encre); white-space: normal; }
.reg-v2 .mi small { display: block; color: var(--rv-gris); font-size: 11px; margin-top: 1px; }
.reg-v2 .mi.sel { background: var(--rv-vert); color: #fff; font-weight: 600; }
.reg-v2 .mi.sel small { color: rgba(255, 255, 255, .82); }
.reg-v2 .msep { height: 1px; background: var(--rv-bord); margin: 4px 6px; }
/* Entrée de menu MORTE. `aria-disabled` est un attribut, pas un signal : il ne
   se voit pas, et un utilisateur a cliqué les cinq entrées sans comprendre
   pourquoi rien ne se passait. Le gris et le curseur barré le disent avant le
   clic ; le `title` dit ce qui manque. */
.reg-v2 .mi.off { opacity: .45; cursor: not-allowed; }
/* Un bouton éteint le DIT — et son `title` dit pourquoi (23/08). Sans la
   marque visuelle, un export sans données se cliquait et rendait un fichier
   vide ; sans le title, il se serait éteint sans raison lisible. */
.btn.off, a.btn.off { opacity: .45; cursor: not-allowed; pointer-events: auto; }
/* La valeur ABSENTE d'une tuile : distincte d'un chiffre au premier coup
   d'œil, sans crier comme une erreur — il n'y a pas de panne, il n'y a pas
   encore de mesure. */
.kpi-vide { font-size: 15px; font-style: italic; color: var(--text-light, #8a8a8a); }
.reg-v2 .mi.off:hover { background: none; }
/* Entrée VIVANTE : elle réagit, donc elle doit avoir l'air vivante. */
.reg-v2 .mi[role="button"] { cursor: pointer; }
.reg-v2 .mi[role="button"]:hover,
.reg-v2 .mi[role="button"]:focus { background: var(--rv-gris-clr); outline: none; }
.reg-v2 .mi[role="button"]:focus-visible { box-shadow: 0 0 0 2px var(--rv-vert-clr); }

/* Texte réservé aux lecteurs d'écran : la raison d'une entrée morte doit être
   ENTENDUE, pas seulement survolée à la souris. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.reg-v2 .legend {
  display: flex; gap: 8px; flex-wrap: wrap; padding: 12px 16px;
  border-top: 1px solid var(--rv-bord); background: var(--rv-gris-clr); font-size: 12px;
  color: var(--rv-gris); align-items: center;
}
.reg-v2 .scroll { overflow-x: auto; }

/* ⚠ **La légende est DANS le conteneur qui défile**, sous le tableau : elle
   décrit ses colonnes, elle suit donc sa largeur. Posée dehors, elle restait
   en place pendant que le tableau glissait, et décrivait des colonnes qui
   n'étaient plus en face.

   ⚠ **Et elle garde sa marge basse.** J'allais la retirer : la phrase de pied
   et la répartition ont quitté cet onglet, donc la marge ne séparait plus
   rien — sauf dans le cas qui compte. Quand aucun recalcul n'a jamais tourné,
   l'avertissement qui le dit s'affiche juste dessous, et sans marge il se
   lirait comme la suite de la bande grise. Un test l'a dit avant moi. */
.reg-v2 .legend { margin-bottom: 22px; }

/* La répartition se détache de ce qui la précède, avec ou sans titre — le
   titre portait la marge, et il est devenu facultatif. */
.par-responsable { margin-top: 26px; }

/* ── Arbitrage d'un verdict IA (maquette §4) ──
   `.q`, `.kv`, `.quote` sont des noms très courts : sous `.reg-v2` ils ne
   peuvent rien atteindre d'autre, hors de lui ils auraient repeint la moitié
   de l'application. */
.reg-v2 .arbi { padding: 20px 22px; }
.reg-v2 .q { font-size: 15px; margin: 0 0 16px; max-width: 80ch; }
.reg-v2 .verdict { display: flex; gap: 22px; flex-wrap: wrap; margin: 0 0 18px; }
.reg-v2 .kv { font-size: 12.5px; }
.reg-v2 .kv span {
  display: block; color: var(--rv-gris); font-size: 11px; text-transform: uppercase;
  letter-spacing: .5px; margin-bottom: 2px;
}
.reg-v2 .quote {
  border-left: 3px solid var(--rv-accent); background: var(--rv-gris-clr);
  padding: 12px 16px; border-radius: 0 6px 6px 0; font-size: 13.5px; margin: 0 0 18px;
}
.reg-v2 .quote em {
  color: var(--rv-gris); display: block; font-size: 11.5px; margin-bottom: 6px;
  font-style: normal; text-transform: uppercase; letter-spacing: .5px;
}
/* Un verdict sans citation n'est pas arbitrable : le bandeau passe au rouge
   plutôt que de se faire discret — c'est un manque, pas une variante. */
.reg-v2 .quote.none { border-left-color: var(--rv-rouge); font-style: italic; }
/* Report précédent : bord VERT plutôt qu'accent. Ce bloc ne cite pas une
   preuve, il rapporte l'hésitation d'un humain — lui donner l'allure d'une
   citation ferait lire « je n'ai pas conclu » comme un constat de sonde. */
.reg-v2 .quote.report { border-left-color: var(--rv-vert-clr); }
/* Motif porté par une observation, dans le journal. `inline-block` pour qu'il
   passe à la ligne d'un bloc plutôt que de s'étirer derrière l'identifiant. */
.motif { display: inline-block; margin-top: 4px; font-size: 12px; }
.reg-v2 .btns { display: flex; gap: 10px; flex-wrap: wrap; }

/* Bandeau « spécimen » : il doit se lire AVANT le contenu qu'il qualifie,
   sinon on aura cru à des chiffres inventés le temps de le trouver. */
.reg-v2 .legend.spec {
  border-top: 0; border-bottom: 1px solid var(--rv-bord);
  color: var(--rv-accent); background: rgba(232, 139, 42, .09);
}

/* ─────────── Registre : chips de filtre, onglets, modales ─────────── */
/* Les tuiles occupaient un tiers de l'écran pour des nombres qu'on lit une
   fois. Réduites en chips, elles tiennent sur une ligne ET deviennent
   actionnables : le nombre répond enfin à la question qu'il pose. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.chip {
  font-family: inherit; font-size: 12px; color: var(--text-mid);
  background: var(--white); border: 1.5px solid var(--border);
  border-radius: 20px; padding: 5px 12px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px; transition: all .15s;
}
.chip:hover { border-color: var(--green); color: var(--green); }
.chip.on { background: var(--green-pale); border-color: var(--green); color: var(--green); font-weight: 600; }
.chip:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--green-light); }
.chip-n { font-weight: 700; color: var(--green); }
.chip.on .chip-n { color: var(--green); }
/* Compteur : il ne filtre rien, il ne doit donc pas avoir l'air d'un bouton. */
.chip.compte { cursor: default; background: var(--bg); border-style: dashed; }
.chip.compte:hover { border-color: var(--border); color: var(--text-mid); }
.chip.raz { border-style: dashed; }

/* Onglets — motif ARIA `tablist`. */
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 0; }
.tab {
  font-family: inherit; font-size: 13px; font-weight: 500; color: var(--text-mid);
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: 10px 16px; cursor: pointer; transition: all .15s;
}
.tab:hover { color: var(--green); }
.tab.on { color: var(--green); border-bottom-color: var(--green); font-weight: 600; }
.tab:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--green-light); }

/* Modales. Rendues masquées plutôt que construites au clic : leurs écouteurs
   sont posés une fois pour toutes. */
.modale { position: fixed; inset: 0; z-index: 1000; display: flex;
  align-items: center; justify-content: center; padding: 24px; }

.modale-fond { position: absolute; inset: 0; background: rgba(26, 26, 26, .45); }
.modale-boite {
  position: relative; background: var(--white); border-radius: 16px;
  box-shadow: var(--shadow-hover); max-width: 760px; width: 100%;
  max-height: 85vh; display: flex; flex-direction: column;
}
.modale-tete {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 20px 24px; border-bottom: 1px solid var(--border);
}
.modale-corps { padding: 20px 24px; overflow-y: auto; }
.arbi-appel { margin-bottom: 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* ── Marque de vérification ──
   JAMAIS un badge : ni fond, ni bordure. Le badge d'état est la seule pastille
   pleine de la ligne ; en faire une seconde mettrait au même rang l'état — qui
   dit où on en est — et une tâche de fond qui dit juste que quelque chose
   bouge. BLEU parce que l'orange de cet écran dit « attention » et le rouge
   « défaut » : un travail en cours n'est ni l'un ni l'autre.

   ⚠ **AUCUNE `opacity` ici, et c'est une correction.** La distinction en file
   / prise a reposé sur `.65` d'opacité, puis sur deux vitesses de rotation.
   Les deux tombent en même temps : sous `prefers-reduced-motion`, dans une
   capture, à l'impression, et dès que la colonne se replie sur l'icône seule,
   il ne reste ni nuance ni mouvement. Et `#1c4f82` à `.72` descend à 3,1:1,
   sous AA, sur un texte porteur d'information.

   C'est donc le GLYPHE qui porte la distinction — il survit à tout. Un seul
   bleu à maintenir, mesurable. Voir `MARQUES_VERIFICATION`. */
.mq {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.mq { position: relative; }
.mq .g { display: inline-block; font-size: 12px; line-height: 1; }
/* La marque est atteignable au clavier : son anneau de focus doit se voir. */
.mq:focus-visible { outline: 2px solid var(--encours); outline-offset: 3px; border-radius: 3px; }

/* ── La phrase, au survol et au focus ──
   ⚠ Ce n'est PAS un `title` : un attribut de survol n'existe ni au clavier ni
   au lecteur d'écran, et c'est le reproche qu'on lui faisait déjà ailleurs
   dans ce fichier. La bulle s'ouvre aussi au focus, et le `sr-only` reste. */
/* ⚠ **La bulle sert AUSSI le compteur `.obs-compte`.** Le sélecteur les liste
   tous les deux plutôt que d'être recopié : deux définitions de la même bulle
   divergeraient, et c'est l'autre qu'on croirait. */
.mq .bulle, .obs-compte .bulle {
  /* ⚠ **SOUS la marque, pas au-dessus.** Le conteneur du tableau clippe sur
     les DEUX axes — `overflow-x: auto` force `overflow-y` à en faire autant —
     et au-dessus, la bulle de la première ligne passait derrière les en-têtes
     de colonnes, mesuré à 1 px du bord. En dessous, elle se déploie dans la
     hauteur de la ligne, qui lui appartient. */
  position: absolute; top: calc(100% + 7px); left: 0;
  z-index: 40; width: max-content; max-width: 230px;
  background: var(--rv-encre); color: var(--rv-carte);
  font-size: 11.5px; font-weight: 500; line-height: 1.35; text-align: left;
  padding: 6px 9px; border-radius: 6px; box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
  opacity: 0; visibility: hidden; transition: opacity .12s ease;
  pointer-events: none; white-space: normal;
}
/* ⚠ `:focus` et non `:focus-visible` : le second ne se déclenche pas sur un
   focus posé par programme (ni, selon les heuristiques du navigateur, sur
   certains parcours au clavier). La bulle DOIT s'ouvrir dès que la marque a le
   focus — c'est ce qui la sépare d'un `title` réservé à la souris. L'anneau,
   lui, reste sur `:focus-visible` : on ne le dessine pas pour un clic. */
.mq:hover .bulle, .mq:focus .bulle,
.obs-compte:hover .bulle, .obs-compte:focus .bulle { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .mq .bulle, .obs-compte .bulle { transition: none; }
}
/* ⚠ **Les deux tournent**, et c'est ce qui fait que l'écran répond au clic.
   La distinction passe par la VITESSE : discrète en file, plus vive une fois
   prise. Un détour avait figé « en file » sur un glyphe statique — l'écran
   n'accusait plus réception, et la flèche n'apparaissait jamais puisque rien
   ne consomme la file. */
.mq.queued { color: var(--encours); }        /* 2,4 s — la demande attend */
.mq.in_progress { color: var(--encours); }   /* 1,1 s — un agent est dessus */
/* Échec : la marque RESTE, elle change. Un état terminal négatif est une
   information — l'effacer laisserait la ligne comme si personne n'avait rien
   demandé. */
.mq.failed { color: var(--echec); }
/* ⚠ **La CORRECTION se peint en orange, la vérification en bleu.**
   Le raisonnement de la marque bleue tenait pour une re-sonde : « un travail
   en train de se faire n'est ni une attention ni un défaut ». Il ne tient plus
   ici — un correctif fait ÉCRIRE un agent sur la cible, hors de frixydash.
   L'orange de cet écran dit précisément « attention », et c'est ce qu'on veut
   dire. Deux familles d'événements séparées jusque dans le bus ne peuvent pas
   porter la même couleur ET le même glyphe.
   L'échec, lui, reste rouge pour les deux : un défaut est un défaut. */
.mq.corr.queued, .mq.corr.in_progress { color: var(--sev-attention-encre); }
/* Une proposition en ATTENTE DE VALIDATION n'est ni un travail en cours ni un
   échec : l'orange « attention », fixe, avec un léger battement pour dire
   « un geste humain est attendu ici » — c'est la seule marque de l'écran qui
   demande quelque chose à la personne, elle doit se voir. */
.mq.corr.a-valider { color: var(--sev-attention-encre); }
.mq.corr.a-valider .g { animation: valider-pulse 2s ease-in-out infinite; }
/* « Outil manquant » : orange fixe, sans battement — rien n'attend un geste
   ICI, il manque un correcteur à écrire ailleurs. Distinct des rouges : rien
   n'a échoué. */
.mq.corr.outil-manquant { color: var(--sev-attention-encre); }
/* « ✓ appliqué, non re-vérifié » — le filet entre un correctif réussi et
   l'observation qui le constatera. Vert discret : un fait, pas une alerte. */
.apres-close {
  display: block; margin-top: 3px; font-size: 12px; color: var(--green, #2e7d32);
}
.apres-close strong { font-weight: 600; }
/* Le fil de questions — l'agent demande, l'humain répond.

   ⚠ **`minmax(0, 1fr)` et non `1fr`, et c'est TOUT le correctif de largeur.**
   Un élément de grille a `min-width: auto` par défaut : il refuse de
   descendre sous la largeur MIN-CONTENT de son contenu. Un jeton insécable
   — une URL de 300 caractères dans un message d'agent — posait donc un
   plancher de 1734 px mesuré au banc, la piste s'élargissait, et
   `.modale-corps` rendait un ascenseur horizontal : trois messages coupés
   au bord droit, illisibles sans défiler (grief de Fred, 22/08 22:03). La
   bulle, elle, ne débordait pas — elle obéissait à une piste trop large.
   Le `min-width: 0` sur les tours enlève le même plancher d'un cran plus
   bas, là où vivent les blocs de plan. */
.q-fil { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 4px 8px; }
.q-echange {
  border-left: 3px solid var(--sev-attention-fond, #f6e7c1); padding-left: 10px;
  min-width: 0;
}
/* ⚠ `anywhere` plutôt que `break-word` : `break-word` ne coupe un mot qu'en
   dernier recours et laisse une URL débordder si elle tient « presque ».
   `anywhere` autorise la coupure dès qu'elle évite le débordement — et ne
   change RIEN à la prose ordinaire, qui coupe toujours aux espaces d'abord.
   Même idiome que `.proposition-apercu`, le premier texte long de l'écran. */
.q-question, .q-reponse { overflow-wrap: anywhere; min-width: 0; }
.q-question { margin-bottom: 4px; }
.q-reponse { background: var(--gris-clair, rgba(127,127,127,.06)); border-radius: 6px; padding: 6px 8px; }
/* Le fil a deux côtés (SL-M1) : `.de-session` marque TOUTE bulle écrite par
   l'utilisateur — sa question comme sa réponse — du même fond que portait
   déjà la réponse humaine. L'agent reste sur fond nu : un seul code visuel,
   « fond gris = vous », quel que soit le sens du tour. */
.q-question.de-session { background: var(--gris-clair, rgba(127,127,127,.06)); border-radius: 6px; padding: 6px 8px; }
.q-reponse:not(.de-session) { background: none; padding: 6px 0 0; }
.q-vide { padding: 8px 0; }
/* Le PLAN scellé d'une réponse machine (SL-M2) : bloc préformaté qui défile
   dans son cadre (jamais la page), sceau abrégé en badge, bouton d'exécution
   dessous. Le badge reprend la casse machine : c'est un hash, pas un mot. */
/* ⚠ `overflow-wrap: normal` REMET la barrière que la bulle vient de lever :
   la propriété s'HÉRITE, donc le `anywhere` posé sur `.q-reponse` descendait
   ici et coupait une URL de plan en plein milieu — mesuré au banc, qui
   attendait un ascenseur intérieur et n'en trouvait plus. Du code ne se
   coupe pas n'importe où : il défile dans son cadre, et nulle part ailleurs. */
.plan-reponse { margin: 4px 0; padding: 8px; background: var(--gris-clair, rgba(127,127,127,.06));
  border-radius: 6px; overflow-x: auto; overflow-wrap: normal; white-space: pre-wrap;
  font-size: 13px; }
.plan-pied { display: flex; align-items: center; gap: 10px; margin: 4px 0 0; flex-wrap: wrap; }
.plan-refus { margin-top: 6px; }
.plan-refus-motif { margin: 4px 0 0; font-size: 13px; }
.hash-badge { font-family: ui-monospace, monospace; font-size: 12px; padding: 2px 6px;
  border: 1px solid var(--gris-clair, rgba(127,127,127,.25)); border-radius: 4px; }
.q-ou-poser { margin: 2px 0 0; font-size: 13px; }
/* Panneau « Valider le plan » (avis) : fait pour SCANNER — l'extrait en premier et
   en plein, la mécanique (confiance, cibles, gestes) en dessous, une ligne
   par avis, bord coloré par confiance. Les teintes reprennent la sémantique
   existante : vert = signal fort, ambre = à regarder, gris = à trier. */
.avis-entete { margin: 0 0 10px; font-size: 13px; color: var(--muted, #666); }
.avis-ligne { border-left: 3px solid var(--gris-clair, rgba(127,127,127,.25));
  padding: 8px 10px; margin-bottom: 10px; }
.avis-ligne.avis-haute { border-left-color: var(--green, #2e7d32); }
.avis-ligne.avis-moyenne, .avis-ligne.avis-faible {
  border-left-color: var(--sev-attention-fond, #f6e7c1); }
/* Même règle que les bulles du fil, et pour la même raison : un extrait
   d'avis est du texte qu'on ne choisit pas — une URL collée dans un
   commentaire client déborderait le panneau comme elle débordait la modale.
   Ajouté avec la correction du fil (22/08) : c'est le même défaut, au même
   écran, sur le même genre de contenu. */
.avis-extrait, .avis-justif { overflow-wrap: anywhere; }
.avis-extrait { margin: 0 0 4px; }
.avis-meta { margin: 0 0 2px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.avis-justif { margin: 0 0 4px; font-size: 13px; }
.avis-gestes { margin: 0; display: flex; gap: 8px; }
.avis-select { font: inherit; padding: 4px; max-width: 320px; }
/* Les refus MUETS, en tête du panneau : un bloc à part parce que le geste
   attendu y est différent — expliquer, pas trancher. Le liseré ambre les
   distingue des lignes encore à décider sans crier au défaut. */
.avis-muets { border-left: 3px solid var(--sev-attention-fond, #f6e7c1);
  padding: 8px 10px; margin-bottom: 14px; }
.avis-muet { margin-top: 8px; }
.avis-motif, .avis-motif-lot { margin-top: 6px; }
.avis-reglees { margin-top: 12px; }
.avis-reglees summary { cursor: pointer; }
.avis-reglee { margin: 4px 0 0; font-size: 13px; }
.q-cibler { margin: 2px 0 0; font-size: 13px; }
.q-saisie { display: grid; gap: 8px; margin-top: 6px; }
.q-saisie textarea { width: 100%; font: inherit; padding: 8px; }
.q-fil .ar-qui { font-weight: 600; margin-right: 4px; }
/* Les liens d'ÉPREUVE — le chemin que le plan désigne pour constater. */
/* ⚠ `inherit`, pas une taille : la zone de liens a UNE taille (celle du
   sous-titre .ref) et un banc la garde. Le 11px posé à la Phase 0 faisait
   deux tailles dans la même zone — mesuré par le banc, réparé le 22/08. */
.lien-epreuve code { font-size: inherit; }
/* « N fiche(s) mentionnée(s) au catalogue — cibles non déclarées » : la
   provenance d'une liste HEURISTIQUE, pour qu'elle ne se lise plus comme la
   liste des cibles (le piège 16-vs-3). */
.mentions-note {
  display: block; font-size: 11px; color: var(--rv-gris, #8a8a8a);
  font-style: italic; cursor: help; margin-top: 2px;
}
/* Le bilan PAR PAIRE d'un correctif appliqué — compteurs colorés par
   catégorie, liste repliable sur la ligne (details natif, zéro JS). */
.bilan-paires { margin-top: 3px; font-size: 12px; }
.bilan-paires summary { cursor: pointer; list-style-position: inside; }
.bilan-paires ul { margin: 4px 0 0; padding-left: 18px; }
.bilan-paires li { margin: 1px 0; }
.bilan-paires code { font-size: 11px; }
.paire-compte { font-weight: 600; white-space: nowrap; }
.paire-ecrite { color: var(--sev-acquis-encre, #2e7d32); }
.paire-refus { color: var(--sev-attention-encre, #8a6d1a); }
.paire-echec { color: var(--sev-defaut-encre, #b3261e); }
.paire-deja { color: var(--rv-gris, #8a8a8a); }
/* La piste — colonne « dernier constat » depuis le 23/08 (direction Fred) :
   ce n'est pas un geste mais un CONSTAT, « voilà pourquoi rien ne bougera ».
   Un `p`, pas un bouton : aucune action n'est attendue ici. */
/* ⚠ **PAS de `nowrap` ici, et c'est tout le correctif** (22/08) : le nom de
   l'outil est la seule information utile de ce badge — « il manque
   correcteur-wc-generique-n15 » dit QUOI écrire. En `nowrap`, il dépassait
   sa cellule de 123 px mesurés et se faisait trancher : on lisait qu'il
   manquait quelque chose sans savoir quoi. Il passe donc à la ligne. Pas
   d'ellipse : elle cacherait justement la fin, qui porte le numéro. */
.piste-outil {
  color: var(--sev-attention-encre); font-size: 12px;
  white-space: normal; overflow-wrap: anywhere;
  display: block; max-width: 100%; margin: 4px 0 0;
  cursor: help;
}
/* Le diagnostic structuré d'un « aucun correcteur ne couvre » — des zones
   scannables, pas un blob. */
.diag-correcteur { margin: 4px 0 0; display: grid; gap: 4px; }
.diag-correcteur p { margin: 0; }
.diag-explication { max-width: 68ch; }
.diag-suite strong { color: var(--sev-attention-encre); }
.diag-vu-par { margin: 0; padding-left: 18px; }
.diag-vu-par li { font-size: 12px; }
@keyframes valider-pulse { 50% { opacity: 0.35; } }
.mq.queued .g,
.mq.in_progress .g { animation: verif-tourne 2.4s linear infinite; }
.mq.in_progress .g { animation-duration: 1.1s; }
@keyframes verif-tourne { to { transform: rotate(360deg); } }
/* Une rotation perpétuelle est exactement ce que ce réglage existe pour
   couper. Le glyphe reste — il cesse de tourner, et il reste distinct. */
@media (prefers-reduced-motion: reduce) {
  .mq .g { animation: none !important; }
}
/* ⛔ Plus de repli à gérer : le mot ne s'affiche plus jamais en ligne, il vit
   dans la bulle. Ce qui reste vrai à toute largeur — le glyphe se voit, la
   phrase s'obtient au survol ou au focus, le `sr-only` ne bouge pas. */
/* Contrôle rendu comme un lien : il ouvre une modale, il n'emmène nulle part.
   Un `<a href="#">` serait annoncé comme un lien par un lecteur d'écran et
   ferait remonter la page au clic si le `preventDefault` sautait. */
.lien-inline {
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--green); text-decoration: underline;
}
.lien-inline:hover { color: var(--green-light); }
.lien-inline:focus-visible { outline: 2px solid var(--green-light); outline-offset: 2px; }

/* ─────────── Registre : la barre d'outils unique ─────────── */
/* L'en-tête empilait quatre bandes — chips, onglets, appel à arbitrer, avis de
   filtrage — au-dessus d'un tableau de dix-huit lignes. Le tableau est
   pourtant ce qu'on vient lire. Une seule bande, et les filtres repliés. */
.barre-outils {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; border-bottom: 1px solid var(--border);
}
.barre-outils .tabs { border-bottom: none; flex: 1 1 auto; }
.barre-actions { display: flex; gap: 8px; align-items: center; padding-bottom: 6px; }
/* Le compte d'un onglet : lu SANS quitter des yeux ce qu'il qualifie. */
.tab-n {
  display: inline-block; margin-left: 6px; font-size: 11px; font-weight: 700;
  color: var(--text-light); background: var(--bg); border-radius: 10px; padding: 1px 6px;
}
.tab.on .tab-n { color: var(--green); background: var(--green-pale); }
/* Filtres actifs : le bouton le DIT. Repliés sans ce signal, ils feraient
   filtrer sans qu'on voie qu'on filtre — une liste tronquée se lirait alors
   comme une liste complète. */
#basculer-filtres.on { background: var(--green-pale); border-color: var(--green); font-weight: 600; }
#zone-filtres { padding-top: 12px; }


/* ═════════════════ Coque : nav latérale + en-tête de page ═════════════════
   Elle remplace le bandeau supérieur, la rangée de pilules de période et la
   rangée d'onglets du registre. Trois bandes horizontales deviennent une
   colonne — la hauteur revient au contenu, qui est ce qu'on vient lire. */
.app { display: grid; grid-template-columns: 246px 1fr; min-height: 100vh; }

.rail {
  position: sticky; top: 0; align-self: start; height: 100vh;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--white); border-right: 1px solid var(--line);
  padding: 16px 14px; overflow-y: auto;
}
.rail-marque {
  display: flex; align-items: center; gap: 10px; padding: 4px 8px 18px;
  text-decoration: none; color: inherit;
}
.rail-glyphe {
  width: 30px; height: 30px; border-radius: 8px; background: var(--green);
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.rail-glyphe svg { width: 17px; height: 17px; fill: #fff; }
.rail-nom { font-family: "DM Serif Display", serif; font-weight: 400; font-size: 20px; color: var(--green); }

.rail-sections { display: flex; flex-direction: column; gap: 20px; }
.rail-section { display: flex; flex-direction: column; gap: 2px; }
.rail-label {
  font-size: 11px; font-weight: 700; letter-spacing: .9px; text-transform: uppercase;
  color: var(--ink-3); padding: 0 10px 6px;
}
/* ⚠ **Le retrait est du PADDING, pas une marge.** Le libellé rentre sous son
   étiquette de section, mais la pastille de survol et le fond de l'entrée
   active gardent toute la largeur du rail : une entrée sélectionnée doit rester
   une bande pleine, sinon la hiérarchie se paie d'un alignement cassé à droite
   — là où vivent les compteurs `.rail-n`. */
.rail-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px 8px 22px; border: none; border-radius: 9px; background: none;
  font: inherit; font-size: 14px; font-weight: 500; color: var(--ink-2);
  text-align: left; cursor: pointer; transition: background .15s, color .15s;
}
.rail-item:hover { background: var(--green-soft); color: var(--green-deep); }
.rail-item.on { background: var(--green); color: #fff; }
/* ⚠ Une file vide reste VISIBLE et cliquable — jamais masquée ni désactivée :
   une entrée qui disparaît quand sa file se vide fait croire qu'elle n'existe
   plus, et on ne la retrouve pas quand elle se remplit. */
.rail-item.vide { color: var(--ink-3); }
.rail-item:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

.rail-n {
  margin-left: auto; font-size: 12px; font-weight: 600; padding: 1px 8px;
  border-radius: 20px; min-width: 26px; text-align: center;
  background: var(--green-soft); color: var(--green);
}
/* ⚠ Le rouge n'est JAMAIS seul porteur : le sous-titre de page répète le
   chiffre en toutes lettres. */
.rail-n.alerte { background: var(--red-soft); color: var(--red-deep); }
.rail-n.zero { background: #f0f2f0; color: var(--ink-3); }
.rail-item.on .rail-n { background: #ffffff2e; color: #fff; }

.rail-pied { margin-top: auto; border-top: 1px solid var(--line); padding-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.rail-synchro { font-size: 12px; color: var(--ink-3); display: flex; align-items: center; gap: 7px; }
.rail-pastille { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); flex: 0 0 auto; }
.rail-pastille.frais { background: var(--green); }
.rail-pastille.vieux { background: #e0b400; }
.rail-pastille.echec { background: var(--red-deep); }

.rail-compte { position: relative; }
.rail-compte-btn {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border: none; border-radius: 10px; background: none;
  font: inherit; cursor: pointer; text-align: left;
}
.rail-compte-btn:hover { background: var(--green-soft); }
.rail-compte-btn:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.rail-avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--green-soft); color: var(--green); font-size: 12px; font-weight: 600;
}
.rail-compte-btn[aria-expanded="true"] .rail-avatar { background: var(--green); color: #fff; }
.rail-identite { display: flex; flex-direction: column; min-width: 0; }
.rail-qui { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-role { font-size: 11.5px; color: var(--ink-3); }
.rail-chevron { margin-left: auto; color: var(--ink-3); transition: transform .15s; }
.rail-compte-btn[aria-expanded="true"] .rail-chevron { transform: rotate(180deg); }

.rail-menu {
  position: absolute; bottom: calc(100% + 6px); left: 6px; right: 6px;
  background: var(--white); border: 1px solid var(--line); border-radius: 11px;
  box-shadow: 0 10px 30px #15201f1f; padding: 5px; z-index: 50;
  display: flex; flex-direction: column; gap: 1px;
}

.rail-menu-item {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px;
  border: none; border-radius: 8px; background: none; font: inherit;
  font-size: 13.5px; font-weight: 500; color: var(--ink-2);
  text-decoration: none; cursor: pointer; text-align: left;
}
.rail-menu-item:hover { background: var(--green-soft); }
.rail-menu-item:focus-visible { outline: 2px solid var(--green); outline-offset: -2px; }
.rail-menu-item.danger { color: var(--red-deep); }
.rail-menu-item.danger:hover { background: var(--red-soft); }
.rail-menu-sep { height: 1px; background: var(--line); margin: 4px 0; }

/* ── Page ── */
/* ⚠ `min-width: 0` — un élément de grille vaut `min-width: auto` par défaut,
   c'est-à-dire qu'il REFUSE de descendre sous la largeur de son contenu. Le
   tableau du registre, avec son `min-width` mesuré, poussait donc la page
   entière : `.scroll` s'étirait au lieu de défiler, et c'est la FENÊTRE qui
   défilait de côté — le défaut que le conteneur défilant existe pour éviter. */
.page { padding: 22px 32px 60px; max-width: 1140px; width: 100%; min-width: 0; }
.page-tete { display: flex; align-items: flex-start; justify-content: space-between; gap: 32px; margin-bottom: 22px; flex-wrap: wrap; }
.page-titre { font-family: "DM Serif Display", serif; font-weight: 400; font-size: 28px; line-height: 1.15; color: var(--ink); }
.page-sous { font-size: 14.5px; color: var(--ink-2); margin-top: 5px; max-width: 60ch; text-wrap: pretty; }
.page-sous strong { color: var(--ink); font-weight: 600; }
.page-outils { display: flex; gap: 8px; align-items: center; }
.page-outils .btn { height: 36px; padding: 0 15px; border-radius: 9px; font-size: 14px; font-weight: 500; }
.page-tri { font-size: 13px; color: var(--ink-3); }

@media (max-width: 900px) {
  .app { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; border-right: none; border-bottom: 1px solid var(--line); }
  .page { padding: 18px 18px 60px; }
}

/* ─────────── Formulaire de saisie manuelle ───────────
   Une colonne, des libellés au-dessus : un formulaire qu'on remplit une fois
   de temps en temps se lit mieux qu'il ne se balaie. */
.form-ajout { display: flex; flex-direction: column; gap: 13px; }
.form-ajout .champ {
  display: flex; flex-direction: column; gap: 5px;
  font-size: 12.5px; font-weight: 600; color: var(--rv-encre);
}
.form-ajout input[type="text"],
.form-ajout input[type="date"],
.form-ajout select,
.form-ajout textarea {
  font: inherit; font-size: 13.5px; font-weight: 400;
  padding: 8px 10px; border: 1px solid var(--rv-bord); border-radius: 6px;
  background: var(--rv-carte); color: var(--rv-encre); width: 100%;
}
.form-ajout textarea { resize: vertical; min-height: 42px; }
.form-ajout input:focus-visible,
.form-ajout select:focus-visible,
.form-ajout textarea:focus-visible {
  outline: 2px solid var(--rv-vert-clr); outline-offset: 1px; border-color: var(--rv-vert-clr);
}
.form-ajout .form-ligne { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end; }
.form-ajout .form-ligne .champ { flex: 1 1 160px; }
/* La case et son libellé sur une ligne : une case sous son texte se clique de
   travers. */
.form-ajout .champ-case {
  flex-direction: row; align-items: center; gap: 7px; font-weight: 500;
}
.form-ajout .champ-case input { width: auto; }
.form-ajout .champ-case .muted { font-weight: 400; font-size: 11.5px; }
.form-ajout .modale-actions { display: flex; justify-content: flex-end; margin-top: 2px; }
/* Le retour d'erreur, là où l'œil vient de cliquer — pas en haut de page. */
.form-ajout .ok-msg.erreur { color: var(--rv-rouge); }

/* ─────────── Journal des observations : compact, détail au clic ───────────
   La vue par défaut reste la ligne compacte. Tout déplier d'emblée rendrait
   illisible ce qu'on consulte le plus souvent : qui a conclu quoi, et quand. */

/* ⚠ Le bandeau de la règle 2 : deux constats d'un même run sont UNE lecture
   citée deux fois. Sans lui, six lignes vertes se lisent comme six
   confirmations alors qu'il peut n'y en avoir que deux. */
.obs-regle2 {
  font-size: 12px; line-height: 1.5; color: var(--sev-attention-encre);
  background: var(--sev-attention-fond); border-radius: 6px;
  padding: 8px 11px; margin-bottom: 10px;
}
.obs-motif, .obs-run, .obs-quand { white-space: nowrap; }
.obs-motif { max-width: 22ch; overflow: hidden; text-overflow: ellipsis; }
.obs-run { font-variant-numeric: tabular-nums; }
/* La croix qui dit « ce run revient ». Discrète, mais elle porte une règle. */
.obs-meme-run {
  display: inline-block; font-weight: 700; color: var(--sev-attention-encre);
  cursor: help;
}
.obs-voir { white-space: nowrap; font-size: 11.5px; }

/* ⚠ **Le « 16/18 », NEUTRE et sans couleur de verdict.** Le chiffre vient de
   la sonde ; le verdict aussi, et c'est lui qui conclut. Peindre une bonne
   proportion en vert referait le jugement de la sonde depuis un ratio qu'elle
   n'a pas produit pour ça — et afficherait « presque vert » sur un constat
   qu'elle a refusé de trancher.
   `position: relative` porte la bulle, comme `.mq`. `tabindex` sur l'élément :
   la répartition complète doit s'obtenir au clavier, pas seulement à la
   souris — même reproche que celui fait au `title` partout ailleurs ici. */
.obs-compte {
  position: relative; display: inline-block; margin-left: 6px;
  font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--rv-gris);
  cursor: help;
}
.obs-compte:focus-visible { outline: 2px solid var(--encours); outline-offset: 2px; border-radius: 3px; }

/* ⚠ Le titre qui SÉPARE les correctifs des relevés dans l'historique. Un
   ordre de correction est une intention d'écrire, un relevé est un constat :
   sans coupure visible, la seconde table se lirait comme la suite de la
   première, c'est-à-dire une demande prise pour un fait. */
.hist-titre {
  margin: 22px 0 8px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 13px; font-weight: 600;
}

.obs-detail > td { background: var(--rv-gris-clr); padding: 12px 14px; }

.obs-detail-liste {
  display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px;
  margin: 0; font-size: 12.5px;
}
.obs-detail-liste dt { font-weight: 600; color: var(--rv-encre); }
.obs-detail-liste dd { margin: 0; color: var(--rv-gris); min-width: 0; }
.obs-detail-liste code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
  background: var(--rv-carte); padding: 1px 5px; border-radius: 4px;
  border: 1px solid var(--rv-bord); word-break: break-all;
}
/* `preuves_json` est le champ qu'on vient lire : il a droit à de la place, et
   au défilement plutôt qu'à la troncature — une preuve coupée ne prouve rien. */
/* Le poids de ce qui est joint, sous la zone qui défile. Discret : c'est une
   réponse à « est-ce que je vois tout ? », pas une information qu'on vient
   chercher. */
.obs-preuves-poids { font-size: 11px; margin: 4px 0 0; }

.obs-preuves {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px;
  line-height: 1.5; margin: 0; padding: 9px 11px; max-height: 260px;
  overflow: auto; background: var(--rv-carte); border: 1px solid var(--rv-bord);
  border-radius: 6px; white-space: pre; color: var(--rv-encre);
}

/* Une tuile qui compte quelque chose de gênant le DIT. Sans marque, « 4 / 12 »
   se lit comme n'importe quel autre chiffre de l'écran. */
.reg-v2 .kpi-card.alerte,
.kpi-card.alerte {
  border: 1px solid var(--sev-attention-fond, #FCEBD5);
  background: var(--sev-attention-fond, #FCEBD5);
}
.kpi-card.alerte .kpi-value { color: var(--sev-attention-encre, #8A4F08); }
/* ── Modale « Correction proposée » — l'écran de lecture du HITL ──
   `pre-wrap` et non `pre` : l'aperçu vient d'un motif de clôture écrit d'un
   trait, et une seule longue ligne avec ascenseur horizontal serait illisible
   — or c'est précisément le texte qu'on demande à l'humain de LIRE avant de
   signer. */
.proposition-apercu {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--gris-clair, rgba(127, 127, 127, 0.08));
  border: 1px solid var(--bordure, rgba(127, 127, 127, 0.25));
  border-radius: 6px;
  padding: 12px 14px;
  font-size: 13px;
  max-height: 45vh;
  overflow-y: auto;
}

/* Le tableau d'une proposition : la première colonne est le libellé, elle ne
   doit pas se faire écraser par des valeurs longues. */
.proposition-table td:first-child { font-weight: 600; white-space: nowrap; }
.proposition-notes { margin: 12px 0 0; padding-left: 18px; font-size: 13px; }

/* Le rendu HTML d'une proposition — TOUJOURS dans son bac à sable. La hauteur
   est bornée : c'est un aperçu à lire, pas une page à habiter. */
.proposition-rendu {
  width: 100%;
  height: 40vh;
  border: 1px solid var(--bordure, rgba(127, 127, 127, 0.25));
  border-radius: 6px;
  background: #fff;
}

/* ── Éditorial : une table PLEINE LARGEUR, plus de panneau latéral ──────────
   La colonne Contenu récupère tout ce que le panneau occupait. `table-layout:
   fixed` avec des largeurs posées sur les colonnes COURTES : sans lui, un
   titre long dilatait sa colonne au détriment des autres, et un titre court
   la laissait rétrécir — la table changeait de forme à chaque filtre. */
#edito-liste table.data { table-layout: fixed; width: 100%; }
/* ⛔ Adressées par CLASSE, plus par rang. Les largeurs étaient en
   `nth-child(2..6)` : insérer la colonne « Image » en quatrième position a
   décalé toutes les suivantes d'un cran, et le tableau s'est replié en
   escalier. Le HTML était juste, le rendu était cassé — vu seulement en
   ouvrant l'écran. Par classe, une colonne de plus est inerte. */
/* ⛔ **La somme des largeurs FIXES gouverne la colonne Contenu**, qui prend le
   reste. Ajouter « Image » sans rien reprendre l'a écrasée de 395 px à 185 px :
   les titres se repliaient sur quatre lignes et le tableau se lisait en
   escalier. Mesuré au navigateur, invisible dans le HTML. Les six fixes sont
   donc resserrées pour rendre ~300 px au titre. */
#edito-liste .col-statut { width: 158px; }
#edito-liste .col-page { width: 108px; }
/* La pastille, le select et le bouton tiennent sur une ligne à partir de ~200 px. */
#edito-liste .edito-col-image { width: 232px; }
#edito-liste .col-version { width: 62px; }
#edito-liste .col-faits { width: 56px; }
#edito-liste .col-redaction { width: 112px; }
/* ⚠ **Pas de `nowrap`** : il empêchait la cellule de rétrécir, la ligne
   débordait et le tableau se repliait en escalier. Les deux contrôles sont
   simplement dimensionnés pour tenir. */
/* ⛔ **Une LARGEUR EXPLICITE sur le `select`, pas un `max-width`.** Dans une
   table en `table-layout: fixed`, un `select` impose sa largeur INTRINSÈQUE —
   celle de sa plus longue option, ici « FRIXY – Lessive – Parfum Forêt –
   Sachet… ». Il poussait la cellule au-delà des 200 px déclarés, la ligne
   débordait, et le tableau se repliait en escalier. `max-width` ne suffit pas :
   il borne sans contraindre l'intrinsèque. Vu au navigateur, invisible dans le
   HTML et dans les tests. */
#edito-liste .edito-col-image { line-height: 1.9; }
#edito-liste .edito-col-image .edito-image-choix {
  width: 108px; box-sizing: border-box; vertical-align: middle;
  font-size: 12px; padding: 2px 4px;
}
#edito-liste .edito-col-image .btn.sm { margin-left: 4px; vertical-align: middle; padding: 2px 8px; font-size: 12px; }
#edito-liste .edito-col-image .pill { margin-right: 3px; }
#edito-liste td:first-child { white-space: normal; word-break: break-word; }
#edito-liste .edito-ligne { cursor: pointer; }

/* Une modale qui ne porte qu'une PHRASE ne doit pas occuper 86 % de l'écran.
   La hauteur fixe sert la lecture d'un article ; sur « réécriture demandée »
   elle laissait 800 px de vide sous une ligne de texte — un vide qui se lit
   comme un chargement qui n'a pas fini, exactement ce qu'on voulait éviter. */
#modale-contenu:has(.edito-attente) .modale-boite { height: auto; }

/* L'en-tête de la modale Contenu : ce qu'on décide, nommé. Il occupe toute la
   largeur AU-DESSUS des deux colonnes — un titre coincé dans l'une des deux se
   lirait comme appartenant à cette colonne-là. */
.prop-entete {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 14px 24px; border-bottom: 1px solid var(--border);
}
.prop-entete-titre {
  margin: 0; font-family: "DM Serif Display", serif; font-size: 19px; font-weight: 400;
}
.prop-entete-url { font-size: 12.5px; color: var(--muted, #5A6368); word-break: break-all; }
#modale-contenu .modale-corps { flex-direction: column; }

/* « Bloqué — N affirmations n'ont pas de source » — au-dessus des verbes,
   jamais à côté : c'est la raison pour laquelle « Approuver » est gris, elle
   doit se lire avant qu'on cherche à cliquer.

   ⚠ Remplace `.edito-garde-source`, la case « j'approuve malgré N affirmations
   non sourcées » (v1.59.0 → v1.76.0). Même place, autre propos : on n'invite
   plus à passer outre, on dit l'état et sa cause.

   ⚠ Le fond est celui du refus, pas celui de l'attente : depuis que le serveur
   répond 409, ce n'est plus un avertissement qu'on peut lever d'un clic. La
   pastille « Bloqué » qu'il contient porte déjà `.pill.bad` — l'encadré la
   suit plutôt que de la contredire. */
.edito-bloque {
  font-size: 12.5px; line-height: 1.45;
  padding: 8px 10px; margin: 0 0 8px; border-radius: 6px;
  background: var(--red-pale); border: 1px solid rgba(165, 41, 27, 0.28);
}
.edito-bloque .pill { margin-right: 6px; }

/* Le geste de contestation, sur CHAQUE point bloquant.

   ⚠ Discret et à droite : c'est la sortie de secours, pas le chemin nominal.
   Un bouton aussi visible que l'affirmation inviterait à contester avant
   d'avoir lu — or l'ordre voulu est l'inverse, on lit puis on juge. */
.bloquant > [data-contester] { margin-left: auto; flex: none; }
/* Un point contesté RESTE affiché : sa trace est le sujet même du geste.
   Il s'efface visuellement sans disparaître — le superviseur doit pouvoir le
   relire, et l'humain doit voir ce qu'il a levé. */
.bloquant-leve { opacity: 0.62; }
.bloquant-leve .bloquant-affirmation { text-decoration: line-through; }
.pill.bloquant-conteste { background: var(--gris-clr, #EEF0F2); color: var(--muted, #5A6368); }
.edito-sortie { margin-top: 8px; }

/* Le formulaire de relance d'une réécriture — replié tant qu'on n'a pas
   demandé le geste, pour que le cartouche reste une PHRASE et pas un
   formulaire qu'on remplit par réflexe. */
.edito-relance { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; max-width: 620px; }
.edito-relance textarea {
  width: 100%; resize: vertical; font: inherit; font-size: 13px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
}
.edito-attente .pill { margin-right: 6px; }

/* L'historique en accordéon dans la modale — replié, il rend le bas de la
   colonne aux quatre verbes ; déplié, il défile avec elle. */
.edito-histo > summary {
  cursor: pointer; font-size: 12.5px; color: var(--muted, #5A6368);
  padding: 6px 0; user-select: none;
}
.edito-histo[open] > summary { margin-bottom: 4px; }
.edito-histo .section-title { margin: 10px 0 4px; }
/**
 * ⛔ **Un tableau trop large défile CHEZ LUI, pas sous toute la colonne.**
 * `.prop-panneau` porte `overflow-y: auto`, ce qui force `overflow-x` à `auto`
 * aussi (règle CSS : dès qu'un axe n'est plus `visible`, l'autre non plus).
 * Cinq colonnes d'historique dépassaient de quelques dizaines de pixels, et la
 * barre de défilement apparaissait au BAS DE LA COLONNE ENTIÈRE — on croyait
 * devoir faire glisser les boutons de décision pour lire une date.
 *
 * ⚠ Ce cadre reste utile même après l'élargissement à 460 px : il rend le
 * débordement local et prévisible au lieu de dépendre d'une largeur.
 */
.table-defilante { overflow-x: auto; max-width: 100%; }

/* ── La GRANDE modale de proposition — la maquette, pas une boîte de dialogue ──
   Deux colonnes comme l'écran 6 des maquettes : à GAUCHE le contenu tel qu'il
   paraîtra (la chose qu'on approuve), à DROITE la file de validation — statut,
   chiffres et sources, et les gestes. La lecture occupe la place, le panneau
   guide la décision. */
/**
 * ⚠ **Plus grande sur les DEUX dimensions** (grief Fred, 25/08 : le corps était
 * étroit en hauteur, et « en largeur aussi »). L'ancienne borne à 1 180 px
 * laissait 700 px inutilisés sur un écran de 1920, pendant que l'article se
 * lisait dans une colonne étroite.
 *
 * ⛔ **Et ce n'est PAS ce qui corrige la surimpression.** Une modale plus grande
 * n'aurait fait que déplacer le seuil : le recouvrement serait revenu sur le
 * premier écran plus petit, en ayant l'air réglé sur celui de Fred. La cause est
 * traitée plus bas, sur le plancher de `.zone-corps`. Ici, c'est du confort.
 *
 * ⚠ **La colonne de décision n'est plus une piste FIXE** (elle l'a été à 360 px
 * de la v1.65.1 à la v1.66.4). Elle est RÉGLABLE : sa largeur vit dans
 * `--decision-largeur`, la personne la tire, et le réglage lui survit. Ce qui
 * n'a pas changé : toute la largeur qu'elle ne prend pas va au corps.
 */
#modale-proposition .modale-boite,
#modale-contenu .modale-boite {
  max-width: min(1600px, 97vw);
  height: min(94vh, 1200px);
  /* ⚠ `max-height` AUSSI, sans quoi le `max-height: 85vh` de `.modale-boite`
     plafonne la hauteur qu'on vient de fixer. Le banc l'a attrapé : 918 px
     mesurés sur un écran de 1080, soit exactement 85 %. Poser une hauteur sans
     lever le plafond qui la borne, c'est écrire un réglage qui n'a aucun effet
     et croire qu'on a livré. */
  max-height: 94vh;
}
/* Le cadre de la modale reprend 24 px de marge à l'écran, sur les quatre côtés. */
#modale-proposition.modale,
#modale-contenu.modale { padding: 12px; }
#modale-proposition .modale-corps,
#modale-contenu .modale-corps { display: flex; flex: 1; min-height: 0; padding: 0; }
#proposition-corps,
#contenu-corps { display: flex; flex: 1; min-height: 0; width: 100%; }
/**
 * ⛔ **La modale Contenu a DEUX enfants depuis v1.59.0, et ce conteneur en
 * attendait un seul.**
 *
 * `#contenu-corps` est un conteneur flex, direction `row` par défaut — un
 * réglage écrit quand `corpsContenu` ne rendait QUE `.prop-grille` : « flex:1
 * sur l'unique enfant » était alors une façon correcte de remplir la boîte.
 * L'en-tête (titre + URL + état) est venue s'y ajouter sans que personne
 * revisite la direction. Les deux sont donc devenus des colonnes SŒURS :
 * l'en-tête a pris 819 px, la grille a été comprimée à 361, et **l'iframe du
 * corps s'est réduite à 2 pixels de large**. Mesuré, pas déduit.
 *
 * ⚠ Le corps était bien là — 8 549 octets dans le `srcdoc`. Rien ne manquait,
 * rien n'échouait : il était rendu dans une boîte trop étroite pour se voir.
 * C'est le pire genre de défaut d'affichage, celui qui ressemble à une donnée
 * absente.
 *
 * ⚠ `#proposition-corps` (registre) n'est PAS touché : il n'a toujours qu'un
 * enfant, et lui imposer une direction qu'il n'utilise pas serait un réglage
 * sans raison, que le prochain lecteur croirait nécessaire.
 */
#contenu-corps { flex-direction: column; }
/**
 * ⚠ **TROIS pistes : le corps, la POIGNÉE, la décision.** Celle du milieu n'est
 * pas une marge — c'est le séparateur qu'on tire. Toute vue qui rend
 * `.prop-grille` doit donc produire `.prop-poignee`, sinon le panneau tombe
 * dans une gouttière de 7 px. `poigneeDecision()`
 * (`public/lib/panneau-decision.js`) est la SEULE source de ce HTML,
 * précisément pour que les deux vues qui rendent cette grille — la modale
 * d'article et celle du registre — ne puissent pas diverger.
 *
 * ⛔ **Les bornes sont ICI, pas seulement dans le JS.** `minmax(240px, 1fr)` sur
 * le corps, `minmax(0, …)` sur la décision : une largeur héritée d'un écran
 * plus large rétrécit d'elle-même au lieu d'écraser l'article. Le bornage JS
 * est du confort de geste ; celui-ci est la garantie — il tient même si le
 * script n'a pas tourné.
 *
 * ⚠ Le repli passe par une RÈGLE, pas par les variables : `app.js` écrit
 * `--decision-largeur` en style INLINE sur `<html>`, qui l'emporterait sur
 * toute redéfinition en feuille. Redéclarer `grid-template-columns` sous
 * `:root[data-decision-repliee]` ne dépend, lui, d'aucune variable.
 */
.prop-grille {
  display: grid;
  grid-template-columns:
    minmax(240px, 1fr)
    var(--decision-gouttiere, 7px)
    minmax(0, var(--decision-largeur, 460px));
  flex: 1; min-height: 0; width: 100%;
}
:root[data-decision-repliee="oui"] .prop-grille {
  grid-template-columns: minmax(240px, 1fr) 44px 0;
}
:root[data-decision-repliee="oui"] .prop-panneau { display: none; }
:root[data-decision-repliee="oui"] .prop-poignee-barre { display: none; }
:root[data-decision-repliee="oui"] .prop-replier-chevron { transform: rotate(180deg); }

/* ── La poignée : la barre qu'on tire, le bouton qui replie ───────────────── */
.prop-poignee { position: relative; background: var(--border); }
/* ⚠ La zone SENSIBLE déborde la piste de 3 px de chaque côté : viser 7 px à la
   souris est un exercice, pas un geste. Le trait reste fin, la cible ne l'est
   pas. */
.prop-poignee-barre {
  position: absolute; inset: 0 -3px; z-index: 1;
  cursor: col-resize; background: transparent;
  transition: background .12s;
}
.prop-poignee-barre:hover,
.prop-poignee[data-tirage="en-cours"] .prop-poignee-barre { background: var(--accent, #E88B2A); }
.prop-poignee-barre:focus-visible { outline: 2px solid var(--accent, #E88B2A); outline-offset: 0; }
/* Le bouton chevauche les deux colonnes : c'est la couture qu'il commande. */
.prop-replier {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%); z-index: 2;
  width: 22px; height: 34px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; line-height: 1; cursor: pointer;
  color: var(--muted, #5A6368); background: var(--bg-soft, #F1F3F5);
  border: 1px solid var(--border); border-radius: 5px;
}
.prop-replier:hover { color: var(--text, #1B1F23); background: #fff; }
.prop-replier:focus-visible { outline: 2px solid var(--accent, #E88B2A); outline-offset: 1px; }
.prop-replier-chevron { display: block; transition: transform .12s; }
/**
 * ⚠ **Direction posée EXPLICITEMENT, pour plusieurs enfants — pas par défaut
 * pour un seul.** C'est précisément la dette qui a écrasé le corps en v1.60.1 :
 * `#contenu-corps` était réglé quand il n'avait qu'un enfant, et le second
 * ajouté plus tard est devenu une colonne sœur. Cette colonne en porte
 * désormais QUATRE — en-tête scellée, légende, cadre, métadonnées de
 * production — et la direction est écrite pour eux, pas héritée d'un temps où
 * il n'y en avait qu'un.
 */
.prop-rendu {
  padding: 20px 24px; overflow-y: auto; min-height: 0;
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 4px;
}
/**
 * ⛔ **`flex` et non `calc(100% - 32px)`.** L'ancienne hauteur soustrayait la
 * seule légende, la seule chose qui accompagnait le cadre à l'époque. Ajouter
 * deux blocs autour l'aurait fait déborder de sa colonne — le calcul aurait
 * continué de rendre un nombre, simplement faux. Une hauteur qui se déduit du
 * contenu ne se recalcule pas à la main à chaque ajout.
 */
/**
 * ⛔ **Le plancher vit sur l'élément FLEX, jamais sur son contenu.**
 *
 * `min-height: 320px` était posé ici, sur l'iframe — un enfant de `.zone-corps`,
 * qui porte `min-height: 0` et le droit de rétrécir. Quand la place manquait,
 * le conteneur rétrécissait, l'enfant refusait de suivre, et `overflow: visible`
 * le laissait PEINDRE PAR-DESSUS le frère suivant : « Comment cette version a
 * été produite » apparaissait en surimpression au milieu de l'article (grief
 * Fred, 25/08). Le parent ne défilait pas — de son point de vue, tout tenait.
 *
 * ⚠ **Et le défaut ne se voyait qu'en dessous de ~800 px de haut.** Mesuré :
 * aucun recouvrement à 1920×1080, 1720×900 ni 1440×900 ; +27 px à 1366×768,
 * +28 px à 1280×720 et en dessous. Le banc e2e tourne à 1720×900 — il ne
 * pouvait pas le voir, et il ne l'a pas vu.
 *
 * Le plancher remonté sur `.zone-corps`, c'est `.prop-rendu` — déjà
 * `overflow-y: auto` — qui DÉFILE au lieu de laisser deux boîtes se superposer.
 */
.prop-rendu .proposition-rendu { flex: 1 1 auto; height: auto; min-height: 0; }

/* ── « Chiffres et leur source » : ce qui bloque, lisible ─────────────────── */
.bloquants-liste { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.bloquant {
  display: flex; flex-direction: column; gap: 3px; font-size: 12.5px;
  padding: 8px 10px; border-radius: 6px;
  background: rgba(192, 57, 43, 0.05); border: 1px solid rgba(192, 57, 43, 0.22);
}
.bloquant-marqueur { align-self: flex-start; }
/* L'affirmation d'abord, en pleine voix : c'est elle qu'on assume. */
.bloquant-affirmation { color: var(--ink, inherit); }
/* Le motif ensuite, en retrait : il explique, il ne s'assume pas. */
.bloquant-motif { color: var(--muted, #5A6368); font-size: 12px; }

/* ── L'éditeur du corps : barre d'outils + zone contenteditable ───────────── */
/* ⚠ Même règle qu'en lecture : le plancher est ICI, sur l'élément flex, et
   `.corps-champ` n'en porte plus — sinon le champ déborderait de l'éditeur et
   recouvrirait les gestes qui le suivent. 400 px = 320 pour le texte, plus la
   barre d'outils et les deux boutons. */
.corps-edition { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 400px; gap: 6px; }
/* Les gestes de tableau forment un groupe à part : ils n'agissent que dans
   une cellule, et la séparation le dit avant que le survol l'explique. */
.corps-barre-sep {
  width: 1px; align-self: stretch; margin: 2px 4px;
  background: var(--border);
}
.corps-barre-groupe { display: inline-flex; gap: 4px; }
/* Éteint : visible, mais visiblement hors de portée. */
.corps-outil[disabled] { opacity: .4; cursor: not-allowed; }
/* Une cellule vide reste CLIQUABLE. Sans cela elle se réduit à ses bordures —
   mesuré à 11 px de haut : la ligne existe dans le HTML et à l'enregistrement,
   mais on ne peut plus y poser le curseur pour la remplir. On aurait ajouté une
   ligne inutilisable.

   ⚠ Par un PSEUDO-élément, jamais par un `<br>` posé dans la cellule : un
   pseudo-élément ne figure dans aucun `innerHTML`, donc rien n'entre dans
   l'article enregistré. La doctrine « zéro artefact » tient jusque dans la CSS. */
.corps-champ td:empty, .corps-champ th:empty { min-width: 40px; }
.corps-champ td:empty::after, .corps-champ th:empty::after {
  content: "\00a0"; display: inline-block;
}

.corps-barre {
  display: flex; gap: 4px; flex-wrap: wrap; padding: 6px;
  border: 1px solid var(--border); border-radius: 6px 6px 0 0;
  background: var(--gris-clair, rgba(127, 127, 127, 0.05));
}
.corps-outil { min-width: 34px; font-weight: 600; }
/* ⚠ La zone d'édition occupe la place du CADRE qu'elle remplace : passer en
   édition ne doit pas faire sauter la mise en page sous les doigts. */
.corps-champ {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 14px 16px; background: #fff; color: #1A1D1F;
  border: 1px solid var(--border); border-top: 0; border-radius: 0 0 6px 6px;
  font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.corps-champ:focus { outline: 2px solid var(--vert, #1B5E4A); outline-offset: -2px; }
.corps-champ h2 { font-size: 19px; margin: 16px 0 8px; }
.corps-champ h3 { font-size: 16px; margin: 14px 0 6px; }
.corps-champ p { margin: 0 0 10px; }
.corps-champ table { border-collapse: collapse; width: 100%; margin: 10px 0; }
.corps-champ td, .corps-champ th { border: 1px solid #ccc; padding: 5px 8px; text-align: left; }
.corps-gestes { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ── Brouillons sans image à la une ───────────────────────────────────────── */
/* ⛔ **Le CSS du widget « Brouillons sans image » a été RETIRÉ avec lui**
   (v1.117.0), et son retrait est le correctif lui-même.

   `.edito-image-ligne` posait `display: flex` — c'était une puce `<li>`. J'ai
   réemployé ce nom de classe sur la RANGÉE du tableau pour garder le
   gestionnaire de `brancherChoixImage` intact… et la rangée est devenue un
   conteneur flex : `tr` en `display: flex`, `td` en `block`, cellules empilées
   par paires. **Le HTML était juste, les tests verts, et le tableau se lisait
   en escalier.**

   ⚠ **Un nom de classe réemployé comme point d'accroche JS transporte son
   style avec lui.** Mesuré au navigateur en comparant `getComputedStyle` de la
   ligne 0 (sans image, `table-row`) et de la ligne 1 (avec image, `flex`) —
   c'est la seule mesure qui l'a montré. */
.edito-images-vide { margin: 0; font-size: 12.5px; }
.edito-image-choix {
  font: inherit; font-size: 13px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: 5px; max-width: 280px;
}

/* ── Reformuler un fragment : la barre sur la sélection, le panneau dessous ── */
/**
 * ⛔ **`fixed` et z-index AU-DESSUS de la modale (1000).** La barre naît dans
 * `.prop-rendu`, qui porte `overflow-y: auto` : en `absolute`, elle serait
 * rognée au bord de la colonne et défilerait avec le texte — c'est-à-dire
 * qu'elle quitterait la phrase qu'elle désigne. Et sans z-index supérieur,
 * elle passerait SOUS la boîte de la modale qui la contient.
 *
 * ⚠ Les coordonnées sont posées par le script (`placerBarreReformulation`),
 * bornées aux deux côtés de la fenêtre : une sélection en bord droit pousserait
 * sinon la barre hors du champ visible, où elle existe sans être atteignable.
 */
.reform-hote { position: fixed; z-index: 1100; left: 0; top: 0; }
.reform-hote[hidden] { display: none; }
.reform-barre {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 8px;
  background: var(--white, #fff); border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(21, 32, 31, .18);
}
/* ⛔ Le motif du refus est ÉCRIT, pas seulement en `title` : une infobulle
   n'existe ni au clavier ni au doigt, et c'est précisément quelqu'un qui vient
   d'essayer qui a besoin de lire pourquoi. */
.reform-motif { margin: 0; font-size: 12px; max-width: 260px; color: var(--muted, #5A6368); }
.reform-compte { font-size: 12px; }

.reform-panneau {
  margin-top: 10px; padding: 12px 14px;
  border: 1px solid var(--accent, #E88B2A); border-radius: 8px;
  background: var(--gris-clair, rgba(127, 127, 127, 0.04));
  display: flex; flex-direction: column; gap: 8px;
}
.reform-panneau .section-title { margin: 0; }
/**
 * ⚠ **`auto-fit`, donc c'est la PLACE qui décide** — côte à côte quand la
 * colonne est large, l'un sous l'autre quand elle ne l'est plus. Le
 * constructeur ne tranche pas : il ne connaît pas la largeur d'une colonne qui
 * se règle à la main depuis la v1.67.0.
 */
.reform-face-a-face {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.reform-cote {
  padding: 8px 10px; border-radius: 6px;
  background: var(--white, #fff); border: 1px solid var(--border);
}
/* La proposition se distingue de l'existant — sinon on compare deux boîtes
   identiques et on accepte celle de droite par habitude. */
.reform-cote-neuf { border-color: var(--accent, #E88B2A); }
.reform-etiquette {
  margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--muted, #5A6368);
}
.reform-texte { margin: 0; font-size: 13px; line-height: 1.5; white-space: pre-wrap; }
.reform-metriques, .reform-avertissement { margin: 0; font-size: 12px; }
/* ⛔ Ce qui sera PERDU se lit avant qu'on tranche, pas après. L'agent rend du
   texte : accepter efface le lien que portait le bloc. Sur un tableau de bord
   SEO, un maillage interne qui disparaît sans un mot est un défaut. */
.reform-perte {
  margin: 0; padding: 6px 8px; font-size: 12px;
  border-left: 3px solid var(--accent, #E88B2A);
  background: rgba(232, 139, 42, .08); border-radius: 0 4px 4px 0;
}
.reform-gestes { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.reform-erreur-seule { margin: 10px 0 0; }
/* L'attente ne se pare pas de l'orange de la décision : il n'y a rien à
   trancher tant que rien n'est revenu. */
.reform-attente { border-color: var(--border); }
.prop-rendu > [data-editer-corps] { align-self: flex-start; margin-top: 8px; }

/* L'en-tête en cours d'ÉDITION. Les champs restent dans la grille de la liste :
   passer d'une lecture à un formulaire ne doit pas déplacer les libellés, sinon
   l'œil perd la ligne qu'il suivait. */
.entete-champ {
  width: 100%; resize: vertical; font: inherit; font-size: 13px; line-height: 1.4;
  padding: 5px 8px; border: 1px solid var(--border); border-radius: 5px;
}
.entete-gestes { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

/* ── Le CRAYON — le geste d'édition, posé sur la zone qu'il édite ────────── */
/* Discret par la taille et la couleur, PRÉSENT par la permanence : un crayon
   qui n'apparaît qu'au survol n'existe ni au clavier ni au doigt. */
.crayon {
  flex: 0 0 auto; width: 24px; height: 24px; padding: 0; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; cursor: pointer;
  background: transparent; color: var(--muted, #5A6368);
  border: 1px solid transparent; border-radius: 6px;
  transition: background .12s, color .12s, border-color .12s;
}
.crayon:hover { background: var(--bg-soft, #F1F3F5); color: var(--text, #1B1F23); border-color: var(--border); }
.crayon:focus-visible { outline: 2px solid var(--accent, #2563EB); outline-offset: 1px; }
/* Refusé : l'hôte reçoit le survol à sa place et affiche le motif. */
.crayon[disabled] { opacity: .35; cursor: not-allowed; pointer-events: none; }
.crayon-hote { display: inline-flex; flex: 0 0 auto; cursor: not-allowed; }

/* La ligne de titre de l'en-tête : le titre pousse, le crayon reste à droite. */
.zone-tete { display: flex; align-items: flex-start; gap: 8px; }
.zone-tete > .section-title { flex: 1 1 auto; min-width: 0; }

/* Le cadre du corps : le crayon flotte en haut à droite, au-dessus du texte. */
.zone-corps { position: relative; display: flex; flex-direction: column; min-height: 320px; flex: 1 1 auto; }
.zone-corps > .crayon, .zone-corps > .crayon-hote { position: absolute; top: -2px; right: 0; z-index: 1; }
/* La légende ne passe jamais SOUS le crayon. */
.zone-corps > .muted { padding-right: 30px; }

/* ── L'en-tête d'article, montrée AVANT d'être scellée ────────────────────── */
.entete-scellee { flex: 0 0 auto; margin-bottom: 10px; }
.entete-scellee .section-title { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.entete-liste { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 3px 14px; }
.entete-ligne { display: contents; }
.entete-liste dt { font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted, #5A6368); }
.entete-liste dd { margin: 0; font-size: 13px; word-break: break-word; }
/* Un champ absent se LIT comme absent, il ne se confond pas avec une valeur. */
.entete-vide { font-style: italic; opacity: .7; }

/* ══ Ce qui sera posé sur WordPress — un cadre, deux natures d'engagement ══
   ⛔ UN cadre, parce que les deux moitiés décrivent un seul envoi : la ligne
   déduite du cocon et les quatre champs scellés partent ensemble. Séparés, ils
   se lisaient comme deux sujets.
   ⚠ Mais les deux pastilles restent : ○ auto est RECALCULÉ à la publication,
   ● scellé est FIGÉ par l'approbation. Fondre les deux sans le dire ferait
   croire qu'approuver engage les six valeurs — il n'en engage que quatre. */
.entete-wordpress {
  flex: 0 0 auto; margin-bottom: 10px;
  border: 1px solid var(--line, #e2e6e2); border-radius: 10px; padding: 8px 10px;
}
.entete-wordpress > .section-title { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.wp-part + .wp-part { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line, #e2e6e2); }
.wp-sous-titre {
  display: flex; align-items: center; gap: 8px; margin: 0 0 4px;
  font-size: 11.5px; color: var(--muted, #5A6368);
}
/* ⚠ `margin-left: auto` sur le crayon : il se range à droite de son sous-titre,
   comme il l'était dans l'ancienne `zone-tete`. La proximité EST la
   désignation — le déplacer ailleurs lui ferait perdre sa cible. */
.wp-sous-titre .crayon, .wp-sous-titre .crayon-hote { margin-left: auto; }
.wp-pastille { font-weight: 600; color: var(--muted, #5A6368); white-space: nowrap; }
.wp-pastille.scelle { color: var(--green-deep, #2c5f3f); }

/* ══ La ligne compacte — trois postes et un ↻, sur une seule ligne ══
   ⚠ `flex-wrap` et non `nowrap` : sur une fenêtre étroite, une ligne qui
   déborde cache le poste de droite — et c'est justement là que vit la
   catégorie. Elle passe à la ligne plutôt que de disparaître. */
.wp-ligne-inline {
  margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px;
  font-size: 13px;
}
.wp-poste { display: inline-flex; align-items: baseline; gap: 5px; min-width: 0; }
.wp-etiquette { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted, #5A6368); }
/* La description longue vit dans le `title` : la valeur, elle, se lit d'un coup. */
.wp-valeur { color: var(--ink-2, #2f3634); }
/* L'identifiant trace, il ne décide pas — mais il reste LISIBLE : c'est lui
   qu'on recopie dans un message quand la publication se trompe de cible. */
.wp-id { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--muted, #5A6368); }
.wp-precision { color: var(--muted, #5A6368); }
/* ⛔ Une ignorance se voit. Sur un écran où l'on signe, un « inconnu » discret
   se lit comme un détail — or c'est le seul poste qui demande un geste. */
.wp-inconnu { color: var(--red-deep, #b3261e); font-weight: 600; }
.wp-inconnu .wp-precision { color: inherit; font-weight: 400; opacity: .8; }
/* ⚠ Un `<button>`, jamais un `<span>` cliquable : une icône seule sans nom
   accessible est muette au lecteur d'écran et injoignable au clavier. */
.wp-recharger {
  margin-left: auto; border: none; background: none; cursor: pointer;
  font: inherit; font-size: 13px; line-height: 1; padding: 2px 5px; border-radius: 6px;
  color: var(--muted, #5A6368);
}
.wp-recharger:hover { background: var(--green-soft, #e8f2ec); color: var(--green-deep, #2c5f3f); }
.wp-recharger:focus-visible { outline: 2px solid var(--green, #3f8f63); outline-offset: 1px; }

/* ⚠ Les deux règles de l'état de publication en tête de modale sont parties
   avec leur bandeau (v1.130.8). Une règle qui ne s'applique à aucun nœud est
   du poids que personne ne relit, et qui fait chercher un élément absent. */

.entete-production { flex: 0 0 auto; margin-top: 10px; }
.entete-production > summary { cursor: pointer; font-size: 12.5px; color: var(--muted, #5A6368); padding: 6px 0; }
.entete-production .section-title { margin: 10px 0 4px; }
.entete-requetes { margin: 0; padding-left: 18px; font-size: 13px; }
/* Le titre de la page proposée — même voix que la page publiée, pas que la coque. */
.prop-titre-page {
  margin: 0 0 14px; font-family: "DM Serif Display", serif;
  font-size: 22px; font-weight: 400; line-height: 1.25;
}
.prop-panneau {
  padding: 20px; overflow-y: auto; min-height: 0;
  display: flex; flex-direction: column; gap: 14px;
  background: var(--gris-clair, rgba(127, 127, 127, 0.04));
}
.prop-panneau .section-title { margin: 0; }
.prop-statut { display: flex; align-items: center; gap: 8px; }
.prop-sources td { font-size: 12.5px; padding: 6px 8px; }
.prop-gestes { margin-top: auto; display: flex; flex-direction: column; gap: 8px; padding-top: 12px; border-top: 1px solid var(--border); }
.prop-gestes textarea {
  width: 100%; min-height: 64px; resize: vertical; font: inherit; font-size: 13px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
}
/**
 * ⚠ **Empilées, les colonnes n'ont plus de largeur à régler — ni de repli.**
 * Le repli est neutralisé ICI, et pas seulement la poignée : sans cette ligne,
 * quelqu'un qui a replié sur grand écran puis rétréci sa fenêtre perdrait les
 * quatre verbes (`display: none` sur `.prop-panneau`) ET le bouton pour les
 * ramener (`.prop-poignee` masquée). L'écran l'aurait piégé sans rien afficher.
 */
@media (max-width: 900px) {
  .prop-grille,
  :root[data-decision-repliee="oui"] .prop-grille { grid-template-columns: 1fr; }
  .prop-poignee { display: none; }
  :root[data-decision-repliee="oui"] .prop-panneau { display: flex; }
  .prop-rendu { border-right: 0; border-bottom: 1px solid var(--border); }
}

/* La réponse de l'agent, sous l'instruction qui l'a provoquée — reliées, pas
   éparpillées : c'est le fil de la conversation, pas deux listes. */
.prop-reponse {
  margin-top: 4px; padding: 6px 8px; font-size: 12px;
  border-left: 3px solid var(--accent, #E88B2A);
  background: var(--gris-clair, rgba(127, 127, 127, 0.06));
  border-radius: 0 4px 4px 0;
}

/* Le fil des allers-retours — une conversation, pas une table : dans 360px,
   une réponse de trois phrases doit avoir la pleine largeur pour se lire. */
.ar-fil { display: flex; flex-direction: column; gap: 10px; }
.ar-echange {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12.5px;
}
.ar-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ar-instruction { overflow-wrap: anywhere; }
.ar-qui { font-weight: 700; }
.prop-reponse { overflow-wrap: anywhere; }

/* ── Le centre de notifications : badge d'en-tête + pastilles de ligne ──
   Le badge dit COMBIEN de gestes humains attendent ; la pastille dit LEQUEL,
   sur la ligne concernée. Zéro attente = zéro badge — un « 0 » permanent
   apprendrait à ne plus regarder. */
.notif-badge {
  display: inline-grid; place-items: center; min-width: 22px; height: 22px;
  padding: 0 6px; border-radius: 999px; font-size: 12px; font-weight: 700;
  background: var(--sev-attention-encre, #E88B2A); color: #fff;
  vertical-align: middle; margin-left: 8px;
}

/* ── Le bandeau « personne ne prend les décisions » ──
   Discret entre 5 et 30 minutes, ambre au-delà : au-delà de la fenêtre de
   bail, ce n'est plus une attente, c'est une panne. */
.edito-attente-file {
  margin: 0 0 10px; padding: 8px 12px; border-radius: 6px; font-size: 13px;
  background: var(--gris-clr, #ECEFF0); color: var(--gris, #5A6368);
  border: 1px solid var(--bord, #DCE1E3);
}
.edito-attente-file.warn { background: #FCEBD5; color: #9A5A0B; border-color: #E8B978; }

/* ── #102 : le CTA de ligne, le compteur dépliable, le surlignage ──
   Le badge EST le geste : un verbe, une couleur d'urgence, un motif au survol. */
.cta-action {
  font-size: 12px; font-weight: 700; padding: 4px 10px; border-radius: 6px;
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
}
.cta-action.warn { background: #FCEBD5; color: #9A5A0B; border-color: #E8B978; }
.cta-action.ok { background: #DCF0E7; color: #15694F; border-color: #7FC9A8; }
.cta-action.bad { background: #F8DAD6; color: #A5291B; border-color: #E39A90; }
.cta-action.afaire { background: var(--gris-clr, #ECEFF0); color: var(--gris, #5A6368); border-color: var(--bord, #DCE1E3); }
.cta-action:hover { filter: brightness(0.96); }
.cta-action.mini { font-size: 11px; padding: 2px 8px; cursor: inherit; }
.notif-badge.cliquable { cursor: pointer; border: 0; }
#suivre-panneau {
  position: absolute; z-index: 40; margin-top: 6px;
  background: var(--carte, #fff); border: 1px solid var(--bord, #DCE1E3);
  border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,.12);
  max-width: min(480px, 90vw); max-height: 50vh; overflow-y: auto;
}
.suivre-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border: 0; background: none; cursor: pointer; text-align: left;
  border-bottom: 1px solid var(--bord, #ECEFF0); font-size: 13px;
}
.suivre-item:last-child { border-bottom: 0; }
.suivre-item:hover { background: var(--gris-clr, #F3F5F6); }
.suivre-titre { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Le surlignage bref après « y aller » : la ligne se montre puis s'efface. */
@keyframes surligne-pulse { 0% { background: #FCEBD5; } 100% { background: transparent; } }
tr.surligne > td { animation: surligne-pulse 2s ease-out; }

/* La description d'une action : la PORTÉE d'abord, le détail replié ensuite.
   ⚠ La cellule est en `font-weight: 600` (c'est le titre) : sans remise à 400,
   quatre cents caractères de description sortiraient en gras et écraseraient
   le titre qu'ils accompagnent. */
.reg-v2 td .desc { display: block; font-weight: 400; margin-top: 4px; }
.reg-v2 td .desc-portee { display: block; font-size: 11.5px; line-height: 1.45; }
/* ⛔ Le repli est discret, la portée ne l'est pas : elle se lit sans clic. */
.reg-v2 td .desc-repli { margin-top: 3px; }
.reg-v2 td .desc-repli > summary {
  font-size: 11.5px; color: var(--rv-gris); cursor: pointer; list-style: none;
}
.reg-v2 td .desc-repli > summary::-webkit-details-marker { display: none; }
.reg-v2 td .desc-repli[open] > summary { color: var(--rv-txt, inherit); }
.reg-v2 td .desc-texte {
  display: block; font-size: 11.5px; line-height: 1.5; margin-top: 3px;
}
/* ⚠ La flèche TOURNE à l'ouverture. Un « ▸ » figé au-dessus d'un texte déplié
   dit « replié » alors que c'est ouvert — petit mensonge d'état, celui qu'on
   remarque sans savoir le nommer. */
.reg-v2 td .desc-fleche { display: inline-block; transition: transform .12s ease; }
.reg-v2 td .desc-repli[open] > summary .desc-fleche { transform: rotate(90deg); }


/* ── Références éditoriales ─────────────────────────────────────────────────
   ⚠ Le contenu est du TEXTE BRUT dont les blancs comptent : la zone d'édition
   et les aperçus utilisent une police à chasse fixe et conservent les espaces.
   Un rendu proportionnel ferait paraître alignée une indentation qui ne l'est
   pas — sur un YAML, c'est la structure elle-même qu'on lirait de travers. */
.ref-grille { display: flex; flex-direction: column; gap: 20px; }
.ref-carte h3 { display: flex; align-items: center; gap: 8px; }
.ref-description { margin: 0 0 8px; }
.ref-manque { margin: 0 0 8px; }
.ref-poids { margin: 0 0 8px; }
.ref-texte,
.ref-curl,
.ref-version-texte {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre;
  tab-size: 2;
}
.ref-texte { width: 100%; box-sizing: border-box; resize: vertical; }
/* ⚠ `overflow-x: auto` sur les blocs figés : une ligne longue ne doit pas
   pousser la page entière, elle défile dans son propre cadre. */
.ref-curl,
.ref-version-texte { overflow-x: auto; padding: 10px 12px; border-radius: 6px; }
.ref-versement { margin: 0 0 10px; }
.ref-mesure { align-self: center; }
.ref-historique { margin-top: 12px; }
.ref-version { margin-bottom: 8px; }
.ref-version summary { cursor: pointer; }

/* ── Références v2 : la grille, le collage et le rendu ──────────────────────
   ⚠ Côte à côte est l'intérêt même de l'écran : sans le rendu visible, on
   édite des cellules sans savoir ce que le modèle lira. Sur écran étroit, la
   colonne du rendu passe DESSOUS plutôt que de rétrécir la grille.

   ⚠ Refonte v1.94.0 — Fred : « années 90, on est en 2026 ». Rien d'ajouté au
   projet : la palette, l'ombre et le bouton viennent des variables déjà là.
   Le mono-espacé est RÉSERVÉ au contenu machine (clé, TSV, rendu servi) ;
   partout ailleurs il faisait lire une prose de terminal. */
/* ⛔ **UNE colonne, et le rendu EN TÊTE.** Tranche de Fred sur ses 152 lignes
   réelles : en deux colonnes côte à côte, la colonne « Clé » tombait à trois
   caractères et rendait « Sou / rce / _un / iqu / e » sur cinq lignes. Le
   côte-à-côte était juste sur six lignes d'exemple et faux sur cent
   cinquante-deux.

   ⚠ Le cap de largeur existe pour la LISIBILITÉ, pas par frilosité : au-delà,
   une ligne de valeur traverse l'écran et l'œil perd la ligne au retour. */
.ref-v2 { display: flex; flex-direction: column; gap: 20px; max-width: 1400px; }
/* ⚠ Borné : un rendu de 400 lignes posé en tête repousserait la grille hors de
   l'écran, ce qui reviendrait à ne plus la montrer. */
.refs2-sortie { max-height: 340px; }

/* La carte : le conteneur commun aux trois panneaux. */
.refs2-carte {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow);
  padding: 18px 20px;
}

/* ── Hiérarchie typographique : titre, sous-titre, corps ────────────────── */
.refs2-tete {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.refs2-tete h3 {
  margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -0.01em;
  color: var(--text);
}
.refs2-sous { margin: 3px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--text-mid); }
.refs2-sous code {
  font-size: 11.5px; padding: 1px 5px; border-radius: 4px;
  background: var(--green-pale); color: var(--green);
}
/* Le compte, à droite du titre : un chiffre lisible sans compter les lignes. */
.refs2-compte {
  flex: none; font-size: 12px; font-weight: 650; color: var(--green);
  background: var(--green-pale); border-radius: 999px; padding: 4px 11px;
}

/* ── La grille ─────────────────────────────────────────────────────────────
   ⚠ Le cadre défile SEUL : une valeur longue ne pousse pas la page. */
.refs2-cadre { overflow-x: auto; border: 1px solid var(--border); border-radius: 8px; }
.refs2-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.refs2-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--cream, #faf8f3);
  border-bottom: 1px solid var(--border);
  padding: 9px 12px; text-align: left;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--text-mid); white-space: nowrap;
}
.refs2-table td {
  padding: 9px 12px; vertical-align: top;
  border-bottom: 1px solid var(--border);
  word-break: break-word;
}
.refs2-table tbody tr:last-child td { border-bottom: none; }
/* Alternance discrète : elle sert à suivre une ligne longue jusqu'au bout. */
.refs2-table tbody tr:nth-child(even) { background: var(--bg); }
.refs2-table tbody tr:hover { background: var(--green-pale); }
/* ⛔ Une CONSIGNE n'est pas une donnée : le liseré la distingue sans la crier. */
.refs2-table tr.est-consigne td:first-child { box-shadow: inset 3px 0 0 var(--gold); }

.refs2-jeton {
  display: inline-block; font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 5px; white-space: nowrap;
  background: var(--green-pale); color: var(--green);
}
.refs2-jeton.pale { background: #eceff0; color: var(--text-mid); }
.refs2-cle {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px; color: var(--text);
}
.refs2-valeur { color: var(--text); line-height: 1.45; }
.refs2-preuve { font-size: 12px; color: var(--text-mid); }
/* ⚠ Une ligne sans preuve le DIT. Une cellule vide se lit comme « pas encore
   rempli » ; « sans preuve » se lit comme ce que c'est. */
.refs2-sans-preuve { font-style: italic; color: var(--text-light, #6b7671); }
/* ⚠ **Le même gris que « sans preuve », jamais le doré de « à compléter ».**
   Une ligne non annotée est l'état NORMAL — la consigne dit une réserve d'emploi,
   et la plupart des lignes n'en ont pas. Lui donner la couleur qui appelle à
   l'action aurait mis cent cinquante lignes en alerte pour rien. */
.refs2-sans-consigne { font-style: italic; color: var(--text-light, #6b7671); }
/* ⚠ La consigne est une annotation, pas une donnée : elle se lit un cran en
   dessous de la valeur, pour que l'œil qui balaye la grille tombe d'abord sur
   ce que le référentiel AFFIRME. */
.refs2-c-consigne { font-size: 12px; color: var(--text-light, #6b7671); }
.refs2-marques { white-space: nowrap; text-align: right; }
.refs2-marque {
  display: inline-block; font-size: 10.5px; font-weight: 650;
  padding: 2px 7px; border-radius: 999px; margin-left: 4px;
}
.refs2-marque.consigne { background: var(--gold-pale); color: var(--gold); }
.refs2-marque.editorial { background: #eceff0; color: var(--text-mid); }

/* ── L'état vide : il MONTRE le format plutôt que de le nommer ──────────── */
.refs2-vide {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 30px 20px 26px;
  border: 1px dashed var(--green-line, #cfe2d6); border-radius: 10px;
  background: linear-gradient(180deg, var(--bg) 0%, var(--white) 70%);
}
.refs2-vide-icone {
  width: 44px; height: 44px; margin-bottom: 12px;
  fill: none; stroke: var(--green); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; opacity: 0.5;
}
.refs2-vide-titre { margin: 0 0 5px; font-size: 14.5px; font-weight: 650; color: var(--text); }
.refs2-vide-dit {
  margin: 0 0 16px; max-width: 46ch; font-size: 13px; line-height: 1.5; color: var(--text-mid);
}
.refs2-exemple {
  border-collapse: collapse; margin-bottom: 16px; text-align: left;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 11.5px;
  background: var(--white); border: 1px solid var(--border); border-radius: 8px;
  overflow: hidden;
}
.refs2-exemple th {
  padding: 6px 10px; background: var(--green-pale); color: var(--green);
  font-weight: 700; border-bottom: 1px solid var(--green-line, #cfe2d6); white-space: nowrap;
}
.refs2-exemple td {
  padding: 6px 10px; color: var(--text-mid);
  border-bottom: 1px solid var(--border); vertical-align: top;
}
.refs2-exemple tr:last-child td { border-bottom: none; }
.refs2-vide-action { font-size: 12.5px; }

/* ── Le collage ────────────────────────────────────────────────────────── */
.refs2-garde {
  margin: 0 0 12px; padding: 9px 12px; border-radius: 7px;
  background: var(--gold-pale); color: #7a5a08;
  font-size: 12.5px; line-height: 1.45;
  border-left: 3px solid var(--gold);
}
.refs2-zone {
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  padding: 10px 12px; color: var(--text);
}
.refs2-zone:focus {
  outline: none; border-color: var(--green-light);
  box-shadow: 0 0 0 3px var(--green-pale); background: var(--white);
}
.refs2-zone::placeholder { color: var(--text-light, #6b7671); opacity: 0.75; }
/* ⚠ Le principal PLEIN, le secondaire en contour : la hiérarchie se lit avant
   de lire les mots. Le principal reste éteint tant qu'aucun aperçu n'a été
   regardé — l'ordre du geste est une garde, pas une décoration. */
.refs2-actions { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.refs2-actions .btn { font-size: 13px; padding: 8px 16px; }
.refs2-actions .btn:disabled { cursor: not-allowed; }

/* ── Le rendu servi ────────────────────────────────────────────────────── */
.refs2-jauge {
  display: flex; align-items: baseline; gap: 8px;
  padding: 11px 14px; margin-bottom: 12px;
  background: var(--green-pale); border-radius: 8px;
  border-left: 3px solid var(--green);
}
.refs2-jauge-nombre {
  font-size: 15px; font-weight: 700; color: var(--green-deep, #0f3d26);
  font-variant-numeric: tabular-nums;
}
/* ⛔ Un référentiel VIDE n'est pas un succès. Le peindre de la couleur qui dit
   « c'est bon » ferait lire un manque comme un état atteint — le même défaut
   que « jamais rédigé » affiché à côté d'un bouton « Publier ». */
.refs2-jauge.est-vide { background: var(--gold-pale); border-left-color: var(--gold); }
.refs2-jauge.est-vide .refs2-jauge-nombre { color: #7a5a08; }
.refs2-jauge-dit { font-size: 12.5px; color: #7a5a08; }
.refs2-sortie {
  margin: 0; overflow: auto;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.refs2-sortie-vide { color: var(--text-light, #6b7671); font-style: italic; white-space: normal; }

.ref-diff { margin-top: 12px; }
.ref-absentes,
.ref-modifiees { margin: 6px 0 0 18px; }

/* ── Les documents entiers, repliés ─────────────────────────────────────── */
.ref-v1 { margin-top: 28px; border-top: 1px solid var(--border); padding-top: 18px; }
.ref-v1 summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--text-mid);
  padding: 8px 12px; border-radius: 7px; background: var(--bg);
  border: 1px solid var(--border); list-style: none;
  display: flex; align-items: center; gap: 8px;
}
.ref-v1 summary::-webkit-details-marker { display: none; }
.ref-v1 summary::before { content: "▸"; color: var(--green); font-size: 11px; }
.ref-v1[open] summary::before { content: "▾"; }
.ref-v1 summary:hover { background: var(--green-pale); color: var(--green); }
#edito-cle,
#edito-preuve { width: 100%; box-sizing: border-box; margin-bottom: 8px; }
/* ⚠ Les trois issues d'un point bloquant tiennent sur une ligne, et passent à
   la ligne suivante plutôt que de déborder de la carte. */
.bloquant-issues { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

/* ⚠ Le cadre de l'en-tête incomplet reprend l'allure de « Bloqué » : même
   nature — une porte fermée qui dit pourquoi et comment l'ouvrir — donc même
   forme, pour qu'on la reconnaisse sans la relire. */
.edito-entete-manquante { margin-bottom: 10px; }

/* ── Le fil ALLERS-RETOURS : demandes et versions entrelacées ───────────────
   ⚠ La version produite se distingue de la demande par un liseré, pas par une
   couleur de fond : le fil doit se lire comme UN dialogue, pas comme deux
   listes accolées. */
.ar-version { border-left: 3px solid var(--line, #d0d7de); padding-left: 10px; }
.ar-entetes { margin: 6px 0 0 18px; padding: 0; }
.ar-entetes li { list-style: disc; }
.ar-sans-date { font-style: italic; }
.ar-apercu summary { cursor: pointer; }
/* ⚠ Le corps d'une version est du texte long : il défile dans son cadre plutôt
   que de pousser la colonne entière. */
.ar-corps {
  max-height: 40vh;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 12.5px;
}

/* ── Le formulaire de BRIEF — demander un article qui n'existe pas encore ───
   ⚠ Rien de grisé : l'envoi reste cliquable et NOMME ce qui manque. Un bouton
   éteint qui ne dit pas pourquoi oblige à deviner (grief tranché v1.92.0). */
.brief-form { display: flex; flex-direction: column; gap: 14px; min-width: min(460px, 80vw); }
.brief-champ { display: flex; flex-direction: column; gap: 5px; }
.brief-libelle { font-size: 12.5px; font-weight: 650; color: var(--text); }
.brief-champ input,
.brief-champ select {
  padding: 8px 11px; font-size: 13px; color: var(--text);
  background: var(--white); border: 1px solid var(--border); border-radius: 7px;
}
.brief-champ input:focus,
.brief-champ select:focus {
  outline: none; border-color: var(--green-light);
  box-shadow: 0 0 0 3px var(--green-pale);
}
/* ⚠ Le mono sur la seule adresse : c'est le champ qui compose un identifiant,
   et le mono dit « ceci est une valeur machine » sans phrase. */
.brief-mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 12.5px; }
.brief-aide { font-size: 11.5px; line-height: 1.4; color: var(--text-mid); }
/* ⛔ L'adresse est le SEUL champ irréparable après l'envoi : elle porte donc
   le seul avertissement du formulaire. En mettre partout n'alerterait plus. */
.brief-aide.brief-alerte {
  color: #7a5a08; background: var(--gold-pale);
  border-left: 3px solid var(--gold); border-radius: 0 5px 5px 0; padding: 6px 9px;
}
.brief-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .brief-duo { grid-template-columns: 1fr; } }
.brief-plus summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--green);
  padding: 6px 0; list-style: none;
}
.brief-plus summary::-webkit-details-marker { display: none; }
.brief-plus summary::before { content: "▸ "; font-size: 10px; }
.brief-plus[open] summary::before { content: "▾ "; }
.brief-plus .brief-champ { margin-top: 10px; }
.brief-actions { display: flex; gap: 10px; margin-top: 4px; }

/* ── Références v2 : l'édition en place ────────────────────────────────────
   ⚠ Les largeurs viennent d'un `<colgroup>` : `table-layout` reste automatique
   pour qu'une cellule courte n'impose pas sa largeur à tout le monde. */
.refs2-table { table-layout: fixed; }
.refs2-c-jeton,
.refs2-c-cle,
.refs2-c-valeur,
.refs2-c-preuve,
.refs2-c-consigne { overflow-wrap: anywhere; }
/* ⛔ La clé ne se coupe QUE si elle n'a pas le choix : c'est elle qui rendait
   l'escalier illisible quand la colonne était trop étroite. */
.refs2-c-cle { word-break: normal; }

/* ⚠ Une cellule éditable se signale au survol, pas en permanence : un cadre sur
   chacune des 900 cellules ferait un damier, et plus rien ne se lirait. */
.refs2-editable { cursor: text; border-radius: 4px; }
.refs2-editable:hover { box-shadow: inset 0 0 0 1px var(--green-line, #cfe2d6); }
.refs2-editable:focus-visible { outline: 2px solid var(--green-light); outline-offset: -2px; }
/* ⚠ Une valeur vide le DIT — c'est le cas que Fred vient chercher : compléter
   les lignes sans valeur. Une cellule blanche ne se distingue pas d'une marge. */
.refs2-vide-cellule { color: var(--gold); font-style: italic; font-size: 12px; }
.refs2-saisie {
  width: 100%; box-sizing: border-box; padding: 4px 6px; font: inherit;
  border: 1px solid var(--green-light); border-radius: 5px; background: var(--white);
}
/* ⛔ Une cellule MODIFIÉE mais pas encore enregistrée se distingue des deux
   autres états : elle n'est ni ce qui est en base, ni ce qui est en train
   d'être tapé. Sans ce troisième signal, on croit avoir enregistré. */
.refs2-table td.est-modifiee {
  background: var(--gold-pale); box-shadow: inset 2px 0 0 var(--gold);
}

/* ── Le bandeau des modifications en attente ───────────────────────────────
   ⛔ Caché tant qu'il n'y a rien, jamais grisé : un bandeau permanent qui dit
   « 0 modifiée » apprend à ne plus le regarder, et c'est celui-là qu'il faut
   voir le jour où il dit « 12 ». */
.refs2-pendantes {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px; padding: 10px 14px; border-radius: 8px;
  background: var(--gold-pale); border-left: 3px solid var(--gold);
}
.refs2-pendantes-compte { font-size: 13px; font-weight: 650; color: #7a5a08; }
.refs2-pendantes-liste { flex: 1 1 100%; margin: 0; padding-left: 18px; font-size: 12px; color: #7a5a08; }
.refs2-pendantes-liste li { margin: 2px 0; }
/* ⛔ La perte d'accent se voit AVANT d'être enregistrée. Le contrôle avant
   bascule est délibérément sensible aux accents ; une valeur retapée sans les
   siens a l'air identique et n'est plus reconnue. */
.refs2-accent { color: var(--red); font-weight: 650; }
.refs2-pendantes .btn { font-size: 12.5px; padding: 6px 13px; }

/* ── Décisions STAGNANTES — personne n'est venu les chercher ────────────────
   ⛔ Distinct du bandeau « non traitées » : celui-là compte les reprises, donc
   un agent qui ESSAIE. Celui-ci compte l'absence d'agent, et c'est plus grave —
   d'où le rouge plutôt que l'ambre. Mesuré le 29/08 : `publish` manquait à la
   liste blanche du consommateur DEPUIS TOUJOURS, et rien ne le disait. */
.edito-stagnantes {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 12px; padding: 11px 15px; border-radius: 8px;
  background: var(--red-pale); border-left: 3px solid var(--red);
  color: var(--red-deep, #b42318); font-size: 13.5px; line-height: 1.5;
}
.edito-stagnantes code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px; padding: 1px 6px; border-radius: 4px;
  background: rgba(180, 35, 24, 0.1);
}
/* ⚠ La phrase qui dit QUOI FAIRE reste lisible, pas décorative : c'est elle
   qui distingue « bloqué » de « voici comment sortir ». */
.edito-stagnantes .muted { flex: 1 1 100%; font-size: 12.5px; color: #8a3a32; }
.edito-stagnantes .btn { flex: none; }
.edito-stagnantes-detail { flex: 1 1 100%; margin: 6px 0 0; padding-left: 20px; font-size: 12.5px; }
.edito-stagnantes-detail li { margin: 2px 0; }

/* ── Ce qu'il RESTE à faire après une correction ────────────────────────────
   ⛔ Fred a signalé « la modification d'un paragraphe » comme mockée. Elle ne
   l'est pas — elle crée une vraie version. Mais la route de corps ne touche NI
   décision NI WordPress : corriger ne change rien à la page publiée tant qu'on
   n'a pas approuvé puis publié. Ce n'était pas un composant mort, c'était un
   parcours interrompu — plus trompeur, parce que tout a l'air de marcher. */
.edito-suite-correction {
  margin: 0 0 12px; padding: 11px 15px; border-radius: 8px;
  background: var(--green-pale); border-left: 3px solid var(--green);
  color: var(--green-deep, #0f3d26); font-size: 13.5px; line-height: 1.5;
}
/* ⚠ L'avertissement sur la page en ligne se voit DANS la phrase : c'est le seul
   endroit où l'écart entre ce qu'on lit et ce que le public lit est dit. */
.edito-suite-correction strong { font-weight: 650; }

/* ── Les cinq outils de catalogue ──────────────────────────────────────────
   ⛔ Repliés et sous les gestes de relecture : poser une catégorie n'engage
   aucun octet et ne gèle rien. Les mettre au même niveau qu'« Approuver »
   ferait hésiter devant six boutons là où il n'y a qu'une question. */
.outils-catalogue { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
.outils-catalogue summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--green); list-style: none;
}
.outils-catalogue summary::-webkit-details-marker { display: none; }
.outils-catalogue summary::before { content: "▸ "; font-size: 10px; }
.outils-catalogue[open] summary::before { content: "▾ "; }
.outils-aide { margin: 8px 0 10px; font-size: 12px; }
.outil-ligne { display: grid; grid-template-columns: 190px 1fr auto; gap: 8px; align-items: center; margin-bottom: 7px; }
@media (max-width: 720px) { .outil-ligne { grid-template-columns: 1fr; } }
.outil-ligne label { font-size: 12.5px; color: var(--text-mid); }
.outil-ligne input {
  padding: 6px 9px; font-size: 12.5px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--white); color: var(--text);
}
.outil-ligne input:focus { outline: none; border-color: var(--green-light); box-shadow: 0 0 0 3px var(--green-pale); }

/* ── Rendu Markdown — « Ce que le modèle lit » (v1.113.0) ────────────────
 *
 * ⚠ Portée au conteneur `.md-rendu` : ces règles décrivent un DOCUMENT, pas
 * l'application. Les laisser fuir sur `h1`/`table` global aurait repeint des
 * écrans que personne n'a demandé de changer.
 */
.md-rendu { line-height: 1.6; max-width: 78ch; }
.md-rendu .md-h1 { font-size: 1.35rem; margin: 0 0 .6rem; }
.md-rendu .md-h2 { font-size: 1.15rem; margin: 1.4rem 0 .5rem; border-top: 1px solid var(--bord, #e2e8e4); padding-top: .8rem; }
.md-rendu .md-h3 { font-size: 1rem; margin: 1rem 0 .4rem; }
.md-rendu p { margin: .5rem 0; }
.md-rendu .md-liste { margin: .5rem 0 .5rem 1.3rem; padding: 0; }
.md-rendu .md-liste li { margin: .2rem 0; }
.md-rendu .md-quote { margin: .7rem 0; padding: .4rem .9rem; border-left: 3px solid var(--accent, #1a5c3a); background: rgba(0,0,0,.03); }
.md-rendu .md-code { background: rgba(0,0,0,.04); border: 1px solid var(--bord, #e2e8e4); border-radius: 6px; padding: .7rem .9rem; overflow-x: auto; font-size: .82rem; }
.md-rendu code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .85em; }
.md-rendu .md-table { border-collapse: collapse; margin: .8rem 0; font-size: .88rem; display: block; overflow-x: auto; }
.md-rendu .md-table th, .md-rendu .md-table td { border: 1px solid var(--bord, #e2e8e4); padding: .35rem .6rem; text-align: left; }
.md-rendu .md-table th { background: rgba(0,0,0,.04); }
.md-rendu hr { border: 0; border-top: 1px solid var(--bord, #e2e8e4); margin: 1.2rem 0; }

/* ── Faits manquants + référentiel, côte à côte (v1.115.0) ───────────────
 *
 * ⚠ La question qu'on se pose devant l'un est celle qu'on résout avec l'autre :
 * « ce fait manque-t-il vraiment, ou est-il là et mal retrouvé ? ». Les
 * empiler verticalement obligerait à faire défiler entre les deux.
 */
.contexte-grille { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 1.4rem; }
@media (max-width: 900px) { .contexte-grille { grid-template-columns: 1fr; } }
.contexte-gauche h3, .contexte-droite h3 { margin: 0 0 .6rem; font-size: 1rem; }
.faits-manquants { list-style: none; margin: 0; padding: 0; }
.fait-manquant { display: flex; align-items: center; gap: .55rem; padding: .45rem 0; border-bottom: 1px solid var(--bord, #e2e8e4); flex-wrap: wrap; }
.fait-manquant:last-child { border-bottom: none; }
/* ⚠ Le compteur en tête : c'est lui qui dit par où commencer. */
.fait-compteur { min-width: 1.7rem; text-align: center; font-weight: 700; background: var(--accent, #1a5c3a); color: #fff; border-radius: 999px; padding: .05rem .4rem; font-size: .8rem; }
.fait-titre { flex: 1 1 auto; min-width: 8rem; }
.fait-sujet { font-size: .75rem; opacity: .65; }

/* ── ALLERS-RETOURS repliables (v1.116.0) ────────────────────────────────
 *
 * ⛔ Grief de Fred, 30/08 : tout était déployé, les boutons d'action se
 * retrouvaient à des écrans de distance, et l'on arrivait sur le HAUT d'un
 * historique dont c'est la FIN qui intéresse.
 */
.ar-fil details.ar-echange { border: 1px solid var(--bord, #e2e8e4); border-radius: 8px; margin: .4rem 0; background: rgba(0,0,0,.015); }
.ar-fil details.ar-echange > summary { cursor: pointer; padding: .5rem .7rem; list-style: none; display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
/* ⚠ Le marqueur natif est retiré des DEUX façons : WebKit a la sienne. */
.ar-fil details.ar-echange > summary::-webkit-details-marker { display: none; }
/* Le chevron : ▶ fermé, ▼ déployé. Discret, et il tourne sans animation lourde. */
.ar-fil details.ar-echange > summary::before { content: "▶"; font-size: .7rem; opacity: .55; transition: transform .12s ease; display: inline-block; }
.ar-fil details.ar-echange[open] > summary::before { transform: rotate(90deg); }
.ar-fil details.ar-echange > summary:hover { background: rgba(0,0,0,.03); }
.ar-fil details.ar-echange > *:not(summary) { padding: 0 .7rem; }
.ar-fil details.ar-echange > *:last-child { padding-bottom: .6rem; }
/* ⚠ Le résumé du message : une ligne, coupée proprement s'il déborde. */
.ar-resume { flex: 1 1 12rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: .8; font-size: .88rem; }

/* Le brief en attente — encadré, sous « rien à relire » (v1.116.0). */
.brief-envoye { margin-top: 1rem; border: 1px solid var(--bord, #e2e8e4); border-left: 3px solid var(--accent, #1a5c3a); border-radius: 8px; padding: .8rem 1rem; background: rgba(0,0,0,.02); }
.brief-champs { list-style: none; margin: .4rem 0 .7rem; padding: 0; }
.brief-champs li { margin: .2rem 0; }
.brief-champs .muted { display: inline-block; min-width: 8.5rem; }

/* ── Le formulaire « donnez-lui sa source » (v1.118.0) ────────────────────
 *
 * ⛔ Grief de Fred, 30/08 : « cet écran n'est pas compréhensible ». Mesuré au
 * navigateur — le formulaire n'avait aucun cadre, son POURQUOI vivait sous le
 * bouton, et l'affirmation à sourcer était à 847 px au-dessus du bord.
 */
.edito-saisie-cadre {
  border: 1px solid var(--border); border-left: 3px solid var(--vert, #1B5E4A);
  border-radius: 8px; padding: 12px 14px; margin: 10px 0 14px;
  background: var(--gris-clair, rgba(127, 127, 127, 0.04));
}
/* L'affirmation rappelée : c'est d'ELLE qu'on parle, elle se lit en premier. */
.edito-saisie-cible {
  margin: 0 0 8px; padding: 8px 10px; border-radius: 6px;
  background: rgba(180, 60, 40, 0.07); font-size: 13px; font-style: italic;
}
.edito-saisie-titre { display: block; font-weight: 650; margin: 0 0 4px; }
/* ⚠ Le POURQUOI coiffe les champs. Sous le bouton, on le lit après avoir
   décidé — ou jamais. */
.edito-saisie-aide { margin: 0 0 10px; font-size: 12.5px; }
/* ⚠ Les exemples SORTENT du champ : en `placeholder` ils se lisaient comme des
   valeurs déjà saisies — le brief lui-même s'y est trompé. */
.edito-exemple { margin: 2px 0 10px; font-size: 12px; opacity: .75; }
.edito-saisie-cadre label { display: block; margin: 6px 0 2px; font-size: 12.5px; }
.edito-saisie-cadre input[type="text"], .edito-saisie-cadre textarea { width: 100%; box-sizing: border-box; }
/* Les gestes généraux, nommés pour ce qu'ils sont — ils ne portent pas sur
   l'affirmation en cours. */
.edito-autres { margin: 14px 0 4px; }

/* ── « Voir dans le contenu » et l'affirmation étiquetée (v1.119.0) ───────
 *
 * ⛔ Fred, 30/08 : « on ne comprend pas la valeur… on valide quoi ? » et « il
 * faudrait quand on clique sur le texte arriver sur la phrase soulignée
 * quelques secondes, pour bien comprendre où c'est utilisé ».
 */
.edito-cible-bloc { margin: 0 0 10px; }
.edito-cible-titre {
  margin: 0 0 4px; font-size: 11px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-light); font-weight: 600;
}
/* ⚠ Le rappel reste SÉLECTIONNABLE : Fred doit pouvoir copier la phrase. Le
   geste vit sur un bouton à part, pas sur le texte. */
.edito-saisie-cible { user-select: text; cursor: text; }
.edito-voir-bloquant { margin-top: 6px; }
/* Les deux temps du geste, numérotés — deux champs sans lien lisible se lisent
   comme deux questions séparées. */
.edito-etape {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-right: 6px; border-radius: 999px;
  background: var(--vert, #1B5E4A); color: #fff; font-size: 11px; font-weight: 700;
}
/* ⚠ Le surlignage se retire tout seul après ~2 s : permanent, il deviendrait un
   état de plus à gérer, et deux clics laisseraient deux cartes marquées. */
.bloquant-surligne {
  animation: bloquant-flash 2.2s ease-out;
  scroll-margin: 16px;
}
@keyframes bloquant-flash {
  0%, 55% { background: rgba(27, 94, 74, 0.16); box-shadow: 0 0 0 3px rgba(27, 94, 74, 0.35); }
  100% { background: transparent; box-shadow: 0 0 0 3px rgba(27, 94, 74, 0); }
}

/* ═══════════════════════════ Les PROMPTS SEO ═══════════════════════════ */
/* La matrice verbe × type, et le détail d'un couple. Le vocabulaire suit
   celui des autres écrans SEO : `card`, `data`, `pill`, `muted`. */

.prompt-matrice-defile { overflow-x: auto; max-width: 100%; }

/* Quarante cases, dont trente-quatre vides au départ : sans une largeur
   minimale, les colonnes pourvues écrasent les colonnes à pourvoir et la
   matrice cesse de dire ce qui manque. */
.prompt-table th[scope="row"] { white-space: nowrap; font-weight: 600; color: var(--text); }
.prompt-table .prompt-case { min-width: 190px; vertical-align: top; }
.prompt-table .prompt-case .btn { margin-top: 4px; }

.prompt-case-titre {
  display: block;
  margin-top: 3px;
  font-weight: 400;
  color: var(--text-mid);
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Le verbe sans consommateur : lisible, jamais actionnable. */
.prompt-case-avenir { background: var(--bg); }

.prompt-detail .prompt-champ { display: block; margin: 14px 0; font-weight: 600; }
.prompt-detail .prompt-champ input,
.prompt-detail .prompt-champ textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-top: 5px;
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  color: var(--text);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 6px;
}
/* Les trois morceaux du prompt en chasse fixe : ce sont des gabarits, et un
   `{{placeholder}}` mal fermé se voit à l'œil dans une police à chasse fixe. */
.prompt-detail textarea.prompt-texte {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  resize: vertical;
}
.prompt-duo { display: flex; gap: 16px; }
.prompt-duo .prompt-champ { flex: 1; }

.prompt-actions { display: flex; align-items: center; gap: 12px; margin: 12px 0 4px; }
/* ⛔ `not-allowed`, pas le `progress` de `.btn:disabled` : ce bouton n'attend
   rien, il REFUSE. Un curseur d'attente ferait croire à un envoi en cours. */
.prompt-enregistrer:disabled { cursor: not-allowed; }

.prompt-verdict { min-height: 1.2em; margin: 8px 0 0; font-size: 13px; color: var(--text-light); }
.prompt-verdict-refus { color: var(--red); font-weight: 600; }

.prompt-absent { color: var(--red); font-weight: 600; }
.prompt-detail .prompt-retour { margin-bottom: 10px; }
.prompt-histo td { vertical-align: middle; }

/* ══════════════════ Stock d'idées (/#stock) ══════════════════
   ⚠ Dans le stylesheet GLOBAL, comme les prompts et les références : le
   gabarit ne charge qu'une feuille (`index.html`), et un second fichier
   n'arriverait jamais au navigateur. */
.stock-planning { margin-bottom: 16px; }
.stock-planning > summary { cursor: pointer; font-weight: 600; }
.stock-planning form { margin-top: 14px; }
.stock-jours { border: 1px solid var(--border); border-radius: 8px; padding: 8px 12px 12px; }
.stock-jours legend { font-size: 12px; color: var(--text-light); padding: 0 6px; }
.stock-jour { display: inline-flex; align-items: center; gap: 5px; margin-right: 14px; font-size: 13px; }
.stock-planning-duo { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 14px; align-items: flex-start; }
.stock-champ { display: flex; flex-direction: column; gap: 4px; font-weight: 600; font-size: 13px; }
.stock-champ input[type="number"], .stock-champ input[type="text"] {
  border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px;
  font: inherit; font-weight: 400; max-width: 160px;
}
.stock-champ-bascule { flex-direction: row; align-items: center; gap: 6px; align-self: center; }
.stock-aide { font-weight: 400; font-size: 12px; color: var(--text-light); }
.stock-avertissement {
  margin: 14px 0 0; padding: 8px 10px; border-left: 3px solid var(--gold);
  background: var(--gold-pale); font-size: 13px; color: var(--text);
}
.stock-verdict { min-height: 1.2em; margin: 8px 0 0; font-size: 13px; color: var(--text-light); }
.stock-verdict-refus { color: var(--red); font-weight: 600; }
.stock-planning-actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.stock-barre { justify-content: space-between; }
/* ⚠ La table déborde sur un portable : c'est ELLE qui défile, jamais la page. */
.panel-card:has(.stock-table) { overflow-x: auto; }
.stock-table td { vertical-align: middle; }
.stock-rang { width: 44px; text-align: right; color: var(--text-light); font-variant-numeric: tabular-nums; }
.stock-priorite { width: 70px; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-light); }
.stock-sujet strong { display: block; }
.stock-slug { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text-light); }
.stock-mot-cle { color: var(--text-light); }
.stock-gestes { white-space: nowrap; text-align: right; }
.stock-gestes .btn { margin-left: 4px; }
/* Un geste impossible se voit éteint — il ne disparaît pas : la colonne
   garderait sa largeur, et les boutons sauteraient d'une ligne à l'autre. */
.stock-gestes .btn:disabled { opacity: 0.35; cursor: not-allowed; }
.stock-lot p { margin: 0 0 10px; }
.stock-lot-actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; }

/* ── Le panneau BYO : copier un prompt, verser le TSV qu'il produit ────────── */
.stock-lot-ia { margin-top: 18px; }
.stock-lot-ia h3 { margin: 0 0 4px; }
.stock-lot-bloc { margin-top: 18px; }
.stock-lot-bloc h4 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-light); }

/* ⛔ **Le prompt est BORNÉ en hauteur et défile.** Un modèle de quarante lignes
   repousserait le champ de dépôt hors de l'écran — or les deux blocs ne valent
   que l'un par l'autre : on copie ici, on revient déposer juste en dessous. */
.stock-prompt {
  max-height: 260px; overflow: auto; margin: 0;
  padding: 12px 14px; border: 1px solid var(--green-line, #cfe2d6); border-radius: 6px;
  background: var(--bg); font-size: 12px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.stock-prompt-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; }

/* ⚠ Le DORÉ de « à faire », pas le rouge d'une panne : un prompt non défini est
   une configuration à poser, pas une erreur à signaler. */
.stock-prompt-absent {
  padding: 12px 14px; border-left: 3px solid var(--gold); border-radius: 4px;
  background: var(--gold-pale, #fdf6e3); font-size: 13px; line-height: 1.6;
}
.stock-prompt-absent code { font-size: 12px; }

.stock-lot-depot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.stock-lot-avertit { margin: 10px 0 0; font-size: 12px; }

/* ⚠ **Des pastilles, pas du texte courant.** Ce sont des noms à RECOPIER dans
   un tableur : sans fond ni respiration, « sujet slug requete_cible » se lit
   comme une phrase, et on recopie le mauvais mot. */
.stock-lot-bloc code {
  padding: 1px 5px; border-radius: 3px; background: var(--bg);
  border: 1px solid var(--green-line, #cfe2d6); font-size: 11.5px;
}
/* ⚠ Le compteur du titre H1 : discret tant qu'on est dans les clous, criant
   au-delà — c'est le seul moment où il a quelque chose à dire. */
.brief-compteur { margin-left: 6px; font-variant-numeric: tabular-nums; }
.brief-compteur-trop { color: var(--red); font-weight: 700; }
/* ⚠ Les divergences se lisent SANS geste : ni accordéon, ni repli. C'est
   l'information que Fred ne va pas chercher, parce qu'il ignore qu'elle existe. */
.edito-divergences {
  margin: 14px 0; padding: 10px 12px;
  border-left: 3px solid var(--gold); background: var(--gold-pale);
}
.edito-divergences h4 { margin: 0 0 4px; font-size: 14px; }
.edito-divergences p { margin: 0 0 8px; font-size: 13px; }
.edito-divergences ul { margin: 0; padding-left: 20px; font-size: 13px; }
.edito-divergences li { margin-bottom: 4px; }
/* ⚠ Le badge d'un titre hors plafond : visible sur la ligne, pour qu'on n'ait
   pas à ouvrir trente modales pour trouver les vingt-trois concernées. */
.stock-badge-h1 { margin-left: 6px; font-size: 11px; vertical-align: middle; }
/* Un slug cliquable DANS un message : un lien, pas un bouton — il navigue. */
.lien-slug {
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  color: var(--green-deep, #1f6f4a); text-decoration: underline;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
}
/* ══════ Le référentiel rendu lisible (v1.126.11) ══════
   ⚠ La vue brute reste : c'est le seul endroit où l'on vérifie les octets qui
   partent réellement à l'agent. La bascule choisit les lunettes, pas la donnée. */
.refs2-bascule { display: flex; gap: 6px; margin: 10px 0; }
.refs2-lisible { max-height: 60vh; overflow-y: auto; padding-right: 6px; }
.refs2-lisible .md-h2 { margin: 18px 0 6px; font-size: 15px; }
.refs2-lisible .md-quote {
  margin: 6px 0; padding: 6px 10px; border-left: 3px solid var(--border);
  background: var(--bg); font-size: 13px;
}
.refs2-lisible .md-table { width: 100%; font-size: 13px; }
.refs2-conso-defile { overflow-x: auto; }
.refs2-conso-table th[scope="rowgroup"] {
  vertical-align: top; text-align: left; white-space: nowrap;
  border-right: 2px solid var(--border); padding-right: 10px;
}
/* Le slug sous le nom : l'identifiant reste lisible sans encombrer le titre. */
.refs2-conso-slug {
  display: block; font-weight: 400; font-size: 11px; color: var(--text-light);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Un filet marque le début de chaque produit — le rowspan seul ne suffit pas
   à l'œil quand la table défile. */
.refs2-conso-ligne.est-premiere td { border-top: 2px solid var(--border); }
/* ⛔ Vu à l'œil, pas au banc : les en-têtes étaient centrés et le corps aligné
   à gauche — les colonnes ne se lisaient pas ensemble. Un test qui compte des
   cellules ne voit pas ça. */
.refs2-conso-table th,
.refs2-conso-table td { text-align: left; vertical-align: top; }
.refs2-conso-table thead th { white-space: nowrap; }
/* ⛔ Des largeurs EXPLICITES, deuxième correction à l'œil. La première laissait
   « Valeur » écrasée sur deux lignes pendant que « Note », vide, prenait la
   moitié du tableau : `width: 1%` sur les clés poussait tout à gauche. Ce que
   la table doit donner à lire, c'est la VALEUR. */
.refs2-conso-table { table-layout: fixed; width: 100%; }
.refs2-conso-table th[scope="rowgroup"] { width: 20%; }
.refs2-conso-table td:nth-child(1),
.refs2-conso-table tr.est-premiere td:nth-child(2) { width: 14%; }
.refs2-conso-table thead th:nth-child(2) { width: 14%; }
.refs2-conso-table thead th:nth-child(3) { width: 32%; }
.refs2-conso-table thead th:nth-child(4) { width: 20%; }
.refs2-conso-table thead th:nth-child(5) { width: 14%; }
/* ⚠ Une URL longue ne doit pas élargir la table : elle se coupe dans sa case. */
.refs2-conso-table td { overflow-wrap: anywhere; }
/* ══════ Le menu « autres actions » du stock (v1.126.13) ══════
   ⚠ Même forme que celui du registre : un second vocabulaire de menu dans la
   même application ferait diverger deux comportements que l'œil lit comme un
   seul. Les règles sont recopiées parce que celles du registre sont portées
   par `.reg-v2`, un conteneur que cet écran n'a pas. */
.stock-menucell { position: relative; display: inline-block; }
.stock-gestes .stock-caret { margin-left: 4px; font-weight: 700; }
.stock-menu {
  position: fixed; z-index: 30;
  background: #fff; border: 1px solid var(--border);
  border-radius: 8px; box-shadow: 0 8px 22px rgba(0, 0, 0, .16); padding: 5px;
  width: 250px; font-size: 12.5px; text-align: left;
}
.stock-menu .mi { padding: 7px 9px; border-radius: 5px; cursor: pointer; white-space: normal; }
.stock-menu .mi:hover, .stock-menu .mi:focus { background: var(--bg); outline: none; }
/* ⚠ La conséquence est écrite SOUS l'entrée, pas dans une infobulle : un geste
   sans retour doit dire ce qu'il coûte avant le clic, pas au survol. */
.stock-menu .mi small { display: block; color: var(--red); font-size: 11px; margin-top: 2px; }
/* ⚠ L'entrée destructrice porte sa couleur : un menu où toutes les lignes se
   ressemblent fait cliquer la mauvaise. */
.stock-menu .mi-danger { color: var(--red); font-weight: 600; }
.stock-menu .msep { height: 1px; background: var(--border); margin: 4px 6px; }
/* ══════ /#references : rangé par TYPE puis GROUPE (v1.126.15) ══════
   ⚠ Deux niveaux de titre, pas deux colonnes répétées : le regroupement dit ce
   que 201 répétitions de « DOCTRINE » disaient moins bien. */
.refs2-groupe .refs2-bloc-type { margin-bottom: 22px; }
.refs2-type-titre {
  margin: 0 0 6px; padding-bottom: 4px; font-size: 14px; letter-spacing: .04em;
  text-transform: uppercase; border-bottom: 2px solid var(--border);
  display: flex; align-items: baseline; gap: 10px;
}
.refs2-type-compte { font-size: 11px; font-weight: 400; color: var(--text-light); text-transform: none; }
.refs2-groupe-titre {
  margin: 12px 0 4px; font-size: 12.5px; font-weight: 600; color: var(--text-light);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.refs2-bloc-groupe { margin-left: 10px; }
/* ⚠ Discret mais atteignable : une suppression sans retour ne doit ni se
   cliquer par mégarde, ni obliger à chercher. */
.refs2-c-geste { text-align: right; width: 5%; }
.refs2-supprimer {
  background: none; border: 0; cursor: pointer; opacity: .35;
  font-size: 13px; padding: 2px 4px; border-radius: 4px;
}
.refs2-supprimer:hover, .refs2-supprimer:focus { opacity: 1; background: var(--red-pale); outline: none; }
#refs2-message { margin: 8px 0 0; }
