/* ============================================================
   PROJET PROPRETÉ v2 — Luxury Industrial Software
   Inspired by: cockpit industriel, supervision flotte,
   logiciel premium B2B (Bentley / Siemens / Volvo fleet vibe).
   Anthracite sidebar + mineral whites + sparingly used red.
   ============================================================ */

/* --- Tokens ----------------------------------------------- */
:root {
  /* ═══ S73 — ÉCHELLE TYPOGRAPHIQUE (jetons, comme S65 pour les
     couleurs). Mesuré avant : 320 tailles littérales, 32 valeurs
     distinctes, dont 66 déclarations à 10,5 px ou moins. Neuf crans
     remplacent les 32 valeurs ; le PLANCHER est 12 px — plus aucun
     texte porteur d'information en dessous.
     ⚠ Toute valeur a augmenté ou est restée : jamais une mention
     rétrécie pour payer l'agrandissement. ═══ */
  --t-xs: 12px;
  --t-sm: 13px;
  --t-md: 14px;
  --t-lg: 16px;
  --t-xl: 18px;
  --t-2xl: 22px;
  --t-3xl: 28px;
  --t-4xl: 36px;
  --t-display: 56px;

  /* TROIS niveaux de graisse, pas quatre : 500 et 600 se ressemblaient
     (57 déclarations à elles deux) et brouillaient la hiérarchie.
     Fusionnées en une seule intermédiaire — moins de graisses, plus
     tranchées. */
  --poids-normal: 400;
  --poids-appuye: 600;
  --poids-fort: 700;

  /* ════════════════════════════════════════════════════════════════════════
     S65 [habillage-client] — MARQUE DU CLIENT.
     Le rouge ci-dessous est LU DANS LE LOGO fourni (design_v3/marque/
     bucher-municipal.svg, tracé du symbole) — il n'est pas approché à l'œil.
     UN SEUL rouge sert la marque ET l'urgence : ils ne s'annulent pas parce
     que ce qui les distingue est le REMPLISSAGE, pas la teinte — aplat plein
     pour ce qui se rend maintenant, filet pour le décor (règle de la pastille
     de compte à rebours, §pastille plus bas). Un filet de 2 px ne se confond
     pas avec un aplat, même délavé par un vidéoprojecteur.
     ⚠ Changer de client = changer CES lignes, pas parcourir la feuille.
     ════════════════════════════════════════════════════════════════════════ */
  --marque-rouge:      #dc241f;
  --marque-rouge-doux: rgba(220, 36, 31, 0.10);
  --marque-rouge-trait: rgba(220, 36, 31, 0.38);

  /* Core brand — used VERY sparingly, signal only.
     ALIAS depuis S65 : les ~30 sélecteurs écrits avant ce bloc continuent de
     dire `var(--signal-red)` et suivent désormais la marque du client sans
     être réécrits un par un (une réécriture de masse = un risque de régression
     visuelle pour zéro gain). */
  --signal-red:    var(--marque-rouge);
  --signal-red-soft: var(--marque-rouge-doux);

  /* rouge d'ÉCHÉANCE (S61 : réservé à ce qui se rend aujourd'hui). Il était
     appelé `var(--danger-red, #c0392b)` par TROIS règles alors que le jeton
     N'EXISTAIT PAS : seule la valeur de repli s'appliquait — une couleur
     fantôme, invisible à la lecture du :root. Il existe. */
  --danger-red:    var(--marque-rouge);

  /* rouge d'ERREUR / d'écartement — volontairement DISTINCT de la marque :
     un message d'erreur ne doit pas emprunter l'identité du client. */
  --erreur-rouge:      #9C3F3F;
  --erreur-rouge-doux: rgba(178, 58, 58, 0.08);
  --erreur-rouge-trait: rgba(178, 58, 58, 0.30);

  /* accents « module optionnel » (bandeaux premium/roadmap) — famille dorée,
     jusqu'ici répétée en clair dans une trentaine de règles */
  --premium-or:        #C4A37A;
  --premium-or-fonce:  #8C6F44;
  --premium-or-sombre: #6B5A3D;
  --premium-or-nuit:   #4A3D27;
  --premium-or-doux:   rgba(196, 163, 122, 0.35);

  /* bleu acier soutenu (texte sur fond acier clair) */
  --steel-deep:    #455461;
  /* textes d'ETAT soutenus : posés sur leur propre aplat clair, ils doivent
     rester lisibles en projection — d'où des valeurs plus sombres que les
     accents dont ils dérivent (--tech-green / --warning-orange). */
  --tech-green-deep:    #4F6655;
  --warning-orange-deep: #9A5E1E;
  --text-tertiary:      #6B6F76;
  /* texte posé SUR un aplat de couleur (pastille pleine, badge plein) */
  --sur-aplat:     #FFFFFF;
  /* traits et ombres tirés de l'anthracite du texte — neutres, ils ne
     changent pas avec le client, mais ils étaient recopiés 22 fois */
  --trait-04:      rgba(31, 35, 40, 0.04);
  --trait-05:      rgba(31, 35, 40, 0.05);
  --trait-06:      rgba(31, 35, 40, 0.06);
  --trait-10:      rgba(31, 35, 40, 0.10);

  /* Backgrounds — mineral, industrial */
  --bg-primary:    #F5F5F3;
  --bg-secondary:  #EFEFEA;
  --bg-tertiary:   #E7E5E1;

  /* Cards / surfaces */
  --card-bg:       rgba(255, 255, 255, 0.78);
  --card-solid:    #FFFFFF;
  --card-border:   rgba(31, 35, 40, 0.07);
  --hairline:      rgba(31, 35, 40, 0.08);
  --hairline-strong: rgba(31, 35, 40, 0.16);

  /* Typography */
  --text-primary:   #1F2328;
  --text-secondary: #5F6772;
  --text-muted:     #8A919B;
  --text-faint:     #B5BAC0;

  /* Industrial accents */
  --steel-blue:     #5B6C7A;
  --steel-soft:     rgba(91, 108, 122, 0.12);
  --tech-green:     #6F8A78;
  --tech-soft:      rgba(111, 138, 120, 0.14);
  --warning-orange: #D9822B;
  --warning-soft:   rgba(217, 130, 43, 0.13);

  /* Anthracite */
  --machine-dark:   #2B2F34;
  --machine-darker: #1E2125;
  --machine-on:     #E8E9EB;
  --machine-on-mid: #A4A8AE;
  --machine-on-dim: #6E7278;
  --machine-hairline: rgba(255, 255, 255, 0.07);

  /* Type */
  /* S73 — police de MARQUE du client (relevée sur buchermunicipal.com :
     Noto Sans, licence SIL Open Font, deux graisses). Arial en repli, comme
     chez eux. Servie en local : aucune requête externe (S16). */
  --sans: "Noto Sans", ui-sans-serif, system-ui, -apple-system, Arial, Helvetica, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Effects */
  --shadow-soft:   0 1px 0 rgba(31,35,40,0.03),
                   0 4px 22px rgba(31,35,40,0.04);
  --shadow-pop:    0 1px 0 rgba(31,35,40,0.05),
                   0 18px 60px rgba(31,35,40,0.18),
                   0 2px 6px rgba(31,35,40,0.08);

  /* Radii — restrained */
  --r-xs: 3px;
  --r-sm: 5px;
  --r-md: 8px;
  --r-lg: 12px;

  /* Spacing */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 32px;  --sp-8: 40px;
  --sp-9: 56px; --sp-10: 72px;

  /* Motion */
  --t-fast: 140ms cubic-bezier(0.2, 0, 0, 1);
  --t:      240ms cubic-bezier(0.2, 0, 0, 1);
  --t-slow: 420ms cubic-bezier(0.2, 0, 0, 1);
}

/* --- Reset ------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--sans);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--t-lg);
  line-height: 1.45;
  font-feature-settings: "ss01", "tnum", "cv11";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -0.005em;
  min-height: 100vh;
}
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
a { color: inherit; text-decoration: none; }
input, select, textarea { font: inherit; color: inherit; }

/* Mineral texture backdrop */
body::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(1400px 700px at 75% -10%, rgba(91, 108, 122, 0.045), transparent 60%),
    radial-gradient(1000px 600px at -10% 40%, rgba(111, 138, 120, 0.035), transparent 60%);
}

/* --- Utility ---------------------------------------------- */
.mono    { font-family: var(--mono); font-feature-settings: "tnum","zero"; letter-spacing: -0.005em; }
.kicker  { font-size: var(--t-xs); letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-muted); font-weight: var(--poids-appuye); }
.label   { font-size: var(--t-xs);   letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); font-weight: var(--poids-appuye); }
.dim     { color: var(--text-muted); }
.hairline{ height: 1px; background: var(--hairline); }
.vsep    { width: 1px; height: 18px; background: var(--hairline); }

/* --- App shell -------------------------------------------- */
.app {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 248px 1fr;
  min-height: 100vh;
}

/* --- Sidebar (anthracite) -------------------------------- */
.sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  padding: var(--sp-6) var(--sp-5);
  background: var(--machine-dark);
  color: var(--machine-on);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  border-right: 1px solid rgba(0,0,0,0.2);
}

.brand {
  display: flex; align-items: center; gap: 11px;
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--machine-hairline);
}
.brand-mark {
  width: 32px; height: 32px;
  border-radius: 6px;
  background: linear-gradient(135deg, #3A3E45 0%, #1B1D21 100%);
  display: grid; place-items: center;
  position: relative;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.06), 0 2px 4px rgba(0,0,0,0.4);
}
.brand-mark::before {
  content: ""; position: absolute; inset: 8px;
  border: 1.5px solid rgba(255,255,255,0.92);
  transform: rotate(45deg);
}
.brand-mark::after {
  content: ""; position: absolute; top: 7px; right: 7px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--signal-red);
  box-shadow: 0 0 6px rgba(238, 50, 36, 0.6);
}
.brand-text { line-height: 1.1; }
.brand-text .name { font-size: var(--t-lg); font-weight: var(--poids-appuye); color: var(--machine-on); letter-spacing: -0.005em; }
.brand-text .sub  { font-size: var(--t-xs); color: var(--machine-on-mid); letter-spacing: 0.06em; text-transform: uppercase; margin-top: 3px; }

.nav-section + .nav-section { margin-top: var(--sp-5); }
.nav-title {
  font-size: var(--t-xs); font-weight: var(--poids-appuye); letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--machine-on-dim);
  margin: 0 0 var(--sp-2) 6px;
}
.nav-item {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: var(--t-md);
  color: var(--machine-on-mid);
  width: 100%;
  text-align: left;
  transition: background var(--t-fast), color var(--t-fast);
  position: relative;
}
.nav-item:hover { background: rgba(255,255,255,0.04); color: var(--machine-on); }
.nav-item.active {
  background: rgba(255,255,255,0.07);
  color: var(--machine-on);
}
.nav-item.active::before {
  content: ""; position: absolute;
  left: -20px; top: 8px; bottom: 8px;
  width: 2px;
  background: var(--signal-red);
  border-radius: 0 2px 2px 0;
}
.nav-item svg { width: 16px; height: 16px; flex: none; opacity: 0.85; }
.nav-item .nav-count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--machine-on-dim);
}
.nav-item.active .nav-count { color: var(--machine-on-mid); }

.sidebar-footer {
  margin-top: auto;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--machine-hairline);
  font-size: var(--t-sm);
  color: var(--machine-on-dim);
}
.sidebar-footer .row { display: flex; align-items: center; gap: 8px; }
.status-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--tech-green);
  box-shadow: 0 0 0 3px rgba(111, 138, 120, 0.18);
  display: inline-block;
}
.sidebar-footer .build { font-family: var(--mono); font-size: var(--t-xs); color: var(--machine-on-dim); margin-top: 8px; letter-spacing: 0.02em; }

/* --- Header ----------------------------------------------- */
.header {
  position: sticky; top: 0; z-index: 20;
  height: 64px;
  display: flex; align-items: center; gap: var(--sp-5);
  padding: 0 var(--sp-8);
  background: rgba(245, 245, 243, 0.82);
  backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid var(--hairline);
}
.breadcrumb {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--t-md);
  color: var(--text-muted);
}
.breadcrumb .here { color: var(--text-primary); font-weight: var(--poids-appuye); }
.breadcrumb .sep { color: var(--text-faint); }

.search {
  flex: 1;
  max-width: 480px;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border-radius: var(--r-sm);
  background: rgba(255,255,255,0.62);
  border: 1px solid var(--hairline);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.search:focus-within { border-color: var(--hairline-strong); background: var(--card-solid); }
.search input { flex: 1; border: 0; background: transparent; outline: 0; font-size: var(--t-md); }
.search input::placeholder { color: var(--text-faint); }
.search kbd {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--text-muted);
  padding: 2px 6px; border-radius: 3px;
  background: var(--trait-05);
  border: 1px solid var(--hairline);
}

.header-meta { display: flex; align-items: center; gap: var(--sp-4); margin-left: auto; }
.header-tick {
  display: flex; flex-direction: column; line-height: 1.1; text-align: right; font-size: var(--t-sm);
}
.header-tick .v { font-family: var(--mono); font-size: var(--t-md); color: var(--text-primary); }
.header-tick .l { color: var(--text-muted); font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase; margin-top: 2px; }

.icon-btn {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  transition: background var(--t-fast);
  position: relative;
}
.icon-btn:hover { background: var(--trait-05); color: var(--text-primary); }
.icon-btn svg { width: 17px; height: 17px; }
.icon-btn .dot {
  position: absolute; top: 7px; right: 8px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--signal-red);
  box-shadow: 0 0 0 2px rgba(245, 245, 243, 1);
}

.avatar {
  width: 32px; height: 32px; border-radius: 50%;
  background: linear-gradient(135deg, var(--steel-blue), var(--machine-dark));
  display: grid; place-items: center;
  color: white; font-size: var(--t-sm); font-weight: var(--poids-appuye); letter-spacing: 0.04em;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15);
}

/* --- Main ------------------------------------------------- */
.main { padding: var(--sp-8) var(--sp-8) 80px; width: 100%; }

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  margin-bottom: var(--sp-7);
  gap: var(--sp-6);
}
.page-head h1 {
  font-size: var(--t-4xl);
  font-weight: var(--poids-normal);
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin: 8px 0 0;
  color: var(--text-primary);
}
.page-head .h-sub {
  margin-top: 8px;
  color: var(--text-secondary);
  font-size: var(--t-lg);
  max-width: 640px;
  line-height: 1.5;
}
.page-head .h-actions { display: flex; align-items: center; gap: 10px; }

/* --- Buttons ---------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px;
  border-radius: var(--r-sm);
  font-size: var(--t-md); font-weight: var(--poids-appuye);
  letter-spacing: -0.005em;
  transition: background var(--t-fast), border-color var(--t-fast), transform var(--t-fast), color var(--t-fast);
  border: 1px solid transparent;
  white-space: nowrap;
}
.btn svg { width: 14px; height: 14px; }
.btn.primary {
  background: var(--machine-dark);
  color: var(--card-solid);
}
.btn.primary:hover { background: var(--machine-darker); }
.btn.ghost {
  background: rgba(255,255,255,0.6);
  border-color: var(--hairline);
  color: var(--text-secondary);
}
.btn.ghost:hover { background: var(--card-solid); border-color: var(--hairline-strong); color: var(--text-primary); }
.btn.accent {
  background: var(--steel-blue);
  color: white;
}
.btn.accent:hover { background: #4D5C6A; }
.btn.danger {
  background: var(--signal-red);
  color: white;
}

/* --- Tabs (Prévision / Marchés publiés) ------------------ */
.view-tabs {
  display: flex; align-items: center; gap: 0;
  margin-bottom: var(--sp-7);
  border-bottom: 1px solid var(--hairline);
  position: relative;
}
.view-tab {
  display: flex; align-items: baseline; gap: 8px;
  padding: 0 var(--sp-4) 14px;
  margin-bottom: -1px;
  font-size: var(--t-lg);
  color: var(--text-muted);
  font-weight: var(--poids-appuye);
  transition: color var(--t-fast);
  border-bottom: 2px solid transparent;
  position: relative;
  white-space: nowrap;
}
.view-tab:first-child { padding-left: 0; }
.view-tab:hover { color: var(--text-secondary); }
.view-tab.active {
  color: var(--text-primary);
  border-bottom-color: var(--text-primary);
}
.view-tab .cnt {
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--text-muted);
  padding: 2px 7px;
  border-radius: 10px;
  background: var(--trait-05);
}
.view-tab.active .cnt {
  background: var(--text-primary);
  color: var(--card-solid);
}
.view-tab .ico { width: 13px; height: 13px; opacity: 0.65; align-self: center; }
.view-tab.active .ico { opacity: 1; }

/* --- KPI strip -------------------------------------------- */
.kpis {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-bottom: var(--sp-7);
  box-shadow: var(--shadow-soft);
}
.kpi {
  background: var(--card-solid);
  padding: 22px 22px 18px;
  position: relative;
  min-height: 132px;
  display: flex; flex-direction: column;
  transition: background var(--t-fast);
}
.kpi:hover { background: #FDFDFC; }
.kpi .kpi-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 16px;
}
.kpi .kpi-label {
  font-size: var(--t-xs); font-weight: var(--poids-appuye);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-muted);
}
.kpi .kpi-icon {
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: 4px;
  background: var(--trait-04);
  color: var(--text-secondary);
}
.kpi .kpi-icon svg { width: 14px; height: 14px; }
.kpi .kpi-value {
  font-family: var(--mono);
  font-size: var(--t-4xl);
  font-weight: var(--poids-normal);
  color: var(--text-primary);
  letter-spacing: -0.025em;
  line-height: 1;
}
.kpi .kpi-value .unit { font-size: var(--t-lg); color: var(--text-muted); margin-left: 4px; }
.kpi .kpi-foot {
  margin-top: auto;
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-sm); color: var(--text-muted);
}
.delta {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--mono); font-size: var(--t-sm);
}
.delta.up   { color: var(--tech-green); }
.delta.down { color: var(--signal-red); }
.delta::before {
  content: ""; width: 0; height: 0;
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
}
.delta.up::before   { border-bottom: 4px solid var(--tech-green); }
.delta.down::before { border-top: 4px solid var(--signal-red); }
.spark {
  position: absolute;
  right: 16px; bottom: 14px;
  width: 64px; height: 22px;
  opacity: 0.5;
}
.kpi.critical .kpi-icon { background: var(--signal-red-soft); color: var(--signal-red); }
.kpi.critical .kpi-value { color: var(--signal-red); }

/* --- Section header -------------------------------------- */
.section { margin-bottom: var(--sp-8); }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: var(--sp-4);
  gap: var(--sp-4);
}
.section-head .left h2 {
  font-size: var(--t-2xl);
  font-weight: var(--poids-appuye);
  letter-spacing: -0.015em;
  margin: 0;
  color: var(--text-primary);
}
.section-head .left p {
  font-size: var(--t-md);
  color: var(--text-muted);
  margin: 4px 0 0;
  max-width: 580px;
  line-height: 1.5;
}
.section-head .h-meta { font-size: var(--t-md); color: var(--text-muted); }
.section-head .h-meta .mono { color: var(--text-secondary); }

/* ============================================================
   FORECAST CARDS — The differentiator UI
   ============================================================ */
.forecast-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}
.fc-card {
  background: var(--card-bg);
  backdrop-filter: blur(14px);
  border: 1px solid var(--card-border);
  border-radius: var(--r-md);
  padding: 22px 24px;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "head     prob"
    "title    prob"
    "meta     prob"
    "signals  signals"
    "foot     foot";
  gap: 12px 20px;
  cursor: pointer;
  transition: transform var(--t-fast), box-shadow var(--t-fast), border-color var(--t-fast);
  position: relative;
}
.fc-card:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-soft);
  border-color: var(--hairline-strong);
}
.fc-card.starred::before {
  content: "";
  position: absolute;
  top: 0; left: 16px; right: 16px;
  height: 1px;
  background: var(--signal-red);
  opacity: 0.7;
}

.fc-head { grid-area: head; display: flex; align-items: center; gap: 10px; }
.fc-head .id { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-muted); letter-spacing: 0.01em; }
.fc-head .horizon {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: var(--t-sm);
  padding: 3px 8px; border-radius: 10px;
  background: var(--tech-soft);
  color: var(--tech-green-deep);
}
.fc-head .horizon::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--tech-green);
}
.fc-head .star {
  margin-left: auto; color: var(--text-faint); transition: color var(--t-fast);
}
.fc-card.starred .fc-head .star { color: var(--signal-red); }

.fc-title { grid-area: title; }
.fc-title .buyer {
  font-size: var(--t-xl);
  font-weight: var(--poids-appuye);
  color: var(--text-primary);
  letter-spacing: -0.01em;
  margin-bottom: 4px;
  line-height: 1.25;
}
.fc-title .cat {
  font-size: var(--t-md);
  color: var(--text-secondary);
  display: flex; align-items: center; gap: 7px;
}
.fc-title .cat .dash {
  width: 16px; height: 1.5px;
  background: var(--c, var(--steel-blue));
  flex: none;
}

.fc-meta {
  grid-area: meta;
  display: flex; flex-wrap: wrap; gap: 18px;
  font-size: var(--t-md);
  color: var(--text-muted);
  padding-top: 4px;
}
.fc-meta .m-item { display: flex; flex-direction: column; gap: 2px; line-height: 1.2; }
.fc-meta .m-item .v { font-family: var(--mono); color: var(--text-primary); font-size: var(--t-md); }
.fc-meta .m-item .l { font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }

.fc-prob {
  grid-area: prob;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 110px;
  padding-left: 16px;
  border-left: 1px solid var(--hairline);
}
.fc-prob .ring { position: relative; width: 88px; height: 88px; }
.fc-prob .ring svg { transform: rotate(-90deg); }
.fc-prob .ring .track { fill: none; stroke: var(--hairline); stroke-width: 5; }
.fc-prob .ring .arc { fill: none; stroke-width: 5; stroke-linecap: round; transition: stroke-dashoffset 700ms cubic-bezier(0.2,0,0,1); }
.fc-prob .center {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  line-height: 1;
}
.fc-prob .center .v {
  font-family: var(--mono);
  font-size: var(--t-3xl);
  font-weight: var(--poids-normal);
  letter-spacing: -0.02em;
  color: var(--text-primary);
}
.fc-prob .center .l {
  font-size: var(--t-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 4px;
}
.fc-prob .conf {
  margin-top: 8px;
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.fc-signals {
  grid-area: signals;
  margin-top: 4px;
  padding-top: 14px;
  border-top: 1px dashed var(--hairline);
}
.fc-signals .lbl {
  font-size: var(--t-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
  display: flex; align-items: center; gap: 10px;
}
.fc-signals .lbl .bar-mini {
  flex: 1; height: 2px;
  border-radius: 1px;
  background: var(--trait-06);
  position: relative; overflow: hidden;
  display: flex;
}
.fc-signals .lbl .bar-mini span {
  display: block; height: 100%;
}
.signal-list {
  display: flex; flex-direction: column; gap: 7px;
}
.signal-row {
  display: grid;
  grid-template-columns: 130px 1fr 50px;
  gap: 12px;
  align-items: center;
  font-size: var(--t-md);
  color: var(--text-secondary);
}
.signal-row .src {
  display: flex; align-items: center; gap: 7px;
  color: var(--text-primary);
  font-weight: var(--poids-appuye);
  font-size: var(--t-sm);
}
.signal-row .src .src-dot {
  width: 7px; height: 7px; border-radius: 1px;
  background: var(--steel-blue);
}
.signal-row .src .src-dot.budget    { background: var(--steel-blue); }
.signal-row .src .src-dot.approch   { background: var(--tech-green); }
.signal-row .src .src-dot.delib     { background: #876B4A; }
.signal-row .src .src-dot.histo     { background: var(--text-secondary); }
.signal-row .src .src-dot.env       { background: var(--warning-orange); }
.signal-row .lbl-row {
  color: var(--text-secondary);
  font-size: var(--t-md);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.signal-row .w {
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--text-muted);
  text-align: right;
}

.fc-foot {
  grid-area: foot;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-md);
  color: var(--text-muted);
}
.fc-foot .tags { display: flex; gap: 6px; flex-wrap: wrap; }
.fc-foot .tag {
  font-size: var(--t-sm);
  padding: 3px 8px;
  border-radius: 10px;
  background: rgba(91, 108, 122, 0.07);
  color: var(--steel-deep);
  border: 1px solid rgba(91, 108, 122, 0.12);
}
.fc-foot .actions { display: flex; gap: 6px; }
.fc-foot .actions button {
  font-size: var(--t-sm);
  padding: 4px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-secondary);
  transition: background var(--t-fast), color var(--t-fast);
}
.fc-foot .actions button:hover { background: var(--trait-04); color: var(--text-primary); }

/* --- Forecast → Published timeline strip ----------------- */
.horizon-strip {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 22px 26px 26px;
  margin-bottom: var(--sp-7);
  box-shadow: var(--shadow-soft);
}
.horizon-strip .strip-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 18px;
}
.horizon-strip .strip-head h3 {
  font-size: var(--t-lg);
  font-weight: var(--poids-appuye);
  margin: 0;
  letter-spacing: -0.005em;
}
.horizon-track {
  position: relative;
  height: 36px;
  margin-top: 4px;
}
.horizon-track .axis {
  position: absolute;
  left: 0; right: 0;
  top: 18px;
  height: 1px;
  background: var(--hairline);
}
.horizon-track .axis-now {
  position: absolute;
  top: 8px; bottom: 8px;
  width: 1px;
  background: var(--signal-red);
  opacity: 0.7;
}
.horizon-track .axis-now-label {
  position: absolute;
  top: -10px;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--signal-red);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.horizon-pin {
  position: absolute;
  top: 12px;
  transform: translateX(-50%);
  cursor: pointer;
  transition: transform var(--t-fast);
}
.horizon-pin:hover { transform: translateX(-50%) translateY(-2px); }
.horizon-pin .dot {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--card-solid);
  border: 2px solid var(--c, var(--steel-blue));
  box-shadow: 0 0 0 4px rgba(91,108,122, 0.08);
}
.horizon-pin .tip {
  position: absolute; top: -38px; left: 50%; transform: translateX(-50%);
  white-space: nowrap;
  background: var(--machine-dark);
  color: var(--machine-on);
  font-size: var(--t-xs);
  padding: 4px 8px;
  border-radius: var(--r-xs);
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-fast);
  font-family: var(--mono);
}
.horizon-pin:hover .tip { opacity: 1; }
.horizon-ticks {
  display: flex; justify-content: space-between;
  margin-top: 14px;
  font-family: var(--mono); font-size: var(--t-xs); color: var(--text-muted);
  letter-spacing: 0.06em;
}

/* --- Account intelligence -------------------------------- */
.accounts-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.acc-card {
  background: var(--card-bg);
  backdrop-filter: blur(14px);
  border: 1px solid var(--card-border);
  border-radius: var(--r-md);
  padding: 18px 18px 16px;
  transition: background var(--t-fast), border-color var(--t-fast);
  display: flex; flex-direction: column; gap: 10px;
}
.acc-card:hover { border-color: var(--hairline-strong); }
.acc-card .top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px;
}
.acc-card .name {
  font-size: var(--t-lg);
  font-weight: var(--poids-appuye);
  color: var(--text-primary);
  letter-spacing: -0.005em;
  line-height: 1.25;
}
.acc-card .region {
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin-top: 2px;
}
.acc-card .relscore {
  font-family: var(--mono);
  font-size: var(--t-lg);
  color: var(--text-primary);
}
.acc-card .relscore .pct { font-size: var(--t-xs); color: var(--text-muted); }
.acc-card .fleet {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding: 10px 0;
  gap: 6px;
}
.acc-card .fleet .it { text-align: center; }
.acc-card .fleet .v { font-family: var(--mono); font-size: var(--t-lg); color: var(--text-primary); display: block; line-height: 1; }
.acc-card .fleet .l { font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); margin-top: 4px; display: block; }
.acc-card .meta-rows {
  display: flex; flex-direction: column; gap: 5px;
  font-size: var(--t-sm); color: var(--text-secondary);
}
.acc-card .meta-rows .mr {
  display: flex; justify-content: space-between;
}
.acc-card .meta-rows .mr .l { color: var(--text-muted); }
.acc-card .meta-rows .mr .v { color: var(--text-primary); font-family: var(--mono); font-size: var(--t-sm); }
.acc-card .orient {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 2px;
}
.acc-card .orient .o {
  font-size: var(--t-xs);
  padding: 2px 7px;
  border-radius: 9px;
  background: rgba(91, 108, 122, 0.07);
  color: var(--steel-deep);
  border: 1px solid rgba(91, 108, 122, 0.10);
}
.acc-card .windownote {
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.acc-card .windownote .v {
  font-family: var(--mono);
  color: var(--tech-green);
  letter-spacing: -0.005em;
  text-transform: none;
  font-size: var(--t-sm);
}

/* --- Published view: table & filters --------------------- */
.filters {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 11px 14px;
  background: var(--card-bg);
  backdrop-filter: blur(10px);
  border: 1px solid var(--hairline);
  border-bottom: 0;
  border-radius: var(--r-md) var(--r-md) 0 0;
  flex-wrap: wrap;
}
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 11px;
  border-radius: 14px;
  font-size: var(--t-md);
  background: transparent;
  border: 1px solid var(--hairline);
  color: var(--text-secondary);
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.filter-chip:hover { background: rgba(255,255,255,0.7); color: var(--text-primary); }
.filter-chip.active {
  background: var(--machine-dark);
  color: var(--machine-on);
  border-color: var(--machine-dark);
}
.filter-chip .count {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--text-muted); margin-left: 2px;
}
.filter-chip.active .count { color: var(--machine-on-mid); }
.filters .mini-search {
  margin-left: auto;
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px;
  font-size: var(--t-md);
  border-radius: var(--r-sm);
  background: rgba(255,255,255,0.6);
  border: 1px solid var(--hairline);
  height: 28px;
  min-width: 220px;
}
.mini-search input { border: 0; background: transparent; outline: 0; flex: 1; font-size: var(--t-md); }

.table-wrap {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: 0 0 var(--r-md) var(--r-md);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}
table.tenders {
  width: 100%; border-collapse: collapse; font-size: var(--t-md);
}
table.tenders thead th {
  text-align: left;
  font-size: var(--t-xs);
  font-weight: var(--poids-appuye);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 15px 18px;
  border-bottom: 1px solid var(--hairline);
  background: rgba(255,255,255,0.4);
  white-space: nowrap;
}
table.tenders thead th.sortable { cursor: pointer; transition: color var(--t-fast); }
table.tenders thead th.sortable:hover { color: var(--text-primary); }
table.tenders thead th .sortcaret { display: inline-block; margin-left: 4px; opacity: 0.45; }

table.tenders tbody tr {
  border-bottom: 1px solid var(--hairline);
  transition: background var(--t-fast);
  cursor: pointer;
}
table.tenders tbody tr:last-child { border-bottom: 0; }
table.tenders tbody tr:hover { background: rgba(31,35,40,0.02); }
table.tenders tbody tr.is-active { background: rgba(91, 108, 122, 0.06); }
table.tenders tbody td {
  padding: 17px 18px;
  vertical-align: middle;
  border: 0;
}
table.tenders td.col-id        { width: 110px; }
table.tenders td.col-title     { min-width: 280px; }
table.tenders td.col-buyer     { width: 200px; }
table.tenders td.col-family    { width: 150px; }
table.tenders td.col-amount    { width: 120px; text-align: right; }
table.tenders td.col-deadline  { width: 130px; }
table.tenders td.col-score     { width: 90px; }
table.tenders td.col-status    { width: 130px; }

.cell-id    { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-muted); }
.cell-title { display: flex; flex-direction: column; gap: 3px; }
.cell-title .t { font-weight: var(--poids-appuye); color: var(--text-primary); line-height: 1.3; }
.cell-title .t-sub { font-size: var(--t-sm); color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.crit-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--signal-red);
  box-shadow: 0 0 0 3px var(--signal-red-soft);
  display: inline-block;
  flex-shrink: 0;
}
.cell-buyer { display: flex; flex-direction: column; line-height: 1.25; }
.cell-buyer .name { color: var(--text-primary); font-size: var(--t-md); }
.cell-buyer .region { font-size: var(--t-sm); color: var(--text-muted); }
.cell-amount { font-family: var(--mono); color: var(--text-primary); font-size: var(--t-md); }
.cell-deadline { display: flex; flex-direction: column; line-height: 1.25; }
.cell-deadline .date { font-family: var(--mono); font-size: var(--t-md); color: var(--text-primary); }
.cell-deadline .days { font-size: var(--t-xs); color: var(--text-muted); letter-spacing: 0.08em; text-transform: uppercase; }
.cell-deadline.urgent .days { color: var(--warning-orange); }
.cell-deadline.critical .days { color: var(--signal-red); font-weight: var(--poids-appuye); }

.score-chip {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: var(--t-md);
  color: var(--text-primary);
}
.score-chip .bar {
  width: 36px; height: 3px;
  border-radius: 2px;
  background: var(--hairline);
  overflow: hidden;
  position: relative;
}
.score-chip .bar::after {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: var(--w, 0%);
  background: var(--c, var(--tech-green));
}
.family-tag { display: inline-flex; align-items: center; gap: 7px; font-size: var(--t-md); color: var(--text-secondary); }
.family-tag .dash { width: 18px; height: 1.5px; background: var(--c, var(--steel-blue)); flex: none; opacity: 0.85; }

.status {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-sm);
  letter-spacing: 0.04em;
  padding: 4px 9px;
  border-radius: 12px;
  background: rgba(31,35,40,0.045);
  color: var(--text-secondary);
  border: 1px solid var(--hairline);
  white-space: nowrap;
}
.status::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--text-muted); }
.status.tone-steel  { background: var(--steel-soft); color: var(--steel-deep); border-color: rgba(91, 108, 122, 0.16); }
.status.tone-steel::before { background: var(--steel-blue); }
.status.tone-green  { background: var(--tech-soft); color: var(--tech-green-deep); border-color: rgba(111, 138, 120, 0.20); }
.status.tone-green::before { background: var(--tech-green); }
.status.tone-forecast { background: rgba(217, 130, 43, 0.10); color: #A1641C; border-color: rgba(217, 130, 43, 0.20); }
.status.tone-forecast::before { background: var(--warning-orange); }
.status.tone-muted  { background: var(--trait-04); color: var(--text-muted); }

/* --- Side panel (detail) --------------------------------- */
.scrim {
  position: fixed; inset: 0;
  background: rgba(15, 18, 22, 0.32);
  backdrop-filter: blur(2px);
  z-index: 80;
  opacity: 0; pointer-events: none;
  transition: opacity var(--t);
}
.scrim.open { opacity: 1; pointer-events: auto; }
.panel {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 740px; max-width: 92vw;
  background: var(--card-solid);
  z-index: 90;
  box-shadow: var(--shadow-pop);
  display: flex; flex-direction: column;
  transform: translateX(110%);
  transition: transform var(--t-slow);
  border-left: 1px solid var(--hairline);
}
.panel.open { transform: translateX(0); }
.panel-head {
  padding: 22px 28px 18px;
  border-bottom: 1px solid var(--hairline);
  display: flex; flex-direction: column; gap: 10px;
  background: var(--card-solid);
}
.panel-head .row-top { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.panel-close {
  width: 28px; height: 28px; border-radius: 6px;
  display: grid; place-items: center;
  background: var(--trait-05);
  color: var(--text-secondary);
  margin-left: auto;
  transition: background var(--t-fast);
}
.panel-close:hover { background: var(--trait-10); }
.panel-head h2 {
  font-size: var(--t-3xl); font-weight: var(--poids-appuye); letter-spacing: -0.02em;
  margin: 0; line-height: 1.25; text-wrap: balance;
}
.panel-head .head-meta {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  font-size: var(--t-md); color: var(--text-muted);
}
.panel-head .head-meta strong { color: var(--text-secondary); font-weight: var(--poids-appuye); }

.panel-body {
  flex: 1; overflow-y: auto;
  padding: var(--sp-6) 28px var(--sp-7);
  display: flex; flex-direction: column; gap: var(--sp-5);
}
.score-feature {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: var(--sp-6);
  align-items: center;
  padding: 22px 24px;
  background: linear-gradient(180deg, rgba(255,255,255,0.7), rgba(255,255,255,0.3));
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
}
.gauge { position: relative; width: 140px; height: 140px; }
.gauge svg { transform: rotate(-90deg); }
.gauge .track { fill: none; stroke: var(--hairline); stroke-width: 6; }
.gauge .arc   { fill: none; stroke-width: 6; stroke-linecap: round; transition: stroke-dashoffset 800ms cubic-bezier(0.2,0,0,1); }
.gauge .center {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  line-height: 1;
}
.gauge .center .v {
  font-family: var(--mono); font-size: var(--t-display);
  letter-spacing: -0.03em; font-weight: var(--poids-normal); color: var(--text-primary);
}
.gauge .center .l {
  font-size: var(--t-xs); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--text-muted); margin-top: 6px;
}
.fit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3) var(--sp-6); }
.fit-row { display: flex; flex-direction: column; gap: 5px; }
.fit-row .top { display: flex; justify-content: space-between; align-items: baseline; font-size: var(--t-md); }
.fit-row .top .l { color: var(--text-secondary); }
.fit-row .top .v { font-family: var(--mono); font-size: var(--t-md); color: var(--text-primary); }
.fit-row .meter {
  height: 4px; border-radius: 2px;
  background: var(--trait-06);
  position: relative; overflow: hidden;
}
.fit-row .meter::after {
  content: ""; position: absolute; inset: 0;
  width: var(--w, 0%);
  background: var(--c, var(--steel-blue));
  border-radius: 2px;
  transition: width 700ms cubic-bezier(0.2,0,0,1);
}
.card-block {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 18px 20px;
  background: rgba(255,255,255,0.55);
}
.card-block h3 {
  font-size: var(--t-xs); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-muted); margin: 0 0 10px; font-weight: var(--poids-appuye);
}
.card-block p.summary {
  margin: 0; font-size: var(--t-lg); line-height: 1.55;
  color: var(--text-secondary); text-wrap: pretty;
}
.tag-cloud { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-cloud .tag {
  font-family: var(--mono);
  font-size: var(--t-sm);
  padding: 4px 9px;
  background: var(--steel-soft);
  color: var(--steel-deep);
  border-radius: 12px;
  border: 1px solid rgba(91, 108, 122, 0.14);
}
.crit-list { display: flex; flex-direction: column; gap: 8px; }
.crit-list .cr {
  display: grid;
  grid-template-columns: 1fr 70px 80px;
  align-items: center;
  font-size: var(--t-md);
  padding: 6px 0;
  border-bottom: 1px dashed var(--hairline);
}
.crit-list .cr:last-child { border-bottom: 0; }
.crit-list .cr .l { color: var(--text-secondary); }
.crit-list .cr .w { font-family: var(--mono); color: var(--text-primary); text-align: right; }
.crit-list .cr .b {
  height: 4px; border-radius: 2px;
  background: var(--trait-06);
  position: relative; overflow: hidden;
  margin-left: 12px;
}
.crit-list .cr .b::after {
  content: ""; position: absolute; inset: 0;
  width: var(--w); background: var(--steel-blue);
  border-radius: 2px;
}
.timeline { display: flex; flex-direction: column; gap: 0; }
.tl-row {
  display: grid;
  grid-template-columns: 110px 20px 1fr;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: 9px 0;
  font-size: var(--t-md);
  position: relative;
}
.tl-row .d { font-family: var(--mono); color: var(--text-muted); font-size: var(--t-sm); padding-top: 2px; }
.tl-row .node { position: relative; width: 20px; padding-top: 4px; }
.tl-row .node::before {
  content: ""; position: absolute; top: 0; bottom: -18px;
  left: 9px; width: 1px; background: var(--hairline);
}
.tl-row:last-child .node::before { display: none; }
.tl-row .node .dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--card-solid);
  border: 1.5px solid var(--text-faint);
  margin: 6px 0 0 6px;
}
.tl-row.done .node .dot { background: var(--text-primary); border-color: var(--text-primary); }
.tl-row.current .node .dot {
  background: var(--warning-orange); border-color: var(--warning-orange);
  box-shadow: 0 0 0 4px var(--warning-soft);
}
.tl-row .lbl { color: var(--text-secondary); padding-top: 2px; }
.tl-row.current .lbl { color: var(--text-primary); font-weight: var(--poids-appuye); }
.tl-row.done .lbl { color: var(--text-muted); }

.panel-foot {
  padding: 16px 28px;
  border-top: 1px solid var(--hairline);
  display: flex; gap: var(--sp-3); align-items: center;
  background: var(--card-solid);
}
.panel-foot .left { color: var(--text-muted); font-size: var(--t-md); }
.panel-foot .right { margin-left: auto; display: flex; gap: 8px; }

/* --- Generator dialog ------------------------------------ */
.gen-stage {
  background: linear-gradient(180deg, var(--tech-soft), rgba(111, 138, 120, 0));
  border: 1px solid rgba(111, 138, 120, 0.18);
  border-radius: var(--r-md);
  padding: 22px 24px;
  display: flex; flex-direction: column; gap: 16px;
}
.gen-stage.is-done { background: var(--card-solid); border-color: var(--hairline); }
.gen-progress {
  height: 2px; border-radius: 2px;
  background: var(--hairline);
  position: relative; overflow: hidden;
}
.gen-progress::after {
  content: ""; position: absolute; inset: 0;
  width: var(--w, 0%);
  background: var(--tech-green);
  transition: width 400ms ease-out;
}
.gen-steps { display: flex; flex-direction: column; gap: 6px; font-size: var(--t-md); color: var(--text-muted); }
.gen-steps .gs { display: flex; align-items: center; gap: 8px; }
.gen-steps .gs .mark {
  width: 14px; height: 14px; border-radius: 50%;
  border: 1.5px solid var(--text-faint);
  display: grid; place-items: center;
  flex: none;
}
.gen-steps .gs.done .mark {
  background: var(--text-primary); border-color: var(--text-primary); color: white;
}
.gen-steps .gs.done .mark::after {
  content: ""; display: block; width: 6px; height: 3px;
  border-left: 1.5px solid white; border-bottom: 1.5px solid white;
  transform: rotate(-45deg) translate(0px, -1px);
}
.gen-steps .gs.active { color: var(--text-primary); }
.gen-steps .gs.active .mark {
  border-color: var(--tech-green); background: var(--tech-soft);
}

.doc {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 28px 32px;
  font-size: var(--t-lg);
  line-height: 1.65;
  color: var(--text-secondary);
  min-height: 180px;
}
.doc h4 {
  font-size: var(--t-lg); font-weight: var(--poids-appuye); color: var(--text-primary);
  margin: 0 0 6px; letter-spacing: -0.005em;
}
.doc .head-line {
  font-size: var(--t-sm); letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 16px;
}
.doc p { margin: 0 0 12px; }
.doc strong { color: var(--text-primary); font-weight: var(--poids-appuye); }
.doc[contenteditable="true"] { outline: 1px dashed rgba(91, 108, 122, 0.45); outline-offset: 4px; }

/* --- Kanban (macro stages) ------------------------------- */
.kanban-wrap {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}
.k-macro {
  background: var(--card-bg);
  backdrop-filter: blur(10px);
  border: 1px solid var(--card-border);
  border-radius: var(--r-md);
  padding: 16px 14px;
  display: flex; flex-direction: column;
  gap: 12px;
  min-height: 320px;
}
.k-macro-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 0 4px 10px;
  border-bottom: 1px solid var(--hairline);
}
.k-macro-head .name {
  font-size: var(--t-sm); font-weight: var(--poids-appuye);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-primary);
}
.k-macro-head .cnt { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-muted); }

.k-sub {
  display: flex; flex-direction: column; gap: 8px;
}
.k-sub-label {
  font-size: var(--t-xs); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-muted); padding: 0 2px;
}
.k-card {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  padding: 11px 12px;
  cursor: grab;
  transition: box-shadow var(--t-fast), transform var(--t-fast), border-color var(--t-fast);
  user-select: none;
  display: flex; flex-direction: column; gap: 6px;
}
.k-card:hover { box-shadow: var(--shadow-soft); border-color: var(--hairline-strong); transform: translateY(-1px); }
.k-card.dragging { opacity: 0.5; cursor: grabbing; }
.k-card .k-top { display: flex; align-items: center; justify-content: space-between; }
.k-card .k-id { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-muted); }
.k-card .k-title {
  font-size: var(--t-md); line-height: 1.35; color: var(--text-primary); font-weight: var(--poids-appuye);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.k-card .k-meta {
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-sm); color: var(--text-muted);
}
.k-card .k-meta .amt { font-family: var(--mono); color: var(--text-secondary); }
.k-card .k-fam { width: 14px; height: 2px; border-radius: 1px; background: var(--c, var(--steel-blue)); }
.k-sub.over { background: rgba(91, 108, 122, 0.05); border-radius: var(--r-sm); padding: 6px 4px; }

/* --- Responsive ------------------------------------------ */
@media (max-width: 1340px) {
  .kpis { grid-template-columns: repeat(3, 1fr); }
  .forecast-grid { grid-template-columns: 1fr; }
  .accounts-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 980px) {
  .app { grid-template-columns: 1fr; }
  .sidebar { display: none; }
  .main { padding: var(--sp-5); }
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .accounts-grid { grid-template-columns: 1fr; }
  .header { padding: 0 var(--sp-5); }
  .header-meta .header-tick { display: none; }
  .page-head h1 { font-size: var(--t-3xl); }
  .kanban-wrap { grid-template-columns: 1fr; }
  .fc-card {
    grid-template-areas:
      "head     head"
      "title    title"
      "prob     prob"
      "meta     meta"
      "signals  signals"
      "foot     foot";
  }
  .fc-prob { padding-left: 0; border-left: 0; padding-top: 12px; border-top: 1px solid var(--hairline); flex-direction: row; gap: 18px; }
  table.tenders thead { display: none; }
  table.tenders, table.tenders tbody, table.tenders tr, table.tenders td { display: block; width: auto; }
  table.tenders tbody tr {
    padding: 12px 14px; margin-bottom: 10px;
    border: 1px solid var(--hairline);
    border-radius: var(--r-md);
    background: var(--card-solid);
  }
  table.tenders tbody td { padding: 4px 0; }
  .panel { width: 100vw; }
  .score-feature { grid-template-columns: 1fr; }
}

.panel-body::-webkit-scrollbar,
.kanban-wrap::-webkit-scrollbar { height: 8px; width: 8px; }
.panel-body::-webkit-scrollbar-thumb,
.kanban-wrap::-webkit-scrollbar-thumb {
  background: rgba(31,35,40,0.15);
  border-radius: 4px;
}

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
.kpi, .section, .horizon-strip { animation: fadeUp var(--t-slow) both; }
.kpi:nth-child(1) { animation-delay: 0ms;  }
.kpi:nth-child(2) { animation-delay: 40ms; }
.kpi:nth-child(3) { animation-delay: 80ms; }
.kpi:nth-child(4) { animation-delay: 120ms;}
.kpi:nth-child(5) { animation-delay: 160ms;}
.kpi:nth-child(6) { animation-delay: 200ms;}

/* ============================================================
   v3 — view switching: published (default) / forecast / pipeline / sources
   ============================================================ */
.view-only-forecast,
.view-only-published,
.view-only-pipeline,
.view-only-memoire,
.view-only-tableau,
.view-only-sources { display: none; }
body.view-forecast   .view-only-forecast   { display: block; }
body.view-published  .view-only-published  { display: block; }
body.view-pipeline   .view-only-pipeline   { display: block; }
body.view-memoire    .view-only-memoire    { display: block; }
body.view-sources    .view-only-sources    { display: block; }

/* Pipeline / Sources / Mémoire views suppress the KPI strip and main view tabs */
body.view-pipeline #kpis,
body.view-pipeline .view-tabs,
body.view-sources  #kpis,
body.view-sources  .view-tabs,
body.view-memoire  #kpis,
body.view-memoire  .view-tabs,
body.view-tableau  #kpis,
body.view-tableau  .view-tabs { display: none; }
body.view-tableau  .view-only-tableau { display: block; }

/* ============================================================
   S82 — TABLEAU DE BORD : écran ANNONCÉ, jamais simulé.
   Zéro chiffre par construction (voir index.html). Le style dit
   « projet », pas « produit livré » : filet tireté, teinte du texte
   secondaire, aucune des surfaces pleines réservées aux vraies
   mesures. 100 % jetons — aucune couleur ajoutée (cliquet S65).
   ============================================================ */
.tdb-annonce { margin-bottom: var(--sp-6); }
.tdb-etat {
  display: inline-block; font-size: var(--t-xs); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted);
  border: 1px dashed var(--text-faint); border-radius: 10px;
  padding: 3px 11px; margin-bottom: var(--sp-3);
}
.tdb-intro {
  max-width: 62ch; font-size: var(--t-md); line-height: 1.55;
  color: var(--text-secondary); margin: 0;
}
.tdb-grille {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}
.tdb-brique {
  border: 1px dashed var(--hairline-strong); border-radius: var(--r-md);
  padding: var(--sp-4) var(--sp-5); background: var(--card-bg);
}
.tdb-brique h3 {
  margin: 0 0 var(--sp-2); font-size: var(--t-md);
  font-weight: var(--poids-appuye); color: var(--text-primary);
}
.tdb-brique p {
  margin: 0 0 var(--sp-2); font-size: var(--t-sm); line-height: 1.5;
  color: var(--text-secondary);
}
/* la DÉPENDANCE est ce qui rend l'annonce crédible : elle dit ce qui
   manque encore et pourquoi. En retrait, jamais cachée. */
.tdb-brique .tdb-dep {
  margin: 0; padding-top: var(--sp-2); border-top: 1px solid var(--hairline);
  font-size: var(--t-xs); color: var(--text-muted); font-style: italic;
}

/* ============================================================
   Vue Mémoire de marché (S16) — réutilise les primitives v3
   ============================================================ */
.view-only-memoire .kpis { display: grid; margin-bottom: 14px; }
.mem-note {
  font-size: var(--t-md); line-height: 1.5; color: var(--text-muted);
  border: 1px solid rgba(217,130,43,0.35); border-left: 3px solid var(--warning-orange);
  background: rgba(217,130,43,0.06); border-radius: 8px; padding: 10px 14px; margin: 0 0 14px;
}
.mem-badge {
  display: inline-block; font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.08em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 9px; margin-left: 6px;
  background: rgba(217,130,43,0.10); color: #8C5F2B; border: 1px solid rgba(217,130,43,0.32);
  vertical-align: middle; white-space: nowrap;
}
.mem-badge.grp { background: rgba(90,120,200,0.10); color: #46608F; border-color: rgba(90,120,200,0.32); }
.mem-year {
  font-family: var(--mono); font-size: var(--t-sm); padding: 5px 12px; border-radius: 14px;
  border: 1px solid rgba(0,0,0,0.14); background: transparent; cursor: pointer;
}
.mem-year.active { background: #1d1f24; color: var(--sur-aplat); border-color: #1d1f24; }
.mem-foot { font-size: var(--t-sm); color: var(--text-muted); padding: 10px 2px 0; }
.mem-erreur {
  padding: 26px; text-align: center; color: var(--text-muted); font-size: var(--t-md);
  border: 1px dashed rgba(0,0,0,0.2); border-radius: 10px; margin-top: 10px;
}
/* Recherche S19 */
.mem-search-wrap { position: relative; display: flex; gap: 10px; align-items: center; margin: 0 0 14px; }
.mem-results {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; width: min(640px, 92%);
  max-height: 420px; overflow-y: auto; background: #fff; border: 1px solid rgba(0,0,0,0.14);
  border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,0.14); padding: 8px;
}
.mem-res-titre { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-muted); padding: 8px 10px 4px; }
.mem-res-item { display: flex; gap: 10px; align-items: baseline; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; background: transparent; border-radius: 8px; cursor: pointer; font-size: var(--t-md); }
.mem-res-item:hover { background: rgba(0,0,0,0.05); }
.mem-res-item .nom { font-weight: var(--poids-appuye); }
.mem-res-item .meta { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-muted); margin-left: auto; white-space: nowrap; }
.mem-res-vide { padding: 14px 12px; font-size: var(--t-md); color: var(--text-muted); line-height: 1.5; }
tr.mem-hl > td { background: rgba(217,130,43,0.16) !important; transition: background 1.2s ease; }

/* ============================================================
   v3 — sidebar module badges (premium / roadmap)
   ============================================================ */
.nav-item .nav-badge {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--machine-on-dim);
  background: rgba(255,255,255,0.025);
  white-space: nowrap;
  line-height: 1.4;
  font-weight: var(--poids-appuye);
}
.nav-item .nav-badge.premium {
  /* one shade up: signals "buy this" without screaming */
  color: var(--premium-or);
  border-color: rgba(196, 163, 122, 0.22);
  background: rgba(196, 163, 122, 0.06);
}
.nav-item .nav-badge.roadmap {
  color: var(--machine-on-dim);
}
.nav-item.is-locked { opacity: 0.78; }
.nav-item.is-locked:hover { opacity: 1; }

/* Sidebar footer — OVH / 2FA / RGPD line. One step above background. */
.sidebar-footer .sec-line {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed rgba(255,255,255,0.06);
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  display: flex;
  align-items: center;
  gap: 8px;
  line-height: 1.4;
}
.sidebar-footer .sec-line .lock {
  width: 9px; height: 9px;
  color: var(--text-tertiary);
  flex: none;
}

/* ============================================================
   v3 — header tick (3 / 9 sources)
   ============================================================ */
.header-tick.coverage { cursor: pointer; transition: opacity var(--t-fast); }
.header-tick.coverage:hover { opacity: 0.85; }
.header-tick.coverage .v { display: inline-flex; align-items: baseline; gap: 4px; }
.header-tick.coverage .v .total { color: var(--text-muted); font-size: var(--t-md); }
.header-tick.coverage .pellets {
  display: flex; gap: 2px; margin-top: 4px;
  justify-content: flex-end;
}
.header-tick.coverage .pellets span {
  width: 10px; height: 3px; border-radius: 1px;
  background: var(--trait-10);
}
.header-tick.coverage .pellets span.on { background: var(--tech-green); }
.header-tick.coverage .pellets span.premium { background: var(--premium-or); }

/* ============================================================
   v3 — tabs : premium badge
   ============================================================ */
.view-tab .tab-badge {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 9px;
  color: var(--premium-or-fonce);
  border: 1px solid var(--premium-or-doux);
  background: rgba(196, 163, 122, 0.10);
  margin-left: 4px;
  font-weight: var(--poids-appuye);
  align-self: center;
}

/* ============================================================
   v3 — Premium banner inside Prévision view
   ============================================================ */
.premium-banner {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 18px;
  margin-bottom: var(--sp-6);
  background: linear-gradient(180deg, rgba(196,163,122,0.10), rgba(196,163,122,0.04));
  border: 1px solid rgba(196, 163, 122, 0.28);
  border-radius: var(--r-md);
  font-size: var(--t-md);
  color: var(--premium-or-sombre);
  line-height: 1.5;
}
.premium-banner .pb-tag {
  font-family: var(--mono); font-size: var(--t-xs);
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 10px;
  background: var(--premium-or); color: var(--sur-aplat);
  flex: none;
}
.premium-banner .pb-text { flex: 1; color: var(--premium-or-sombre); }
.premium-banner .pb-text strong { color: var(--premium-or-nuit); font-weight: var(--poids-appuye); }
.premium-banner .pb-action {
  font-size: var(--t-sm);
  color: var(--premium-or-sombre);
  padding: 5px 10px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(196, 163, 122, 0.4);
  background: rgba(255,255,255,0.6);
  white-space: nowrap;
}
.premium-banner .pb-action:hover { background: #FFF; color: var(--premium-or-nuit); }

/* Dim watermark on premium content underneath */
.premium-section { position: relative; }
.premium-section::after {
  content: "DÉMONSTRATION";
  position: absolute; top: 12px; right: 12px;
  font-family: var(--mono);
  font-size: var(--t-xs); letter-spacing: 0.2em;
  color: rgba(196,163,122,0.45);
  pointer-events: none;
}

/* ============================================================
   v3 — Roadmap overlay (two-time demo)
   ============================================================ */
.roadmap-overlay {
  position: sticky; top: 64px; z-index: 15;
  margin: -8px 0 var(--sp-6);
  padding: 10px 18px;
  background: var(--machine-dark);
  color: var(--machine-on);
  border-radius: var(--r-md);
  display: flex; align-items: center; gap: 14px;
  font-size: var(--t-md);
  letter-spacing: -0.005em;
  box-shadow: var(--shadow-soft);
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity var(--t), transform var(--t);
}
.roadmap-overlay.show { opacity: 1; transform: none; pointer-events: auto; }
.roadmap-overlay .ov-tag {
  font-family: var(--mono); font-size: var(--t-xs);
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--premium-or);
  padding: 3px 8px; border-radius: 9px;
  border: 1px solid var(--premium-or-doux);
  background: rgba(196, 163, 122, 0.08);
  flex: none;
}
.roadmap-overlay .ov-text { flex: 1; color: var(--machine-on); }
.roadmap-overlay .ov-text strong { color: var(--sur-aplat); font-weight: var(--poids-appuye); }
.roadmap-overlay .ov-close {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: 4px;
  color: var(--machine-on-mid);
}
.roadmap-overlay .ov-close:hover { background: rgba(255,255,255,0.08); color: var(--machine-on); }

/* ============================================================
   v3 — "Voir la roadmap" CTA in header actions
   ============================================================ */
.btn.btn-roadmap {
  background: rgba(196, 163, 122, 0.08);
  border: 1px solid rgba(196, 163, 122, 0.32);
  color: var(--premium-or-sombre);
}
.btn.btn-roadmap:hover {
  background: rgba(196, 163, 122, 0.14);
  border-color: rgba(196, 163, 122, 0.5);
  color: var(--premium-or-nuit);
}
.btn.btn-roadmap .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--premium-or);
}

/* ============================================================
   v3 — Sources view
   ============================================================ */
.sources-wrap {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: var(--sp-6);
  margin-top: var(--sp-3);
}
.sources-summary {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 22px 22px 18px;
  box-shadow: var(--shadow-soft);
  align-self: start;
  position: sticky; top: 96px;
}
.sources-summary h3 {
  font-size: var(--t-sm); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-muted); margin: 0 0 14px; font-weight: var(--poids-appuye);
}
.sources-summary .big-fig {
  font-family: var(--mono);
  font-size: var(--t-display);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--text-primary);
}
.sources-summary .big-fig .denom { color: var(--text-faint); font-size: var(--t-4xl); margin-left: 4px; }
.sources-summary .caption {
  font-size: var(--t-md); color: var(--text-secondary);
  margin-top: 6px; line-height: 1.5;
}
.sources-summary .legend {
  margin-top: 18px;
  display: flex; flex-direction: column; gap: 8px;
  border-top: 1px solid var(--hairline);
  padding-top: 16px;
}
.sources-summary .leg {
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-md); color: var(--text-secondary);
}
.sources-summary .leg .l { display: flex; align-items: center; gap: 8px; }
.sources-summary .leg .l .pip {
  width: 8px; height: 8px; border-radius: 2px; flex: none;
}
.sources-summary .leg .v { font-family: var(--mono); color: var(--text-primary); }
.sources-summary .footnote {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px dashed var(--hairline);
  font-size: var(--t-sm); color: var(--text-muted);
  line-height: 1.5;
}

.sources-list {
  display: flex; flex-direction: column; gap: 10px;
}
.src-card {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: 18px;
  align-items: center;
  padding: 18px 22px;
  background: var(--card-bg);
  backdrop-filter: blur(10px);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--c, var(--text-faint));
  border-radius: var(--r-md);
  transition: background var(--t-fast), border-color var(--t-fast);
}
.src-card:hover { background: var(--card-solid); }
.src-card .src-num {
  font-family: var(--mono); font-size: var(--t-sm);
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.src-card .src-info { display: flex; flex-direction: column; gap: 4px; }
.src-card .src-info .name {
  font-size: var(--t-lg); font-weight: var(--poids-appuye);
  color: var(--text-primary);
  letter-spacing: -0.005em;
  display: flex; align-items: center; gap: 10px;
}
.src-card .src-info .name .type {
  font-family: var(--mono); font-size: var(--t-xs);
  color: var(--text-muted);
  font-weight: var(--poids-normal);
  padding: 2px 8px;
  border-radius: 9px;
  background: var(--trait-04);
  border: 1px solid var(--hairline);
}
.src-card .src-info .full {
  font-size: var(--t-md); color: var(--text-secondary);
  line-height: 1.4;
}
.src-card .src-info .meta {
  display: flex; gap: 18px;
  margin-top: 6px;
  font-size: var(--t-sm); color: var(--text-muted);
}
.src-card .src-info .meta .pair { display: flex; gap: 6px; align-items: baseline; }
.src-card .src-info .meta .pair .l {
  font-size: var(--t-xs); letter-spacing: 0.12em;
  text-transform: uppercase;
}
.src-card .src-info .meta .pair .v {
  font-family: var(--mono); color: var(--text-secondary); font-size: var(--t-sm);
}
.src-card .src-right {
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
.src-status {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px;
  border-radius: 13px;
  font-size: var(--t-sm);
  font-weight: var(--poids-appuye);
  letter-spacing: 0.04em;
  border: 1px solid;
  white-space: nowrap;
}
.src-status::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
}
.src-status.v1 {
  background: rgba(111, 138, 120, 0.10);
  color: var(--tech-green-deep);
  border-color: rgba(111, 138, 120, 0.30);
}
.src-status.v1::before { background: var(--tech-green); box-shadow: 0 0 0 3px rgba(111,138,120,0.18); }
.src-status.premium {
  background: rgba(196, 163, 122, 0.10);
  color: var(--premium-or-sombre);
  border-color: var(--premium-or-doux);
}
.src-status.premium::before { background: var(--premium-or); }
.src-status.roadmap {
  background: var(--trait-04);
  color: var(--text-muted);
  border-color: var(--hairline);
}
.src-status.roadmap::before { background: var(--text-faint); }
.src-card .src-volume {
  font-family: var(--mono);
  font-size: var(--t-xl);
  color: var(--text-primary);
  letter-spacing: -0.01em;
}
.src-card .src-volume .l {
  font-family: var(--sans);
  font-size: var(--t-xs); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-muted);
  display: block;
  margin-top: 2px;
  text-align: right;
}

/* Status-specific left rail color */
.src-card[data-status="v1"]       { --c: var(--tech-green); }
.src-card[data-status="premium"]  { --c: var(--premium-or); }
.src-card[data-status="roadmap"]  { --c: rgba(31, 35, 40, 0.12); }
.src-card[data-status="roadmap"]  { background: rgba(255,255,255,0.55); }
.src-card[data-status="roadmap"] .src-info .name,
.src-card[data-status="roadmap"] .src-volume { opacity: 0.85; }

/* ============================================================
   v3 — Pipeline kanban : premium grayed sub-stages
   ============================================================ */
.k-macro.premium-grayed {
  background: rgba(255,255,255,0.42);
  border-style: dashed;
}
.k-macro.premium-grayed .k-macro-head {
  border-bottom-style: dashed;
}
.k-macro.premium-grayed .k-macro-head .name {
  color: var(--text-muted);
}
.k-macro .premium-pill {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--premium-or-fonce);
  border: 1px solid var(--premium-or-doux);
  background: rgba(196, 163, 122, 0.10);
  padding: 2px 7px;
  border-radius: 9px;
  margin-left: 6px;
  font-weight: var(--poids-appuye);
}
.k-macro.premium-grayed .k-card {
  opacity: 0.78;
  filter: saturate(0.7);
}
.k-macro.premium-grayed .k-card:hover { opacity: 1; filter: none; }
.k-macro.premium-grayed .k-empty {
  font-size: var(--t-sm);
  color: var(--text-muted);
  text-align: center;
  padding: 14px 8px;
  border: 1px dashed var(--hairline);
  border-radius: var(--r-sm);
  margin-top: 8px;
  line-height: 1.5;
}
.k-macro.premium-grayed .k-empty strong {
  color: var(--premium-or-fonce); font-weight: var(--poids-appuye);
}

/* ============================================================
   v3 — Detail panel : Assistance commerciale placeholder
   (replaces fake LLM generator from v2)
   ============================================================ */
.assistance-block {
  border: 1px dashed rgba(196, 163, 122, 0.45);
  border-radius: var(--r-md);
  padding: 22px 24px;
  background: linear-gradient(180deg, rgba(196,163,122,0.06), rgba(196,163,122,0.02));
  position: relative;
}
.assistance-block .pb-row {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 12px;
}
.assistance-block .pb-tag {
  font-family: var(--mono); font-size: var(--t-xs);
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 10px;
  background: var(--premium-or); color: var(--sur-aplat);
  font-weight: var(--poids-appuye);
}
.assistance-block h3 {
  font-size: var(--t-lg); font-weight: var(--poids-appuye);
  letter-spacing: -0.005em;
  color: var(--text-primary);
  margin: 0; text-transform: none;
}
.assistance-block .pb-desc {
  font-size: var(--t-md);
  color: var(--text-secondary);
  line-height: 1.55;
  margin: 0 0 16px;
  text-wrap: pretty;
}
.assistance-block .pb-desc strong { color: var(--text-primary); font-weight: var(--poids-appuye); }
.assistance-block .pb-mockup {
  background: var(--card-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 18px 20px;
  position: relative;
  overflow: hidden;
}
.assistance-block .pb-mockup .mock-head {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--t-xs);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 12px;
}
.assistance-block .pb-mockup .mock-head::after {
  content: ""; flex: 1; height: 1px; background: var(--hairline);
}
.assistance-block .pb-mockup .lines {
  display: flex; flex-direction: column; gap: 8px;
}
.assistance-block .pb-mockup .lines .ln {
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(90deg, rgba(31,35,40,0.07) 0%, var(--trait-04) 60%, rgba(31,35,40,0.07) 100%);
}
.assistance-block .pb-mockup .lines .ln.w-90 { width: 92%; }
.assistance-block .pb-mockup .lines .ln.w-80 { width: 84%; }
.assistance-block .pb-mockup .lines .ln.w-70 { width: 74%; }
.assistance-block .pb-mockup .lines .ln.w-60 { width: 62%; }
.assistance-block .pb-mockup .lines .ln.w-40 { width: 42%; }
.assistance-block .pb-mockup .lines .ln.h-block {
  height: 14px;
  background: linear-gradient(90deg, var(--trait-10) 0%, var(--trait-06) 60%, var(--trait-10) 100%);
  margin-bottom: 4px;
}
.assistance-block .pb-mockup::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 30%, rgba(255,255,255,0.92) 95%);
  pointer-events: none;
}
.assistance-block .pb-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 16px;
  font-size: var(--t-sm);
  color: var(--text-muted);
}
.assistance-block .pb-foot .pb-cta {
  font-size: var(--t-md);
  padding: 6px 12px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(196, 163, 122, 0.4);
  background: rgba(255,255,255,0.7);
  color: var(--premium-or-sombre);
  font-weight: var(--poids-appuye);
}
.assistance-block .pb-foot .pb-cta:hover { background: #FFF; color: var(--premium-or-nuit); }

/* Premium-style tab indicator (gold underline) */
.view-tab.is-premium.active {
  border-bottom-color: var(--premium-or);
  color: var(--premium-or-sombre);
}
.view-tab.is-premium .cnt {
  background: rgba(196,163,122,0.12);
  color: var(--premium-or-fonce);
}
.view-tab.is-premium.active .cnt {
  background: var(--premium-or);
  color: var(--sur-aplat);
}

/* Forecast cards : dim badge when premium */
.view-only-forecast .fc-card {
  position: relative;
}

/* Accounts in forecast view get a small premium ribbon */
.accounts-grid::before {
  content: "";
}

/* Mobile : sidebar collapsible toggle */
.sidebar-toggle {
  display: none;
  position: fixed; top: 12px; left: 12px; z-index: 60;
  width: 36px; height: 36px;
  border-radius: var(--r-sm);
  background: var(--machine-dark);
  color: var(--machine-on);
  align-items: center; justify-content: center;
}
.sidebar-toggle svg { width: 18px; height: 18px; }
@media (max-width: 980px) {
  .sidebar-toggle { display: flex; }
  body.sidebar-open .sidebar {
    display: flex;
    position: fixed; top: 0; left: 0; bottom: 0;
    width: 248px; z-index: 50;
  }
  .sources-wrap { grid-template-columns: 1fr; }
  .sources-summary { position: relative; top: auto; }
  .src-card { grid-template-columns: 28px 1fr; row-gap: 10px; }
  .src-card .src-right { grid-column: 1 / -1; flex-direction: row; justify-content: space-between; align-items: center; }
}

/* S26 — pipeline statuts (badges, actions, motif, historique) : styles ADDITIFS.
   Retour HUMAN 22/07 : couleurs FRANCHES (les fonds à 10-15 % d'alpha se lisaient
   gris) et boutons qui FONT bouton. */
.st-cell { display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.st-badge {
  display: inline-block; padding: 3px 10px; border-radius: 10px;
  font-size: var(--t-xs); font-weight: var(--poids-fort); letter-spacing: 0.05em; text-transform: uppercase;
  cursor: pointer; border: 1px solid transparent;
}
.st-nouveau { background: var(--steel-blue); color: var(--sur-aplat); }
.st-a_traiter { background: var(--warning-orange); color: var(--sur-aplat); }
.st-en_preparation { background: rgba(217,130,43,0.16); color: var(--warning-orange-deep); border-color: rgba(217,130,43,0.55); }
.st-depose { background: var(--machine-dark); color: var(--sur-aplat); }
.st-gagne { background: var(--tech-green); color: var(--sur-aplat); }
.st-perdu { background: var(--signal-red); color: var(--sur-aplat); }
.st-sans_suite { background: var(--bg-tertiary); color: var(--text-secondary); border-color: var(--hairline-strong); }
.st-ecarte { background: var(--bg-tertiary); color: var(--text-muted); border-color: var(--hairline-strong); text-decoration: line-through; }
.st-actions { display: flex; flex-wrap: wrap; gap: 5px; }
.st-act {
  font-size: var(--t-sm); font-weight: var(--poids-appuye); padding: 4px 10px; border-radius: var(--r-sm);
  border: 1px solid var(--hairline-strong); background: var(--card-solid);
  color: var(--text-primary); cursor: pointer;
  box-shadow: 0 1px 2px var(--hairline);
  transition: background var(--t-fast), border-color var(--t-fast);
}
.st-act:hover { background: var(--machine-dark); border-color: var(--machine-dark); color: var(--sur-aplat); }
.st-motif { display: flex; gap: 4px; margin-top: 4px; }
.st-motif input {
  font-size: var(--t-sm); padding: 3px 6px; border: 1px solid var(--hairline-strong);
  border-radius: var(--r-xs); width: 190px;
}
.st-histo-row td { background: var(--bg-secondary); padding: 8px 14px; }
.st-histo-ligne { font-size: var(--t-md); padding: 2px 0; color: var(--text-primary); }
.st-histo-ligne .mono { color: var(--text-muted); margin-right: 8px; }
.st-histo-ligne .meta { color: var(--text-muted); }

/* S23 — fixes vitrine (audit design C1-C3, C5, M1, M3, M6) : styles ADDITIFS seulement */
/* C2 : les cartes mémoire (.l/.v/.s) n'avaient JAMAIS été stylées — un KPI = un chiffre
   dominant + libellé court. C1 : la grille mémoire = 4 colonnes (pas les 6 du mock). */
#mem-kpis.kpis { grid-template-columns: 1fr 1fr 1fr 1.8fr; }
#mem-kpis .kpi { min-height: 116px; }
#mem-kpis .kpi .l {
  font-size: var(--t-xs); font-weight: var(--poids-appuye); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 10px;
}
#mem-kpis .kpi .v {
  font-family: var(--mono); font-size: var(--t-3xl); font-weight: var(--poids-normal);
  color: var(--text-primary); letter-spacing: -0.025em; line-height: 1.1;
  white-space: nowrap;
}
#mem-kpis .kpi .s { font-size: var(--t-sm); color: var(--text-muted); margin-top: auto; padding-top: 8px; }
.kpi-liste .kpi-ligne {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; font-size: var(--t-md); padding: 2px 0; color: var(--text-primary);
}
.kpi-liste .kpi-ligne .nom {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.kpi-liste .kpi-ligne .mono { white-space: nowrap; }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }
/* C3 : le .mini-search de la zone mémoire n'avait PAS le layout flex (scopé .filters
   dans le mock) — l'input rendait ~150px et tronquait le placeholder en plein mot */
.mem-search-wrap .mini-search {
  display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 7px 12px;
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); font-size: var(--t-md);
}
.mem-search-wrap .mini-search input {
  flex: 1; width: 100%; min-width: 0;
  border: 0; background: transparent; outline: 0; font-size: var(--t-md);
}
/* la règle #mem-kpis (spécificité id) écrase le breakpoint 1340px du mock — il faut
   le SIEN, sinon les cartes descendent à ~68px utiles entre 981 et 1340px (audit S23) */
@media (max-width: 1340px) {
  #mem-kpis.kpis { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 980px) {
  #mem-kpis.kpis { grid-template-columns: 1fr 1fr; }
}

/* S21 — graphiques mémoire (Chart.js vendorée) : styles ADDITIFS seulement */
.mem-graphs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 14px 0; }
.mem-graph, .mem-echeances {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-md); padding: 12px 14px;
}
.mem-graph h3, .mem-echeances h3 {
  font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-secondary); margin: 0 0 8px;
}
.mem-graph-note { font-size: var(--t-sm); color: var(--text-muted); margin-top: 6px; }
.mem-echeances { margin-bottom: 14px; }
.mem-ech-row {
  display: grid; grid-template-columns: 92px 1fr auto; gap: 10px;
  padding: 6px 0; border-bottom: 1px solid var(--hairline);
  cursor: pointer; font-size: var(--t-md); align-items: baseline;
}
.mem-ech-row:hover { background: var(--bg-secondary); }
.mem-ech-row .meta { color: var(--text-muted); font-size: var(--t-md); }
@media (max-width: 980px) {
  .mem-graphs { grid-template-columns: 1fr; }
  .mem-ech-row { grid-template-columns: 80px 1fr; }
  .mem-ech-row > span:last-child { grid-column: 1 / -1; text-align: left; }
}

/* S20 — modale détail marché : styles ADDITIFS seulement, le drawer reste .panel/.scrim */
.nr { color: var(--text-muted); font-style: italic; }
#mem-body tr { cursor: pointer; }
.md-body h3 {
  margin: var(--sp-4) 0 var(--sp-2);
  font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-secondary);
}
.md-grid {
  display: grid; grid-template-columns: 220px 1fr; gap: 6px 14px; margin: 0;
}
.md-grid dt { color: var(--text-muted); font-size: var(--t-md); }
.md-grid dd { margin: 0; font-size: var(--t-lg); color: var(--text-primary); }
.md-tit { padding: 5px 0; border-bottom: 1px solid var(--hairline); font-size: var(--t-lg); }
.md-tit .meta { color: var(--text-muted); margin-left: 8px; font-size: var(--t-md); }
.md-foot { display: flex; flex-direction: column; gap: 6px; font-size: var(--t-md); color: var(--text-secondary); }
.md-lien { color: var(--steel-blue); text-decoration: underline; }
@media (max-width: 980px) {
  .md-grid { grid-template-columns: 1fr; gap: 2px 0; }
  .md-grid dd { margin-bottom: 6px; }
}

/* S27 — dispatch agrégatrice → profils : styles ADDITIFS seulement */
#veille-contexte {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 12px; margin: 0 0 10px;
  background: var(--card-solid); border: 1px solid var(--hairline-strong);
  border-left: 3px solid var(--steel-blue); border-radius: var(--r-sm);
}
#veille-contexte .ctx-label {
  font-size: var(--t-xs); font-weight: var(--poids-appuye); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-muted);
}
#veille-contexte #ctx-select {
  font-size: var(--t-md); padding: 4px 8px; border: 1px solid var(--hairline-strong);
  border-radius: var(--r-xs); background: var(--bg-primary); color: var(--text-primary);
  max-width: 340px;
}
#veille-contexte .ctx-note { font-size: var(--t-sm); color: var(--text-secondary); }
.asg-select {
  font-size: var(--t-sm); padding: 3px 6px; max-width: 150px;
  border: 1px solid var(--hairline-strong); border-radius: var(--r-xs);
  background: var(--bg-primary); color: var(--text-primary);
}
.asg-badge {
  display: inline-block; padding: 2px 8px; border-radius: 10px;
  font-size: var(--t-sm); font-weight: var(--poids-appuye);
  background: var(--bg-tertiary); color: var(--text-primary);
  border: 1px solid var(--hairline-strong);
}
.asg-badge.non-assigne { color: var(--text-muted); font-style: italic; border-style: dashed; }

/* S28 — planning des dates limites : styles ADDITIFS seulement */
.view-only-planning { display: none; }
body.view-planning .view-only-planning { display: block; }
body.view-planning #kpis, body.view-planning .view-tabs { display: none; }
/* le bandeau Point de vue ne s'affiche QUE sur la veille (S37/A3 : le dispatch vit
   là ; au planning le contexte actif se dit passivement dans le méta) — caché
   partout ailleurs (rien d'inerte, S23), le conteneur vidé ne laisse pas de boîte */
body.view-memoire #veille-contexte, body.view-pipeline #veille-contexte,
body.view-sources #veille-contexte, body.view-forecast #veille-contexte,
body.view-planning #veille-contexte { display: none; }
.pl-groupe { margin: 0 0 20px; }
.pl-groupe h3 {
  font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-secondary); margin: 0 0 8px;
  display: flex; gap: 8px; align-items: baseline;
}
.pl-groupe h3 .n { font-family: var(--mono); }
.pl-groupe.echu h3 { color: var(--signal-red); }
.pl-item {
  display: grid; grid-template-columns: 96px 96px 1fr auto;
  gap: 12px; padding: 9px 12px; align-items: center; font-size: var(--t-md);
  background: var(--card-solid); border: 1px solid var(--hairline); border-bottom: 0;
  cursor: pointer;
}
.pl-groupe .pl-item:first-of-type { border-radius: var(--r-sm) var(--r-sm) 0 0; }
.pl-groupe .pl-item:last-of-type { border-bottom: 1px solid var(--hairline); border-radius: 0 0 var(--r-sm) var(--r-sm); }
.pl-groupe .pl-item:first-of-type:last-of-type { border-radius: var(--r-sm); }
.pl-item:hover { background: var(--bg-secondary); }
.pl-groupe.echu .pl-item { border-left: 3px solid var(--signal-red); }
.pl-j { font-family: var(--mono); font-weight: var(--poids-fort); }
.pl-item.echu .pl-j, .pl-item.echu .pl-date { color: var(--signal-red); }
.pl-item.urgent .pl-j { color: var(--warning-orange); }
.pl-corps { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* RECALAGE S47 (gate HUMAN 03/08) : l'objet du planning s'affiche EN ENTIER
   (fini le nowrap-ellipsis une ligne) — wrap complet, tokens contenus */
.pl-corps .t { overflow-wrap: anywhere; }
.pl-sub { color: var(--text-muted); font-size: var(--t-md); }
.pl-badges { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.pl-vide { padding: 42px; text-align: center; color: var(--text-muted); }
/* saut planning -> veille : la ligne d'arrivée FLASHE (on sait où on atterrit) */
.flash-avis { animation: flashAvis 2.2s ease-out; }
@keyframes flashAvis { 0% { background: rgba(217, 130, 43, 0.35); } 100% { background: transparent; } }
@media (max-width: 980px) {
  .pl-item { grid-template-columns: 80px 1fr; }
  /* audit S28 : l'objet passait dans une colonne de 80 px — pleine largeur ligne 2 */
  .pl-corps { grid-column: 1 / -1; }
  .pl-badges { grid-column: 1 / -1; justify-content: flex-start; }
}

/* S30 — page détail marché plein écran + vue concurrence : styles ADDITIFS seulement.
   Norme visuelle §D (backlog_polish_ui) : densité AÉRÉE, une info dominante par
   élément, couleur = sens jamais décoration. */
.view-only-marche { display: none; }
body.view-marche .view-only-marche { display: block; }
body.view-marche #kpis, body.view-marche .view-tabs,
body.view-marche #veille-contexte { display: none; }
/* retour HUMAN S30 (30/07) : la page détail occupait la moitié gauche des écrans
   larges (max-width 1100px) — pleine largeur demandée, les cartes s'étendent */
#marche-page { max-width: none; }
.mp-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.mp-badges { display: flex; gap: 6px; flex-wrap: wrap; }
.mp-objet { font-size: var(--t-3xl); line-height: 1.3; margin: 0 0 6px; letter-spacing: -0.01em; }
.mp-sous { color: var(--text-muted); font-size: var(--t-md); margin-bottom: 22px; }
.mp-domine {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 0 0 22px;
}
.mp-montant {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-md); padding: 16px 18px; min-height: 104px;
  display: flex; flex-direction: column;
}
.mp-montant .l {
  font-size: var(--t-xs); font-weight: var(--poids-appuye); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 8px;
}
.mp-montant .v { font-size: var(--t-3xl); letter-spacing: -0.02em; color: var(--text-primary); }
.mp-montant .s { font-size: var(--t-sm); color: var(--text-muted); margin-top: auto; padding-top: 8px; }
.mp-grille { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
.mp-carte {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-md); padding: 16px 18px;
}
.mp-carte h3 {
  font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-secondary); margin: 0 0 12px;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.mp-carte .md-grid { grid-template-columns: 200px 1fr; gap: 8px 14px; }
.mp-tit {
  display: flex; align-items: baseline; gap: 10px; padding: 8px 0;
  border-bottom: 1px solid var(--hairline); font-size: var(--t-lg);
}
.mp-tit:last-child { border-bottom: 0; }
.mp-tit .pos { color: var(--text-muted); }
.mp-tit .nom { flex: 1; }
.mp-tit .meta { color: var(--text-muted); font-size: var(--t-md); }
.mp-trace { margin-bottom: 22px; }
.mp-trace-ligne { padding: 5px 0; font-size: var(--t-md); }
.mp-trace-ligne.meta { color: var(--text-muted); font-size: var(--t-md); }
.mp-btn-officiel {
  display: inline-block; margin-top: 4px; padding: 8px 14px;
  border: 1px solid var(--hairline-strong); border-radius: var(--r-sm);
  background: var(--bg-primary); color: var(--steel-blue); text-decoration: none;
  font-size: var(--t-md); font-weight: var(--poids-appuye);
}
.mp-btn-officiel:hover { background: var(--bg-secondary); }
@media (max-width: 980px) {
  .mp-domine, .mp-grille { grid-template-columns: 1fr; }
  .mp-carte .md-grid { grid-template-columns: 1fr; }
}
/* concurrence (mémoire) — l'estimatif masqué laisse 2 graphiques rétrospectifs */
.mem-graphs.sans-estimatif { grid-template-columns: repeat(2, 1fr); }
.mem-concurrence { margin: 0 0 16px; }
.mem-cc-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.mem-cc-head h3 { font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-secondary); margin: 0 0 4px; }
.mem-concurrence > .mem-graph-note { margin-bottom: 10px; }
.mem-cc-grille { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.mem-cc-liste { margin-top: 10px; display: flex; flex-direction: column; }
.mem-cc-item {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: baseline;
  padding: 8px 10px; border: 0; border-bottom: 1px solid var(--hairline);
  background: transparent; cursor: pointer; text-align: left; font-size: var(--t-md);
  color: var(--text-primary);
}
.mem-cc-item:last-child { border-bottom: 0; }
.mem-cc-item:hover { background: var(--bg-secondary); }
.mem-cc-item .nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mem-cc-item .n { font-weight: var(--poids-fort); }
.mem-cc-item .mt { color: var(--text-secondary); }
@media (max-width: 980px) {
  .mem-cc-grille { grid-template-columns: 1fr; }
  .mem-graphs.sans-estimatif { grid-template-columns: 1fr; }
}
/* S30 (audit) : objets très longs (jusqu'à 1000 caractères en base) — taille réduite,
   jamais tronqués (c'est LA page de référence du marché) */
.mp-objet.long { font-size: var(--t-xl); }

/* S33 — corrections UI critiques : styles ADDITIFS seulement */
/* U5 : UN chiffre domine la veille, le reste = mentions secondaires libellées */
.kpi-dominant .kpi-value { font-size: var(--t-4xl); }
.kpi-secondaires { display: flex; flex-direction: column; justify-content: center; gap: 7px; }
.kpi-secondaires .ligne { font-size: var(--t-md); color: var(--text-secondary); }
.kpi-secondaires .ligne b { color: var(--text-primary); }
/* U1→S35 : le repli des échus (S33) a ÉVOLUÉ en catégorie à part — bascule
   « À rendre / Dépassées » (patron .mem-year de la bascule tri S34 V5, réutilisé,
   jamais un 2e système). Compteur permanent sur chaque bouton, ouverture sur À rendre. */
.pl-bascule { display: flex; gap: 6px; margin: 0 0 16px; }
.pl-bascule .mem-year .mono { opacity: .75; }
.pl-bascule .pl-onglet-echu.active { background: var(--signal-red); border-color: var(--signal-red); color: var(--sur-aplat); }
.pl-note {
  padding: 10px 14px; margin: 0 0 12px; font-size: var(--t-md);
  color: var(--text-secondary); border-left: 3px solid var(--signal-red);
  background: rgba(200, 60, 60, 0.06); border-radius: 0 var(--r-sm) var(--r-sm) 0;
}

/* S36 [dispositions] : liste / cartes / par échéance — même donnée, trois rendus.
   Sélecteur = patron .mem-year (jamais un 2e système), icône + LIBELLÉ toujours. */
.dispo-host { margin-left: auto; padding-left: 14px; }
.section-head.compact .dispo-host { order: 2; }
.section-head.compact { align-items: center; gap: 12px; }
.dispo-bascule { display: inline-flex; gap: 6px; white-space: nowrap; }
.dispo-bascule .ico { font-size: var(--t-md); opacity: .8; }
/* cartes veille : norme §D — densité aérée, une info dominante (l'échéance),
   couleur = sens (urgence/échu partagés avec la liste) */
.av-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 14px; align-items: start;
}
.av-carte {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 14px 16px;
  display: flex; flex-direction: column; gap: 7px; min-width: 0;
}
.av-carte:hover { border-color: var(--hairline-strong); }
.av-carte.avis-echu { box-shadow: inset 3px 0 0 var(--signal-red); background: var(--bg-secondary); }
.av-carte.avis-echu .av-objet { color: var(--text-secondary); }
.av-tete { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.av-ref { color: var(--text-muted); font-size: var(--t-sm); }
.av-objet { font-size: var(--t-lg); font-weight: var(--poids-appuye); line-height: 1.45; }
.av-sub { color: var(--text-muted); font-size: var(--t-md); }
.av-pied {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  flex-wrap: wrap; margin-top: 4px; border-top: 1px solid var(--hairline); padding-top: 10px;
}
.av-carte .cell-id-sub { font-size: var(--t-sm); color: var(--text-faint); }
/* parité S34 (U9) : les actions secondaires apparaissent aussi au survol d'une CARTE */
.av-carte:hover .st-actions .st-act.secondaire { display: inline-flex; }
/* cartes planning */
.pl-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 14px; align-items: start;
}
.pl-carte {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 13px 15px; cursor: pointer;
  display: flex; flex-direction: column; gap: 6px; min-width: 0; font-size: var(--t-md);
}
.pl-carte:hover { background: var(--bg-secondary); }
.pl-carte .pl-badges { justify-content: flex-start; margin-top: 2px; }
.pl-carte.echu .pl-j, .pl-carte.echu .pl-date { color: var(--signal-red); }
.pl-carte.urgent .pl-j { color: var(--warning-orange); }
.pl-grille.echu .pl-carte { box-shadow: inset 3px 0 0 var(--signal-red); }

/* S37 [epuration-nav] : MASQUAGE réversible des entrées hors V1 (jamais une
   suppression — les badges Premium/Roadmap revendront les options post-signature :
   remise en service = retirer la classe sur l'élément). */
.nav-hors-v1 { display: none !important; }

/* S41 [page-detail-ao] : page détail de l'AVIS — MÊME patron que la page marché
   (S30), aucun second modèle. Plein écran, cartes par section, réglage d'affichage. */
.view-only-avis { display: none; }
body.view-avis .view-only-avis { display: block; }
body.view-avis #kpis, body.view-avis .view-tabs,
body.view-avis #veille-contexte { display: none; }
#ao-page { max-width: none; }
.ao-tete { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin: 0 0 14px; flex-wrap: wrap; }
/* l'OBJET mène (patron S30/D1) — le titre de page est volontairement vide */
.ao-objet { font-size: var(--t-3xl); line-height: 1.3; margin: 0 0 6px; font-weight: var(--poids-appuye); }
.ao-sous-titre { font-size: var(--t-md); color: var(--text-muted); margin: 0 0 18px; }
.ao-charge { padding: 42px; text-align: center; color: var(--text-muted); }
.ao-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 16px; align-items: start;
}
.ao-carte {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 16px 18px; min-width: 0;
}
.ao-carte h3 {
  margin: 0 0 12px; font-size: var(--t-md); text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--text-secondary);
  display: flex; gap: 8px; align-items: baseline;
}
.ao-ligne {
  display: grid; grid-template-columns: 190px 1fr; gap: 12px;
  padding: 7px 0; border-bottom: 1px solid var(--hairline); font-size: var(--t-md);
  align-items: baseline;
}
.ao-ligne:last-of-type { border-bottom: 0; }
.ao-ligne .k { color: var(--text-muted); font-size: var(--t-md); cursor: help; }
.ao-ligne .v { min-width: 0; overflow-wrap: anywhere; }
.ao-note { font-size: var(--t-sm); color: var(--text-faint); line-height: 1.6; margin-top: 10px; }
.ao-vide { font-size: var(--t-md); color: var(--text-muted); padding: 6px 0; }
.ao-brut {
  font-size: var(--t-xs); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--warning-orange); cursor: help; margin-left: 4px;
}
/* la traçabilité ne se masque JAMAIS — elle le DIT */
.ao-tracabilite { grid-column: 1 / -1; background: var(--bg-secondary); }
.ao-toujours {
  font-size: var(--t-xs); letter-spacing: 0.08em; text-transform: none;
  color: var(--text-faint); font-weight: var(--poids-normal);
}
/* diff de rectificatif : le changement CLÉ (date limite) se voit */
.ao-diff { padding: 9px 0; border-bottom: 1px solid var(--hairline); font-size: var(--t-md); }
.ao-diff:last-of-type { border-bottom: 0; }
.ao-diff .t { font-weight: var(--poids-appuye); margin-bottom: 4px; }
.ao-diff.critique { border-left: 3px solid var(--warning-orange); padding-left: 10px; }
.ao-crit {
  font-size: var(--t-xs); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--warning-orange); font-weight: var(--poids-fort);
}
/* S58 — critères d'attribution : la PONDÉRATION doit sauter aux yeux (c'est
   elle qui dit où porter l'effort) ; le texte libre publié par l'acheteur est
   restitué tel quel, sauts de ligne compris. */
/* S63 : un lot = un bloc. Les pondérations d'un lot ne se lisent qu'avec
   celles de son lot — les mélanger produisait des sommes impossibles. */
.ao-lot { margin-bottom: 10px; }
.ao-lot + .ao-lot { border-top: 1px dashed var(--hairline); padding-top: 8px; }
.ao-lot-tete {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); margin-bottom: 3px;
}
.ao-attrib { margin: 0; padding-left: 16px; font-size: var(--t-md); line-height: 1.65; }
.ao-attrib li { margin-bottom: 3px; }
.ao-attrib-p { color: var(--accent, var(--text-primary)); font-weight: var(--poids-fort); }
.ao-attrib-texte {
  font-size: var(--t-md); line-height: 1.6; white-space: pre-line;
  max-height: 180px; overflow-y: auto;
}
/* conditions de participation : présentes mais en retrait (le brief interdit
   d'ajouter sept champs à la file sans hiérarchie) */
.ao-sous-bloc { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--hairline); }
.ao-sous-bloc .ao-ligne { font-size: var(--t-md); opacity: 0.85; }
/* S60 — l'ÉCART de date limite est ce qui fait perdre un dossier : il se lit
   d'un coup d'œil, à côté du champ concerné. */
/* ══ S61 [accueil-a-rendre] ══════════════════════════════════════════════
   Un seul énoncé du total, avec son assiette — remplace quatre compteurs qui
   annonçaient la même population avec trois valeurs différentes. */
.kpi-enonce { padding: 16px 18px; }
.enonce-chiffres { display: flex; gap: 22px; align-items: baseline; flex-wrap: wrap; }
.enonce-chiffres .e-principal { font-size: var(--t-lg); }
.enonce-chiffres .e-principal b { font-size: var(--t-4xl); line-height: 1; margin-right: 6px; }
.enonce-chiffres .e-secondaire { font-size: var(--t-md); color: var(--text-secondary); }
.enonce-chiffres .e-secondaire b { font-size: var(--t-xl); }
.enonce-assiette { margin-top: 10px; font-size: var(--t-md); color: var(--text-secondary); }
.enonce-source { margin-top: 4px; font-size: var(--t-sm); color: var(--text-faint); }

/* S61 : le point de vue devient un contrôle compact, plus un bandeau qui
   occupe une bande entière de l'écran d'accueil */
#veille-contexte.ctx-compact {
  display: inline-flex; align-items: center; gap: 8px; padding: 0;
  border: 0; background: none; margin: 0 0 10px;
}
#veille-contexte.ctx-compact .ctx-label {
  font-size: var(--t-sm); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); cursor: help;
}
#veille-contexte.ctx-compact select { max-width: 260px; }

/* groupes d'échéance : la STRUCTURE de l'écran (l'ordre ne se négocie plus) */
.pl-groupe { margin-bottom: 20px; }
.pl-groupe > h3, .pl-groupe > summary {
  font-size: var(--t-md); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-secondary); margin: 0 0 8px; display: flex; gap: 8px;
  align-items: baseline;
}
.pl-groupe > summary { cursor: pointer; padding: 6px 0; }
.pl-groupe .n {
  font-size: var(--t-sm); background: var(--bg-secondary); border: 1px solid var(--hairline);
  border-radius: 10px; padding: 1px 8px; color: var(--text-muted);
}
/* le rouge SEULEMENT pour ce qui se rend aujourd'hui */
.pl-groupe.gr-aujourd_hui > h3 { color: var(--danger-red); }
.pl-groupe.gr-aujourd_hui > h3 .n {
  background: rgba(192, 57, 43, 0.1); border-color: rgba(192, 57, 43, 0.35);
  color: var(--danger-red);
}
.pl-groupe.gr-semaine > h3 { color: var(--warning-orange); }
.pl-groupe.replie { border-top: 1px dashed var(--hairline); padding-top: 6px; }
/* S61 : l'accueil ne déborde plus horizontalement (contrôle en 1280×720,
   résolution de vidéoprojecteur). Même remède qu'en S39 pour le kanban : la
   colonne applicative est bornée, et la table défile dans son propre cadre. */
body.view-published .app > div { min-width: 0; }
.pl-groupe { min-width: 0; }
.gr-scroll { overflow-x: auto; max-width: 100%; }
.gr-table { width: 100%; }

/* « nouveau » redevient discriminant : réservé aux parutions de moins de 7 j */
.av-neuf {
  font-size: var(--t-xs); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent, var(--text-primary)); border: 1px solid currentColor;
  border-radius: 8px; padding: 0 5px; margin-left: 4px;
}

/* état calme : l'écran propose ce qui vient plutôt que de rester muet */
.pl-calme { padding: 28px 22px; border: 1px dashed var(--hairline); border-radius: var(--r-sm); }
.pl-calme .calme-titre { font-size: var(--t-lg); margin-bottom: 10px; }
.pl-calme .calme-pistes { margin: 0 0 12px; padding-left: 18px; font-size: var(--t-md); line-height: 1.7; }
.pl-calme .calme-note { font-size: var(--t-sm); color: var(--text-faint); }

.ao-ecart { color: var(--warning-orange); font-size: var(--t-sm); }
/* cumul de cascade : le pas-à-pas seul masquerait un report total */
.ao-cumul {
  margin-top: 5px; font-size: var(--t-sm); color: var(--text-muted);
  border-left: 2px solid var(--hairline); padding-left: 8px;
}
/* historique des versions : consultable, jamais au premier plan */
.ao-versions { margin-top: 10px; border-top: 1px dashed var(--hairline); padding-top: 8px; }
.ao-versions > summary {
  font-size: var(--t-md); color: var(--text-muted); cursor: pointer; padding: 4px 0;
}
.ao-versions .ao-ligne { font-size: var(--t-md); grid-template-columns: 150px 1fr; }
.ao-diff-l { display: grid; grid-template-columns: 56px 1fr; gap: 8px; font-size: var(--t-md); }
.ao-diff-l .lab { color: var(--text-faint); font-size: var(--t-sm); }
.ao-diff-l .av { color: var(--text-muted); text-decoration: line-through; }
.ao-diff-l .ap { color: var(--text-primary); font-weight: var(--poids-appuye); }
/* réglage d'affichage */
.ao-reglage { font-size: var(--t-md); }
.ao-reglage summary { cursor: pointer; color: var(--text-secondary); }
.ao-reglage-corps {
  display: flex; flex-direction: column; gap: 7px; padding: 12px 14px; margin-top: 8px;
  background: var(--card-solid); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  min-width: 280px;
}
.ao-reglage-corps label { display: flex; gap: 8px; align-items: center; cursor: pointer; }
.ao-reset {
  align-self: flex-start; font-size: var(--t-sm); padding: 4px 11px; cursor: pointer;
  border: 1px solid var(--hairline-strong); background: transparent;
  border-radius: var(--r-xs); color: var(--text-secondary);
}
.ao-reset:hover { background: var(--machine-dark); border-color: var(--machine-dark); color: var(--sur-aplat); }
/* S41 (retour HUMAN 01/08) : le bouton « détail » est retiré — c'est le CLIC SUR
   LA LIGNE qui ouvre la page. Le survol le rend lisible sans ajouter d'élément. */
tbody tr[data-avis]:hover { background: var(--bg-secondary); }
.av-carte[data-avis]:hover, .pl-carte[data-avis]:hover { border-color: var(--hairline-strong); }
/* les actions du pipeline sur la page détail (le clic-ligne y mène) */
.ao-actions { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 12px; }
.ao-actions .st-act { display: inline-flex; }

/* S40 [segmentation-metier] : étiquette de famille métier. Couleur = SENS (norme
   §D) — le matériel (le métier du client) se distingue, la prestation et le
   consommable s'atténuent SANS disparaître : on segmente, on n'exclut pas. */
.seg-badge {
  display: inline-block; font-size: var(--t-xs); padding: 1px 7px; border-radius: 10px;
  border: 1px solid; vertical-align: middle; white-space: nowrap; cursor: help;
  font-weight: var(--poids-appuye); letter-spacing: 0.01em;
}
.seg-materiel { background: rgba(46, 125, 80, 0.12); color: #1F6B3B; border-color: rgba(46, 125, 80, 0.45); }
.seg-prestation { background: rgba(120, 128, 140, 0.12); color: #5A626E; border-color: rgba(120, 128, 140, 0.42); }
.seg-consommable { background: rgba(120, 128, 140, 0.08); color: #6E7682; border-color: rgba(120, 128, 140, 0.32); }
.seg-indetermine { background: rgba(217, 130, 43, 0.12); color: var(--warning-orange-deep); border-color: rgba(217, 130, 43, 0.45); }
.seg-note {
  margin-left: 10px; font-size: var(--t-xs); color: var(--text-faint);
  font-style: italic; cursor: help;
}

/* S39 [finition-sprint] : kanban RÉEL — colonnes = filtres_statut servis, cartes =
   celles de la veille (parité S36). Aucune affordance de déplacement (les actions
   du contrat vivent sur la carte) : ni draggable, ni curseur de saisie. */
/* le conteneur #kanban porte encore la grille 4 colonnes de la MAQUETTE
   (.kanban-wrap) : elle écraserait le rendu réel dans une seule cellule —
   neutralisée quand il contient .kr-grille (la maquette garde son style ailleurs) */
/* la colonne « 1fr » de .app a min-width:auto (défaut grid) : elle s'élargit au
   contenu, donc 6 colonnes de kanban poussaient le méta et les actions hors écran.
   Borné UNIQUEMENT sur cette vue — aucun effet sur les autres (la table de la
   veille garde son comportement historique). */
body.view-pipeline .app > div { min-width: 0; }
.kanban-wrap:has(.kr-grille) { display: block; min-width: 0; }
.kr-grille {
  display: grid; grid-auto-flow: column; grid-auto-columns: 310px;
  gap: 14px; padding-bottom: 8px; align-items: start;
  /* le kanban défile CHEZ LUI : sans ces bornes, 6 colonnes de 310 px élargissent
     la page entière et poussent le méta / les actions hors de l'écran */
  overflow-x: auto; min-width: 0; max-width: 100%;
}
.kr-colonne {
  background: var(--bg-secondary); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 12px; min-width: 0;
  display: flex; flex-direction: column; gap: 10px;
  /* une colonne peut porter 79 dossiers : elle défile chez elle, l'écran reste
     lisible et les 6 colonnes restent comparables d'un coup d'œil */
  max-height: 68vh; overflow-y: auto;
}
/* RECALAGE S47 (gate HUMAN 03/08) : l'objet s'affiche EN ENTIER dans les listes
   — plus aucun clamp (le nom t-clamp est HÉRITÉ : la classe marque la zone
   objet, le title complet reste). S33/U4 tient toujours : jamais un slice JS. */
.av-objet.t-clamp { overflow: visible; }
.kr-tete { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.kr-tete .n { font-size: var(--t-md); color: var(--text-secondary); font-weight: var(--poids-fort); }
.kr-colonne .av-carte { background: var(--card-solid); }
.kr-vide { padding: 18px 4px; text-align: center; color: var(--text-faint); font-size: var(--t-md); }
.kr-tronque { font-size: var(--t-sm); color: var(--text-muted); line-height: 1.5; }

/* S38 [parametres] : configuration par profil — cartes par périmètre, chips
   actif/inactif (les refus documentés restent VISIBLES avec leur motif). */
.view-only-parametres { display: none; }
body.view-parametres .view-only-parametres { display: block; }
body.view-parametres #kpis, body.view-parametres .view-tabs { display: none; }
.prm-etat {
  padding: 11px 14px; margin: 0 0 16px; font-size: var(--t-md);
  color: var(--text-secondary); border-left: 3px solid var(--warning-orange);
  background: rgba(217, 130, 43, 0.07); border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.prm-etat.flash { animation: flashAvis 1.8s ease-out; }
.prm-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(430px, 1fr));
  gap: 16px; align-items: start;
}
.prm-carte {
  background: var(--card-solid); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 16px 18px;
  display: flex; flex-direction: column; gap: 9px; min-width: 0;
}
.prm-carte h3 { margin: 0; font-size: var(--t-lg); display: flex; gap: 8px; align-items: baseline; }
.prm-lecture {
  font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-faint); font-weight: var(--poids-normal);
}
.prm-sub { color: var(--text-muted); font-size: var(--t-md); }
.prm-famille {
  font-size: var(--t-xs); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--text-secondary); margin-top: 4px;
}
.prm-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.prm-chip {
  display: inline-flex; gap: 6px; align-items: center; max-width: 100%;
  font-size: var(--t-md); padding: 4px 10px; border-radius: 12px;
  border: 1px solid var(--hairline-strong); background: var(--bg-secondary);
}
.prm-chip .v { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prm-chip.inactif { opacity: .62; border-style: dashed; }
.prm-chip.inactif .v { text-decoration: line-through; }
.prm-badge-exclu {
  font-size: var(--t-xs); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--signal-red); font-weight: var(--poids-fort);
}
.prm-toggle {
  font-size: var(--t-xs); padding: 1px 7px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--hairline); background: transparent; color: var(--text-muted);
  visibility: hidden;  /* U9 : l'action apparaît au survol du chip — pas 60 boutons au repos */
}
.prm-chip:hover .prm-toggle, .prm-toggle:focus-visible { visibility: visible; }
.prm-toggle:hover { background: var(--machine-dark); border-color: var(--machine-dark); color: var(--sur-aplat); }
.prm-refus { font-size: var(--t-sm); color: var(--text-muted); }
.prm-refus summary { cursor: pointer; }
.prm-refus-ligne { padding: 3px 0 3px 14px; line-height: 1.5; }
.prm-ajout { display: flex; gap: 6px; margin-top: 6px; align-items: center; }
.prm-ajout .prm-valeur {
  flex: 1; min-width: 0; font-size: var(--t-md); padding: 5px 10px;
  border: 1px solid var(--hairline-strong); border-radius: var(--r-xs);
  font-family: inherit; color: var(--text-primary); background: var(--card-solid);
}
.prm-ajout .prm-type { font-size: var(--t-sm); padding: 4px 8px; }
.prm-ajouter {
  font-size: var(--t-sm); padding: 5px 12px; border-radius: var(--r-xs); cursor: pointer;
  border: 1px solid var(--machine-dark); background: var(--machine-dark); color: var(--sur-aplat);
}
/* U2 : le rectificatif est un SIGNAL (friction n°1 client) — orange = sens, pas déco */
.mem-badge.rectif {
  background: rgba(217, 130, 43, 0.14); color: var(--warning-orange-deep);
  border: 1px solid rgba(217, 130, 43, 0.55); font-weight: var(--poids-fort);
}
.cell-id-sub { font-size: var(--t-xs); color: var(--text-muted); margin-top: 2px; }
/* U4 : ellipse par le CSS (2 lignes max), jamais de coupe en plein mot par slice.
   audit S33 : sans overflow-wrap, un token SANS espace (URL, référence collée —
   objets jusqu'à 1000 caractères en base) pouvait déborder la colonne au lieu
   d'être contenu par le clamp. */
/* RECALAGE S47 (gate HUMAN 03/08) : texte COMPLET en table aussi — le clamp
   saute, l'overflow-wrap reste (tokens sans espace contenus, audit S33) */
.cell-title .t-clamp { overflow-wrap: anywhere; }
/* V2 : le montant hors centrales mène */
.mem-cc-item .mt b { color: var(--text-primary); }
.mem-cc-item .mt .meta { font-size: var(--t-sm); color: var(--text-muted); }

/* retour HUMAN S33 (31/07) : en contexte profil, les consultations au DÉLAI DÉPASSÉ
   se mêlaient aux avis actionnables. Elles restent listées (le dossier vit encore —
   cicatrice S26) mais se DISTINGUENT au premier regard : ligne atténuée + liseré. */
tr.avis-echu > td { background: var(--bg-secondary); }
tr.avis-echu .cell-title .t, tr.avis-echu .cell-buyer .name { color: var(--text-secondary); }
tr.avis-echu > td:first-child { box-shadow: inset 3px 0 0 var(--signal-red); }
.days.echue {
  color: var(--signal-red); font-weight: var(--poids-fort); letter-spacing: 0.04em;
}

/* S34 — confort : espace récupéré, lisibilité, mise en page (styles ADDITIFS) */
/* U6 : le bloc de titre unique respire moins large ; la ligne de méta remplace le
   titre de section supprimé (plus de double titre ni de bande vide) */
.page-head { margin-bottom: var(--sp-5); }
.page-head h1 { font-size: var(--t-4xl); margin-top: 4px; }
.page-head .h-sub { margin-top: 6px; max-width: 760px; font-size: var(--t-lg); }
.section-head.compact {
  display: flex; justify-content: flex-end; align-items: baseline;
  margin: 0 0 10px; padding: 0; border: 0;
}
.section-head.compact .h-meta { text-align: right; }
/* U8 : les selects natifs dévaluaient la table — même grammaire que le design system */
.asg-select, #ctx-select, .mem-cc-tri select {
  appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
  padding-right: 26px; border-radius: var(--r-xs);
  font-family: inherit; color: var(--text-primary);
}
.asg-select:hover, #ctx-select:hover { border-color: var(--hairline-strong); }
/* U9 : une action primaire visible, les autres au survol de la ligne (le reste du
   temps : une ligne = un badge de statut, pas trois éléments empilés) */
.st-actions .st-act.secondaire { display: none; }
tr:hover .st-actions .st-act.secondaire,
.st-cell:focus-within .st-actions .st-act.secondaire { display: inline-flex; }
/* U7 : la portée de chaque KPI mémoire s'affiche (3 cartes acteur + 1 globale) */
.kpi .kpi-portee {
  font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-faint); margin-left: 6px;
}
/* V3 : la méthode se replie — une ligne visible, le détail à la demande */
.mem-cc-methode summary {
  cursor: pointer; font-size: var(--t-sm); color: var(--text-muted);
  list-style: none; display: inline-flex; gap: 6px; align-items: center;
}
.mem-cc-methode summary::-webkit-details-marker { display: none; }
.mem-cc-methode summary:hover { color: var(--text-secondary); }
.mem-cc-methode[open] summary { margin-bottom: 6px; }
.mem-cc-methode .detail { font-size: var(--t-sm); color: var(--text-muted); line-height: 1.6; }
/* V5 : bascule de classement (nombre / montant) */
.mem-cc-tri { display: inline-flex; gap: 6px; align-items: center; font-size: var(--t-sm); }
.mem-cc-tri .mem-year { padding: 3px 10px; font-size: var(--t-sm); }
/* D3 : page détail — les cartes s'équilibrent (le lieu rejoint les conditions) */
.mp-grille { grid-template-columns: repeat(3, 1fr); }
.mp-grille .mp-carte.large { grid-column: span 2; }
@media (max-width: 1180px) {
  .mp-grille { grid-template-columns: 1fr 1fr; }
  .mp-grille .mp-carte.large { grid-column: span 2; }
}
@media (max-width: 980px) {
  .mp-grille { grid-template-columns: 1fr; }
  .mp-grille .mp-carte.large { grid-column: span 1; }
}

/* ── S45 : montants écartés des totaux ─────────────────────────────────────
   Badge rouge doux (distinct de l'orange plafonds et du bleu groupement) ;
   le montant écarté reste LU — barré, jamais masqué (écarter ≠ supprimer). */
.mem-badge.ecarte { background: var(--erreur-rouge-doux); color: var(--erreur-rouge); border-color: var(--erreur-rouge-trait); }
.mt-ecarte { text-decoration: line-through; text-decoration-thickness: 1px; text-decoration-color: rgba(178,58,58,0.55); color: var(--text-muted); }
.mp-montant.mp-ecarte .v { text-decoration: line-through; text-decoration-thickness: 1.5px; text-decoration-color: rgba(178,58,58,0.55); color: var(--text-muted); }
.mp-ecarte-motif { color: var(--erreur-rouge); }
.mp-sig-erreur { color: var(--erreur-rouge); }
#mp-btn-signalement { margin-top: 8px; align-self: flex-start; }

/* ── S42 : annonce du filtre de gamme (jamais un filtre silencieux) ─────────
   Bandeau NEUTRE (pas une erreur, pas un warning) : il informe, en permanence. */
.gamme-annonce {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: var(--t-md); color: var(--text-muted);
  background: rgba(90,120,200,0.06); border: 1px solid rgba(90,120,200,0.18);
  border-radius: var(--r-md); padding: 7px 12px; margin: 0 0 10px;
}
.gamme-annonce b { color: var(--text-strong, #1d1f24); }
.gamme-annonce .mem-year { margin-left: 4px; padding: 3px 10px; font-size: var(--t-sm); }

/* ── S47 : TEXTES COMPLETS D'EMBLÉE (gate HUMAN 03/08 : « les mettre en
   complet déjà sur la liste au lieu d'un bouton ») ──────────────────────────
   Doctrine : partout où un objet de marché/d'avis se liste, il s'affiche EN
   ENTIER — aucun clamp, aucun slice JS, aucun déplieur. Le title complet
   reste (copie facile) ; overflow-wrap contient les tokens sans espace. */
.mem-objet-clamp { overflow-wrap: anywhere; }
.mem-res-item .res-objet { overflow-wrap: anywhere; }
.mem-ech-row .ech-objet { overflow-wrap: anywhere; }

/* ── S51 : modale d'écartement (confirmation, motif pré-rempli) + relecture ── */
.mod-overlay {
  position: fixed; inset: 0; background: rgba(20,22,26,0.42); z-index: 300;
  display: flex; align-items: center; justify-content: center;
}
.mod-boite {
  background: var(--card-solid, #fff); border-radius: var(--r-md, 10px);
  padding: 22px 24px; max-width: 460px; width: calc(100% - 48px);
  box-shadow: 0 18px 50px rgba(0,0,0,0.25);
}
.mod-boite h3 { margin: 0 0 8px; font-size: var(--t-xl); }
.mod-sub { font-size: var(--t-md); color: var(--text-muted); margin: 0 0 14px; }
.mod-label { display: block; font-size: var(--t-sm); color: var(--text-muted); }
.mod-motif {
  display: block; width: 100%; margin-top: 4px; padding: 7px 10px;
  border: 1px solid rgba(0,0,0,0.18); border-radius: 8px; font-size: var(--t-md);
}
/* S98 : signalement — drapeau discret sur la ligne + modale + registre */
.st-signal {
  border: 0; background: none; cursor: pointer; opacity: 0.35;
  font-size: var(--t-md); padding: 0 4px; line-height: 1;
}
.st-signal:hover { opacity: 1; color: var(--signal-red, #b3261e); }
.sig-choix { display: block; margin: 6px 0; font-size: var(--t-md); cursor: pointer; }
.sig-aide { color: var(--text-muted); font-size: var(--t-sm); }
textarea.mod-motif { resize: vertical; font: inherit; }
.sig-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.sig-table th, .sig-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--card-border); vertical-align: top; }
.sig-objet { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#prm-signalements { margin-top: 18px; }

/* S93 : le motif devient un SELECT servi + une précision facultative */
select.mod-motif { background: var(--card-solid, #fff); }
.mod-label + .mod-label { display: block; margin-top: 10px; }
.mod-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
.mod-erreur { color: var(--erreur-rouge); font-size: var(--t-md); margin-top: 8px; }
.st-echu-un { margin-left: 6px; }


/* S61 : l'assiette vit dans l'ÉNONCÉ UNIQUE en tête d'écran — l'annonce de
   gamme ne répète plus les mêmes nombres sous une autre forme (c'est
   exactement le « quatre compteurs pour une population » qu'on supprime).
   Elle reste dans le DOM : elle porte le bouton « voir tout » (acquis S42). */
.gamme-annonce { font-size: 0; padding: 0; border: 0; margin: 0 0 6px; }
.gamme-annonce > b, .gamme-annonce > .meta { display: none; }
.gamme-annonce > button { font-size: var(--t-sm); }


/* ══ S64 [accueil-densite] ═══════════════════════════════════════════════════
   MESURE D'ENTRÉE : en 1280 × 720 (résolution de vidéoprojecteur), l'écran
   d'accueil montrait ZÉRO avis sans défiler — 720 px consommés par un en-tête,
   quatre rangées de contrôle et deux énoncés du même total. Cible unique du
   bloc : CINQ avis lisibles sans défilement. Rien n'est supprimé : les
   contrôles passent en RETRAIT (déplieur), l'ornement passe en DISCRÉTION.
   Le décompte AVANT/APRÈS vit dans docs/specs/mesure_densite_accueil_s64.md. */

/* ── (1) EN-TÊTE — la méthode se replie derrière sa question ─────────────── */
.page-head .h-methode { margin-top: 6px; }
.page-head .h-methode > summary {
  font-size: var(--t-md); color: var(--text-muted); cursor: pointer;
  list-style: none; display: inline-flex; align-items: center; gap: 6px;
}
.page-head .h-methode > summary::-webkit-details-marker { display: none; }
.page-head .h-methode > summary::before { content: "›"; font-size: var(--t-lg); }
.page-head .h-methode[open] > summary::before { content: "⌄"; }
.page-head .h-methode > summary:hover { color: var(--text-secondary); }
.page-head .h-methode .h-sub { margin-top: 8px; }
body.view-published .page-head { margin-bottom: var(--sp-4); }
body.view-published .page-head h1 { font-size: var(--t-3xl); margin-top: 2px; }

/* ── (2) ONGLETS DE VUE — la nav de gauche porte déjà les deux métiers depuis
   S61 ; les onglets répétaient « Marchés publiés / Pipeline » 60 px plus bas.
   MASQUÉS sur l'accueil (jamais retirés : ils portent le compteur .cnt et
   restent le chemin de secours si la nav change). ───────────────────────── */
body.view-published .view-tabs { display: none; }

/* ── (3) ÉNONCÉ UNIQUE — conservé (acquis S61), resserré ─────────────────── */
body.view-published .kpis { margin-bottom: var(--sp-4); }
body.view-published .kpi-enonce { padding: 11px 16px; }
body.view-published .enonce-chiffres .e-principal b { font-size: var(--t-3xl); }
body.view-published .enonce-assiette { margin-top: 5px; }

/* ── (4) RANGÉE UNIQUE DE CONTRÔLES + déplieur ───────────────────────────
   Un filtre ACTIF reste toujours visible, avec son geste d'annulation : caché,
   il redeviendrait le « filtre silencieux » supprimé en S42. */
body.view-published .section-head.compact {
  display: flex; justify-content: flex-start; align-items: center;
  flex-wrap: wrap; gap: 10px; margin: 0 0 8px;
}
body.view-published .section-head.compact .h-meta { text-align: left; }
body.view-published .section-head.compact .dispo-host { margin-left: auto; order: 3; }
#veille-contexte.ctx-compact { margin: 0; }
.ctrl-rangee { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ctrl-actif, .ctrl-plus {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-family: inherit; font-size: var(--t-sm); line-height: 1;
  padding: 5px 10px; border-radius: 12px; border: 1px solid var(--hairline);
  background: var(--bg-secondary); color: var(--text-secondary);
}
.ctrl-actif {
  border-color: var(--hairline-strong, var(--hairline));
  color: var(--text-primary); background: var(--card-solid, #fff);
}
.ctrl-actif .x { font-size: var(--t-md); color: var(--text-muted); }
.ctrl-actif:hover .x { color: var(--danger-red); }
.ctrl-plus.active { color: var(--text-primary); border-color: var(--hairline-strong); }
.ctrl-panneau {
  border: 1px solid var(--hairline); border-radius: var(--r-sm);
  padding: 8px 10px 2px; margin: 0 0 10px; background: var(--bg-secondary);
}
.ctrl-panneau > .filters { margin-bottom: 6px; }
.ctrl-panneau[hidden] { display: none; }

/* ── (5) UNE ACTION DOMINANTE PAR LIGNE ──────────────────────────────────
   Au repos, une ligne porte son objet, son échéance et son statut — un seul
   geste s'offre : la ligne elle-même ouvre la fiche (S41). Les transitions
   (contrat serveur S26) et le lien vers l'avis officiel restent RENDUS et
   accessibles, ils cessent d'être répétés cinquante fois à l'écran. Ils
   reviennent au survol, au focus clavier, et RESTENT VISIBLES au doigt
   (aucun survol sur tactile) ainsi que sur les cartes. */
@media (hover: hover) {
  body.view-published .gr-table .st-actions .st-act,
  body.view-published .gr-table .md-lien { opacity: 0.16; transition: opacity 0.12s; }
  body.view-published .gr-table tr:hover .st-actions .st-act,
  body.view-published .gr-table tr:hover .md-lien,
  body.view-published .gr-table tr:focus-within .st-actions .st-act,
  body.view-published .gr-table tr:focus-within .md-lien { opacity: 1; }
}

/* ── (6) LE STATUT PAR DÉFAUT CESSE DE CRIER ─────────────────────────────
   Le libellé est SERVI par l'API (invariant S34) : il n'est pas renommé, pas
   retiré, pas recalculé — seul son POIDS VISUEL change. Un badge que 90 % des
   lignes portent n'apprend rien ; il ne doit pas peser autant qu'un statut de
   travail, qui lui garde sa couleur pleine. */
.st-badge.st-nouveau {
  background: transparent; color: var(--text-faint);
  border: 1px solid var(--hairline); font-weight: var(--poids-normal);
}
tr:hover .st-badge.st-nouveau, .av-carte:hover .st-badge.st-nouveau {
  color: var(--text-secondary); border-color: var(--hairline-strong);
}
.av-neuf { font-size: var(--t-xs); color: var(--text-muted); border-color: var(--hairline); }

/* ── (7) UN COMPTEUR : UN NOMBRE OU RIEN ─────────────────────────────────
   Le tiret occupait la place d'une valeur inconnue et se lisait comme une
   valeur (zéro ? vide ?). Sans contenu, la pastille disparaît. */
.nav-count:empty, .view-tab .cnt:empty { display: none; }

/* ── (8) DENSITÉ DES GROUPES ET DES LIGNES ───────────────────────────────
   Le groupement par échéance (S61) est la structure de l'écran : il reste
   entier, il occupe moins de hauteur morte. */
body.view-published .pl-groupe { margin-bottom: 12px; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { margin-bottom: 4px; }
body.view-published .pl-groupe > summary { padding: 3px 0; }
body.view-published .main { padding-top: var(--sp-6); }

/* ── (9) LA HAUTEUR MORTE, MESURÉE APRÈS LA PREMIÈRE PASSE ───────────────
   Première passe : 0 → 2 avis visibles. Le reste de la hauteur était pris
   par trois blocs qui ne montraient RIEN :
   · le bandeau roadmap (démo Temps 2) reste dans le flux quand il est
     invisible — 62 px de vide permanent sous l'en-tête. Il en sort tant
     qu'il n'est pas montré ; montré, il reprend sa place exacte.
   · l'énoncé unique héritait du min-height des six cartes de KPI qu'il a
     remplacées (132 px pour trois lignes de texte).
   · la référence de l'avis (« 26-61355 ») se coupait en deux lignes faute
     de largeur : la colonne d'objet, elle, débordait sur trois. Colonnes
     serrées sur leur contenu = objet plus large = lignes plus courtes. */
.roadmap-overlay:not(.show) {
  position: absolute; height: 0; padding: 0; margin: 0; overflow: hidden;
}
body.view-published .kpi-enonce { min-height: 0; }
/* deux ID en amont (#veille-contexte #ctx-select) : la rangée unique ne peut
   se refermer sur une seule ligne qu'en gagnant sur cette spécificité-là */
body.view-published #veille-contexte #ctx-select { max-width: 190px; }
body.view-published .section-head.compact { flex-wrap: nowrap; }

/* LARGEURS DE COLONNES ARBITRÉES (table-layout: fixed). Mesuré avant :
   l'auto-layout donnait 129 px au département (« 71 — Saône-et-Loire ») et
   77 px à la collectivité, dont le nom partait alors sur CINQ lignes — c'est
   elle, pas l'objet, qui fixait la hauteur de ligne (94 px mesurés). Les parts
   sont en pourcentage : la table reste fluide, et son défilement propre
   (acquis S61) demeure. S47 tient : aucun texte n'est coupé ni tronqué. */
.gr-table { table-layout: fixed; }
.gr-table td { overflow-wrap: anywhere; }
.gr-table td.col-id:first-child { width: 6%; white-space: nowrap; }
.gr-table td.col-title  { width: 31%; }
.gr-table td.col-buyer  { width: 15%; }
.gr-table td.col-id     { width: 8%; }
.gr-table td.col-deadline.mono { width: 7%; }
.gr-table td.col-deadline { width: 10%; }
.gr-table td.col-family { width: 11%; }
.gr-table td.col-status { width: 7%; }
.gr-table td.col-id:last-child { width: 5%; }
body.view-published .main { padding-top: 14px; }

/* ── (10) TROISIÈME PASSE, SUR MESURE — 3 avis visibles, cible 5 ─────────
   Ce qui restait : la colonne d'objet à 284 px imposait TROIS lignes de titre
   (97 px de ligne). Les parts sont réarbitrées en faveur de l'objet — c'est
   lui qu'on lit pour décider — et la hauteur morte des blocs de tête (énoncé,
   en-têtes de groupe, interlignes) est ramenée au strict nécessaire.
   S47 tient : l'objet reste ENTIER, il tient simplement sur deux lignes. */
.gr-table td.col-id:first-child { width: 5%; }
.gr-table td.col-title  { width: 38%; }
.gr-table td.col-buyer  { width: 14%; }
.gr-table td.col-id     { width: 7%; }
.gr-table td.col-deadline.mono { width: 6%; }
.gr-table td.col-deadline { width: 9%; }
.gr-table td.col-family { width: 10%; }
.gr-table td.col-status { width: 6%; }
.gr-table td.col-id:last-child { width: 5%; }

body.view-published .kpi-enonce { padding: 9px 16px; }
body.view-published .enonce-chiffres .e-principal b { font-size: var(--t-3xl); }
body.view-published .enonce-assiette { margin-top: 4px; font-size: var(--t-md); }
body.view-published .enonce-source { margin-top: 2px; font-size: var(--t-sm); }
body.view-published .kpis { margin-bottom: 10px; }
body.view-published .page-head { margin-bottom: 10px; }
body.view-published .page-head h1 { margin-top: 0; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { margin-bottom: 2px; font-size: var(--t-sm); }
body.view-published .pl-groupe { margin-bottom: 8px; }

/* ── (11) CORRECTION MESURÉE — la colonne STATUT pilotait la hauteur ─────
   Passe 3 : toutes les lignes exactement à 111 px, une à 500 px. Diagnostic :
   `overflow-wrap: anywhere` posé sur TOUTES les cellules coupait les libellés
   d'action (« À traiter ») au milieu des mots dans une colonne de 55 px — ce
   n'était plus l'objet qui fixait la hauteur, c'était le bouton. La coupure
   n'appartient qu'à l'objet du marché (règle S47 déjà en place sur .t-clamp) ;
   les libellés d'action, statuts, dates et liens ne se coupent JAMAIS. */
.gr-table td { overflow-wrap: normal; }
.gr-table .st-act, .gr-table .st-badge, .gr-table .av-neuf,
.gr-table .md-lien, .gr-table .asg-select, .gr-table .days { white-space: nowrap; }
.gr-table td.col-id:first-child { width: 6%; }
.gr-table td.col-title  { width: 33%; }
.gr-table td.col-buyer  { width: 14%; }
.gr-table td.col-id     { width: 7%; }
.gr-table td.col-deadline.mono { width: 7%; }
.gr-table td.col-deadline { width: 10%; }
.gr-table td.col-family { width: 10%; }
.gr-table td.col-status { width: 8%; }
.gr-table td.col-id:last-child { width: 5%; }

/* ── (12) DERNIÈRE PASSE MESURÉE — 4 avis, il en manque un ───────────────
   Reste à gagner : la hauteur morte des blocs de tête (67 px d'en-tête pour
   trois lignes de texte, 86 px d'énoncé pour trois autres) et 25 px de
   largeur d'objet, pris sur des colonnes qui ne s'en servaient pas
   (collectivité mesurée à deux lignes dans 128 px). */
.gr-table td.col-title { width: 35%; }
.gr-table td.col-buyer { width: 12%; }
.gr-table td.col-family { width: 9%; }
body.view-published .page-head { margin-bottom: 8px; }
body.view-published .page-head .kicker { font-size: var(--t-xs); }
body.view-published .page-head .h-methode { margin-top: 3px; }
body.view-published .kpi-enonce { padding: 7px 16px; }
body.view-published .kpis { margin-bottom: 8px; }
body.view-published .section-head.compact { margin-bottom: 6px; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { margin-bottom: 1px; }

/* ── (13) LE DERNIER AVIS — resserrer la LIGNE SECONDAIRE, sans rien cacher
   Mesuré : 4 avis, le cinquième déborde de 25 px. La ligne secondaire de
   chaque ligne (nature · type · famille · étiquette métier — mentions S40
   qu'on ne supprime pas : c'est par elles que la règle de segmentation se
   conteste) pesait 19 px par avis, soit 95 px sur cinq. Elle reste ENTIÈRE
   et lisible, à la taille du secondaire. Le titre, la collectivité et les
   dates ne bougent pas : ce sont eux qu'on lit pour décider. */
body.view-published .main { padding-top: 10px; }
body.view-published .enonce-chiffres .e-principal b { font-size: var(--t-2xl); }
body.view-published .enonce-source { margin-top: 1px; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { padding: 1px 0; }

/* ── (14) LA MARGE — le cinquième avis finissait à 720 px PILE ───────────
   Un écran qui tient au pixel près ne tient pas : le premier objet un peu
   long le fait tomber. Dernier resserrement, pris uniquement sur du VIDE
   (interlignes des en-têtes de groupe, marges des blocs de tête). */
body.view-published .kpi-enonce { padding: 6px 16px; }
body.view-published .enonce-assiette { font-size: var(--t-sm); margin-top: 3px; }
body.view-published .kpis { margin-bottom: 6px; }
body.view-published .page-head { margin-bottom: 6px; }
body.view-published .section-head.compact { margin-bottom: 5px; }
body.view-published .pl-groupe { margin-bottom: 6px; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { font-size: var(--t-xs); margin-bottom: 0; padding: 0 0 1px; }

/* ── (15) LE VRAI COUPABLE — 19 px perdus PAR GROUPE ─────────────────────
   Mesuré au pixel : `.gr-scroll` faisait 88 px pour une table de 73 px. La
   table des groupes (créée en S61) n'avait aucune règle de bordure et
   héritait donc du défaut `border-collapse: separate` + `border-spacing: 2px`
   — 20 px de large en trop, donc un DÉBORDEMENT HORIZONTAL permanent, donc
   une barre de défilement de 15 px réservée sous CHAQUE groupe, plus 2 px
   entre chaque ligne. Le cadre de défilement de S61 reste (il sert quand la
   fenêtre est vraiment étroite) : il cesse simplement de se déclencher sur
   une table qui tient. */
.gr-table { border-collapse: collapse; }

/* ── (16) LES TROIS DERNIERS PIXELS ──────────────────────────────────────
   Mesuré : la table déborde de 3 px (938 pour 935) — trois pixels suffisent
   à déclencher les 15 px de barre de défilement de chaque groupe. Les
   coupables, cellule par cellule : la date de parution (84 px demandés pour
   66) et le lien « ouvrir » (50 pour 47). On rend la largeur là où elle
   manque et on resserre les marges intérieures des cellules — plutôt que de
   rogner l'objet du marché, qui est ce qu'on lit. */
body.view-published .gr-table td { padding-left: 5px; padding-right: 5px; }
.gr-table td.col-id { width: 6%; }
.gr-table td.col-deadline.mono { width: 8%; }
.gr-table td.col-id:last-child { width: 6%; }

/* ── (17) ARBITRAGE FINAL DES LARGEURS ───────────────────────────────────
   La passe précédente a supprimé le débordement mais affamé le département
   (« 50 — Manche » sur trois lignes) : la hauteur perdue par la barre de
   défilement revenait par la ligne. Chaque colonne reçoit ce que son
   contenu demande, mesuré ; l'objet du marché prend le reste. Somme = 100 %,
   débordement horizontal nul, aucune barre de défilement réservée. */
.gr-table td.col-id:first-child { width: 7%; }
.gr-table td.col-title { width: 33%; }
.gr-table td.col-id { width: 7%; }

/* ── (18) MARGE — cinq avis ne doivent pas tenir à quatre pixels près ────
   Cible atteinte (5 avis, dernier bas à 716). Quatre pixels de marge, c'est
   un objet un peu plus long et la promesse tombe le jour de la démo. Dernier
   prélèvement sur le titre de page et l'énoncé, tous deux au-dessus du pli. */
body.view-published .page-head h1 { font-size: var(--t-3xl); }
body.view-published .kpi-enonce { padding: 5px 16px; }
body.view-published .enonce-chiffres .e-principal b { font-size: var(--t-2xl); }
/* le déplieur de méthode partage la ligne du titre plutôt que d'occuper la
   sienne (il s'ouvre, lui, en pleine largeur) */
body.view-published .page-head > div:first-child {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px;
}
body.view-published .page-head .kicker { width: 100%; }
body.view-published .page-head .h-methode { margin-top: 0; }
body.view-published .page-head .h-methode[open] { width: 100%; }

/* ── (19) DÉFAUT VU À LA CAPTURE, PAS À LA MESURE ────────────────────────
   Le sélecteur d'assignation demandait 141 px (largeur de sa plus longue
   option) dans une colonne de 84 : il débordait PAR-DESSUS le badge de
   statut. La mesure de hauteur ne pouvait pas le voir — la capture, si.
   Il est borné à sa colonne, qui reçoit la largeur nécessaire ; le nom
   complet du profil reste lisible au survol (title servi, jamais tronqué
   dans la donnée). */
.gr-table td.col-title { width: 31%; }
.gr-table td.col-family { width: 11%; }
.gr-table .asg-select { width: 100%; max-width: 100%; font-size: var(--t-sm); }
/* borné à 100 % de sa cellule, le sélecteur n'a plus besoin d'une colonne
   élargie : la largeur retourne à l'objet du marché (mesuré : +2 % d'objet =
   deux lignes de titre en moins sur les avis longs, soit un avis de plus). */
.gr-table td.col-title { width: 33%; }
.gr-table td.col-family { width: 9%; }
/* la date de PARUTION (secondaire : c'est l'échéance qui décide) demandait
   88 px pour 75 — elle touchait la colonne d'à côté. Elle prend la taille du
   secondaire au lieu de prendre la largeur de l'échéance. */
.gr-table td.col-deadline.mono { font-size: var(--t-sm); }


/* ══ S65 [habillage-client] ═══════════════════════════════════════════════════
   L'outil doit ressembler à un outil INTERNE DU CLIENT, pas à la démonstration
   d'un fournisseur. Marque, palette centralisée (§:root), aération — et RIEN
   de la structure ni des fonctions : mêmes colonnes, mêmes groupes, mêmes
   actions, mêmes libellés servis par l'API. */

/* ── MARQUE : le logo du client remplace le losange générique ──────────────
   Servi en local (design_v3/marque/), lettrage en `currentColor` : il prend
   ici la couleur claire de la barre latérale. */
.brand-logo {
  width: 150px; height: auto; display: block;
  color: var(--machine-on);          /* alimente le currentColor du lettrage */
}
.sidebar .brand { align-items: flex-start; flex-direction: column; gap: 10px; }
.sidebar .brand-text .name { font-size: var(--t-md); }
.sidebar .brand-text .sub {
  font-size: var(--t-xs); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--machine-on-dim);
}
/* l'entrée active porte l'accent de marque (fin, ponctuel — c'est du décor) */
.nav-item.active::before,
.nav-item.active { border-left-color: var(--marque-rouge); }

/* ── PASTILLE DE COMPTE À REBOURS ─────────────────────────────────────────
   Trois régimes, distingués par le REMPLISSAGE avant la couleur :
     · aplat PLEIN  — se rend aujourd'hui ou demain
     · FILET        — les jours suivants
     · texte simple — au-delà
   C'est ce qui permet au rouge d'exister à la fois dans le décor et sur
   l'urgence sans s'annuler : un aplat ne se confond pas avec un filet de 1 px,
   même sur un vidéoprojecteur qui délave les teintes. Le libellé (« J−3 »,
   « AUJOURD'HUI ») porte l'information ; la couleur ne fait que renforcer. */
.cell-deadline .days.j-imminent,
.cell-deadline .days.j-proche {
  display: inline-block; padding: 1px 7px; border-radius: 9px;
  letter-spacing: 0.06em; line-height: 1.5;
}
.cell-deadline .days.j-imminent {
  background: var(--marque-rouge); color: var(--sur-aplat);
  font-weight: var(--poids-appuye); border: 1px solid var(--marque-rouge);
}
.cell-deadline .days.j-proche {
  background: transparent; color: var(--text-primary); font-weight: var(--poids-appuye);
  border: 1px solid var(--marque-rouge-trait);
}
.cell-deadline .days.j-loin { color: var(--text-muted); font-weight: var(--poids-normal); }
/* la couleur héritée des règles d'urgence ne doit pas repeindre la pastille
   pleine (elle est déjà un aplat) — la forme prime, sans !important */
.cell-deadline.critical .days.j-imminent,
.cell-deadline.urgent .days.j-imminent { color: var(--sur-aplat); }
.cell-deadline.urgent .days.j-proche { color: var(--text-primary); }
/* DÉLAI DÉPASSÉ : franc lui aussi, mais d'une autre nature que l'imminent —
   il ne prend pas le rouge de marque, sinon les deux se confondent. */
.days.echue {
  display: inline-block; padding: 1px 7px; border-radius: 9px;
  background: var(--erreur-rouge); color: var(--sur-aplat);
  font-weight: var(--poids-appuye); letter-spacing: 0.04em; line-height: 1.5;
}

/* ── MARQUEURS « PRODUIT EN COURS » ───────────────────────────────────────
   Un bouton désactivé annonce ce qui ne marche pas ; une feuille de route
   affichée dit « chantier ». MASQUÉS, jamais supprimés — patron S37, remise
   en service = retirer une classe. Le chemin de démonstration de la roadmap
   est conservé (voir runbook §2 : ?roadmap dans l'adresse). */
.h-actions .nav-hors-v1 { display: none; }
.sidebar-footer .build {
  color: var(--machine-on-dim); font-size: var(--t-xs); letter-spacing: 0.08em;
}

/* ── ÉNONCÉ DU TOTAL ET SON ASSIETTE — restylés, pas raccourcis ───────────
   Le contenu est INCHANGÉ AU MOT PRÈS (« N à rendre · N délais dépassés · N
   sans date limite publiée », la phrase de périmètre qui dit ce qui est exclu
   et combien, la source et l'ingestion) : c'est la couche d'honnêteté qui
   distingue l'outil d'un agrégateur, elle ne se retouche pas.
   Ce qui change est le CONTENANT : la carte blanche à ombre portée faisait
   flotter l'énoncé au-dessus de la page comme un encart de démonstration ; il
   se pose maintenant sur le fond, tenu par un simple filet. Effet de bord
   recherché : la hauteur ainsi rendue repaye la densité S64 (voir la mesure
   versée dans docs/specs/mesure_habillage_s65.md). */
body.view-published .kpis {
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
  border-bottom: 1px solid var(--hairline);
  overflow: visible; margin-bottom: 8px;
}
body.view-published .kpi-enonce { background: transparent; padding: 0 0 8px; }
body.view-published .kpi-enonce:hover { background: transparent; }
body.view-published .enonce-chiffres .e-principal b { color: var(--text-primary); }

/* ── LA HAUTEUR RENDUE PAR L'HABILLAGE ────────────────────────────────────
   Mesuré : la carte supprimée n'a rendu que 3 px, et il en manquait 2 pour
   retrouver les cinq avis de S64 (la donnée du jour porte des objets plus
   longs qu'au 07/08 — voir la mesure versée). Le reste est pris sur le
   CONTENANT et sur lui seul : chiffre principal, interlignes, marges. Aucun
   mot, aucune mention, aucune taille de texte porteur d'information ne bouge —
   l'aération ne se paie pas en contenu (anti-pattern du brief). */
body.view-published .enonce-chiffres .e-principal b { font-size: var(--t-2xl); }
body.view-published .enonce-chiffres { gap: 16px; }
body.view-published .kpis { margin-bottom: 6px; }
body.view-published .main { padding-top: 8px; }
body.view-published .section-head.compact { margin-bottom: 4px; }
body.view-published .ctrl-actif, body.view-published .ctrl-plus { padding: 4px 10px; }

/* le logo occupe la juste place : lisible, sans écraser la navigation */
.brand-logo { width: 132px; }
body.view-published .page-head h1 { font-size: var(--t-2xl); }
body.view-published .main { padding-top: 6px; }

/* ── DÉBORDEMENTS DE LIGNE — défaut PRÉEXISTANT, corrigé ici ──────────────
   Vu à la capture (et invisible à la mesure de hauteur, comme en S64) : sur un
   avis rectifié, le badge « RECTIFIÉ LE … · DATE LIMITE DÉPLACÉE » sortait de
   sa colonne et se superposait au nom de l'acheteur, et « remplace 26-… »
   chevauchait la ligne suivante. Deux causes, toutes deux héritées de la mise
   en largeurs fixes de S64 :
     · .t-sub est un conteneur flex SANS repli : ses enfants ne pouvaient pas
       passer à la ligne, ils débordaient ;
     · la colonne de référence est en `nowrap` (pour que « 26-61355 » ne se
       coupe pas en deux) — mais le `nowrap` s'appliquait aussi à la sous-ligne
       « remplace … », qui ne pouvait donc plus se replier.
   Deux mentions de l'inventaire redeviennent lisibles. Le brief S65 exige que
   TOUT texte porteur d'information reste lisible : un chevauchement est une
   perte d'information, pas un détail d'esthétique. */
.cell-title .t-sub { flex-wrap: wrap; row-gap: 2px; }
.gr-table td.col-id:first-child .cell-id-sub { white-space: normal; }
.gr-table .mem-badge { white-space: normal; }


/* ══ S66 [accueil-finition] ═══════════════════════════════════════════════════
   Dernière passe sur le CHROME de l'accueil. Le rendez-vous se fait sur un
   portable (1920 × 1080), plus en projection : la contrainte 1280 × 720 devient
   un plancher de sécurité et l'arbitrage se fait en faveur du portable.
   L'agrandissement ci-dessous coûte de la hauteur — c'est assumé et mesuré. */

/* ── CE QU'ON LIT EN PREMIER EN OUVRANT L'OUTIL ───────────────────────────
   La date du jour et le nombre à rendre. Ils étaient à la taille de tout le
   reste. L'assiette, les qualificatifs et la source ne bougent pas — ni leur
   taille ni leur texte : ils portent l'honnêteté du chiffre, pas son emphase. */
body.view-published .page-head .kicker {
  font-size: var(--t-md); letter-spacing: 0.1em; color: var(--text-secondary);
}
body.view-published .enonce-chiffres .e-principal { font-size: var(--t-xl); }
body.view-published .enonce-chiffres .e-principal b {
  font-size: var(--t-4xl); line-height: 1.05; margin-right: 8px;
}
body.view-published .enonce-chiffres .e-secondaire b { font-size: var(--t-2xl); }

/* ── PASTILLES : ÉPOUSER LEUR CONTENU ─────────────────────────────────────
   Étirées sur toute la largeur de leur colonne, les versions en contour
   ressemblaient à des champs de saisie vides : cinq rectangles arrondis
   alignés en face de cinq listes déroulantes, et l'œil lisait deux colonnes
   de formulaire. Le mécanisme plein / contour / texte de S65 ne change pas —
   seule la boîte se referme sur son texte. */
.cell-deadline .days.j-imminent,
.cell-deadline .days.j-proche {
  display: inline-flex; align-items: center; align-self: flex-start;
  width: auto; max-width: max-content;
}
.cell-deadline { align-items: flex-start; }

/* ── UNE ACTION FRANCHE, LES AUTRES AU SURVOL ─────────────────────────────
   S64 avait mis TOUTES les actions de ligne au survol : une action invisible
   est une action qu'on ne peut pas démontrer. La QUALIFICATION (première
   transition servie par le contrat) redevient franche au repos ; le lien vers
   l'avis officiel et les transitions suivantes restent au survol. On ne les
   rend pas toutes franches : ce serait revenir aux 312 cibles cliquables du
   diagnostic d'origine. */
@media (hover: hover) {
  body.view-published .gr-table .st-actions .st-act:not(.secondaire) { opacity: 1; }
  body.view-published .gr-table .st-actions .st-act.secondaire,
  body.view-published .gr-table .md-lien { opacity: 0.16; transition: opacity 0.12s; }
  body.view-published .gr-table tr:hover .st-actions .st-act.secondaire,
  body.view-published .gr-table tr:hover .md-lien,
  body.view-published .gr-table tr:focus-within .st-actions .st-act.secondaire,
  body.view-published .gr-table tr:focus-within .md-lien { opacity: 1; }
}
/* l'action franche se lit comme une action, pas comme une étiquette */
body.view-published .gr-table .st-actions .st-act:not(.secondaire) {
  border-color: var(--hairline-strong); color: var(--text-primary);
  background: var(--card-solid);
}


/* ══ CONFORT DE LECTURE ══════════════════════════════════════════════════════
   ARBITRAGE 2026-08-11 (Alexandre) : on cesse de piloter l'accueil par le
   nombre d'avis visibles sans défilement. Le critère devient le RENDU, la
   LISIBILITÉ et le CONFORT. Tous les contenus restent (inventaire S61 intact) :
   c'est l'AIR qu'on rend, pas de l'information qu'on retire.

   Dette nettoyée au passage : la course à la densité (S64 -> S66) avait empilé
   CINQ déclarations successives de marge intérieure — 7, puis 6, 5, 4, 3 px —
   chacune écrasant la précédente, plus un interligne et deux tailles de texte
   rabotés. Tout cela est retiré ; une seule règle décide désormais.

   ⚠ La marge intérieure HORIZONTALE reste à 5 px : elle n'est pas un choix de
   confort mais une contrainte de largeur (les colonnes somment à 100 % ; au
   delà, la table déborde et réserve une barre de défilement par groupe —
   défaut mesuré en S64). */

/* de l'air dans la ligne, et un filet pour que l'œil SÉPARE les avis :
   sans lui, les lignes se touchent et le regard ne trouve pas où l'une
   finit et l'autre commence. */
body.view-published .gr-table td {
  padding-top: 11px; padding-bottom: 11px;
}
body.view-published .gr-table tbody tr + tr > td {
  border-top: 1px solid var(--hairline);
}
body.view-published .gr-table tbody tr:hover > td { background: var(--bg-secondary); }

/* le texte respire : interligne du titre et taille du secondaire rendus */
body.view-published .cell-title { gap: 5px; }
body.view-published .cell-title .t { line-height: 1.45; }
body.view-published .gr-table .t-sub { font-size: var(--t-sm); }
body.view-published .gr-table .seg-badge { font-size: var(--t-xs); padding: 1px 7px; }

/* les groupes se distinguent de leur contenu */
body.view-published .pl-groupe { margin-bottom: 18px; }
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary {
  font-size: var(--t-sm); margin-bottom: 6px; padding: 2px 0;
}
body.view-published .main { padding-top: 14px; }
body.view-published .page-head { margin-bottom: 12px; }
body.view-published .kpis { margin-bottom: 12px; }
body.view-published .section-head.compact { margin-bottom: 10px; }


/* ══ S68 [lisibilite-segmentation] ═══════════════════════════════════════════
   La famille métier devient balayable D'UN COUP D'ŒIL VERTICAL — par sa
   POSITION, pas par une couleur de plus. Aucune teinte ajoutée : l'écran porte
   déjà le rouge (marque et urgence), l'orange (rectifiés), le vert et l'ambre
   (familles) ; saturer les familles diluerait le seul signal qui ne doit
   jamais se perdre, l'échéance.
   ⚠ Le cap de S67 tient : rien ici ne resserre les lignes. */

/* ── ABSCISSE FIXE ────────────────────────────────────────────────────────
   La pastille ouvre la ligne secondaire dans un bloc de largeur CONSTANTE :
   quelle que soit la longueur du libellé (« Matériel » … « Prestation de
   service »), la pastille commence au même x ET le texte qui suit s'aligne.
   Un avis sans famille servie garde la place vide — sinon sa ligne décalerait
   toutes les autres à l'œil. */
/* La pastille est alignée parce qu'elle OUVRE la ligne — pas parce qu'on lui
   réserve une largeur. La réservation servait à aligner AUSSI le texte qui
   suivait (« Avis de marché · FOURNITURES ») ; ce texte est parti, et la
   largeur réservée ne laissait plus qu'un grand vide entre la pastille et le
   badge « rectifié le… ». Elle est retirée : la famille garde son abscisse
   unique, le badge suit à distance normale. */
.cell-title .t-sub .ts-fam {
  flex: 0 0 auto; display: inline-flex; align-items: center;
}
.cell-title .t-sub .ts-fam .seg-badge { margin-left: 0; }

/* ── « À QUALIFIER » N'EST PAS UN QUATRIÈME CLASSEMENT ────────────────────
   Matériel, prestation et consommable CLASSENT ; « à qualifier » dit « on ne
   sait pas encore ». Lui donner la même forme laisserait croire que c'est une
   réponse. Elle se distingue donc DE NATURE — contour tireté au lieu d'un
   aplat, précédé d'un point d'interrogation — et pas seulement de teinte.
   Le libellé reste celui que sert l'API (invariant S34) : rien n'est renommé,
   c'est la FORME qui parle. */
.seg-badge.seg-indetermine {
  background: transparent;
  border-style: dashed;
  font-style: italic;
}
.seg-badge.seg-indetermine::before {
  content: "?"; font-style: normal; font-weight: var(--poids-appuye); margin-right: 5px;
  opacity: 0.75;
}
/* Le badge « RECTIFIÉ LE … » se collait à la pastille de famille : quand la
   fenêtre se resserre il passe à la ligne, et l'espacement vertical hérité de
   S65 (2 px) le soudait à elle. Les deux axes respirent — horizontalement, la
   place réservée à la famille garde une marge même quand son libellé la
   remplit ; verticalement, un badge qui se replie reste un élément distinct. */
.cell-title .t-sub { row-gap: 7px; }
.cell-title .t-sub .mem-badge { margin-left: 0; }
/* les en-têtes de groupe se calaient sur le bord du cadre blanc : ils s'en
   écartent, alignés sur la marge intérieure du contenu qu'ils annoncent */
body.view-published .pl-groupe > h3,
body.view-published .pl-groupe > summary { padding-left: 14px; }


/* ══ S69 [bandeau-echeances] ═════════════════════════════════════════════════
   Structure reprise d'une maquette de référence : grille de trois mesures
   séparées par des filets, la principale sur fond teinté, l'assiette en pied.
   ⚠ La maquette codait ses couleurs en dur (un bleu, une brique, un fond
   froid) — aucune n'appartient à la marque du client, et S65 venait justement
   de centraliser la palette. TOUT passe ici par les jetons existants : pas un
   littéral nouveau. Son `light-dark()` est sans objet (l'outil n'a pas de mode
   sombre) et ses graisses uniformes à 600 sont ignorées au profit de l'échelle
   en place. */
body.view-published .bandeau {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--card-solid);
  padding: 0;
}
.bd-mesures {
  display: grid; grid-template-columns: repeat(3, 1fr);
  border-bottom: 1px solid var(--hairline);
}
/* les filets SÉPARENT les mesures : ils ne bordent pas la grille */
.bd-mesures > .bd-mesure + .bd-mesure { border-left: 1px solid var(--hairline); }
.bd-mesure {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 12px 18px; background: transparent; border: 0; cursor: pointer;
  font-family: inherit; text-align: left; color: inherit;
  border-radius: 0;
}
.bd-mesure:first-child { border-top-left-radius: var(--r-md); }
.bd-mesure:last-child { border-top-right-radius: var(--r-md); }
/* la mesure principale porte le fond teinté (marque, très dilué) */
.bd-mesure.bd-principale { background: var(--marque-rouge-doux); }
.bd-mesure:hover { background: var(--bg-secondary); }
.bd-mesure.bd-principale:hover { background: var(--marque-rouge-doux); filter: brightness(0.98); }
.bd-mesure.active { box-shadow: inset 0 -2px 0 var(--marque-rouge); }
.bd-valeur {
  font-size: var(--t-4xl); line-height: 1.05; color: var(--text-primary);
  /* chasse TABULAIRE : d'une mesure à l'autre et d'un jour à l'autre, les
     chiffres tombent sur la même grille au lieu de danser */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.bd-libelle { font-size: var(--t-md); color: var(--text-secondary); }
.bd-mesure.bd-principale .bd-valeur { color: var(--text-primary); }

/* ── PIED : l'assiette, la provenance, et la RÉSERVE ──────────────────────
   La mention « règle provisoire — à valider avec le client » vivait dans le
   dépliant depuis S64 : invisible au repos, pendant que la pastille de famille
   qu'elle qualifie s'affiche sur chaque ligne et a été rendue plus visible en
   S68. Sa place est ici, contre l'assiette que cette règle compose. Au-delà du
   principe : une réserve visible transforme une classification contestée en
   question posée, pas en erreur constatée. */
.bd-pied {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px;
  padding: 9px 18px 11px;
}
.bd-pied .enonce-assiette,
.bd-pied .enonce-source { margin: 0; }
.bd-pied .seg-note { margin-left: auto; }
body.view-published .bd-pied .enonce-assiette { font-size: var(--t-md); }


/* ══ S70 [lecture-tableau] ═══════════════════════════════════════════════════
   Deux gestes de lecture, aucune restructuration. */

/* ── (1) LE BANDEAU ÉTAIT COLORÉ À L'ENVERS ───────────────────────────────
   Le plus grand aplat teinté de l'écran portait « à rendre » — c'est-à-dire
   l'ÉTAT NORMAL — pendant que « délais dépassés », la seule mesure qui
   pourrait inquiéter, restait sur fond blanc. La couleur signalait ce qui va
   bien.
   ⚠ Elle n'est pas déplacée sur les dépassés pour autant : on vient de refuser
   le libellé « à traiter en priorité » (S69) parce qu'il envoie travailler sur
   des dossiers auxquels on ne peut PLUS répondre — une couleur d'alarme dirait
   la même chose sans les mots. Le bandeau devient chromatiquement CALME : une
   teinte neutre suffit à détacher la mesure principale, et la taille du
   chiffre fait la hiérarchie. L'urgence vit sur les lignes, dans les pastilles
   J−N, là où elle est actionnable. */
.bd-mesure.bd-principale { background: var(--bg-secondary); }
.bd-mesure.bd-principale:hover { background: var(--bg-tertiary); filter: none; }
.bd-mesure.bd-principale .bd-valeur { font-size: var(--t-4xl); }

/* les trois cellules s'étiraient sur toute la largeur (~1750 px) : un chiffre
   seul au milieu d'un grand rectangle. Le bandeau est borné et cadré à
   gauche — la mesure reste lisible, elle cesse de flotter. */
body.view-published .kpis { max-width: 1040px; }

/* ── (2) NOMMER LES COLONNES ──────────────────────────────────────────────
   L'en-tête est répété dans chaque groupe (voir le commentaire du rendu :
   `position: sticky` est neutralisé par le conteneur de défilement horizontal
   hérité de S61). Il se lit comme une légende, pas comme une barre de plus :
   petites capitales, ton secondaire, un filet pour le séparer des lignes. */
.gr-entete th {
  text-align: left; font-weight: var(--poids-appuye); font-size: var(--t-xs);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted); white-space: nowrap;
  padding: 2px 5px 7px; border-bottom: 1px solid var(--hairline-strong);
}
.gr-entete th.col-title { white-space: normal; }

/* ── (3) L'ÉTAT PAR DÉFAUT S'ÉCRIT, IL NE SE BADGE PLUS ───────────────────
   Mesuré : 198 avis sur 208 portaient « Nouveau » — une pastille sur 95 % des
   lignes n'apprend rien. Le libellé SERVI reste écrit (il est à l'inventaire
   S61 : ne rien rendre était donc exclu), en texte simple. Les états de
   travail, eux, restent des badges : ils sont rares, donc ils se voient. */
.st-texte {
  font-size: var(--t-sm); color: var(--text-muted); cursor: pointer;
  letter-spacing: 0.02em;
}
.st-texte:hover { color: var(--text-secondary); }

/* ── DEUX RÉGRESSIONS INTRODUITES PAR L'EN-TÊTE, VUES À LA CAPTURE ────────
   1. En disposition `table-layout: fixed`, ce sont les cellules de la PREMIÈRE
      ligne qui fixent les colonnes — depuis S70 c'est le <thead>, or toutes
      les largeurs arbitrées en S64→S67 ne visaient que les <td>. L'objet du
      marché s'est retrouvé étranglé (titres sur cinq lignes) et une colonne
      vide s'étalait à droite. Les mêmes parts sont appliquées aux <th> :
      mêmes valeurs, même somme de 100 %, aucun arbitrage nouveau.
   2. Le statut par défaut, passé en texte, héritait du fond plein de sa classe
      d'état (`.st-nouveau`) : il s'affichait surligné. Le texte n'est pas un
      badge — il n'en prend ni le fond, ni la bordure, ni les marges. */
.gr-table th.col-id:first-child { width: 7%; }
.gr-table th.col-title  { width: 33%; }
.gr-table th.col-buyer  { width: 12%; }
.gr-table th.col-id     { width: 7%; }
.gr-table th.col-deadline.mono { width: 8%; }
.gr-table th.col-deadline { width: 10%; }
.gr-table th.col-family { width: 9%; }
.gr-table th.col-status { width: 8%; }
.gr-table th.col-id:last-child { width: 6%; }

.st-texte {
  background: transparent; border: 0; padding: 0; border-radius: 0;
  text-transform: none; font-weight: var(--poids-normal);
}

/* ── BANDEAU : TROIS CASES, PLEINE LARGEUR (retour Alexandre) ─────────────
   Le bandeau reprend toute la largeur de la page, et les trois mesures
   deviennent trois CASES distinctes plutôt qu'un seul rectangle blanc barré
   de filets. La principale prend l'anthracite de la barre latérale : c'est le
   NEUTRE FONCÉ de l'outil, pas une couleur d'alarme — elle se détache
   franchement sans rien signaler d'inquiétant, ce qui était tout l'objet du
   §2.1 du brief. Texte clair sur ce fond (jetons `--machine-on`). */
body.view-published .kpis { max-width: none; }
.bd-mesures {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 10px; border-bottom: 0; padding: 0;
}
.bd-mesures > .bd-mesure + .bd-mesure { border-left: 0; }
.bd-mesure {
  border: 1px solid var(--hairline); border-radius: var(--r-sm);
  background: var(--card-solid); padding: 14px 18px;
}
.bd-mesure:first-child, .bd-mesure:last-child { border-radius: var(--r-sm); }
.bd-mesure.bd-principale {
  background: var(--machine-dark); border-color: var(--machine-dark);
}
.bd-mesure.bd-principale .bd-valeur { color: var(--machine-on); }
.bd-mesure.bd-principale .bd-libelle { color: var(--machine-on-mid); }
.bd-mesure.bd-principale:hover { background: var(--machine-darker); }
.bd-mesure:hover { background: var(--bg-secondary); }
.bd-mesure.active { box-shadow: inset 0 -3px 0 var(--marque-rouge); }
/* le bandeau n'est plus une carte : ce sont les cases qui portent le cadre */
body.view-published .bandeau { border: 0; background: transparent; }
.bd-pied { padding: 10px 2px 0; }
/* ── EN-TÊTE COLLANT : TENTÉ, MESURÉ, ABANDONNÉ ───────────────────────────
   Le brief demandait de mesurer les deux voies (collant / répété) et de
   proposer. Trois causes possibles ont été testées, chacune mesurée :
     1. le cadre de défilement horizontal de S61 (.gr-scroll) — levé, sans effet ;
     2. `.table-wrap`, cadre de la table historique masquée, dont l'overflow
        neutralise tout collant du contenu — levé, sans effet non plus ;
     3. `border-collapse: collapse`, qui empêche Chromium de faire coller un
        <th> — remplacé par son équivalent sans espacement, sans effet.
   Après chaque essai : 0 en-tête à l'écran dans un groupe de 32 avis, à
   2 400 px de défilement. Les trois règles d'essai ont été RETIRÉES plutôt
   que laissées « au cas où » : on ne garde d'une tentative que ce que la
   mesure valide (même discipline qu'en S67).
   VOIE RETENUE : l'en-tête est RÉPÉTÉ dans chaque groupe. Mesuré : 5 groupes,
   5 en-têtes, 25 px chacun ; après 700 px de défilement, deux en-têtes sont à
   l'écran. Limite honnête : dans un groupe long (« au-delà de 15 jours », 32
   avis), l'en-tête sort après une poignée de lignes — c'est dit au document
   de mesure, et le remède éventuel appartient à un autre bloc. */

/* ── LE PIED SE RATTACHE AUX COMPTEURS (retour Alexandre) ─────────────────
   Avec les trois cases séparées, la ligne de provenance flottait sous elles.
   Elle devient une quatrième case, en pleine largeur, du même trait et du même
   fond : l'ensemble se lit comme UN bloc, comme au début.
   L'assiette (composition de la gamme et ventilation des écartés) quitte
   l'affichage permanent sur demande — elle reste au SURVOL de cette ligne :
   l'information n'est pas perdue, elle cesse d'occuper l'écran. C'est le même
   geste qu'en S68 pour les codes de format (descendus en fiche). */
.bd-pied {
  border: 1px solid var(--hairline); border-radius: var(--r-sm);
  background: var(--card-solid);
  padding: 9px 18px; margin-top: 10px;
}
.bd-pied .enonce-source { cursor: help; }

/* ── LES QUATRE CASES N'EN FONT QU'UNE (retour Alexandre) ─────────────────
   Séparées par un intervalle, elles se lisaient comme quatre objets posés
   côte à côte. Fusionnées — plus d'intervalle, des filets partagés, un rayon
   seulement aux angles extérieurs — elles forment UN bloc : les trois mesures
   en haut, la provenance en pied, d'un seul tenant. */
.bd-mesures { gap: 0; }
.bd-mesure {
  border: 1px solid var(--hairline); border-right: 0; border-bottom: 0;
  border-radius: 0;
}
.bd-mesures > .bd-mesure:last-child { border-right: 1px solid var(--hairline); }
.bd-mesures > .bd-mesure:first-child { border-top-left-radius: var(--r-sm); }
.bd-mesures > .bd-mesure:last-child { border-top-right-radius: var(--r-sm); }
.bd-pied {
  margin-top: 0; border-radius: 0 0 var(--r-sm) var(--r-sm);
  border: 1px solid var(--hairline);
}
/* la case principale garde son anthracite, sans laisser voir le filet dessous */
.bd-mesure.bd-principale { border-color: var(--machine-dark); }
/* bouton « Actualiser » du pied de bandeau. Il reprend la GRAMMAIRE des
   boutons de l'outil (.btn.primary : anthracite plein, texte clair, même
   hauteur, même rayon, même icône au trait) plutôt qu'un style à lui —
   l'écran n'a pas besoin d'un dialecte de plus. Il est posé à droite, en
   vis-à-vis de la provenance, et il FAIT quelque chose : il redemande les
   données servies. À terme, c'est ici que se branchera la relance de
   recherche d'avis. */
.bd-actualiser { margin-left: auto; height: 30px; font-size: var(--t-md); }
.bd-actualiser:disabled { opacity: 0.55; cursor: default; }


/* ══ S71 [filtres-menus] ═════════════════════════════════════════════════════
   Le dépliant « plus de filtres (N actifs) » cachait les AXES en même temps
   que les options : on ne savait pas qu'on pouvait filtrer par statut tant
   qu'on n'avait pas ouvert. Un menu fermé et étiqueté dit qu'un axe existe et
   ce qu'il permet. Gain de découvrabilité, pas de décoration.
   Les menus sont des <select> natifs : fermés, ils portent la valeur choisie
   (règle S64 : un filtre actif se voit sans rien ouvrir) ; ouverts, ils
   portent les décomptes SERVIS. Aucun composant inventé, rien à réapprendre. */
.ctrl-panneau {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 18px;
  border: 0; background: transparent; padding: 0; margin: 0 0 10px;
}
.ctrl-panneau > .filters { margin: 0; padding: 0; border: 0; background: none; }
.f-menu { display: inline-flex; flex-direction: column; gap: 3px; }
.f-label {
  font-size: var(--t-xs); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted);
}
.f-select {
  appearance: none; -webkit-appearance: none;
  font-family: inherit; font-size: var(--t-md); color: var(--text-primary);
  padding: 6px 26px 6px 10px; min-width: 168px;
  border: 1px solid var(--hairline-strong); border-radius: var(--r-xs);
  background-color: var(--card-solid);
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.f-select:hover { border-color: var(--text-muted); }
.ctrl-panneau .seg-note { align-self: center; }

/* ── L'ACTION DE MASSE SORT DES FILTRES (point de sûreté du bloc) ──────────
   « écarter les N dépassées… » modifie des dizaines d'enregistrements : elle
   ne peut pas voisiner des menus dont on attend qu'ils changent seulement la
   vue. Elle a sa zone, son intitulé qui annonce une action, et garde sa
   modale, son motif obligatoire et son décompte (acquis S51). */
.zone-action {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 12px; padding: 8px 12px;
  border: 1px dashed var(--hairline-strong); border-radius: var(--r-sm);
  background: var(--bg-secondary);
}
.zone-action:empty { display: none; }
.za-label {
  font-size: var(--t-xs); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted);
}
.za-bouton { height: 30px; font-size: var(--t-md); }

/* ── LES MENUS PRENNENT LA FORME DEMANDÉE (retour Alexandre, maquette) ────
   Boutons pleins, arrondis, libellé appuyé, chevron marqué — la FORME de la
   référence. Le vocabulaire, lui, reste le nôtre : les axes sont ceux de
   l'outil (l'axe « Point concerné » de la maquette n'a aucune donnée derrière
   et reste refusé).
   Le <select> est conservé : c'est lui qui garantit qu'un menu FERMÉ porte la
   valeur choisie (règle S64) — la maquette, elle, n'affiche que l'intitulé de
   l'axe et perdrait le filtre actif. */
.f-select {
  background-color: var(--bg-tertiary);
  border-color: transparent;
  border-radius: 999px;
  font-weight: var(--poids-appuye);
  padding: 8px 30px 8px 16px;
  color: var(--text-primary);
  background-image: linear-gradient(45deg, transparent 50%, var(--text-primary) 50%),
                    linear-gradient(135deg, var(--text-primary) 50%, transparent 50%);
  background-position: calc(100% - 16px) 54%, calc(100% - 11px) 54%;
  background-size: 6px 6px, 6px 6px;
}
.f-select:hover { background-color: var(--hairline); border-color: transparent; }
.f-select:focus-visible { outline: 2px solid var(--marque-rouge); outline-offset: 1px; }
.f-label { color: var(--text-secondary); }

/* « voir tout » faisait DOUBLON avec l'option « Toutes » du menu Périmètre
   métier, et gardait le style d'avant les menus (pastille claire posée au
   milieu des boutons). L'annonce qui le portait était déjà réduite depuis S61 :
   elle sort de l'écran, son mécanisme reste (structurel S45) — le menu fait
   exactement la même chose, mieux nommé. */
body.view-published .gamme-annonce { display: none; }

/* ── LE POINT DE VUE PREND LA MÊME FORME QUE LES AUTRES MENUS ─────────────
   Il restait au style d'avant (select clair bordé) au milieu de menus
   arrondis : un dialecte de plus dans la rangée. Même grammaire pour tous.
   ⚠ Deux ID en amont (#veille-contexte #ctx-select) : la règle doit gagner
   sur cette spécificité-là, comme en S65. */
body.view-published #veille-contexte #ctx-select {
  background-color: var(--bg-tertiary); border-color: transparent;
  border-radius: 999px; font-weight: var(--poids-appuye); max-width: 260px;
  padding: 8px 30px 8px 16px; color: var(--text-primary);
  background-image: linear-gradient(45deg, transparent 50%, var(--text-primary) 50%),
                    linear-gradient(135deg, var(--text-primary) 50%, transparent 50%);
  background-position: calc(100% - 16px) 54%, calc(100% - 11px) 54%;
  background-size: 6px 6px, 6px 6px;
}
body.view-published #veille-contexte #ctx-select:hover { background-color: var(--hairline); }
/* le bloc de badges « Dispatch » est vide depuis que la charge vit dans le
   menu : il ne doit pas laisser un trou dans la rangée */
#veille-dispatch:empty { display: none; }

/* ── EN-TÊTES TRIABLES ────────────────────────────────────────────────────
   Un clic trie, un second inverse, un troisième revient à l'ordre servi. Le
   tri s'applique À L'INTÉRIEUR de chaque groupe d'échéance : la structure de
   l'écran (S61) ne se renégocie pas parce qu'on trie. */
.gr-entete th.th-triable { cursor: pointer; user-select: none; }
.gr-entete th.th-triable:hover { color: var(--text-primary); }
.gr-entete th.th-actif { color: var(--text-primary); }
.gr-entete .th-sens { margin-left: 4px; font-size: var(--t-sm); }

/* ── LES MENUS PRENNENT L'ANGLE DU BANDEAU (retour Alexandre) ─────────────
   Les compteurs du dessus sont arrondis à --r-sm : les menus reprennent le
   même angle plutôt que la pilule. Un seul rayon pour tout l'écran. */
.f-select, body.view-published #veille-contexte #ctx-select {
  border-radius: var(--r-sm);
}

/* ── UN MENU MARQUÉ PROVISOIRE ───────────────────────────────────────────
   Le point de vue ne restera pas sous cette forme. La teinte ambre le dit —
   c'est le jeton d'avertissement de l'outil, pas une couleur nouvelle — et le
   survol l'explique en toutes lettres. Marquer ce qui est provisoire vaut
   mieux que de le faire passer pour définitif. */
.f-select.f-provisoire {
  background-color: var(--warning-soft);
  color: var(--warning-orange-deep);
  box-shadow: inset 0 0 0 1px var(--warning-orange);
  background-image: linear-gradient(45deg, transparent 50%, var(--warning-orange-deep) 50%),
                    linear-gradient(135deg, var(--warning-orange-deep) 50%, transparent 50%);
}
.f-select.f-provisoire:hover { background-color: var(--warning-soft); filter: brightness(0.97); }
#veille-contexte.ctx-compact { display: inline-flex; }

/* ── EN-TÊTE : LA DATE À DROITE, LA PROVENANCE EN HAUT (retour Alexandre) ──
   Trois éléments, trois places, sans toucher au rendu (tout est en mise en
   page) : « d'où viennent ces avis » remonte contre la barre de recherche, à
   gauche ; le titre reste à gauche en dessous ; la date du jour passe à
   droite, en vis-à-vis du titre et plus grande — c'est elle qu'on lit en
   ouvrant l'outil, avec le compteur. */
body.view-published .page-head > div:first-child {
  display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: 16px; row-gap: 2px; width: 100%;
}
body.view-published .page-head .h-methode { order: 1; width: 100%; margin: 0 0 2px; }
body.view-published .page-head h1 { order: 2; }
body.view-published .page-head .kicker {
  order: 3; margin-left: auto;
  font-size: var(--t-lg); letter-spacing: 0.06em; color: var(--text-secondary);
}
/* déplié, le paragraphe de méthode reprend toute la largeur, sous la ligne */
body.view-published .page-head .h-methode[open] { width: 100%; }
/* deux défauts vus à la capture :
   1. la date restait sous le titre — une règle de S66 lui imposait toute la
      largeur (elle était alors seule sur sa ligne). Elle reprend sa taille
      naturelle pour tenir en vis-à-vis du titre ;
   2. le menu provisoire s'affichait en damier : sa règle (deux classes) ne
      gagnait pas sur celle du point de vue (deux ID), donc les dégradés de
      chevron se répétaient sur toute la surface. On redit tout le nécessaire
      à la bonne spécificité. */
body.view-published .page-head .kicker { width: auto; }
body.view-published #veille-contexte #ctx-select.f-provisoire {
  background-color: var(--warning-soft);
  color: var(--warning-orange-deep);
  box-shadow: inset 0 0 0 1px var(--warning-orange);
  background-image: linear-gradient(45deg, transparent 50%, var(--warning-orange-deep) 50%),
                    linear-gradient(135deg, var(--warning-orange-deep) 50%, transparent 50%);
  background-position: calc(100% - 16px) 54%, calc(100% - 11px) 54%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
/* l'en-tête tient sur UNE ligne : titre, provenance à sa suite, date à
   l'opposé — et l'ensemble remonte contre la barre de recherche. */
body.view-published .page-head .h-methode {
  order: 2; width: auto; margin: 0;
}
body.view-published .page-head h1 { order: 1; }
body.view-published .page-head .kicker { order: 3; }
body.view-published .page-head .h-methode[open] { width: 100%; order: 4; }
body.view-published .main { padding-top: 2px; }
body.view-published .page-head { margin-bottom: 8px; }
/* trop collé à la barre de recherche : l'en-tête redescend et respire, sans
   revenir à la hauteur morte d'avant (on cherche l'aise, pas le vide). */
body.view-published .main { padding-top: 18px; }
body.view-published .page-head { margin-bottom: 16px; }

/* ═══════════════════════════════════════════════════════════════════════════
   S71-suite (2026-08-11, règle Alexandre) — UN SEUL EN-TÊTE DE COLONNES
   VISIBLE : celui du groupe OUVERT le plus haut.

   Le balisage en pose un dans CHAQUE groupe (sans quoi replier le groupe du
   haut emporterait les noms de colonnes ET les 8 boutons de tri de tous les
   autres — mesuré). Ce qui se joue ici est l'AFFICHAGE : dès qu'un groupe
   ouvert me précède, je cache le mien. Le premier ouvert n'a aucun frère
   ouvert avant lui : il garde le sien.

   ⚠ Pourquoi en CSS et non en JS : le repli est natif (<details>) et ne
   redéclenche AUCUN rendu. Un porteur choisi au rendu resterait figé dans un
   groupe qu'on vient de refermer ; le sélecteur de frères, lui, se réévalue à
   chaque ouverture et fermeture, sans une ligne de script.
   ═══════════════════════════════════════════════════════════════════════════ */
details.pl-groupe[open] ~ details.pl-groupe[open] .gr-entete { display: none; }

/* ⚠ COROLLAIRE — L'ALIGNEMENT NE DOIT PAS DÉPENDRE DE QUI PORTE L'EN-TÊTE.
   La table est en `table-layout: fixed` : ses colonnes sont calibrées sur la
   PREMIÈRE rangée. En-tête visible, ce sont les pourcentages des `th` ; en-tête
   caché, ceux des `td` — et les deux jeux différaient (titre 33 % contre 31 %,
   acheteur 12 % contre 15 %), si bien que les groupes du bas étaient décalés
   par rapport à celui du haut. Les `td` reprennent donc EXACTEMENT les
   pourcentages des `th` : les colonnes tombent au même endroit dans tous les
   groupes, en-tête montré ou non. */
.gr-table td.col-id:first-child { width: 7%; }
.gr-table td.col-title  { width: 33%; }
.gr-table td.col-buyer  { width: 12%; }
.gr-table td.col-id     { width: 7%; }
.gr-table td.col-deadline.mono { width: 8%; }
.gr-table td.col-deadline { width: 10%; }
.gr-table td.col-family { width: 9%; }
.gr-table td.col-status { width: 8%; }
.gr-table td.col-id:last-child { width: 6%; }

/* ═══════════════════════════════════════════════════════════════════════════
   S72 — RECHERCHE TRANSVERSE : le panneau de résultats sous la barre du haut.

   Choix de forme : des SUGGESTIONS sous le champ, et non un écran de résultats
   dédié. Trois raisons — c'est moins de surface neuve à maintenir, ça dit
   immédiatement OÙ chaque chose vit (une famille = un écran), et ça conduit
   sans faire perdre l'écran où l'on était.

   ⚠ Les familles sont visuellement SÉPARÉES et jamais fondues : un avis n'est
   pas un marché (assiettes et seuils différents). Chaque titre de famille porte
   son compte et, quand elle en a une, son assiette.
   ═══════════════════════════════════════════════════════════════════════════ */
.search { position: relative; }

.rt-panneau {
  display: none;
  position: absolute; top: calc(100% + 6px); left: 0; right: 0;
  z-index: 60;
  max-height: min(80vh, 760px); overflow-y: auto;
  background: var(--card-solid);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-sm);
  box-shadow: 0 12px 32px rgba(0,0,0,0.16);
  padding: 4px;
}

/* une famille = un bloc, séparé du suivant par un filet : la frontière entre
   consultations et marchés doit se VOIR, pas se deviner */
.rt-famille + .rt-famille { border-top: 1px solid var(--hairline); margin-top: 4px; padding-top: 4px; }

.rt-titre {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 7px 10px 5px;
}
.rt-lib {
  font-size: var(--t-sm); font-weight: var(--poids-fort); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-secondary);
}
.rt-n {
  font-family: var(--mono); font-size: var(--t-sm);
  color: var(--text-secondary);
  background: var(--sur-aplat-faible, rgba(0,0,0,0.05));
  border-radius: 999px; padding: 1px 7px;
}
/* l'assiette empêche de lire deux comptes comme deux moitiés d'un même total */
.rt-assiette { font-size: var(--t-sm); color: var(--text-faint); margin-left: auto; }

.rt-item {
  display: block; width: 100%; text-align: left;
  border: 0; background: transparent; cursor: pointer;
  padding: 7px 10px; border-radius: var(--r-xs, 4px);
  font: inherit; color: var(--text-primary);
}
.rt-item:hover, .rt-item:focus-visible { background: var(--sur-aplat-faible, rgba(0,0,0,0.05)); }
.rt-obj {
  display: block; font-size: var(--t-md); line-height: 1.35;
  /* l'objet n'est jamais tronqué au point de mentir (S47/S55) : deux lignes
     pleines, puis coupe — on lit de quoi il s'agit */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.rt-meta { display: block; font-size: var(--t-sm); color: var(--text-secondary); margin-top: 2px; }

/* l'absence se NOMME — jamais un silence (même règle que « non renseigné ») */
.rt-vide { padding: 6px 10px 9px; font-size: var(--t-md); color: var(--text-secondary); font-style: italic; }

.rt-pied {
  padding: 8px 10px; margin-top: 4px;
  border-top: 1px solid var(--hairline);
  font-size: var(--t-sm); color: var(--text-faint);
}

/* la mention d'extrait : discrète, mais jamais absente quand la famille est
   tronquée — quatre lignes sous un badge « 36 » se liraient sinon comme la
   liste entière (S23 : rien de faux à l'écran). */
.rt-extrait {
  padding: 3px 10px 7px;
  font-size: var(--t-sm); font-style: italic; color: var(--text-faint);
}

/* ═══════════════════════════════════════════════════════════════════════════
   S73 [typographie-densite] — CE QUI PAIE LE TEXTE AGRANDI.

   Le geste central du bloc est un ÉCHANGE : l'espace horizontal repris entre
   les colonnes finance le texte plus grand. Les deux vont ensemble.

   ⚠ MESURÉ AVANT DE TOUCHER (page jetable, largeurs réelles au navigateur) :
   la table débordait DÉJÀ de sa zone AVANT ce bloc — 1 116 px pour 1 095
   disponibles. Deux colonnes seules en étaient cause : « Date limite de
   réponse » réclamait 141 px d'intitulé pour 110 alloués, et « Avis en ligne »
   78 pour 66. Toutes les autres avaient de la marge (l'objet : 361 px pour
   104 requis). L'agrandissement n'a pas créé le défaut, il l'a rendu visible.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. GOUTTIÈRES resserrées : 5 px -> 3 px de chaque côté. Sur neuf colonnes,
      cela rend 36 px à la largeur utile — soit davantage que le débordement
      mesuré. C'est l'échange annoncé, et il est intégralement dépensé en
      lisibilité. */
body.view-published .gr-table td,
body.view-published .gr-entete th { padding-left: 3px; padding-right: 3px; }

/* 2. LES INTITULÉS PEUVENT REVENIR À LA LIGNE. « Date limite de réponse » sur
      une seule ligne exigeait 141 px d'une colonne qui n'en a que 110 : c'est
      cet intitulé, et non les données, qui forçait la table hors de sa zone.
      Sur deux lignes il tient, et l'en-tête reste écrit EN ENTIER — le raccourcir
      aurait été payer la place avec de l'information (acquis S70 : les colonnes
      disent ce qu'elles contiennent, « échéance » a déjà été refusé). */
.gr-entete th { white-space: normal; vertical-align: bottom; line-height: 1.25; }

/* 3. LA COLONNE DU LIEN se contente d'une icône (le mot « ouvrir » est parti,
      l'intitulé et l'infobulle portent le sens). Elle rend ses pixels. */
.gr-table th.col-id:last-child, .gr-table td.col-id:last-child { width: 4%; }
.md-lien-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: var(--r-xs, 4px);
  color: var(--text-secondary);
}
.md-lien-icone:hover { color: var(--marque-rouge); background: var(--sur-aplat-faible, rgba(0,0,0,0.05)); }

/* 4. REDISTRIBUTION : la place reprise va à la colonne qui en manquait. Les
      pourcentages des `th` et des `td` restent IDENTIQUES (acquis S71 : la
      table est en table-layout fixed, elle se calibre sur sa première rangée —
      deux jeux différents décaleraient les groupes entre eux). */
.gr-table th.col-deadline, .gr-table td.col-deadline { width: 12%; }
.gr-table th.col-title,    .gr-table td.col-title    { width: 30%; }
.gr-table th.col-buyer,    .gr-table td.col-buyer    { width: 13%; }

/* ═══ ACTION PRINCIPALE — UNE SEULE PAR ÉCRAN ════════════════════════════════
   Le rouge de marque est le signal le plus fort dont l'outil dispose. S'il
   habille deux boutons, il n'en désigne plus aucun : chaque bouton finirait par
   le réclamer et le signal disparaîtrait. La règle est donc posée ICI, en dur :
   `.btn.principale` est unique par écran, et le canary structurel le vérifie.

   ⚠ PAS de vert : dans cet outil le vert ne désigne QUE la famille métier
   « Matériel » (pastilles de segmentation, acquis S68). Lui ajouter un second
   sens — « action » — créerait une ambiguïté sur le même écran. Le rouge, lui,
   est déjà le décor de marque du client (S65) : l'action principale l'emprunte
   sans rien signifier de neuf. */
.btn.principale {
  background: var(--marque-rouge);
  color: var(--sur-aplat);
  border: 1px solid var(--marque-rouge);
  font-weight: var(--poids-fort);
}
.btn.principale:hover { filter: brightness(0.92); }
.btn.principale:disabled { opacity: 0.55; cursor: default; filter: none; }
.btn.principale svg { color: currentColor; }

/* 5. AU PLANCHER (1 280 px), la colonne « Statut » manquait de 13 px et
      poussait la table hors de sa zone. Coupable identifié par la mesure, pas
      supposé : le badge « À traiter » (89 px insécables pour 76 alloués). On
      lui donne ces pixels, repris sur l'objet — la seule colonne dont le
      contenu se coupe proprement, et qui garde 28 %. */
.gr-table th.col-status, .gr-table td.col-status { width: 9%; }
.gr-table th.col-title,  .gr-table td.col-title  { width: 28%; }

/* 6. RENDRE DE LA LARGEUR À L'OBJET — sans rétrécir aucun texte.
      Mesuré : chaque point de pourcentage repris à l'objet le fait s'enrouler
      davantage, donc GRANDIR EN HAUTEUR, donc coûter des avis visibles. « Assigné
      à » disposait de 101 px pour un menu qui en réclame 71 : ces 2 points vont
      à l'objet. C'est le même échange que les gouttières — de la largeur inutile
      convertie en lignes plus courtes. */
.gr-table th.col-family, .gr-table td.col-family { width: 9%; }
.gr-table th.col-title,  .gr-table td.col-title  { width: 30%; }

/* 7. DEUX DÉFAUTS VUS À LA CAPTURE, invisibles à la mesure.

   a) Le champ de recherche tronquait sa propre promesse — « …un objet ou une
      référ ». Le texte agrandi ne tenait plus dans 480 px. Un placeholder coupé
      promet à moitié : le champ s'élargit (la barre du haut en a la place). */
.search { max-width: 620px; }

/*  b) L'icône du lien héritait de l'effacement S66 (opacité 0,16 au repos, pleine
      au survol de la ligne) — réglé pour le mot « ouvrir », il rendait l'icône
      seule quasi invisible. Une icône qu'on ne voit pas ne remplace pas un mot.
      Elle reste discrète — l'action franche de la ligne demeure le statut — mais
      lisible au repos. */
body.view-published .gr-table .md-lien-icone { opacity: 0.55; }
body.view-published .gr-table tr:hover .md-lien-icone,
body.view-published .gr-table tr:focus-within .md-lien-icone { opacity: 1; }

/* ═══ S73-suite (retours Alexandre à la capture) ════════════════════════════

   1. « ASSIGNÉ À » RÉTRÉCIT VRAIMENT (9 % -> 7 %) et rend sa place à l'objet.
      Mesuré : le menu réclame 71 px, il en avait 101. L'objet est l'information
      la plus importante de la ligne — chaque point qu'il gagne raccourcit son
      enroulement, donc la hauteur de ligne, donc rend des avis visibles. */
.gr-table th.col-family, .gr-table td.col-family { width: 7%; }
.gr-table th.col-title,  .gr-table td.col-title  { width: 32%; }
.gr-table .asg-select { max-width: 100%; }

/* 2. LES ACHETEURS EN CAPITALES — pour le relief et l'alternance dans le
      tableau : l'objet est en gras, l'acheteur en capitales, les deux se
      distinguent d'un coup d'œil sans ajouter ni couleur ni graisse.
      ⚠ `text-transform` : c'est l'AFFICHAGE qui met en capitales, la donnée
      reste intacte (la casse lisible reconstruite en S39 n'est pas touchée —
      elle continue de servir la fiche, les exports et la recherche). */
body.view-published .gr-table td.col-buyer {
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.35;
}

/* 3. LE STATUT NE COLLE PLUS AU MENU D'ASSIGNATION : il se centre dans sa
      cellule au lieu de s'aligner en haut, ce qui le collait au menu déroulant
      de la colonne voisine. */
body.view-published .gr-table td.col-status { vertical-align: middle; }
body.view-published .gr-table td.col-status .st-texte,
body.view-published .gr-table td.col-status .st-badge { margin-top: 0; }

/* 4bis. DE L'AIR ENTRE L'ASSIGNATION ET LE STATUT. Le centrage vertical ne
      suffisait pas : les deux colonnes se touchaient horizontalement, le menu
      déroulant venant buter contre le badge d'état. La gouttière générale reste
      à 3 px (c'est elle qui paie le texte agrandi) ; seule cette frontière-là,
      où deux commandes se jouxtent, reçoit un peu plus. */
body.view-published .gr-table td.col-family { padding-right: 10px; }
body.view-published .gr-table td.col-status { padding-left: 10px; }

/* 4ter. LES COLONNES SOMMENT À 100 % (invariant du canary S60, bloc S64) :
      les redistributions successives laissaient 1 point orphelin. Il va à
      l'objet — l'information la plus importante de la ligne (retour Alexandre),
      et la seule dont chaque point gagné raccourcit l'enroulement. */
.gr-table th.col-title, .gr-table td.col-title { width: 33%; }

/* ═══ S73-suite 2 (retours Alexandre) — DU BLANC OBLIGATOIRE ENTRE LES TEXTES.

   Correction d'un excès de ce bloc : les gouttières avaient été ramenées à 3 px
   pour financer le texte agrandi. Vu à l'écran, c'est allé trop loin — les
   colonnes voisines paraissent se toucher, et deux textes qui se frôlent se
   lisent comme un seul. Le blanc entre colonnes n'est pas de la place perdue :
   c'est lui qui sépare les segmentations du tableau.

   8 px de chaque côté, soit 16 px entre deux colonnes — la gouttière redevient
   visible sans revenir aux 5 px d'origine côté cellule, car l'échange du bloc
   (icône à la place du mot « ouvrir », intitulés qui reviennent à la ligne) a
   dégagé assez de largeur pour l'absorber. Mesuré après : aucun débordement à
   la résolution de référence.
   ═══════════════════════════════════════════════════════════════════════════ */
body.view-published .gr-table td,
body.view-published .gr-entete th { padding-left: 8px; padding-right: 8px; }

/* la frontière assignation / statut garde son supplément : deux commandes qui
   se jouxtent demandent plus qu'un simple filet de blanc */
body.view-published .gr-table td.col-family { padding-right: 12px; }
body.view-published .gr-table td.col-status { padding-left: 12px; }

/* LE STATUT SE CENTRE dans sa colonne — au milieu horizontalement comme
   verticalement. Aligné à gauche, il restait aimanté au menu déroulant voisin
   quelle que soit la gouttière. */
body.view-published .gr-table td.col-status,
body.view-published .gr-entete th.col-status { text-align: center; }
body.view-published .gr-table td.col-status .st-actions { justify-content: center; }

/* Le libellé « Non assigné » ne doit pas être coupé : rétrécir la colonne était
   juste, la couper ne l'est pas — un libellé tronqué est une demi-information.
   Un point repris à la date limite, qui tient largement sur une ligne. */
.gr-table th.col-family, .gr-table td.col-family { width: 8%; }
.gr-table th.col-deadline, .gr-table td.col-deadline { width: 11%; }
.gr-table .asg-select { padding-left: 6px; padding-right: 4px; }

/* le statut se centre AUSSI verticalement dans sa cellule (son bloc texte +
   action est aligné sur le milieu, pas sur le haut) */
body.view-published .gr-table td.col-status .st-texte { display: block; }

/* ═══════════════════════════════════════════════════════════════════════════
   S74 [filtres-multiselection] — MENUS À CASES À COCHER.

   Bâtis sur <details>/<summary> et de vraies cases : le clavier, le lecteur
   d'écran et le focus fonctionnent sans une ligne de script. Un <select
   multiple> natif aurait exigé Ctrl+clic — geste que personne ne devine — et un
   composant entièrement dessiné aurait obligé à réécrire l'accessibilité que le
   navigateur donne. Même arbitrage qu'en S71, poussé d'un cran.

   L'arrondi et la hauteur reprennent ceux des menus S71 : de l'extérieur, rien
   ne dit qu'un axe est passé en multi-sélection tant qu'on ne l'ouvre pas.
   ═══════════════════════════════════════════════════════════════════════════ */
.f-cases { position: relative; }
.f-cases > summary {
  list-style: none; cursor: pointer;
  display: flex; flex-direction: column; gap: 2px;
}
.f-cases > summary::-webkit-details-marker { display: none; }
.f-cases .f-valeur {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-width: 190px; max-width: 320px;
  padding: 7px 10px;
  font-size: var(--t-md);
  background: var(--card-solid);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-cases .f-valeur::after {
  content: "▾"; font-size: var(--t-xs); color: var(--text-secondary); flex: 0 0 auto;
}
.f-cases[open] .f-valeur { border-color: var(--text-secondary); }

.f-panneau {
  position: absolute; z-index: 40; top: calc(100% + 4px); left: 0;
  min-width: 260px; max-height: 340px; overflow-y: auto;
  background: var(--card-solid);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-sm);
  box-shadow: 0 12px 32px rgba(0,0,0,0.16);
  padding: 4px;
}
.f-case {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px; border-radius: var(--r-xs, 4px);
  font-size: var(--t-md); cursor: pointer;
}
.f-case:hover { background: var(--sur-aplat-faible, rgba(0,0,0,0.05)); }
.f-case input { width: 15px; height: 15px; accent-color: var(--marque-rouge); flex: 0 0 auto; }
.f-case-lib { flex: 1; }
/* le décompte de chaque option : SERVI, jamais recalculé au front */
.f-case-n { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-secondary); }

/* le pied porte la remise à zéro de l'AXE et la convention de l'axe vide —
   « rien de coché = pas de filtre » se lit, ne se devine pas */
.f-pied {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 9px; margin-top: 4px;
  border-top: 1px solid var(--hairline);
}
.f-raz {
  border: 0; background: transparent; cursor: pointer; padding: 0;
  font: inherit; font-size: var(--t-xs); color: var(--text-secondary);
  text-decoration: underline;
}
.f-raz:hover { color: var(--marque-rouge); }
.f-note { font-size: var(--t-xs); color: var(--text-faint); font-style: italic; }

/* le retour au préréglage : offert tant qu'on s'en est écarté, éteint sinon */
.f-prereglage {
  display: block; width: calc(100% - 18px); margin: 4px 9px 0;
  padding: 6px 8px; font: inherit; font-size: var(--t-xs);
  border: 1px dashed var(--hairline-strong); border-radius: var(--r-xs, 4px);
  background: transparent; color: var(--text-primary); cursor: pointer;
}
.f-prereglage:hover:not(:disabled) { border-color: var(--marque-rouge); color: var(--marque-rouge); }
.f-prereglage:disabled { opacity: 0.45; cursor: default; }

/* (retour Alexandre) LES MENUS SONT GRISÉS, comme les menus S71 qu'ils
   remplacent : de l'extérieur, un axe multi-valeurs doit ressembler à un axe
   ordinaire — c'est la même fonction, avec plus de choix. */
.f-cases .f-valeur { background: var(--card-solid); }
.ctrl-panneau .f-cases .f-valeur,
.f-cases .f-valeur { background-color: var(--sur-aplat-faible, rgba(0,0,0,0.05)); }
.f-cases:hover .f-valeur { border-color: var(--text-muted); }

/* (retour Alexandre) LE PANNEAU NE FLOTTE PLUS : déroulé, il touchait sa case
   à 4 px près, et cet interstice le faisait lire comme un élément séparé plutôt
   que comme le prolongement du menu. Il se colle, les deux bordures se
   superposent (le -1 px) et les coins se rejoignent : haut arrondi sur la case,
   bas arrondi sur le panneau, rien entre les deux. */
.f-panneau { top: calc(100% - 1px); border-top-left-radius: 0; border-top-right-radius: 0; }
.f-cases[open] .f-valeur {
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
  border-bottom-color: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════════
   S75 [colonne-type-selecteur] — LA FAMILLE PREND SA COLONNE.

   S68 l'avait placée en tête de ligne secondaire pour lui donner une abscisse
   fixe : c'était le remède le moins cher. Sa propre colonne est la forme
   complète du même geste — l'œil balaie une verticale au lieu de chercher une
   pastille au début d'une chaîne de longueur variable.

   ⚠ D'OÙ VIENNENT LES PIXELS (mesuré avant de toucher, marge = largeur allouée
   moins contenu réel) : Parution 48 px de marge, Avis officiel 37, Assigné 20,
   Statut 20 — 187 px disponibles au total sans rien tronquer. La colonne Type
   se paie là, PAS sur l'objet du marché (interdit du brief : le tronquer pour
   financer une colonne, c'est perdre ce qu'on lit pour décider).
   ⚠ Et « Dépt » manquait déjà de 25 px avant ce bloc (110 alloués, 135 requis
   par « 23 — Creuse ») : il en reçoit au passage.
   ═══════════════════════════════════════════════════════════════════════════ */
.gr-table th.col-type, .gr-table td.col-type { width: 8%; }
.gr-table th.col-id:first-child, .gr-table td.col-id:first-child { width: 6%; }
.gr-table th.col-title,  .gr-table td.col-title  { width: 30%; }
.gr-table th.col-buyer,  .gr-table td.col-buyer  { width: 12%; }
.gr-table th.col-id,     .gr-table td.col-id     { width: 8%; }   /* Dépt : +1 pt */
.gr-table th.col-deadline.mono, .gr-table td.col-deadline.mono { width: 6%; }
.gr-table th.col-deadline, .gr-table td.col-deadline { width: 11%; }
.gr-table th.col-family, .gr-table td.col-family { width: 7%; }
.gr-table th.col-status, .gr-table td.col-status { width: 8%; }
.gr-table th.col-id:last-child, .gr-table td.col-id:last-child { width: 3%; }

/* la pastille occupe sa cellule sans l'étirer : « Prestation de service » passe
   sur deux lignes plutôt que d'imposer sa largeur à toute la colonne */
body.view-published .gr-table td.col-type { vertical-align: top; }
body.view-published .gr-table td.col-type .seg-badge {
  display: inline-block; max-width: 100%; white-space: normal; text-align: left;
  line-height: 1.3;
}

/* ⚠ LA DISTINCTION DE NATURE DE « À QUALIFIER » SURVIT (acquis S68). Alignée
   dans une colonne, elle risquait de se lire comme un quatrième classement :
   ce n'en est pas un, c'est un aveu d'ignorance. Fond transparent, contour
   tireté, italique et marque « ? » — sa forme la distingue, pas sa couleur. */
body.view-published .gr-table td.col-type .seg-badge.seg-indetermine {
  background: transparent; border-style: dashed; font-style: italic;
}

/* la ligne secondaire ne subsiste que si elle porte quelque chose : mesuré,
   95 lignes sur 110 n'auraient plus rien à y mettre après le départ de la
   famille — 22 px de hauteur chacune pour du vide. Elle n'est plus rendue. */
body.view-published .cell-title .t-sub { margin-top: 4px; }

/* ═══ SÉLECTEUR DE COLONNES — masquage par CODE ═══════════════════════════════
   Le rendu produit TOUJOURS toutes les colonnes ; seul l'affichage les éteint.
   Rien n'est perdu, l'inventaire S61 reste entier (une colonne masquée par
   choix reste offerte) et un choix se défait aussi vite qu'il se fait. */
body[data-cols-masquees~="ref"]      [data-col="ref"],
body[data-cols-masquees~="type"]     [data-col="type"],
body[data-cols-masquees~="acheteur"] [data-col="acheteur"],
body[data-cols-masquees~="dept"]     [data-col="dept"],
body[data-cols-masquees~="parution"] [data-col="parution"],
body[data-cols-masquees~="assigne"]  [data-col="assigne"],
body[data-cols-masquees~="statut"]   [data-col="statut"],
body[data-cols-masquees~="lien"]     [data-col="lien"] { display: none; }

/* le sélecteur reprend l'habillage du réglage de la fiche (patron S41) ; seules
   les colonnes FIGÉES reçoivent une marque propre, pour que « on ne peut pas la
   masquer » se lise à l'endroit du choix et non après coup */
#veille-colonnes-hote .ao-reglage { max-width: 420px; }
#veille-colonnes-hote .ao-reglage-corps label.fige { opacity: 0.75; cursor: default; }
#veille-colonnes-hote .fige-note {
  font-size: var(--t-xs); color: var(--text-faint); font-style: italic;
}

/* les dix colonnes somment à 100 % (invariant du canary S60) : le point
   manquant va à l'objet, l'information la plus importante de la ligne. */
.gr-table th.col-title, .gr-table td.col-title { width: 31%; }

/* Rééquilibrage MESURÉ : « Dépt » réclamait encore 9 px (135 requis par
   « 23 — Creuse » pour 126 alloués) et la table débordait de 19 px. Le point
   vient de « Type », qui gardait 37 px de marge — la pastille tient à 7 %. */
.gr-table th.col-type, .gr-table td.col-type { width: 7%; }
.gr-table th.col-id, .gr-table td.col-id { width: 9%; }
.gr-table th.col-id:first-child, .gr-table td.col-id:first-child { width: 6%; }
.gr-table th.col-id:last-child, .gr-table td.col-id:last-child { width: 3%; }

/* ⚠ LA COLONNE AJOUTÉE COÛTE SES GOUTTIÈRES, pas seulement sa largeur : mesuré,
   la somme des dix colonnes tombait pile sur la zone (1 575 px) et la table
   débordait pourtant de 19 px — soit très exactement les 2 × 8 px de marge
   intérieure de la dixième colonne, plus les bordures. On les reprend sur les
   gouttières (8 → 7 px de chaque côté, donc 14 px entre deux colonnes au lieu
   de 16) plutôt que sur une largeur : le blanc entre colonnes obtenu en S73
   reste, et aucune colonne ne se resserre. */
body.view-published .gr-table td,
body.view-published .gr-entete th { padding-left: 7px; padding-right: 7px; }

/* « Avis officiel » à 3 % coupait son propre intitulé (vu à la capture). Il
   reprend un point à « Statut », qui gardait 20 px de marge mesurée. */
.gr-table th.col-id:last-child, .gr-table td.col-id:last-child { width: 4%; }
.gr-table th.col-status, .gr-table td.col-status { width: 7%; }

/* ═══ S77 [pagination-memoire] — les commandes de page ══════════════════════
   Le pied de liste porte « 1-25 sur 387 » (les deux nombres de la MÊME
   requête), les deux sens de parcours et le choix du nombre de lignes. */
.mem-pagination {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--hairline);
}
.mem-pagination .btn:disabled { opacity: 0.45; cursor: default; }
.mem-page-num { font-size: var(--t-sm); color: var(--text-secondary); }
.mem-taille {
  display: flex; align-items: center; gap: 6px; margin-left: auto;
  font-size: var(--t-sm); color: var(--text-secondary);
}
.mem-taille select {
  font: inherit; font-size: var(--t-sm); padding: 4px 8px;
  border: 1px solid var(--hairline-strong); border-radius: var(--r-xs);
  background: var(--sur-aplat-faible, rgba(0,0,0,0.05)); color: var(--text-primary);
}

/* ═══════════════════════════════════════════════════════════════════════════
   S79 [finitions-liste] — CINQ FINITIONS D'ÉCRAN.
   Mesures de référence : docs/specs/mesure_finitions_s79.md (cadre 1920 × 950).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── (1) LE SURVOL NE DÉPLACE PLUS RIEN ───────────────────────────────────
   Mesuré avant : ligne à 102 px au repos, 110 px quand les actions secondaires
   entrent — +8 px, et TOUT ce qui est en dessous descendait. Le fond n'y était
   pour rien : la cause est l'APPARITION elle-même. `display: none` →
   `inline-flex` fait entrer les boutons dans le flux et ajoute une ligne au
   conteneur flex de la cellule (`.st-cell`, en colonne).
   Elles occupent donc leur place EN PERMANENCE, et leur révélation passe par
   l'opacité — mécanisme déjà en service ici depuis S66 (@:2967). On étend
   celui qui existe, on n'en invente pas un second. */
body.view-published .gr-table .st-actions .st-act.secondaire { display: inline-flex; }

/* ── (2) LE STATUT S'ALIGNE SOUS SON INTITULÉ ─────────────────────────────
   Mesuré : encre de l'en-tête à 32 px, encre du contenu à 12 px — soit
   exactement le `padding-left` de la cellule. Le contenu était donc collé à
   gauche pendant que l'intitulé se centrait.
   ⚠ Le `text-align: center` posé en S73 (@:4176) N'ENTRE PAS dans `.st-cell` :
   c'est un conteneur FLEX en colonne, où l'alignement transversal est commandé
   par `align-items` — qui valait `flex-start`. Le remède est là, pas ailleurs.
   Les neuf autres colonnes sont mesurées alignées (encre à 7 px des deux
   côtés) : le contrôle a bien porté sur toutes, pas seulement sur celle-ci. */
body.view-published .gr-table td.col-status .st-cell { align-items: center; }

/* ── (3) LES COMMANDES D'AFFICHAGE SE REGROUPENT ──────────────────────────
   Le sélecteur de colonnes passe sous Liste / Cartes, à droite : régler CE QUI
   est montré et régler COMMENT c'est montré relèvent du même geste. Sa phrase
   explicative part, son compte (N/M) reste — c'est lui qui porte l'information. */
body.view-published .dispo-host {
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
body.view-published .dispo-host #veille-colonnes-hote { margin: 0; }

/* ── (4) LA COLONNE « TYPE » NE BOUGE PAS, ET C'EST UN RÉSULTAT DE MESURE ──
   Demande initiale : « blanc excessif, la colonne est plus large que son
   contenu ». Mesuré (109 pastilles, cadre 1920 × 950) :
       alloué 110 px · besoin réel 102 px · JEU = 8 px
       pastille médiane 66 px · la plus longue 88 px · aucune sur deux lignes
   Le trou de 37 px que l'œil voit sur une ligne typique se décompose en 22 px
   d'écart entre la pastille médiane et la plus longue — en `table-layout:
   fixed`, une colonne fait la largeur de son contenu LE PLUS LARGE, c'est sa
   définition — plus 7 px de gouttière, plus 8 px de gras.
   Or le canary S64 exige des pourcentages ENTIERS sommant à 100, et 1 % vaut
   15,75 px : passer de 7 % à 6 % donnerait 94 px pour un besoin de 102, donc
   replierait la plus longue pastille — ce que le brief interdit.
   ⚠ CETTE COLONNE EST DÉJÀ À SON MINIMUM. C'est la troisième passe sur elle
   (S68, S75, S79) : on cesse de cranker un bouton qui n'a que 8 px d'autorité,
   et on grave l'équilibre en assertion (canary S79) pour qu'une quatrième
   passe ne se fasse pas à l'estime — et qu'un libellé plus long à l'avenir
   sorte en ROUGE au lieu de se replier en silence.

   ⚠ ARBITRAGE ALEXANDRE 2026-08-14, APRÈS CETTE MESURE : le blanc reste trop
   visible à l'œil, on descend quand même à 6 %. Le constat ci-dessus n'est pas
   annulé — 6 % = 94 px pour un besoin de 102 : la colonne NE PEUT PAS tenir
   telle quelle. On finance donc les 8 px manquants sur les REMBOURRAGES, seule
   réserve restante, et on les prend là où ils ne coûtent aucune lisibilité :
   la gouttière de cette colonne et le rembourrage interne de la pastille.
   Le point de pourcentage libéré va à l'objet du marché (31 → 32 %), comme
   demandé. La somme reste à 100 % (invariant canary S64) et les `th` gardent
   les mêmes parts que les `td` (invariant S71). */
.gr-table th.col-type,  .gr-table td.col-type  { width: 6%; }
.gr-table th.col-title, .gr-table td.col-title { width: 32%; }

/* les 8 px repris sur les rembourrages : gouttière de la colonne Type ramenée
   de 7+7 à 5+4, rembourrage interne de la pastille de 7 à 5 de chaque côté.
   La pastille reste lisible (sa hauteur, sa graisse et son jeton de couleur ne
   bougent pas) ; seul l'air autour d'elle est repris. */
body.view-published .gr-table td.col-type,
body.view-published .gr-entete th.col-type { padding-left: 5px; padding-right: 4px; }
body.view-published .gr-table td.col-type .seg-badge { padding-left: 5px; padding-right: 5px; }

/* ── (5) LA COLONNE « TYPE » SE CENTRE (retour Alexandre, après la réduction).
   Une colonne en layout fixe fait la largeur de son contenu LE PLUS LARGE : le
   reliquat est structurel, on ne peut pas le supprimer. Mais on peut cesser de
   l'accumuler d'un seul côté. Aligné à gauche, tout le jeu s'entassait contre
   l'objet du marché et se lisait comme un trou ; réparti, il devient une
   gouttière symétrique de part et d'autre de la pastille.
   Même remède que pour le statut, et c'est voulu : deux façons de traiter le
   même phénomène dans un même tableau, ce serait une de trop.
   ⚠ Le `text-align` suffit ICI (la pastille est un `inline-block`, il l'atteint)
   alors qu'il ne suffisait pas pour le statut, dont le contenu est un conteneur
   FLEX — c'est `align-items` qui y commandait. Le remède se choisit d'après ce
   qui porte le contenu, pas d'après le symptôme. */
body.view-published .gr-table td.col-type,
body.view-published .gr-entete th.col-type { text-align: center; }

/* ⚠ …ET AU MILIEU VERTICALEMENT (retour Alexandre) : la colonne était en
   `vertical-align: top` depuis S75, pour que la pastille ne s'étire pas quand
   l'objet du marché prend deux ou trois lignes. Mais collée en haut d'une
   cellule haute, elle se lisait de travers — centrée dans les deux sens, elle
   occupe enfin le milieu de sa case. La règle S75 reste en place pour tout le
   reste ; seule cette colonne bascule. */
body.view-published .gr-table td.col-type { vertical-align: middle; }

/* placement du sélecteur de colonnes quand son ancre n'est pas `.dispo-host`
   mais le parent direct de la bascule : il passe à la ligne, à droite. */
body.view-published .dispo-bascule + #veille-colonnes-hote {
  flex-basis: 100%; display: flex; justify-content: flex-end; margin: 6px 0 0;
}

/* ─── S80 — étiquette d'ENTITÉ sur la ligne secondaire ────────────────────────
   Aucune colonne nouvelle, aucun pixel pris à l'objet : l'étiquette se pose SOUS
   le titre, dans `.t-sub` (presque vide depuis S68/S79). Contrainte Alexandre 14/08.
   Les deux ignorances pèsent 85 % des lignes (216 hors périmètre + 30 indéterminées
   sur 287) : elles sont rendues DISCRÈTEMENT pour que l'œil les saute, mais rendues
   quand même — ce sont des étiquettes, pas des filtres. */
/* ⚠ ÉCHELLE ET PALETTE EN JETONS, jamais en littéral. Premier jet rouge deux fois :
   `font-size: 11px` + `font-weight: 600` (S73), puis six littéraux de couleur qui
   poussaient le cliquet S65 de 140 à 146 alors qu'AUCUN jeton vert n'existe au
   :root — ajouter une teinte à la palette la veille du rendez-vous pour une seule
   étiquette n'était pas un arbitrage acceptable.
   ⚠ C'EST LA FORME QUI PORTE LE SENS, PAS LA TEINTE. Vocabulaire déjà établi par
   S65 (plein contre filet sur les pastilles J-N) et S68 (tireté + italique pour
   « à qualifier ») — on le RÉUTILISE, on n'en invente pas un troisième :
     entité du client -> pastille PLEINE (anthracite de la barre latérale)
     ambigu           -> CONTOUR
     indéterminée     -> CONTOUR TIRETÉ
     hors périmètre   -> texte simple en sourdine, AUCUNE pastille
   Bénéfice de bord voulu : les pastilles de FAMILLE sont chromatiques, celles
   d'ENTITÉ sont neutres — l'œil sépare les deux axes de la même ligne sans lire.
   Décision HUMAN 14/08 (mes deux propositions — jeton vert neuf, ou réemploi des
   classes `seg-*` — écartées : la seconde aurait fait porter à l'entité la palette
   de la famille, confondant les deux axes qu'on venait justement de séparer). */
.ent-badge {
  display: inline-block; font-size: var(--t-xs); padding: 1px 7px;
  border-radius: 10px; border: 1px solid transparent; vertical-align: middle;
  white-space: nowrap; cursor: help; letter-spacing: 0.01em;
}
/* ⚠ classes NEUTRES, jamais les noms d'entités du client : S65 interdit de recopier
   une identité client en clair dans la feuille (les libellés viennent de l'API et
   de la config versionnée, la feuille ne connaît que des RÔLES). Premier jet avec
   `.ent-senlis` / `.ent-coudes` / `.ent-ssv` : S65 rouge, à raison — et le jour où
   un deuxième client arrive, la feuille de style n'a pas à être opérée. */
.ent-client {                  /* une entité du groupe, quelle qu'elle soit */
  background: var(--machine-dark); color: var(--sur-aplat);
  border-color: var(--machine-dark); font-weight: var(--poids-appuye);
}
.ent-ambigu {                  /* l'ambigu se VOIT : c'est lui que le client arbitre */
  background: transparent; color: var(--machine-dark);
  border-color: var(--machine-dark); font-weight: var(--poids-appuye);
}
/* l'ignorance ne prend pas la forme d'une réponse (même règle qu'en S68 pour la
   famille « à qualifier ») : contour TIRETÉ, jamais un aplat. */
.ent-indetermine {
  background: transparent; color: var(--text-muted);
  border-color: var(--text-muted); border-style: dashed; font-style: italic;
}
/* 75 % des lignes. Une pastille répétée 216 fois devient du bruit et vole l'œil à
   l'objet du marché : ici PAS de pastille du tout, du texte en sourdine que le
   regard saute. Rendu, jamais masqué — c'est une étiquette, pas un filtre. */
.ent-hors {
  border: 0; padding: 0; background: transparent;
  color: var(--text-faint); font-weight: var(--poids-normal);
}
.ent-hors:hover { color: var(--text-muted); }  /* la raison reste atteignable */

/* ── S85 — écran de connexion ────────────────────────────────────────────────
   `pas-connecte` est posé DANS LE HTML et retiré seulement quand l'API confirme
   une session : l'écran de travail n'est jamais rendu visible avant. Sans ça, on
   verrait le tableau une fraction de seconde puis un écran vide — ce qui se lit
   comme une panne, pas comme un refus. */
body.pas-connecte > *:not(#ecran-connexion) { display: none !important; }
#ecran-connexion {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-primary);
}
body:not(.pas-connecte) #ecran-connexion { display: none; }
#cx-form {
  display: flex; flex-direction: column; gap: .55rem;
  width: min(24rem, 90vw); padding: 2rem 1.75rem;
  background: var(--bg-secondary);
  border: 1px solid var(--hairline); border-radius: 10px;
}
#cx-form h1 { margin: 0; font-size: var(--t-2xl); letter-spacing: .02em; }
#cx-form .cx-sous { margin: 0 0 .75rem; font-size: var(--t-sm); color: var(--text-muted); }
#cx-form label { font-size: var(--t-xs); font-weight: var(--poids-appuye); color: var(--text-muted); }
#cx-form input {
  padding: .5rem .6rem; font: inherit; font-size: var(--t-md);
  border: 1px solid var(--hairline); border-radius: 6px; background: var(--bg-primary);
}
#cx-form input:focus-visible { outline: 2px solid var(--marque-rouge); outline-offset: 1px; }
#cx-valider {
  margin-top: .85rem; padding: .55rem .8rem; font: inherit;
  font-weight: var(--poids-appuye);
  color: var(--bg-primary); background: var(--marque-rouge);
  border: 0; border-radius: 6px; cursor: pointer;
}
#cx-valider:disabled { opacity: .6; cursor: progress; }
/* le refus est ANNONCÉ (role=alert) et lisible, jamais seulement une bordure rouge */
#cx-erreur { margin: .6rem 0 0; min-height: 1.1em; font-size: var(--t-sm); color: var(--marque-rouge); }

/* identité courante + déconnexion : deux sessions côte à côte doivent se distinguer
   à l'œil, sinon on attribue à l'une ce qu'a fait l'autre. */
#identite-courante { display: inline-flex; align-items: center; gap: .5rem; margin-left: auto; }
#identite-courante .identite-nom { font-size: var(--t-sm); font-weight: var(--poids-appuye); }
#identite-courante .identite-sortir {
  font: inherit; font-size: var(--t-xs); padding: .2rem .5rem; cursor: pointer;
  background: transparent; border: 1px solid var(--hairline); border-radius: 5px;
}

/* ── S86 — fraîcheur des données à l'accueil ─────────────────────────────────
   La FORME porte le sens, pas la couleur seule (doctrine §8) : la péremption se
   signale par un contour TIRETÉ et par le mot « périmées ». Aucune teinte nouvelle
   n'est introduite — le rouge reste à la marque et à l'action principale. Un écran
   daltonien, imprimé ou en plein soleil doit dire la même chose. */
.fraicheur { white-space: nowrap; }
.fraicheur.perimee {
  padding: 0 .3em; border: 1px dashed var(--text-secondary); border-radius: 3px;
  font-weight: var(--poids-appuye);
}
/* « jamais » n'est pas « périmé » : on ne sait pas, on ne dit pas que c'est vieux.
   Contour POINTILLÉ (distinct du tireté) et graisse normale — une absence, pas une
   alerte. */
.fraicheur.inconnue {
  padding: 0 .3em; border: 1px dotted var(--text-muted); border-radius: 3px;
  font-style: italic;
}

/* ── S87 — regroupement dépliable (mémoire) ──────────────────────────────────
   La FORME porte le sens : les sous-lignes s'indentent derrière un FILET plein,
   le dépliage se lit à la flèche (▸/▾) et au décompte « i/N » — aucune teinte
   nouvelle (doctrine §8). Le badge-bouton garde l'habit des badges existants. */
.mem-depli { cursor: pointer; border: 1px solid var(--hairline); background: transparent; font: inherit; font-size: var(--t-xs); }
.mem-depli:focus-visible { outline: 2px solid var(--marque-rouge); outline-offset: 1px; }
.mem-pub-ligne { cursor: pointer; }
.mem-pub-ligne td { border-left: 3px solid var(--text-faint); background: var(--bg-secondary); font-size: var(--t-sm); padding-top: .3rem; padding-bottom: .3rem; }
.mem-pub-ligne td.mem-pub-num { border-left: none; text-align: right; color: var(--text-muted); font-size: var(--t-xs); }
.mem-pub-detail .mem-pub-source { font-weight: var(--poids-appuye); margin-right: .8em; }

/* ── S88 §L — habillage de l'écran de connexion ──────────────────────────────
   Tout en JETONS (S65/S73), zéro littéral, zéro requête externe. Le fond est un
   dégradé discret entre deux fonds EXISTANTS + un voile de marque à très faible
   opacité — aucune teinte nouvelle : le rouge reste à la marque et à l'action
   principale (« Se connecter », la seule de l'écran). */
#ecran-connexion {
  background: linear-gradient(160deg, var(--bg-primary) 0%, var(--bg-secondary) 55%, var(--bg-tertiary) 100%);
}
#ecran-connexion::before {
  /* voile de marque : le rouge EXISTANT en trait très dilué, jamais un aplat */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 60% 45% at 50% -10%, var(--marque-rouge-doux), transparent 70%);
}
#cx-form {
  position: relative; gap: .6rem;
  width: min(26rem, 92vw); padding: 2.6rem 2.4rem 2.2rem;
  box-shadow: 0 18px 48px var(--trait-06), 0 2px 8px var(--trait-04);
}
.cx-marque { width: min(13rem, 60%); margin: 0 auto .9rem; display: block; }
#cx-form h1 { text-align: center; letter-spacing: .08em; }
#cx-form .cx-sous {
  margin: .15rem 0 0; text-align: center;
  color: var(--text-secondary); line-height: 1.45;
}
.cx-acces {
  margin: .2rem 0 1.1rem; text-align: center;
  font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-muted);
}

/* ── S89 — différentiel de publications ──────────────────────────────────────
   La FORME dit si le clic vaut la peine : un groupe IDENTIQUE est discret
   (contour pointillé, pas de chevron — on n'invite pas à un dépliement vide) ;
   un groupe DIVERGENT garde le contour plein et le chevron. Aucune teinte
   nouvelle (doctrine §8). */
/* S90 : .mem-depli.identiques retiré avec le badge qu'il habillait (mesure S89 :
   98,6 % — un badge qui ne dit rien apprend à ignorer ceux qui parlent).
   .mem-pub-identiques ci-dessous RESTE : branche défensive du dépli. */
.mem-pub-identiques td.mem-pub-detail { font-style: italic; color: var(--text-secondary); }
.mem-pub-champ { font-size: var(--t-xs); font-weight: var(--poids-appuye); color: var(--text-secondary); text-align: right; white-space: nowrap; }
.mem-pub-valeur { margin-right: 1.1em; }
.mem-pub-valeur .mem-pub-source { font-weight: var(--poids-normal); color: var(--text-muted); margin-left: .35em; }
