/* ============================================================
   VoC.nc — Design System Tokens
   Registre : SaaS transactionnel self-serve, fond clair majoritaire
   Héritage : RESILIENCE (tokens structurels partagés)
   ============================================================ */

/* ---------- Fonts (Geist, auto-hébergées — #334) ----------
   Servies depuis public/fonts/ : aucune requête sortante vers un tiers au
   chargement d'une page, y compris /r/:token et les pages légales.
   Copiées de @fontsource-variable/geist@5.3.0 et geist-mono@5.3.0 ; seuls
   les subsets latin et latin-ext sont embarqués (fr, NC/PF).
   Licence SIL OFL 1.1 — et non MIT comme l'annonçait la version CDN de ce
   commentaire ; la redistribution impose de joindre la notice, d'où les
   fichiers LICENSE-geist*.txt déposés à côté des .woff2. */
@font-face {
  font-family: 'Geist Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/fonts/geist-latin-ext-wght-normal.woff2) format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/fonts/geist-latin-wght-normal.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Geist Mono Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/fonts/geist-mono-latin-ext-wght-normal.woff2) format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(/fonts/geist-mono-latin-wght-normal.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

:root {
  /* ============================================================
     COLOR — Slate (neutre dominant) + Ember (accent chaud unique).
     Pas de troisième couleur marketing.
     ============================================================ */

  /* Slate — neutre dominant, hue ~220, inchangé vs RESILIENCE */
  --slate-50:  #F5F6F8;
  --slate-100: #ECEEF1;
  --slate-200: #D6D8DC;
  --slate-400: #8D9094;
  --slate-600: #4C5159;
  --slate-800: #22262E;
  --slate-900: #0F1117;

  /* Ember — accent ambre chaud, signal CX humaine */
  --ember-50:  #FBEFE2;
  --ember-100: #F5DBB8;
  --ember-200: #EAC089;
  --ember-400: #D18A2A;
  --ember-600: #A66411;
  --ember-800: #7A480B;
  --ember-900: #543109;

  /* Sémantiques — UI système uniquement, jamais décoratifs */
  --success: #3B7A4F;
  --warning: #AA7E2C;
  --danger:  #9E3B30;

  /* Statuts indicateurs (Phase 4 — INDICATEURS-SPEC §1.5) */
  /* Jamais de couleur en dur dans les vues — tout passe par ces tokens. */
  /* Les dégradés de barres de distribution utilisent ces tokens (pas de hex direct). */
  --color-status-critical:  #9E3B30;   /* = --danger     : détracteurs, NPS < 0, FCR < 60% */
  --color-status-warning:   #AA7E2C;   /* = --warning    : zones intermédiaires basses */
  --color-status-warning-strong: var(--ember-800);   /* D-QUID-7 : variante accessible (≥4,5:1 sur #FFF et #F5F6F8) du marqueur « fiabilité faible » du tableau des formulaires actifs. NE colore PAS de headline — --color-status-warning reste la teinte des scores. */
  --color-status-fair:      #6B761F;   /* Bande intermédiaire — eNPS 10-30 « correct ». Réservé à cette bande : n'est extensible à un autre indicateur QUE si sa page publique publie une 5ᵉ bande (l'app s'aligne sur la page, jamais l'inverse — cf. barèmes headline_color_token). Jamais dans le schéma 3 couleurs des distributions (answer_value_status_token) ni dans gradient_status_color. */
  --color-status-neutral:   #8D9094;   /* = --slate-400  : passifs NPS 7-8, valeurs médianes */
  --color-status-good:      #4D9467;   /* Nouvelle valeur : NPS 30-70, FCR 70-80%, CSAT 75-85% */
  --color-status-excellent: #3B7A4F;   /* = --success    : promoteurs, NPS > 70, FCR > 80% */

  /* ---------- Application tokens ---------- */
  --bg-page:      #FFFFFF;                 /* dominante claire */
  --bg-surface:   #FFFFFF;
  --bg-subtle:    var(--slate-50);         /* sidebars, stripes */
  --bg-inverse:   var(--slate-900);        /* cards sombres isolées */
  --bg-muted:     var(--slate-100);

  --fg:           var(--slate-900);        /* jamais noir pur */
  --fg-muted:     var(--slate-600);
  --fg-subtle:    var(--slate-400);
  --fg-inverse:   var(--slate-100);
  --fg-inverse-muted: #9BA3AE;

  --border:        var(--slate-200);
  --border-strong: var(--slate-400);
  --border-inverse: #2A2F39;

  --accent:         var(--ember-600);
  --accent-hover:   var(--ember-800);
  --accent-subtle:  var(--ember-50);

  /* ============================================================
     TYPE — 2 familles, role-exclusif. Pas de serif (retiré).
     ============================================================ */
  --font-sans: "Geist Variable", "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "Geist Mono Variable", "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Type scale */
  --fs-display-xl: 56px;
  --fs-display-l:  44px;
  --fs-display-m:  32px;
  --fs-h2:         24px;
  --fs-h3:         20px;
  --fs-h4:         16px;
  --fs-lead:       18px;
  --fs-body:       16px;
  --fs-ui:         14px;
  --fs-caption:    12px;
  --fs-eyebrow:    12px;

  /* Line heights */
  --lh-display: 1.05;
  --lh-heading: 1.30;
  --lh-body:    1.65;
  --lh-ui:      1.55;

  /* Letter-spacing */
  --ls-display-xl: -0.020em;
  --ls-display-l:  -0.015em;
  --ls-display-m:  -0.010em;
  --ls-h2:         -0.005em;
  --ls-eyebrow:     0.120em;
  --ls-caption:     0.020em;
  --ls-wordmark:    0.040em;  /* voc.nc wordmark, lowercase, serré */

  /* Weights */
  --fw-light:    300;
  --fw-regular:  400;
  --fw-medium:   500;
  --fw-semibold: 600;

  /* ============================================================
     SPACING — base 4, strict (hérité)
     ============================================================ */
  --space-1:  4px;  --space-2:  8px; --space-3: 12px; --space-4: 16px;
  --space-6: 24px;  --space-8: 32px; --space-12: 48px; --space-16: 64px; --space-24: 96px;
  --section-y-desktop: 96px;
  --section-y-mobile:  48px;

  /* ============================================================
     RADII (hérités)
     ============================================================ */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:  12px;
  --radius-full: 999px;

  /* ============================================================
     MOTION
     ============================================================ */
  --duration-fast: 150ms;
  --duration-base: 250ms;
  --duration-slow: 400ms;
  --ease:          cubic-bezier(0.4, 0, 0.2, 1);

  /* ============================================================
     ELEVATION — focus Ember, modal minimal
     ============================================================ */
  --shadow-focus:  0 0 0 3px rgba(166, 100, 17, 0.20);     /* ember-600 @ 20% */
  --shadow-sm:     0 1px 2px rgba(15, 17, 23, 0.04);
  --shadow-md:     0 2px 6px rgba(15, 17, 23, 0.06), 0 1px 2px rgba(15, 17, 23, 0.04);
  --shadow-modal:  0 8px 24px rgba(15, 17, 23, 0.12), 0 2px 6px rgba(15, 17, 23, 0.06);

  /* ============================================================
     LAYOUT
     ============================================================ */
  --container-max: 1200px;
  --container-gutter: 24px;

  /* Dashboard shell */
  --app-sidebar-w: 240px;
  --app-header-h: 56px;

  /* ---------- Phase 02.4 — Dark in-app shell (D-02.4-01) ---------- */
  /* Tokens consommés par .vocnc-sidebar, .vocnc-app__col2, .vocnc-footer-legal--app */
  --app-shell-bg:          var(--slate-900);   /* sidebar + in-app footer */
  --app-shell-fg:          var(--slate-100);
  --app-shell-fg-muted:    #9BA3AE;            /* alias de --fg-inverse-muted existant */
  --app-shell-border:      var(--border-inverse);
  --app-shell-active-bg:   rgba(255, 255, 255, 0.06);
  --app-shell-divider:     rgba(255, 255, 255, 0.08);
  --app-col2-bg:           var(--slate-50);    /* secondary panel (Col 2) */
  --app-col2-bg-strong:    var(--slate-100);
  --app-main-bg:           #FFFFFF;
  --col-width-sidebar:     240px;
  --col-width-list:        360px;

  /* UAT round 7 (2026-05-13) — hauteur uniforme des 3 headers du shell app
     (sidebar__brand + col2-header + vocnc-header). Permet l'alignement
     horizontal visuel des trois zones « brand / nav / titre page ». */
  --header-height:         64px;

  /* UAT round 7 (2026-05-14) — hauteur attendue du footer in-app
     (.vocnc-footer-legal--app). Sert à réserver l'espace inférieur sur
     les colonnes fixées (sidebar, col2) pour qu'elles ne couvrent pas
     le footer quand celui-ci est sticky-bottom (pattern : pieds de page
     collé au bas de l'écran si le contenu n'occupe pas tout le viewport). */
  --footer-height:         72px;
}

/* ============================================================
   BASE
   ============================================================ */
html, body {
  margin: 0;
  padding: 0;
  background: var(--bg-page);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-regular);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* UAT round 7 (2026-05-13) — la règle body flex column 100vh d'UAT-H est
   retirée parce que le footer in-app a été déplacé À L'INTÉRIEUR de
   `.vocnc-app` (row 3 de la grille). Body redevient un block standard, ce
   qui permet à `position: sticky` sur `.vocnc-sidebar` de fonctionner
   correctement contre le scroll document.
   Les layouts publics (auth/legal/marketing/survey) appliquent eux-mêmes
   `display: flex; flex-direction: column; min-height: 100vh` sur leur
   propre classe body (vocnc-auth, vocnc-legal, etc.) — pas d'impact. */

/* ============================================================
   SEMANTIC TYPE
   ============================================================ */

.display-xl { font-family: var(--font-sans); font-size: var(--fs-display-xl); line-height: var(--lh-display); letter-spacing: var(--ls-display-xl); font-weight: var(--fw-medium); }
.display-l  { font-family: var(--font-sans); font-size: var(--fs-display-l);  line-height: 1.10; letter-spacing: var(--ls-display-l); font-weight: var(--fw-medium); }
.display-m  { font-family: var(--font-sans); font-size: var(--fs-display-m);  line-height: 1.20; letter-spacing: var(--ls-display-m); font-weight: var(--fw-medium); }
.h2 { font-family: var(--font-sans); font-size: var(--fs-h2); line-height: var(--lh-heading); letter-spacing: var(--ls-h2); font-weight: var(--fw-medium); }
.h3 { font-family: var(--font-sans); font-size: var(--fs-h3); line-height: 1.40; font-weight: var(--fw-medium); }
.h4 { font-family: var(--font-sans); font-size: var(--fs-h4); line-height: 1.50; font-weight: var(--fw-medium); }

.lead { font-size: var(--fs-lead); line-height: 1.60; color: var(--fg-muted); }
.body { font-size: var(--fs-body); line-height: var(--lh-body); }
.ui-label { font-size: var(--fs-ui); line-height: var(--lh-ui); }
.caption {
  font-size: var(--fs-caption);
  line-height: 1.40;
  letter-spacing: var(--ls-caption);
  color: var(--fg-muted);
}

/* EYEBROW — signature shared with RESILIENCE (ADN commun) */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-eyebrow);
  line-height: 1.40;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  font-weight: var(--fw-medium);
  color: var(--fg-muted);
}
.eyebrow--accent  { color: var(--accent); }
.eyebrow--inverse { color: var(--fg-inverse-muted); }

/* MONO — chiffres alignés, identifiants techniques */
.mono, .tabular {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Link interne — signature */
.link-sys {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  font-weight: var(--fw-medium);
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
  white-space: nowrap;
  display: inline-block;
  transition: color var(--duration-base) var(--ease);
}
.link-sys:hover { color: var(--accent-hover); }

/* Focus ring — Ember */
:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}

::selection {
  background: var(--ember-100);
  color: var(--slate-900);
}

/* ============================================================
   LEGAL PAGES — Typographic hierarchy (Phase 02.2 Plan 12, G-01)
   Scope : uniquement les vues servies par LegalController (layout legal.html.erb).
   Raison : les balises brutes <h1>, <h2>, <h3>, <p>, <li> doivent avoir
   une hiérarchie visuelle claire (UAT test 1 gap G-01). On applique ici
   le type scale Geist sans réécrire les 5 vues ERB.
   ============================================================ */

.vocnc-legal__main {
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.vocnc-legal__main h1 {
  font-family: var(--font-sans);
  font-size: var(--fs-display-m);    /* 32px */
  line-height: 1.20;
  letter-spacing: var(--ls-display-m);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0 0 var(--space-6) 0;       /* premier élément, pas de margin-top */
}

.vocnc-legal__main h2 {
  font-family: var(--font-sans);
  font-size: var(--fs-h2);            /* 24px */
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-h2);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: var(--space-12) 0 var(--space-4) 0;
}

.vocnc-legal__main h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-h3);            /* 20px */
  line-height: 1.40;
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: var(--space-8) 0 var(--space-3) 0;
}

.vocnc-legal__main p {
  margin: 0 0 var(--space-4) 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.vocnc-legal__main ul,
.vocnc-legal__main ol {
  margin: 0 0 var(--space-4) 0;
  padding-left: var(--space-6);
}

.vocnc-legal__main li {
  margin-bottom: var(--space-2);
  line-height: var(--lh-body);
}

.vocnc-legal__main li:last-child {
  margin-bottom: 0;
}

.vocnc-legal__main strong {
  font-weight: var(--fw-semibold);
  color: var(--fg);
}

.vocnc-legal__main a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

.vocnc-legal__main a:hover {
  color: var(--accent-hover);
}

.vocnc-legal__main code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--bg-muted);
  color: var(--fg-muted);
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
}

.vocnc-legal__main time {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Caption utility déjà stylée en global — pas de re-déclaration ici. */

/* Responsive : marges ajustées sur mobile */
@media (max-width: 640px) {
  .vocnc-legal__main h1 {
    font-size: 28px;
  }
  .vocnc-legal__main h2 {
    font-size: 22px;
    margin-top: var(--space-8);
  }
  .vocnc-legal__main h3 {
    font-size: 18px;
    margin-top: var(--space-6);
  }
}
/* ============================================================
   voc.nc — Layouts & Partials (Phase 2.2, A-03)
   Consomme les tokens de vocnc_design.css (source canonique).
   Styles spécifiques aux layouts application/survey/legal + footer.
   ============================================================ */

/* ---------- Wordmark voc.nc ---------- */
/* SKILL.md L22 : lowercase, Geist Medium, letter-spacing 0.04em */
.vocnc-wordmark {
  font-family: var(--font-sans);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wordmark);
  color: var(--fg);
  font-size: var(--fs-h4);
  text-decoration: none;
}

/* Phase 03 UAT 2026-05-10 — wordmark wrappé dans link_to root_path
   (header + footer + sidebar dashboard). Reset des styles UA pour préserver
   l'apparence wordmark (pas d'underline ni de bleu). Le focus visible est
   conservé via :focus-visible (--focus-ring du design system).

   IMPORTANT — spécificité : UA stylesheet a `a:link { text-decoration: underline }`
   avec spécificité (0,1,1). Notre `.vocnc-wordmark-link` seul est (0,1,0) et perd.
   On inclut `:link` dans la liste pour matcher la spécificité, idem `:visited`. */
.vocnc-wordmark-link,
.vocnc-wordmark-link:link,
.vocnc-wordmark-link:visited,
.vocnc-wordmark-link:hover,
.vocnc-wordmark-link:active {
  color: inherit;
  text-decoration: none;
  display: inline-block;
}
.vocnc-wordmark-link:focus-visible {
  outline: 2px solid var(--focus-ring, var(--fg));
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------- Footer légal commun (D-05 / A-03) ---------- */
/* Note UAT-01d : pas de margin-top — l'espacement est porté par le grid shell
   (.vocnc-app gap) ou par padding interne. Évite les doubles gouttières
   entre la dernière section main et le footer légal. */
.vocnc-footer-legal {
  border-top: 1px solid var(--border);
  padding: var(--space-6) 0;
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  background: var(--bg-surface);
}

.vocnc-footer-legal__inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

@media (min-width: 768px) {
  .vocnc-footer-legal__inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

.vocnc-footer-legal__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* UAT round 7 (2026-05-14) — sur mobile, le brand passe en colonne pour que
   la mention "Un produit RESILIENCE · Nouméa" soit ALIGNÉE À GAUCHE avec les
   liens légaux du dessous (au lieu d'être collée au wordmark sur une seule
   ligne avec align-items:center qui décalait optiquement le bloc). Le séparateur
   inter-items "·" disparaît puisque les items sont sur des lignes séparées. */
@media (max-width: 768px) {
  .vocnc-footer-legal__brand {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
  }
  .vocnc-footer-legal__sep {
    display: none;
  }
}

.vocnc-footer-legal__sep {
  color: var(--fg-subtle);
}

/* #310 — pas de soulignement au repos : le pointillé faisait lire RESILIENCE
   comme une abréviation, alors que les 5 liens légaux voisins n'en portent
   aucun. Les 6 liens du pied partagent désormais le même registre. */
.vocnc-footer-legal__rsl {
  color: inherit;
  text-decoration: none;
}

.vocnc-footer-legal__rsl:hover {
  color: var(--fg);
  text-decoration: underline;
}

.vocnc-footer-legal__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

.vocnc-footer-legal__nav a {
  color: inherit;
  text-decoration: none;
}

.vocnc-footer-legal__nav a:hover {
  color: var(--fg);
  text-decoration: underline;
}

/* ---------- Layout legal (pages publiques /mentions-legales, etc.) ---------- */
.vocnc-legal {
  background: var(--bg-subtle);
  color: var(--fg);
  font-family: var(--font-sans);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.vocnc-legal__header {
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}

.vocnc-legal__header-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--space-4) var(--container-gutter);
  display: flex;
  align-items: center;
}

.vocnc-legal__main {
  flex: 1;
  max-width: 768px;
  margin: 0 auto;
  padding: var(--space-12) var(--container-gutter);
  width: 100%;
}

/* ============================================================
   Cookie table (Phase 02.2 Plan 12, G-09)
   Table lisible et cohérente avec le design system sur la page
   politique_cookies.html.erb.
   Scope : uniquement la classe .vocnc-cookie-table.
   ============================================================ */

.vocnc-cookie-table-wrapper {
  width: 100%;
  overflow-x: auto;
  margin: var(--space-6) 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}

.vocnc-cookie-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
  color: var(--fg);
  margin: 0;  /* override du margin global .vocnc-legal__main */
}

.vocnc-cookie-table caption {
  caption-side: bottom;
  padding: var(--space-3) var(--space-4);
  color: var(--fg-muted);
  font-size: var(--fs-caption);
  line-height: 1.40;
  text-align: left;
  border-top: 1px solid var(--border);
}

.vocnc-cookie-table thead {
  background: var(--bg-subtle);
}

.vocnc-cookie-table thead th {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: bottom;
  font-weight: var(--fw-medium);
  font-size: var(--fs-ui);
  color: var(--fg);
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

.vocnc-cookie-table tbody td {
  padding: var(--space-3) var(--space-4);
  vertical-align: top;
  border-top: 1px solid var(--border);
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
}

.vocnc-cookie-table tbody tr:first-child td {
  border-top: 0;
}

.vocnc-cookie-table tbody tr:nth-child(even) {
  background: var(--bg-subtle);
}

.vocnc-cookie-table code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--bg-muted);
  color: var(--fg);
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

.vocnc-cookie-table__col-legal-basis {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}

/* ============================================================
   OPT-05 vues /annuler-mon-opposition + /s/:token/opt-out
   Phase 02.2 Plan 17 round 3 gap closure G-41.
   Classes utilisees par : cancel.html.erb, cancel_verify_form.html.erb,
   cancel_request_sent.html.erb, cancel_success.html.erb,
   optionally fallback_otp.html.erb.
   ============================================================ */

/* Wrapper content — max-width + layout vertical coherent avec .vocnc-legal__main
   mais scope-isole a la classe elle-meme pour eviter collisions. */
.vocnc-legal-content {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--space-4) 0 var(--space-8);
  color: var(--fg);
  font-family: var(--font-sans);
}

.vocnc-legal-content > h1 {
  font-size: var(--fs-display-m, 32px);
  font-weight: var(--fw-semibold, 600);
  line-height: 1.15;
  margin: 0 0 var(--space-4);
  color: var(--fg);
}

.vocnc-legal-content > p,
.vocnc-legal-content > ul,
.vocnc-legal-content > ol {
  font-size: var(--fs-body, 16px);
  line-height: var(--lh-body, 1.55);
  margin: 0 0 var(--space-4);
  color: var(--fg);
}

.vocnc-legal-content > ul,
.vocnc-legal-content > ol {
  padding-left: var(--space-6);
}

/* Formulaire — labels + inputs + spacing vertical */
.vocnc-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: var(--space-6) 0;
}

/* Fix #180 (recette Phase 4 du 05/08) — restreint aux labels SANS classe.
   La règle élément (spécificité 0-1-1) écrasait le `display: flex` et la
   typo des contrôles-labels du formulaire public (spécificité 0-1-0 :
   .vocnc-fcr-row__btn, .vocnc-csat-row__btn, .vocnc-star-survey__star,
   .vocnc-choices__option, .vocnc-radio…) → contenu collé en haut à
   gauche des bulles au lieu d'être centré. Les labels classés portent
   leurs styles en propre (.vocnc-form__label, components:917). */
.vocnc-form label:not([class]) {
  display: block;
  font-size: var(--fs-ui, 14px);
  font-weight: var(--fw-medium, 500);
  margin-bottom: var(--space-2);
  color: var(--fg);
}

.vocnc-form input[type="tel"],
.vocnc-form input[type="text"],
.vocnc-form input[type="email"] {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body, 16px);
  line-height: 1.4;
  border-radius: var(--radius-md);
  box-sizing: border-box;
}

.vocnc-form input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Boutons primaires (CTA) */
.vocnc-btn {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--fs-ui, 14px);
  font-weight: var(--fw-medium, 500);
  line-height: 1.4;
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: opacity 120ms ease;
}

.vocnc-btn--primary {
  background: var(--accent);
  color: #FFFFFF;
}

.vocnc-btn--primary:hover {
  opacity: 0.92;
}

/* Override contextuel : dans le layout legal, `.vocnc-legal__main a`
   (vocnc_design.css) force color: accent + underline avec spécificité (0,1,1),
   supérieure à `.vocnc-btn--primary` (0,1,0). Un link_to avec class .vocnc-btn
   hériterait donc de la couleur accent sur fond accent → texte invisible.
   Les sélecteurs ci-dessous (spécificité 0,2,1) restaurent l'apparence bouton. */
.vocnc-legal__main a.vocnc-btn {
  text-decoration: none;
}
.vocnc-legal__main a.vocnc-btn--primary,
.vocnc-legal__main a.vocnc-btn--primary:hover {
  color: #FFFFFF;
}

.vocnc-btn:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ALTCHA Web Component visibility — le widget rend son propre shadow-DOM,
   mais on force un display:block + spacing vertical pour qu il s inscrive
   proprement dans le flow du form. */
altcha-widget {
  display: block;
  margin: var(--space-4) 0;
  min-height: 56px;      /* evite layout shift pendant le PoW */
}

/* ============================================================
   Phase 02.3 (D-13, D-24, D-30) — Layout app authentifié
   Shell sidebar+header+main, posé sur grille CSS Grid 240px 1fr.
   Mobile : sidebar collapse en burger sous 768px (Stimulus optionnel
   à brancher Plan ultérieur — Wave 1 livre la structure CSS uniquement).
   ============================================================ */

/* UAT round 7 (2026-05-13) — grille 3 rows (header fixed-height / main 1fr / footer auto).
   La row 1 réserve var(--header-height) même si .vocnc-header est `position: fixed`
   (out-of-flow) — main commence donc bien sous le header sticky.
   Footer défile avec le main (pas sticky — voir .vocnc-footer-legal--app). */
.vocnc-app {
  display: grid;
  grid-template-columns: 240px 1fr;
  /* Fix #157 — ligne « notices » (hauteur auto, 0 si vide) entre header et main :
     accueille .vocnc-app__notices (bandeau AUTH-08 + flash). Avant, ces éléments
     sans grid-area étaient auto-placés dans une ligne implicite sous le footer,
     colonne 1, recouverte par la sidebar fixed → invisibles. */
  grid-template-rows: var(--header-height) auto 1fr auto;
  grid-template-areas:
    "sidebar header"
    "sidebar notices"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
  background: var(--bg-page);
  color: var(--fg);
  font-family: var(--font-sans);
}

/* Phase 02.4 — variante 3-pane shell (form builder, settings, campaigns, responses).
   Col2 (panel filtres/onglets/liste) entre sidebar et main. Toutes les aires
   (header/main/footer) sont décalées vers la 3e colonne.
   UAT round 7 (2026-05-14) — la row 3 (footer) est étendue sur col2 + col3
   ("sidebar footer footer") : le footer in-app couvre désormais visuellement
   col2 + main, plus uniquement la colonne main. Le rendu noir était dissonant
   quand seul le main portait le footer (col2 restait blanche jusqu'en bas).  */
.vocnc-app--has-col2 {
  grid-template-columns: var(--col-width-sidebar) var(--col-width-list) 1fr;
  /* Fix #157 — même ligne « notices » que le shell 2-pane (col2 reste fixed,
     la ligne ne concerne que la colonne main). */
  grid-template-areas:
    "sidebar col2    header"
    "sidebar col2    notices"
    "sidebar col2    main"
    "sidebar footer  footer";
}

/* Fix #157 — conteneur des notices globales (bandeau AUTH-08 + flash).
   grid-area explicite : plus jamais d'auto-placement hors écran. */
.vocnc-app__notices {
  grid-area: notices;
  display: flex;
  flex-direction: column;
}

/* Respiration des flash dans la zone notices — le bandeau AUTH-08 garde son
   pleine-largeur (border-bottom) sans marge. */
.vocnc-app__notices .vocnc-alert {
  margin: var(--space-3) var(--space-6) 0;
}

/* UAT round 7 — col2 passe en `position: fixed` (idem sidebar) pour rester
   visible quand le main scrolle. Internal overflow-y: auto pour gérer le
   contenu plus haut que le viewport. `padding-top: 0` pour que le col2-header
   sticky touche le bord supérieur ; padding sides/bottom appliqué pour les
   children (form list, tabs list, etc.).
   UAT round 7 (2026-05-14) — `padding-bottom: var(--footer-height)` ajouté pour
   que le contenu scrollable de col2 ne se masque pas sous le footer in-app
   (qui passe en z-index 11 au-dessus de col2 et couvre les 72px du bas du
   viewport quand sticky-bottom). Sans ça, les derniers items de col2
   (ex: dernier onglet de paramètres) restent inaccessibles, cachés derrière
   le footer dark. */
.vocnc-app__col2 {
  grid-area: col2;
  position: fixed;
  top: 0;
  left: var(--col-width-sidebar);
  width: var(--col-width-list);
  height: 100vh;
  box-sizing: border-box;
  background: var(--app-col2-bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0 var(--space-4) var(--footer-height);
  gap: var(--space-3);
  overflow-y: auto;
  z-index: 4;
}

/* #439 — une carte de filtres (.vocnc-form) posée directement dans col2 hérite
   de `margin: var(--space-6) 0` : 48 px de vide qui, ajoutés à la réservation
   du footer, fabriquent une barre de défilement sans rien à faire défiler
   (653 px de scrollHeight pour 581 px de contenu, mesuré sur /responses).
   L'écart sous le header est déjà donné par le `gap` du col2. */
.vocnc-app__col2 > .vocnc-form {
  margin: 0;
}

/* UAT round 7 — header interne du col2 (titre du panneau : "Mes formulaires",
   "Filtres", "Paramètres", etc.). Sticky en haut du col2 pour rester visible
   quand col2 scrolle internement. Aligné en hauteur sur les autres headers.
   `margin: 0 -space-4` annule le padding latéral du col2 parent pour que la
   border-bottom du header s'étende jusqu'aux bords de col2. */
.vocnc-app__col2-header {
  position: sticky;
  top: 0;
  height: var(--header-height);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin: 0 calc(-1 * var(--space-4));
  padding: 0 var(--space-4);
  background: var(--app-col2-bg);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  z-index: 1;
}

/* Recette headers (2026-07-11) — titre du col2-header. Les vues posaient
   h2.vocnc-section__title, or cette classe n'est stylée QUE dans
   .vocnc-section__header : ici le h2 retombait sur le style UA (24px bold),
   wrappait sur 2 lignes et débordait sous la bande de 64px (builder, settings,
   form_fields). Aligné sur .vocnc-header__title (même rôle : titre de zone
   dans la ligne de headers 64px), single-line + ellipsis car la hauteur de
   bande est fixe. */
.vocnc-app__col2-header > :is(h1, h2, h3) {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  line-height: 1.4;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* UAT-G — Col 3 main des vues 3-pane. Single source of truth pour le padding,
   aligné avec .vocnc-main (2-pane). */
.vocnc-app__main {
  grid-area: main;
  padding: var(--space-6) var(--space-8);
  min-width: 0;
}

.vocnc-sidebar {
  /* UAT round 7 (2026-05-13) — passage de `position: sticky` à `position: fixed`.
     `sticky` refusait de prendre — vraisemblablement à cause d'un containing
     block implicite créé par un parent (`transform`, `contain`, ou la
     combinaison body-flex/grid-1fr). `fixed` court-circuite tout ça : la
     sidebar est ancrée au viewport indépendamment du flow.
     Le `grid-area: sidebar` reste pour signaler l'intention (la colonne 1 du
     grid garde sa largeur de 240px et réserve l'espace), même si le `fixed`
     retire la sidebar du flow grid. */
  grid-area: sidebar;
  position: fixed;
  top: 0;
  left: 0;
  width: 240px;
  height: 100vh;
  /* UAT round 7 (2026-05-13) — `box-sizing: border-box` REQUIS : sans ça,
     `content-box` (default) ajoute le padding (16px top + 16px bottom) au
     height: 100vh → sidebar fait 100vh+32px, déborde sous le viewport, et le
     tenant pill (poussé en bas via margin-top: auto) est clippé. */
  box-sizing: border-box;
  background: var(--app-shell-bg);
  color: var(--app-shell-fg);
  border-right: 1px solid var(--app-shell-border);
  display: flex;
  flex-direction: column;
  padding: var(--space-4) var(--space-3);
  gap: var(--space-4);
  overflow-y: auto;
  z-index: 10;
}

/* UAT round 7 — brand area du sidebar : hauteur uniforme avec les autres
   headers de l'app (col2-header + vocnc-header). Divider bottom matche
   visuellement la border-bottom du vocnc-header. */
.vocnc-sidebar__brand {
  height: var(--header-height);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  padding: 0 var(--space-3);
  border-bottom: 1px solid var(--app-shell-border);
  margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-3)) 0;
  /* margin negative compense le padding du parent .vocnc-sidebar pour que la
     brand area atteigne les bords gauche/droit sans gap visuel. */
  flex-shrink: 0;
}

.vocnc-sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1;
}

.vocnc-sidebar__nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  color: var(--app-shell-fg-muted);
  text-decoration: none;
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  border-radius: var(--radius-md);
  border-left: 2px solid transparent;
  transition: background 120ms ease, color 120ms ease;
}

.vocnc-sidebar__nav-item:hover {
  background: var(--app-shell-active-bg);
  color: var(--app-shell-fg);
}

/* Phase 02.4 — bump contrast WCAG AAA sur fond slate-900 :
   ember-200 (#EAC089) sur slate-900 = 11.3:1 (RESEARCH §10).
   Border ember-400 conservée pour le rappel visuel du jalon Ember (D-03). */
.vocnc-sidebar__nav-item--active {
  color: var(--ember-200);
  border-left-color: var(--ember-400);
  background: var(--app-shell-active-bg);
  font-weight: var(--fw-medium);
}

.vocnc-sidebar__nav-item svg {
  stroke: currentColor;
}

/* UAT round 7 (2026-05-13) — topbar sticky inspirée du sketch 006.
   `position: fixed` (cohérent avec la sidebar) — le grid réserve var(--header-height)
   sur la row 1 pour que le main commence bien sous le header.
   En 2-pane : left = 240px (clear sidebar) ; en 3-pane : left = 240+col-width-list
   (clear sidebar + col2). Hauteur strictement var(--header-height) pour l'alignement
   horizontal avec sidebar__brand et app__col2-header. */
.vocnc-header {
  grid-area: header;
  position: fixed;
  top: 0;
  left: var(--col-width-sidebar);
  right: 0;
  height: var(--header-height);
  box-sizing: border-box;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  padding: 0 var(--space-6);
  /* UAT round 7 (2026-05-14) — passage de flex-direction:column (2-row) à
     flex-direction:row (3 sections : burgers | title-group | actions).
     Pour le détail du raisonnement, voir application.html.erb (UAT round 7
     2026-05-14). Les anciens .vocnc-header__row sont supprimés du markup. */
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
  z-index: 5;
}

/* En 3-pane, le topbar démarre après sidebar + col2 (les 3 zones « header » du
   shell — sidebar__brand, col2-header, vocnc-header — partagent la même ligne). */
.vocnc-app--has-col2 .vocnc-header {
  left: calc(var(--col-width-sidebar) + var(--col-width-list));
}

/* UAT round 7 (2026-05-14) — wrapper title-group : eyebrow EMPILÉ au-dessus du
   h1, les deux left-aligned. Prend l'espace flex restant entre les burgers (à
   gauche, mobile-only) et les actions (à droite). */
.vocnc-header__title-group {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.vocnc-header__eyebrow {
  margin: 0;
  /* Truncation propre si breadcrumb très long (TENANT · Section · Subsection…). */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vocnc-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* UAT round 5 — page title dans le header authentifié (slot :header_left).
   Style sobre : taille H4 + weight medium. */
.vocnc-header__title {
  font-family: var(--font-sans);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0;
  line-height: 1.4;
  /* #525 — même troncature que `.vocnc-header__eyebrow` juste au-dessus, qui la
     portait seule. `.vocnc-header__title-group` a bien `min-width: 0` et les
     actions `flex-shrink: 0` : la BOÎTE du titre se réduisait donc comme il
     faut, mais le texte, lui, débordait par-dessus les actions — constaté sur
     /tenant_settings à 885 px, « Organisation » chevauchant
     « À jour / Annuler / Enregistrer ». Deux lignes du même groupe, un seul
     comportement. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vocnc-main {
  grid-area: main;
  /* UAT-G — aligné sur .vocnc-app__main pour padding consistent cross-routes. */
  padding: var(--space-6) var(--space-8);
  /* UAT round 7 (2026-05-13) — `overflow-y: auto` retiré : le scroll doit
     se faire au niveau du document (pas internal-main), pour que la sidebar
     `position: sticky; top: 0` fonctionne contre le scroll viewport. */
  min-width: 0;
}

/* UAT round 7 (2026-05-14) — état desktop par défaut : boutons toggle drawer
   mobile et backdrop cachés. PLACÉS AVANT la media query @max-width:768px
   pour que les rules mobiles override correctement (specificité égale →
   c'est l'ordre source qui tranche). */
.vocnc-header__mobile-toggle,
.vocnc-mobile-backdrop {
  display: none;
}

@media (max-width: 768px) {
  .vocnc-app,
  .vocnc-app--has-col2 {
    /* UAT round 7 (2026-05-14) — `.vocnc-app--has-col2` également resetée
       en single column (sans cet override, le 3-pane garde son grid-template
       3-cols sur mobile alors que sidebar/col2 sont display:none → grid
       laisse 240+360px de gouttière vide à gauche). */
    grid-template-columns: 1fr;
    /* Fix #367 — la ligne « notices » doit exister ici aussi. Sans elle, le
       `grid-area: notices` de .vocnc-app__notices ne trouve aucune ligne du nom :
       la spec Grid fait alors porter ce nom à toutes les lignes implicites, d'où
       une colonne implicite créée à droite, dimensionnée sur le contenu du
       message — la colonne 1fr était écrasée d'autant. */
    grid-template-areas:
      "header"
      "notices"
      "main"
      "footer";
  }

  /* UAT round 7 (2026-05-14) — sidebar et col2 passent en MODE DRAWER sur
     mobile (au lieu de display:none qui supprimait toute navigation). Par
     défaut off-screen via translateX(-100%) ; mobile-nav controller toggle
     les classes `vocnc-app--sidebar-open` / `vocnc-app--col2-open` qui
     remettent translateX(0). z-index >= backdrop pour passer au-dessus de
     l'overlay sombre quand ouvert. */
  .vocnc-sidebar {
    display: flex;          /* re-active vs display:none antérieur */
    transform: translateX(-100%);
    transition: transform 200ms ease;
    z-index: 20;            /* > backdrop (15) > col2 mobile (16) */
  }
  .vocnc-app--sidebar-open .vocnc-sidebar {
    transform: translateX(0);
  }

  .vocnc-app__col2 {
    display: flex;
    /* col2 sur mobile : drawer même UX que sidebar mais starts at left:0
       (sans offset sidebar, puisque sidebar est elle-même drawer). Largeur
       limitée à 320px pour que l'utilisateur voie un peu du main derrière. */
    left: 0;
    width: min(320px, 90vw);
    height: 100vh;
    transform: translateX(-100%);
    transition: transform 200ms ease;
    padding-bottom: var(--space-4); /* override du var(--footer-height) desktop */
    z-index: 16;
  }
  .vocnc-app--col2-open .vocnc-app__col2 {
    transform: translateX(0);
  }

  /* Backdrop semi-transparent visible quand un drawer est ouvert. Le hidden
     attribute est synchronisé par mobile-nav#syncBackdrop.
     UAT round 7 (2026-05-14) — `:not([hidden])` requis : la règle desktop
     `.vocnc-mobile-backdrop { display: none }` (au-dessus de cette media)
     gagne par défaut. On override via display:block UNIQUEMENT quand
     l'attribut hidden est absent (= drawer ouvert). Quand hidden=true, on
     retombe sur display:none → backdrop invisible et non-cliquable. */
  .vocnc-mobile-backdrop:not([hidden]) {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 15;
  }

  .vocnc-header,
  .vocnc-app--has-col2 .vocnc-header {
    /* Sidebar masquée → header part du bord gauche.
       UAT round 7 (2026-05-14) — padding latéral réduit (8px au lieu de 24px
       desktop) + gap entre items resserré (4px au lieu de 12px) pour rapprocher
       le burger du bord gauche et coller les 2 toggles burger + col2. */
    left: 0;
    padding: 0 var(--space-2);
    gap: var(--space-1);
  }
  .vocnc-main {
    padding: var(--space-4);
  }

  /* Boutons de toggle drawer dans le topbar — visibles uniquement sur mobile.
     UAT round 7 (2026-05-14) — margin-right retiré : redondant avec
     `.vocnc-header { gap: var(--space-1) }` du media block, et créait un
     décalage cumulé (8px margin + 4px gap = 12px entre toggles). */
  .vocnc-header__mobile-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    color: var(--fg);
    cursor: pointer;
    flex-shrink: 0;
  }
  .vocnc-header__mobile-toggle:hover,
  .vocnc-header__mobile-toggle:focus-visible {
    background: var(--bg-subtle);
    border-color: var(--border);
  }
  .vocnc-header__mobile-toggle svg {
    stroke: currentColor;
  }
}

/* Squelette layout auth (consommé Plan 06 — Devise auth pages) */
.vocnc-auth {
  background: var(--bg-subtle);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
}
.vocnc-auth__header {
  padding: var(--space-6) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}
.vocnc-auth__main {
  flex: 1;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-8) var(--space-4);
}

/* ============================================================
   Layout survey public — /r/:token (Phase 02.3 Plan 03, D-25)
   Mobile-first, RGPD-strict (no-Stimulus, no-cookie, SURV-01 / SURV-06).
   Le bouton submit `.vocnc-btn--primary` est l'unique élément Ember
   visible (D-03 inviolable). Footer D-12 reskinné en classes vocnc-*.
   ============================================================ */
.vocnc-survey-body {
  background: var(--bg-subtle);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  color: var(--fg);
  margin: 0;
}

.vocnc-survey {
  flex: 1;
  max-width: 480px;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4);
  /* Fix #525 — `box-sizing: border-box` REQUIS : le dépôt n'a aucun reset
     global, donc en content-box `width: 100%` + 2 × var(--space-4) donnait
     407 px de boîte pour un viewport de 375 — les 32 px de scroll horizontal
     mesurés le 28/08 sur TOUTES les pages du layout survey. Même mécanisme
     entre 640 et 704 px avec var(--space-8). */
  box-sizing: border-box;
}

@media (min-width: 640px) {
  .vocnc-survey {
    max-width: 640px;
    padding: var(--space-8);
  }
}

.vocnc-survey__brand {
  padding-block: var(--space-4);
  text-align: center;
  margin-bottom: var(--space-4);
}

.vocnc-survey__footer {
  max-width: 480px;
  margin: var(--space-8) auto 0;
  padding: var(--space-4);
  /* Fix #525 — même raison que .vocnc-survey ci-dessus. */
  box-sizing: border-box;
  border-top: 1px solid var(--border);
  text-align: center;
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}

.vocnc-survey__footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.vocnc-survey__footer-links a {
  color: var(--slate-600);
  text-decoration: none;
}

.vocnc-survey__footer-links a:hover {
  color: var(--slate-900);
  text-decoration: underline;
}

.vocnc-survey__footer .vocnc-eyebrow a {
  color: inherit;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
}

/* ============================================================
   Phase 02.4 — Footer dark variant in-app (D-02.4-01)
   Modificateur appliqué uniquement par application.html.erb (Plan 02
   passe `variant: "app"` au partial _footer_legal). La règle de base
   .vocnc-footer-legal reste claire pour les layouts publics
   (legal/auth/survey/marketing) — invariant T2 / regression UAT-01d.
   ============================================================ */
.vocnc-footer-legal--app {
  /* UAT round 7 (2026-05-13) — footer placé en row 3 de la grille .vocnc-app
     (colonne droite uniquement, sidebar restant visible à gauche).
     UAT round 7 (2026-05-14) — `position: relative; z-index: 11` pour que le
     footer passe AU-DESSUS de col2 (z-index 4, position fixed) en mode
     3-pane. Sans ça, col2 (fixed) couvre le footer en flow → footer invisible
     derrière col2 dans les 72px du bas du viewport (sticky-bottom case). */
  grid-area: footer;
  position: relative;
  z-index: 11;
  background: var(--app-shell-bg);
  color: var(--app-shell-fg);
  border-top: 1px solid var(--app-shell-border);
}

.vocnc-footer-legal--app a {
  color: var(--app-shell-fg);
}

.vocnc-footer-legal--app a:hover {
  color: var(--ember-200);
}

.vocnc-footer-legal--app .vocnc-footer-legal__brand {
  color: var(--app-shell-fg);
}

/* UAT round 7 (2026-05-14) — wordmark masqué dans la variante dark in-app.
   Raison : `.vocnc-wordmark` force `color: var(--fg)` (slate-900 / dark)
   qui devient INVISIBLE sur le footer slate-900 → un gros vide sur mobile
   (le span occupait sa hauteur sans rien afficher). Le wordmark est déjà
   omniprésent dans le shell (sidebar__brand) — le re-rendre dans le footer
   est redondant. Sur les layouts publics (light footer), le wordmark reste
   visible (couleur fg sombre sur bg clair). */
/* #310 — le séparateur suit le wordmark dans la MÊME règle : il ne sépare que
   lui de la mention RESILIENCE, masquer l'un sans l'autre laissait un « · »
   orphelin en tête de pied sur desktop (il n'était masqué que sous 768 px). */
.vocnc-footer-legal--app .vocnc-wordmark-link,
.vocnc-footer-legal--app .vocnc-footer-legal__sep {
  display: none;
}

/* UAT round 7 (2026-05-13) — alignement du footer in-app avec la colonne main.
   Sans cet override, le footer interne (.vocnc-footer-legal__inner) utilise
   `max-width: var(--container-max)` + `padding: 0 var(--container-gutter)` (24px),
   alors que la colonne main (.vocnc-main / .vocnc-app__main) utilise un padding
   horizontal de `var(--space-8)` (32px) sans max-width. Résultat : décalage
   visuel de 8px entre le footer et le contenu main, et centrage du footer sur
   les écrans larges. Override : aligner padding sur space-8, retirer max-width
   pour que le footer suive la largeur réelle de la colonne. Invariant T2
   préservé : la règle de base `.vocnc-footer-legal` (layouts publics) reste
   inchangée — seul le variant `--app` est ajusté. */
.vocnc-footer-legal--app .vocnc-footer-legal__inner {
  max-width: none;
  padding: 0 var(--space-8);
}

@media (max-width: 768px) {
  .vocnc-footer-legal--app .vocnc-footer-legal__inner {
    padding: 0 var(--space-4);
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Recette 2026-07-11 — état sélectionné de la carte formulaire (/forms col 2).
   La carte dont l'aperçu est affiché en col 3 est marquée visuellement. Pas
   d'Ember ici : la règle « 1 seul Ember par écran » réserve --accent au bouton
   « + Nouveau » (col 2). On utilise donc un fond subtil + une bordure neutre
   renforcée + une barre latérale --border-strong (signal neutre, pas Ember).
   ───────────────────────────────────────────────────────────────────────── */
.vocnc-q-card--selected {
  border-color: var(--border-strong);
  background: var(--bg-subtle);
  box-shadow: inset 2px 0 0 0 var(--border-strong);
}
/* ============================================================
   voc.nc — Components atomiques (Phase 02.3, D-07)
   Consomme tokens vocnc_design.css (LOCKED). BEM-like (D-09).
   RESERVED Ember : .vocnc-btn--primary, .vocnc-sidebar__nav-item--active,
   a:focus-visible (ring), .vocnc-stat__delta--up. Voir UI-SPEC §Ember.
   Règle inviolable : un seul élément Ember visible par écran.
   ============================================================ */


/* ─── Composant 1 : Card + variants (D-16) ─── */
.vocnc-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}

.vocnc-card--compact {
  padding: var(--space-4);
}

.vocnc-card--inverse {
  background: var(--slate-900);
  color: var(--slate-100);
  border-color: var(--slate-800);
}

.vocnc-card__header {
  /* UAT round 7 (2026-05-13) — passage de flex row à flex column.
     L'ancienne valeur `flex-direction: row + justify-content: space-between`
     ne convient à AUCUN des usages réels : tous les card__header empilent
     verticalement eyebrow / title / subtitle (api_tokens/new, imports/*,
     forms/*, shared_reports, admin/*, tenant_settings/*). 6 pages Devise
     contournaient avec `style="display:block"` inline — preuve que le
     défaut était mal calibré. Column par défaut + items align-items start. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}

/* #451 — en-tête portant une ACTION à droite du titre. Le défaut (column) reste
   celui de tous les autres en-têtes de carte ; ce modificateur ne sert qu'aux
   rares en-têtes qui doivent aligner un titre et un contrôle sur la même ligne.
   Il remplace le `style="flex-direction: row; …"` inline qui était jusqu'ici la
   seule façon de l'obtenir (scopes/_perimetres_section.html.erb, à rallier). */
.vocnc-card__header--split {
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
}

/* L'action garde sa largeur naturelle : un libellé coupé en deux lignes ferait
   du bouton un pavé à côté du titre. Filet, pas remède — le remède est un
   libellé court (recette du 26/08, cf. forms/edit.html.erb). */
.vocnc-card__header--split > .vocnc-btn {
  white-space: nowrap;
  flex-shrink: 0;
}

.vocnc-card__title {
  font-family: var(--font-sans);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0 0 var(--space-3) 0;
}

/* Sous-titre carte — tagline mutée sous le titre dans .vocnc-card__header
   (Phase 02.3 WR-02 — utilisé par api_tokens/new, imports/new, imports/show,
   shared_reports/access_code). Slate 600 caption — pas Ember (D-03). */
.vocnc-card__subtitle {
  font-size: var(--fs-caption);
  color: var(--slate-600);
  margin: 0 0 var(--space-3) 0;
}

.vocnc-card__body {
  color: var(--fg);
  font-size: var(--fs-body);
  line-height: var(--lh-body, 1.55);
}

.vocnc-card__footer {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Fix #213 — lanceur de campagne (campaigns/new). Ces trois classes étaient
   référencées par la vue sans être définies nulle part : le lien-card (<a>,
   inline par défaut) s'effondrait en bande étroite qui recouvrait son bouton
   « Lancer ». Card-rangée cliquable : contenu à gauche, action à droite. */
.vocnc-card-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.vocnc-card--interactive {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  text-decoration: none;
  transition: border-color var(--duration-fast) ease,
              box-shadow var(--duration-fast) ease;
}

.vocnc-card--interactive:hover {
  border-color: var(--slate-400);
  box-shadow: var(--shadow-md);
}

.vocnc-card__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}


/* ─── Composant 2 : Stat / KPI (D-17) ─── */
.vocnc-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.vocnc-stat__value {
  font-family: var(--font-mono);
  font-weight: var(--fw-medium);
  font-size: 32px;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
  line-height: 1.1;
  margin: 0;
}

.vocnc-stat__delta {
  font-size: var(--fs-caption);
  color: var(--slate-600);
  margin: 0;
}

.vocnc-stat__delta--up {
  color: var(--ember-600);
}

.vocnc-stat__delta--down {
  color: var(--danger);
}

.vocnc-stat--inverse .vocnc-stat__value {
  color: var(--slate-100);
}

.vocnc-stat--inverse .vocnc-stat__delta {
  color: var(--slate-200);
}


/* ─── Composant 3 : Eyebrow (D-22) ─── */
.vocnc-eyebrow {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  color: var(--slate-600);
  margin: 0;
}

.vocnc-eyebrow--inverse {
  color: var(--slate-200);
}

/* Chantier SEL (consensus période/contraste, retour de recette staging David,
   3e vague dataviz) — tokens dédiés aux charts Chart.js, lus par
   lib/chart_defaults.js via getComputedStyle DU CANVAS (cascade — jamais :root
   inconditionnellement, sinon les surcharges .vocnc-kpi-card--dark ci-dessous
   seraient toujours manquées). Valeurs claires ici, surchargées sous
   .vocnc-kpi-card--dark (voir bloc P4-C14 plus bas). Dérivés des tokens
   vocnc_design.css (LOCKED) — jamais un hex en dur (C12/R18). */
:root {
  --chart-line:            var(--slate-600);
  --chart-line-dim:        var(--slate-400);
  --chart-tick:            var(--slate-600);
  --chart-grid:            var(--slate-100);
  --chart-tooltip-border:  transparent;
}

/* I5 (04.1-VIZ-INTERACTIVITY-SPEC) — le canvas ne porte JAMAIS sa taille :
   il remplit un conteneur DÉDIÉ dimensionné (.vocnc-indicator-card__chart côté
   rapport, .vocnc-perimeter-card__sparkline / .vocnc-kpi-card--dark__sparkline
   côté cockpit) — le resize Chart.js se calcule sur ce parent. Le conteneur
   .vocnc-chart des 4 charts de série du rapport a disparu avec eux (5e vague,
   survivant unique : le chart de card indicateur est LA série du rapport). */
.vocnc-chart__canvas {
  width: 100%;
  height: 100%;
}

/* I18 — pill de filtre retirable (état canonique HTML, pas un état canvas
   fragile) affichée au-dessus de la card Résultats quand un drill-down
   clic->période est actif. */
.vocnc-filter-pill {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.vocnc-filter-pill__remove {
  color: var(--slate-600);
  text-decoration: none;
  font-weight: var(--fw-medium);
}

.vocnc-filter-pill__remove:hover {
  color: var(--fg);
  text-decoration: underline;
}


/* ─── Composant 4 : Grid responsive ─── */
.vocnc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}

.vocnc-grid--cols-4 {
  grid-template-columns: repeat(4, 1fr);
}

/* Recette staging (David, chantier 4 — gauntlet visuel rapport) : les 4 cards
   de séries temporelles (forms/report.html.erb) débordaient horizontalement
   du viewport (scrollbar bas de page à 1280px) — chaque `<canvas
   data-controller="chart">` a une taille intrinsèque (avant application du
   `responsive:true` de Chart.js) que Grid respecte via son `min-width: auto`
   par défaut sur les items, empêchant les 4 colonnes `1fr` de rétrécir
   sous cette largeur intrinsèque. `min-width: 0` restaure le comportement
   `1fr` attendu (colonnes réellement égales, jamais plus larges que leur
   part du conteneur) — cf. https://css-tricks.com/preventing-a-grid-blowout
   pattern, déjà nécessaire dès qu'un canvas/contenu non compressible est
   placé dans un enfant direct de grid. */
.vocnc-grid--cols-4 > * {
  min-width: 0;
}

@media (max-width: 1024px) {
  .vocnc-grid--cols-4 {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
}


/* Checkpoint (audit UX/design, majeur DESIGN-3) — grille clé/valeur pour une
   fiche détail en lecture seule (ex : campagnes/show, responses/show).
   La classe était déjà utilisée par ces deux vues sans jamais être définie
   (rendu par défaut du navigateur pour <dl>/<dt>/<dd>) — cette définition
   les répare toutes les deux sans y toucher. Pattern proche de
   .vocnc-q-card__meta (grid dt/dd) mais 2 colonnes fixes (label large,
   valeur qui s'étire) plutôt qu'auto/1fr compact. */
.vocnc-meta-grid {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-2) var(--space-6);
  margin: 0;
}

.vocnc-meta-grid dt {
  color: var(--fg-muted);
  font-size: var(--fs-caption);
}

.vocnc-meta-grid dd {
  margin: 0;
}


/* ─── Composant 5 : Section ─── */
.vocnc-section {
  margin-block: var(--space-8);
}

.vocnc-section__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-4);
  gap: var(--space-3);
  flex-wrap: wrap;
}

.vocnc-section__header h1,
.vocnc-section__header h2,
.vocnc-section__header .vocnc-section__title {
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0;
}

/* Conteneur d'actions à droite du header de section
   (filtre date + CTA "Exporter CSV", boutons "Créer un formulaire"…). */
.vocnc-section__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* D9 (passe C) — le bouton d'export et sa mention de portée. La mention doit
   être SOUS le bouton, pas à côté : dans .vocnc-section__actions (flex row),
   un <p> nu deviendrait un item de la rangée. Colonne dédiée, alignée à droite
   comme le bouton qu'elle qualifie. */
.vocnc-export {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
}

.vocnc-export__hint {
  margin: 0;
  max-width: 22rem;
  text-align: right;
}

/* Sous le plancher produit (DV-7 : 360 px), la rangée d'actions se replie :
   la mention repasse à gauche, comme le reste de la colonne. */
@media (max-width: 767px) {
  .vocnc-export {
    align-items: flex-start;
  }

  .vocnc-export__hint {
    text-align: left;
  }
}

/* Fil d'Ariane simple (forms/report — Plan 02 Task 2). */
.vocnc-breadcrumb {
  font-family: var(--font-sans);
  font-size: var(--fs-ui);
  color: var(--slate-600);
  margin-bottom: var(--space-2);
}

/* D12 (passe C, tour 2) — `.vocnc-breadcrumb a` A ÉTÉ RETIRÉE, PAS AJUSTÉE.
   Elle posait `color: var(--slate-600); text-decoration: none` — EXACTEMENT la
   couleur du conteneur, donc du <span> non cliquable — et sa spécificité (0,1,1)
   écrasait `.vocnc-link` (0,1,0) que le tour 1 venait de poser sur les 8 liens
   des 5 fils. La classe était présente et sans effet : le défaut D12(a) restait
   entier au style CALCULÉ, et le spec du tour 1, qui ne vérifiait que la
   présence de la classe, était vert dessus.

   Retirée plutôt que surspécifiée parce qu'elle n'a plus aucun consommateur :
   les 8 liens des 5 fils portent tous `.vocnc-link` (vérifié un par un), et
   `.vocnc-link:hover` couvre déjà ce que faisait `.vocnc-breadcrumb a:hover`.
   Monter la spécificité aurait gardé une règle qui ne sert qu'à être battue. */


/* D13a (passe C) — CONTENEUR DÉFILANT. `width: 100%` sur une table à 7 colonnes
   n'est pas une contrainte : au viewport plancher du produit (360 px, DV-7) la
   largeur intrinsèque des colonnes gagne, la table est peinte PAR-DESSUS la
   bordure de sa card et court jusqu'au bord de l'écran. Le document déborde
   alors horizontalement, et le navigateur applique un shrink-to-fit à TOUTE la
   page — mesuré à 57,8 % sur le rapport, 703 px de scrollWidth sur le cockpit.
   C'est la cause unique de huit constats mobiles de la passe C : les libellés
   « collés » (R153/R154) et le pied « trop petit » (R160) n'étaient que cette
   réduction. Le débordement est rendu au conteneur, qui le porte en défilement
   local — la page, elle, cesse de déborder.
   Même cause racine que l'issue #292 (tableau des périmètres).

   `max-width: 100%` est nécessaire EN PLUS de `overflow-x` : dans une grille ou
   un flex, la base de taille `auto` d'un enfant est sa largeur intrinsèque, et
   un `overflow-x: auto` seul ne l'empêcherait pas de pousser son parent.

   Preuve par mutation (V-C-13e, 2026-08-14) : `overflow-x: visible` fait tomber
   V-C-13a en nommant la cause — `doc_scroll` 627 px pour `doc_client` 360 px sur
   la page de rapport, table intrinsèque 585 px. */
.vocnc-table-scroll {
  overflow-x: auto;
  max-width: 100%;
}

/* ─── Composant 6 : Table (D-18, généralisation .vocnc-cookie-table) ─── */
.vocnc-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: var(--fs-ui);
  color: var(--fg);
  background: var(--bg-surface);
}

.vocnc-table__head {
  background: var(--bg-subtle);
}

.vocnc-table__head th {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-weight: var(--fw-medium);
  font-size: var(--fs-ui);
  color: var(--slate-600);
  border-bottom: 1px solid var(--border-strong);
  vertical-align: bottom;
}

.vocnc-table__row td {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
  vertical-align: top;
}

.vocnc-table__row:hover {
  background: var(--bg-subtle);
}

.vocnc-table__row--invalid {
  border-left: 3px solid var(--danger);
  background: #FFF7F6;
}

/* IN-07 — Lien sur première colonne d'une .vocnc-table__row pour remplacer
   l'ancien `onclick="window.location=..."` (a11y + CSP). Le lien hérite de
   la couleur slate-900 du td parent et reste lisible. Recette #357 (19/08) :
   plus d'underline au hover — la ligne entière est cliquable (#356) et signale
   déjà l'affordance (bg-subtle + pointer), le soulignement du seul titre était
   redondant. L'underline reste au focus clavier : c'est le repère visuel du
   porteur d'accessibilité dans un tableau dense. */
.vocnc-table__link {
  color: inherit;
  text-decoration: none;
}
.vocnc-table__link:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* #356 — ligne entière cliquable (contrôleur Stimulus row-link) : le curseur
   annonce la zone sensible étendue, le hover bg-subtle est déjà porté par
   .vocnc-table__row:hover. */
.vocnc-table__row--clickable {
  cursor: pointer;
}

/* Variante densité admin pour tableaux ledger 500+ lignes (D-29 Plan 07).
   Padding compressé 8/12 (au lieu de 12/16 base) + font-size caption (13 au lieu de 14)
   pour scroll efficace sur ledger SystemSmsCredit append-only.
   Hover slate-50 préservé (déjà sur .vocnc-table__row:hover, mais explicit ici
   pour éviter ambiguïté si la base change). */
.vocnc-table--admin .vocnc-table__head th,
.vocnc-table--admin .vocnc-table__row td {
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-caption);
}

.vocnc-table--admin .vocnc-table__head th {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.vocnc-table--admin .vocnc-table__row:hover {
  background: var(--bg-subtle);
}

/* Checkpoint (audit UX/design, mineur DESIGN-5) — variante densité pour les
   tableaux compacts (ex : correspondance référence <-> lien sur la
   restitution), remplace le style inline font-size: var(--fs-caption)
   dupliqué en vue. Pattern calqué sur .vocnc-table--admin ci-dessus. */
.vocnc-table--dense .vocnc-table__head th,
.vocnc-table--dense .vocnc-table__row td {
  font-size: var(--fs-caption);
}

/* Checkpoint (audit UX/design, mineur DESIGN-6) — troncature d'une cellule
   longue (ex : URL de réponse) avec ellipsis, remplace le style inline
   max-width/overflow/text-overflow/white-space dupliqué en vue. */
.vocnc-table__cell--truncate {
  max-width: 256px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* G2 (gauntlet indicateurs) — n accolé au score dans la table des formulaires
   actifs (« pas de score sans n »). Discret : plus petit et atténué, il ne vole
   pas la vedette au score. */
.vocnc-table__n {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}

/* Marqueur « fiabilité faible » (score sous le seuil de fiabilité de
   l'indicateur) — teinte ambre foncée --color-status-warning-strong
   (≥ 4,5:1 à 12 px sur ligne normale et survolée — D-QUID-7), taille caption,
   non emphatique. */
.vocnc-table__low-confidence {
  font-size: var(--fs-caption);
  color: var(--color-status-warning-strong);
}


/* ─── Composant 7 : Input + textarea + select (D-15) ─── */
.vocnc-input,
.vocnc-textarea,
.vocnc-select {
  display: block;
  width: 100%;
  height: 40px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.4;
  border-radius: var(--radius-md);
  box-sizing: border-box;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

/* Variante compacte pour les filtres inline (date-filter, alert_threshold). */
.vocnc-input--sm {
  height: 32px;
  padding: var(--space-1) var(--space-2);
  font-size: var(--fs-caption);
  width: auto;
}

/* Checkpoint (audit UX/design, mineur DESIGN-8) — largeur réduite pour un
   champ numérique isolé (ex : "Nombre de liens à générer"), remplace le
   style inline max-width dupliqué en vue. */
.vocnc-input--narrow {
  max-width: 240px;
}

.vocnc-textarea {
  height: auto;
  min-height: 96px;
  resize: vertical;
}

/* Recette 2026-07-11 (TÂCHE 3) — l'éditeur de champ (_field_form.html.erb) applique
   la classe `vocnc-input--textarea` à ses text_area (Question, choix, motifs FCR),
   mais cette classe n'était définie NULLE PART : les textarea héritaient donc du
   `height: 40px` de `.vocnc-input` et clampaient leur contenu multi-lignes à une
   seule ligne visible (« le contenu de certains champs n'est pas toujours visible »).
   On rétablit une hauteur auto pilotée par l'attribut rows, avec resize vertical et
   un min-height cohérent (~3 lignes). Le width reste 100% via `.vocnc-input`. */
.vocnc-input--textarea {
  height: auto;
  min-height: calc(3 * 1.4em + 2 * var(--space-2));
  resize: vertical;
  line-height: 1.4;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

.vocnc-input:focus-visible,
.vocnc-textarea:focus-visible,
.vocnc-select:focus-visible {
  border-color: var(--accent);
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}

.vocnc-input:disabled,
.vocnc-textarea:disabled,
.vocnc-select:disabled {
  background: var(--slate-50);
  color: var(--slate-600);
  cursor: not-allowed;
}


/* ─── Composant 8 : Input group (icon + input alignés) ─── */
.vocnc-input-group {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.vocnc-input-group .vocnc-input,
.vocnc-input-group .vocnc-select {
  flex: 1;
}


/* ─── Composant 9 : Form field (label + input + helper) ─── */
.vocnc-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* Phase 03 UAT round 7 — row 2-col pour grouper deux champs côte-à-côte
   (signup: name+legal_form, postal_code+city ; tenant_settings: idem).
   Sur écran étroit (<640px), les deux champs se replient en colonne. */
.vocnc-form__row--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
@media (max-width: 640px) {
  .vocnc-form__row--split {
    grid-template-columns: 1fr;
  }
}

.vocnc-form__field--checkbox {
  flex-direction: row;
  /* Checkbox centrée verticalement avec sa description (Phase 03.5 UAT). */
  align-items: center;
  gap: var(--space-2);
}

.vocnc-form__label--checkbox {
  font-weight: var(--fw-regular) !important;
  font-size: var(--fs-ui);
  line-height: 1.45;
  color: var(--fg);
  cursor: pointer;
  /* Annule les marges verticales de .vocnc-form__label (pensées pour un champ
     empilé label-au-dessus-input) : ici le label est inline à côté de la
     checkbox, toute marge verticale le désaligne. */
  margin: 0;
}
.vocnc-form__label--checkbox a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.vocnc-form__label--checkbox a:hover {
  color: var(--accent-hover);
}

/* Phase 03 UAT round 3 — sections de form (signup multi-section).
   <fieldset class="vocnc-form__section"> regroupe N fields liés
   (Compte / Organisation / Consentements). Pas de border par défaut
   pour rester sobre — le séparateur visuel est porté par le legend. */
.vocnc-form__section {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-8);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.vocnc-form__section:last-of-type {
  margin-bottom: var(--space-6);
}
.vocnc-form__section-title {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--fg-muted);
  font-weight: var(--fw-medium);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-2);
  width: 100%;
}

.vocnc-form__field label {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--fg);
}

.vocnc-form__field__helper,
.vocnc-form__helper {
  font-size: var(--fs-caption);
  color: var(--slate-600);
  /* Phase 03.5 UAT — marges verticales resserrées à 4px (sinon marge UA <p>). */
  margin: var(--space-1) 0;
}

/* Phase 03 UAT round 4 — feedback inline d'erreur de validation client-side
   (Stimulus password_match_controller). Style aligné sur .vocnc-form__helper
   mais coloré --danger pour signaler l'erreur sans agressivité. */
.vocnc-form__field-error {
  font-size: var(--fs-caption);
  color: var(--danger);
  margin: var(--space-1) 0 0;
  font-weight: var(--fw-medium);
}
.vocnc-input[aria-invalid="true"] {
  border-color: var(--danger);
}

/* Variante inline : aligne label + input + bouton sur une seule ligne
   (filtre date forms/report, alert_threshold dashboard). */
.vocnc-form--inline {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  /* UAT round 7 (2026-05-13) — `align-items: end` mal calibré : sur le widget
     alert_threshold (label eyebrow + input--sm 32px + span eyebrow + éventuel
     alert warning), les enfants se collaient au bas du flex row au lieu d'être
     centrés visuellement. Pas d'usage qui réclame un alignement bottom. */
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.vocnc-form--inline .vocnc-form__field {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
}
/* UAT round 7 (2026-05-13) — neutralise le `margin-bottom` hérité de
   `.vocnc-form label` (vocnc_layouts.css). En layout stacked le margin
   sert d'écart label↔input ; en row il décale le label d'~4px au-dessus
   du centre du flex row pendant que input et span eyebrow restent centrés.
   Visible sur tenant_settings#sms (widget alert_threshold) et forms#report
   (filtres date inline). */
.vocnc-form--inline .vocnc-form__field label {
  margin: 0;
}


/* ─── Composant 10 : Button variants (D-14, extend .vocnc-btn de vocnc_layouts.css) ─── */
.vocnc-btn--secondary {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
}

.vocnc-btn--secondary:hover {
  background: var(--slate-50);
}

.vocnc-btn--ghost {
  background: transparent;
  border: 0;
  color: var(--slate-600);
}

.vocnc-btn--ghost:hover {
  background: var(--slate-100);
  color: var(--slate-900);
}

.vocnc-btn--danger {
  background: var(--danger);
  color: #FFFFFF;
}

.vocnc-btn--danger:hover {
  opacity: 0.92;
}

.vocnc-btn--block {
  display: block;
  width: 100%;
}

.vocnc-btn--sm {
  padding: var(--space-1) var(--space-3);
  font-size: var(--fs-caption);
}

.vocnc-btn--lg {
  padding: var(--space-3) var(--space-6);
  font-size: var(--fs-body);
}

/* UAT round 6 (2026-05-13) — état disabled générique pour .vocnc-btn.
   Utilisé par signup-form-gate Stimulus controller pour bloquer le submit
   tant que les champs requis ne sont pas tous renseignés. Style sobre :
   opacity 50% (visible mais clairement inactif) + cursor not-allowed.
   pointer-events: none empêche le hover state et tous les events souris
   (les attribut natif disabled bloque déjà le clic ; doublon défensif
   au cas où le button serait remplacé par <a aria-disabled>). */
.vocnc-btn:disabled,
.vocnc-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}


/* ─── Composant 11 : Badge / Status pill (D-19) ─── */
.vocnc-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  line-height: 1.2;
}

.vocnc-badge--success {
  background: #EAF3EE;
  color: var(--success);
}

/* Recette #357 (19/08) — un badge qui est un LIEN (ex. « Correspondance
   disponible » sur /campaigns) doit se distinguer au survol de la navigation
   de ligne (#356), comme les boutons d'action : fond assombri à la manière
   de .vocnc-btn--ghost:hover. Ne cible que les <a> — les badges statiques
   (span) ne bougent pas. */
a.vocnc-badge--success:hover {
  background: #DCEBE3;
}

.vocnc-badge--warning {
  background: #FBF3E1;
  color: var(--warning);
}

.vocnc-badge--danger {
  background: #FCEDEB;
  color: var(--danger);
}

.vocnc-badge--neutral {
  background: var(--slate-50);
  color: var(--slate-600);
}

/* Phase 4 Plan 08 — Badge Organisation (catégorie unique par compte, KPI cockpit).
   Fond slate-900 (dark) pour distinguer visuellement la catégorie protégée (SCOP-03).
   Contraste : texte slate-50 sur fond slate-900 (ratio > 7:1). */
.vocnc-badge--org {
  background: var(--slate-900);
  color: var(--slate-50);
}

/* Phase 4 Plan 08 — Dropdown autocomplete scope (SCOP-02).
   Positionné en absolute via le Turbo Frame (scope_autocomplete_controller.js Plan 03).
   Recette 2026-08-04 (test 7) — sélecteur renommé `.vocnc-scope-autocomplete-panel`.
   Il valait `.vocnc-scope-autocomplete`, classe portée AUSSI par l'enveloppe du champ
   (_field_form.html.erb:90) : l'enveloppe héritait donc de `overflow: hidden` alors
   qu'elle est le bloc conteneur (`position: relative`, cf. règle plus bas) du panneau
   positionné sous elle → panneau rogné, invisible en toutes circonstances. Le
   `z-index: 50` du panneau n'y pouvait rien, le clipping par overflow prime. */
.vocnc-scope-autocomplete-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md, 0 4px 12px rgba(0,0,0,0.08));
  overflow: hidden;
}

.vocnc-scope-autocomplete__section {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.vocnc-scope-autocomplete__section:last-child {
  border-bottom: none;
}

.vocnc-scope-autocomplete__heading {
  display: block;
  padding: var(--space-2) var(--space-3);
  color: var(--fg-muted);
}

.vocnc-scope-autocomplete__item {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-ui);
  color: var(--fg);
  line-height: 1.4;
}

.vocnc-scope-autocomplete__item:hover {
  background: var(--bg-subtle);
}

.vocnc-scope-autocomplete__empty {
  padding: var(--space-3);
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  margin: 0;
}

.vocnc-scope-autocomplete__meta {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}

.vocnc-scope-autocomplete__create {
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
}

.vocnc-scope-autocomplete__create-btn {
  width: 100%;
  padding: var(--space-2) 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-size: var(--fs-ui);
  color: var(--accent);
  font-weight: var(--fw-medium);
}

.vocnc-scope-autocomplete__create-btn:hover {
  text-decoration: underline;
}

/* ─── Actions périmètre (SCOP-04) : conteneur + popover de confirmation ───
   Déplacé depuis les styles inline de _perimetres_section.html.erb : l'audit
   no-Tailwind (D-43) scanne uniquement app/views et matchait par erreur les
   valeurs CSS `flex-wrap` et `var(--shadow-md)`. Ces classes vivent ici (hors
   scan) et utilisent les mêmes tokens design. */
.vocnc-scope-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.vocnc-scope-action-popover {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-1));
  min-width: 280px;
  z-index: 10;
  padding: var(--space-3);
  background: var(--bg-page);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}


/* ─── Modale native <dialog> (recette staging 2026-06 — BUG 3) ───
   showModal() place le dialog dans le top-layer : au-dessus du footer (z-index:11)
   sans guerre de z-index, centré, scrollable, backdrop natif + fermeture Échap.
   Remplace la pop-in details/summary absolue qui passait sous le footer. */
.vocnc-modal {
  width: min(440px, calc(100vw - 2 * var(--space-4)));
  padding: 0;
  border: none;
  background: transparent;
}

.vocnc-modal::backdrop {
  /* slate-900 @ 45% — cohérent avec le backdrop du drawer (sketch 010). */
  background: rgba(15, 17, 23, 0.45);
}

.vocnc-modal__card {
  max-height: min(85vh, calc(100vh - 2 * var(--space-6)));
  overflow-y: auto;
  padding: var(--space-6);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

.vocnc-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.vocnc-modal__close {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1);
  border: none;
  background: none;
  color: var(--fg-muted);
  cursor: pointer;
  border-radius: var(--radius-md);
}

.vocnc-modal__close:hover {
  color: var(--fg);
  background: var(--slate-100);
}


/* ─── Composant 12 : Alert (D-20) ─── */
.vocnc-alert {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border-left: 3px solid var(--slate-400);
  background: var(--slate-50);
  color: var(--fg);
  font-size: var(--fs-body);
  line-height: var(--lh-body, 1.55);
}

.vocnc-alert--info {
  border-left-color: var(--slate-400);
  background: var(--slate-50);
}

.vocnc-alert--success {
  border-left-color: var(--success);
  background: #EAF3EE;
}

.vocnc-alert--warning {
  border-left-color: var(--warning);
  background: #FBF3E1;
}

.vocnc-alert--danger {
  border-left-color: var(--danger);
  background: #FCEDEB;
}

/* Checkpoint (audit UX/design, majeur DESIGN-1) — un lien nu dans une alerte
   héritait de la couleur bleu par défaut du navigateur (3e couleur hors
   palette voc.nc, en plus de --fg et --accent). Bénéficie à toute alerte
   contenant un link_to, pas seulement imports/show. */
.vocnc-alert a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}


/* ─── Composant 13 : Empty state (D-21 — pas d'icône, texte + CTA Ember unique) ─── */
.vocnc-empty-state {
  padding: var(--space-12) var(--space-4);
  text-align: center;
  max-width: 480px;
  margin: 0 auto;
}

.vocnc-empty-state__title {
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: var(--space-3) 0;
}

.vocnc-empty-state__body {
  color: var(--slate-600);
  margin: 0 0 var(--space-6) 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body, 1.55);
}


/* ─── Composant 14 : Icon utility (Lucide stroke 1.5 via initializer D-32) ─── */
.icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  vertical-align: middle;
  color: currentColor;
  flex-shrink: 0;
}

.icon--sm { width: 16px; height: 16px; }
.icon--md { width: 20px; height: 20px; }
.icon--lg { width: 24px; height: 24px; }
.icon--xl { width: 32px; height: 32px; }


/* ─── Composant 15 : .mono — utilitaire texte mono-tabular ─── */
/* Consommé par les colonnes numériques (Plan 02 dashboard tables, Plan 04
   forms tables, Plan 05 imports preview, Plan 07 admin ledger). */
.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}


/* ─── Composants survey (Plan 03 — /r/:token) ─── */
/* Scale field (CSAT 1-5 ou NPS 0-10) — pattern segment control accessible.
   Le radio natif est masqué (position absolue) et l'état checked est piqué via
   `:has(input:checked)` (Safari 15.4+, Chrome 105+, Firefox 121+) → background
   slate-50 + border slate-800. Pas de Stimulus (RGPD-strict SURV-01). */
/* Recette staging 2026-06 (mineur) — grille d'échelle en CSS Grid plutôt qu'en
   flex-wrap. En flex, les options de la DERNIÈRE ligne (ex. le « 10 » du NPS ou
   le « 5 » du CSAT en mobile) s'étiraient en pleine largeur (flex-grow) → cartes
   orphelines. Grid avec colonnes uniformes (auto-fit) garde un remplissage régulier
   ET aligne les items de la dernière ligne sur la largeur de colonne, sans étirement. */
.vocnc-scale {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(48px, 1fr));
  gap: var(--space-1);
}

.vocnc-scale--nps {
  grid-template-columns: repeat(auto-fit, minmax(40px, 1fr));
}

.vocnc-scale--nps .vocnc-scale__option {
  min-width: 40px;
}

.vocnc-scale__option {
  position: relative;
  flex: 1 1 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-ui);
  color: var(--fg);
  background: var(--bg-surface);
  transition: background 120ms ease, border-color 120ms ease;
}

.vocnc-scale__option input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-scale__option:hover {
  background: var(--slate-50);
}

.vocnc-scale__option:has(input:checked) {
  background: var(--slate-100);
  border-color: var(--slate-800);
  font-weight: var(--fw-medium);
}

/* Focus-visible sur l'input radio masqué — propage le ring sur le label parent
   pour conserver l'accessibilité (D-03 ring Ember). */
.vocnc-scale__option:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}

/* Ligne de choix (radio choices, boolean Oui/Non, cases du choix multiple).
   #453 — le choix multiple réutilise CETTE ligne plutôt qu'une classe à lui :
   une ligne de choix est le même composant, que son contrôle soit exclusif ou
   non (CLAUDE.md §Un même contrôle, un même rendu). Seul le sélecteur d'accent
   ci-dessous a dû s'élargir au type checkbox. */
.vocnc-radio {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  cursor: pointer;
  border-radius: var(--radius-md);
  transition: background 120ms ease;
}

.vocnc-radio:hover {
  background: var(--slate-50);
}

.vocnc-radio input[type="radio"],
.vocnc-radio input[type="checkbox"] {
  /* accent-color slate-800 — n'introduit pas d'Ember en plus du submit
     (D-03 inviolable : 1 Ember par écran = le bouton "Envoyer"). */
  accent-color: var(--slate-800);
}

/* Form layout extras pour /r/:token */
.vocnc-form__label {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--fg);
  display: block;
  /* Phase 03.5 UAT — respiration verticale : 8px au-dessus, 4px en dessous. */
  margin: var(--space-2) 0 var(--space-1);
}

.vocnc-form__fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.vocnc-form__fieldset legend {
  padding: 0;
  margin-bottom: var(--space-2);
}

.vocnc-form__actions {
  margin-top: var(--space-4);
}


/* ─── Composant 16 : focus-visible global accessibilité (D-03 ring) ─── */
/* Règle Ember réservée — chaque interactive non-button avec focus-visible
   reçoit le ring Ember 600 (cohérence Pattern 02.2). */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
  border-radius: var(--radius-sm);
}


/* ─── Composant 17 : Form Builder 3-pane composition (D-27, Plan 04 Wave 3a) ─── */
/* Composition responsive forms/edit : sidebar paramètres + canvas champs sortables.
   Mobile <1024 = stack 1-col. Desktop ≥1024 = 2-col (settings + fields).
   Préserve les wirings Stimulus sortable + form-preview (FORM-04 + FORM-07). */
/* Recette staging 2026-06 (mineur) — l'ancienne grille 2 colonnes du form builder
   (minmax(320px,380px) 1fr) est SUPPRIMÉE. Elle s'appliquait sur le même élément que
   le shell 3-pane `.vocnc-app--has-col2` (modifier "vocnc-app--has-col2 vocnc-form-builder")
   et écrasait sa grille `240px 360px 1fr` à partir de 1024px → à 1280px le main démarrait
   à ~530px, SOUS le col2 fixe (bord droit 600px) → chevauchement + labels tronqués.
   Le shell 3-pane est désormais l'unique propriétaire de la grille (cf vocnc_layouts.css). */

.vocnc-form-builder__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: var(--space-6);
}

.vocnc-form-builder__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 200ms ease;
}

.vocnc-form-builder__item:hover {
  border-color: var(--slate-400);
}

.vocnc-form-builder__handle {
  cursor: grab;
  background: transparent;
  border: 0;
  color: var(--slate-400);
  padding: var(--space-1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.vocnc-form-builder__handle:hover {
  color: var(--slate-800);
}

.vocnc-form-builder__handle:active {
  cursor: grabbing;
}

.vocnc-form-builder__item-meta {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.vocnc-form-builder__item-label {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0;
}

.vocnc-form-builder__item-actions {
  display: flex;
  gap: var(--space-1);
}

/* Form preview iframe (Phase 02.3 WR-03 — remplace .hidden + classes Tailwind
   "w-full border-0" injectées par form_preview_controller.js).
   L'iframe reçoit max-width:375px (viewport mobile) + height:667px pour un
   aperçu WYSIWYG du formulaire /r/:token.

   #524 — les deux classes `.vocnc-form-preview__pane` / `--collapsed` ont été
   RETIRÉES : l'aperçu s'ouvre désormais dans un <dialog>, dont l'ouverture et
   la fermeture sont portées par le navigateur. Plus aucun appelant. */
.vocnc-form-preview__iframe {
  width: 100%;
  border: 0;
  max-width: 375px;
  height: 667px;
  margin: 0 auto;
  display: block;
  /* #524 — tokens du design system au lieu des valeurs en dur d'origine. */
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* #524 — modificateurs de la pop-in d'aperçu. Aucune classe de modale n'est
   créée ici : `.vocnc-modal`, `::backdrop`, `__card`, `__header` et `__close`
   viennent du composant canonique ci-dessus, et `modal_controller.js` n'est pas
   touché. Même contrat que `.vocnc-modal--shot` (vocnc_marketing.css) : du
   contenu de largeur fixe, plus étroit que la modale par défaut.
   Largeur = 375 px d'iframe + les paddings de la carte. */
.vocnc-modal--preview {
  width: min(407px, calc(100vw - 2 * var(--space-4)));
}

.vocnc-modal__card--preview {
  padding: var(--space-4);
}

/* Sur un écran bas, l'iframe se comprime plutôt que d'imposer un second
   défilement à l'intérieur de la carte (elle-même en max-height 85vh). */
.vocnc-modal--preview .vocnc-form-preview__iframe {
  height: min(667px, 72vh);
}

/* Inline form helper pour button_to inline (Phase 02.3 Wave 3a) :
   neutralise le block-display par défaut des <form> Rails wrappant button_to. */
.inline-form {
  display: inline-block;
  margin: 0;
}


/* ─── Composant 18 : Form links (Phase 02.3 Plan 06 — Devise auth pages) ─── */
/* Liens secondaires sous les forms Devise (mot de passe oublié, créer un compte,
   renvoyer la confirmation). Slate 600 underline — pas Ember (D-03 inviolable :
   l'unique Ember est le bouton submit --primary --block au-dessus). */
.vocnc-form__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  text-align: center;
}

.vocnc-form__links a {
  color: var(--slate-600);
  font-size: var(--fs-ui);
  text-decoration: underline;
}

.vocnc-form__links a:hover {
  color: var(--slate-900);
}


/* ─── Composant 19 : Checkbox (Phase 02.3 Plan 06 — Devise sessions remember_me) ─── */
/* Checkbox accessible avec accent-color slate-800 — n'introduit pas d'Ember
   en plus du submit primary (D-03). Cohérent avec .vocnc-radio Plan 03. */
.vocnc-checkbox {
  width: 18px;
  height: 18px;
  accent-color: var(--slate-800);
  cursor: pointer;
}


/* ─── Composant 20 : Link secondaire (Phase 02.3 WR-01) ─── */
/* Lien secondaire générique slate-600 underline — utilisé par les vues optouts
   (confirmed, show, fallback_otp, fallback_verify_form) pour les actions
   secondaires sous le CTA principal. Cohérent avec .vocnc-form__links a et
   .vocnc-survey__footer-links a. Pas Ember (D-03 inviolable). */
.vocnc-link {
  color: var(--slate-600);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.vocnc-link:hover {
  color: var(--slate-900);
}


/* ─── Composant 21 : SMS counter state (Phase 02.3 IN-12) ─── */
/* Compteur de caractères du formulaire builder (sms_counter_controller.js).
   Couleur muted (slate-500) en état nominal, danger lorsqu'on dépasse 160 chars.
   Remplace les anciennes classes Tailwind text-gray-500 / text-red-600 dans
   le controller (Plan 09 Wave 4d — purge JS avec audit IN-12). */
.vocnc-sms-counter {
  color: var(--slate-500);
}

.vocnc-sms-counter--over {
  color: var(--danger);
}


/* ============================================================
   Phase 02.4 — Sidebar-scoped component overrides (D-02.4-01)
   Évite les composants light (slate-700 sur slate-900) illisibles
   dans la sidebar dark. Scope strict à .vocnc-sidebar pour ne pas
   leaker dans le main canvas (Col 3) qui reste blanc.
   ============================================================ */

.vocnc-sidebar .vocnc-btn--ghost {
  color: var(--app-shell-fg-muted);
  border-color: var(--app-shell-divider);
}

.vocnc-sidebar .vocnc-btn--ghost:hover {
  background: var(--app-shell-active-bg);
  color: var(--app-shell-fg);
  border-color: var(--app-shell-border);
}

.vocnc-sidebar .vocnc-badge--neutral {
  background: var(--app-shell-active-bg);
  color: var(--app-shell-fg);
  border-color: var(--app-shell-divider);
}

.vocnc-sidebar .vocnc-wordmark {
  color: var(--app-shell-fg);
}

/* ---------- Phase 02.4 — Tenant pill + drawer (D-02.4-01) ---------- */
.vocnc-tenant-pill {
  position: relative;
  margin-top: auto;          /* sticks to bottom inside flex sidebar */
}

.vocnc-tenant-pill__trigger {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) 10px;
  border: 1px solid var(--app-shell-divider);
  border-radius: var(--radius-md);
  background: var(--app-shell-active-bg);
  color: var(--app-shell-fg);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease);
}

.vocnc-tenant-pill__trigger:hover,
.vocnc-tenant-pill--open .vocnc-tenant-pill__trigger {
  background: rgba(255, 255, 255, 0.10);
  border-color: var(--app-shell-border);
}

/* Phase 02.4 FIX-03 (UI-REVIEW Pillar 2 FLAG) — active state quand on est sur
   /tenant_settings (la sidebar nav 5-items ne contient pas Settings, donc le
   pill devient l'unique point de repère "vous êtes ici"). D-03 respecté :
   l'Ember de l'écran shift de la sidebar nav vers la border ember-400 du pill. */
.vocnc-tenant-pill--current-page .vocnc-tenant-pill__trigger {
  border-color: var(--ember-400);
  background: var(--app-shell-active-bg);
}

.vocnc-tenant-pill__avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--ember-100);
  color: var(--ember-800);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--fw-medium);
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

.vocnc-tenant-pill__info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  text-align: left;
}

.vocnc-tenant-pill__name {
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--app-shell-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vocnc-tenant-pill__user {
  font-size: 11px;
  color: var(--app-shell-fg-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vocnc-tenant-pill__chevron {
  color: var(--app-shell-fg-muted);
  flex-shrink: 0;
  transition: transform var(--duration-fast) var(--ease);
}

.vocnc-tenant-pill--open .vocnc-tenant-pill__chevron {
  transform: rotate(180deg);
}

/* Drawer popup */
.vocnc-tenant-pill__drawer {
  position: absolute;
  bottom: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  background: var(--bg-surface);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* Phase 02.4 FIX-04 (MN-01) — `--shadow-lg` était indéfini (tokens existants :
     --shadow-sm/md/modal/focus). On utilise --shadow-modal (tier équivalent à
     popup floating) — sketch-011 V1 attend une élévation perceptible du drawer. */
  box-shadow: var(--shadow-modal);
  list-style: none;
  margin: 0;
  padding: var(--space-1);
  z-index: 50;
}

.vocnc-tenant-pill__drawer[hidden] {
  display: none;
}

.vocnc-tenant-pill__menuitem {
  display: block;
  width: 100%;
  /* UAT round 7 (2026-05-13) — `box-sizing: border-box` REQUIS : sans ça,
     `<a>` (default content-box) calcule sa largeur à 100% du parent + padding
     en plus, donc le hover bg déborde du drawer. `<button>` (Déconnexion)
     hérite déjà de border-box par défaut UA — d'où la dissymétrie observée. */
  box-sizing: border-box;
  padding: var(--space-2) var(--space-3);
  border: 0;
  background: transparent;
  text-align: left;
  font-family: inherit;
  font-size: 13px;
  color: var(--fg);
  border-radius: var(--radius-sm);
  text-decoration: none;
  cursor: pointer;
}

.vocnc-tenant-pill__menuitem:hover,
.vocnc-tenant-pill__menuitem:focus-visible {
  background: var(--bg-subtle);
  outline: none;
}

.vocnc-tenant-pill__menuitem--danger {
  color: var(--danger);
}

.vocnc-tenant-pill__menuitem-divider {
  height: 1px;
  margin: var(--space-1) 0;
  background: var(--border);
}

/* ============================================================
   Phase 02.4 (Plan 05) — Réponses index + détail (T7)
   ============================================================
   Append-only — succède à la section _tenant_pill (Plan 02). Aucune
   réécriture de classes existantes. */

/* Code block — réponse brute JSON sur /responses/:id (UI-SPEC §7.3) */
.vocnc-code-block {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg);
  overflow-x: auto;
  white-space: pre;
  margin: 0;
}

/* Kaminari pagination wrapper — sur /responses?page=N.
   Kaminari rend `<nav class="pagination">` ; on encadre dans .vocnc-pagination
   pour BEM-namespacer le styling sans avoir à override le partial Kaminari. */
.vocnc-pagination {
  margin-top: var(--space-4);
  display: flex;
  justify-content: center;
}

.vocnc-pagination .pagination {
  display: flex;
  gap: var(--space-1);
  list-style: none;
  padding: 0;
  margin: 0;
}

.vocnc-pagination .pagination a,
.vocnc-pagination .pagination span {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--fg);
  font-size: var(--fs-ui);
}

.vocnc-pagination .pagination a:hover {
  background: var(--bg-subtle);
}

.vocnc-pagination .pagination .current {
  background: var(--bg-subtle);
  font-weight: var(--fw-medium);
}


/* ───────────────────────────────────────────────────────────────────────────
   Phase 02.4 Plan 03 (T8) — Settings shell : tabs (Col 2) + topbar sticky
   + status indicator + coquille vide .vocnc-card--placeholder.
   Sketch 011 V1 manual save. UI-SPEC §6.2 / §6.3 / §6.5.
   D-03 (1 Ember par écran) : seul le bouton Enregistrer du topbar est
   `.vocnc-btn--primary`. Annuler est `--ghost` ; les liens d'onglets ne
   sont pas des boutons.
─────────────────────────────────────────────────────────────────────────── */

/* Coquille vide — Branding / Membres / Factures. */
.vocnc-card--placeholder {
  background: var(--bg-subtle);
  border-style: dashed;
  text-align: center;
}

/* Settings tab list (Col 2). */
.vocnc-settings-tabs__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.vocnc-settings-tabs__item {
  display: block;
  padding: var(--space-2) var(--space-4);
  color: var(--fg-muted);
  text-decoration: none;
  border-left: 2px solid transparent;
  font-size: 14px;
  transition: background var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease),
              border-color var(--duration-fast) var(--ease);
}

.vocnc-settings-tabs__item:hover {
  background: var(--app-col2-bg-strong);
  color: var(--fg);
}

.vocnc-settings-tabs__item--active {
  background: var(--bg-surface);
  color: var(--fg);
  border-left-color: var(--ember-600);
  font-weight: var(--fw-medium);
}

/* UAT round 7 (2026-05-14) — règles `.vocnc-settings-topbar` (+ __left/__right)
   et `.vocnc-settings-content` retirées. Causes :
   - vocnc-settings-topbar : remplacé par `.vocnc-header` global (slots
     :header_eyebrow / :header_left / :header_right) lors de l'unification
     du topbar UAT round 7. Plus aucune vue ne portait la classe.
   - vocnc-settings-content : ajoutait `padding: var(--space-4)` qui shadowait
     le `padding: var(--space-6) var(--space-8)` canonique de `.vocnc-app__main`.
     Suppression → tenant_settings adopte le padding partagé avec forms /
     campaigns / responses (cohérence cross-pages 3-pane).
   `.vocnc-settings-status` (+ __dot --clean/--dirty) reste : encore consommé
   par le slot :header_right de tenant_settings/show pour le feedback
   dirty/clean du save-state controller. */
.vocnc-settings-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 12px;
  color: var(--fg-muted);
}

.vocnc-settings-status__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  display: inline-block;
  /* Default state : clean (vert). Le fallback hex couvre l'absence du token --success. */
  background: var(--success, #2BAE5C);
}

.vocnc-settings-status__dot--clean {
  background: var(--success, #2BAE5C);
}

.vocnc-settings-status__dot--dirty {
  background: var(--ember-400);
}

/* ============================================================
   Phase 02.4 Plan 06 — Form builder 3-pane mode-aware (T10/T11/T12)
   UI-SPEC §5.3 (.vocnc-q-card), §5.4 (.vocnc-save-bar), §5.5 (.vocnc-segmented).
   Append-only — ne pas modifier les blocs existants au-dessus.
   ============================================================ */

/* ---------- Question card / Form card (.vocnc-q-card) ---------- */
.vocnc-q-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: border-color var(--duration-fast) var(--ease);
  text-decoration: none;
  color: inherit;
}

.vocnc-q-card:hover {
  border-color: var(--border-strong);
}

.vocnc-q-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.vocnc-q-card__title {
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  margin: 0;
}

/* <dl> meta bloc (browse mode) — grille label/value 2-col. */
.vocnc-q-card__meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px var(--space-3);
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  margin: 0;
}

.vocnc-q-card__meta dt {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
}

.vocnc-q-card__meta dd {
  margin: 0;
}

/* Variante <p> (picker card) — texte simple sans grille. */
.vocnc-q-card__meta-text {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  margin: 0;
}

.vocnc-q-card__actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.vocnc-q-card--picker {
  text-align: center;
  align-items: center;
}

.vocnc-q-card--question {
  list-style: none;
}

.vocnc-q-card__handle {
  cursor: grab;
  background: transparent;
  border: 0;
  padding: var(--space-1);
  color: var(--fg-muted);
}

.vocnc-q-card__handle:active { cursor: grabbing; }

/* ---------- Segmented control (.vocnc-segmented) ---------- */
/* 6-button picker T12. :has(input:checked) requires Chrome 105+, Safari 15.4+,
   Firefox 121+ — acceptable for the B2B SaaS dashboard target users. */
.vocnc-segmented {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 2px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 2px;
}

@media (max-width: 768px) {
  .vocnc-segmented { grid-template-columns: repeat(3, 1fr); }
}

.vocnc-segmented__item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  color: var(--fg-muted);
  transition: background var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease);
}

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

.vocnc-segmented__item:hover {
  background: var(--bg-surface);
  color: var(--fg);
}

.vocnc-segmented__item:has(input:checked) {
  background: var(--bg-surface);
  color: var(--fg);
  font-weight: var(--fw-medium);
  box-shadow: var(--shadow-sm);
}

/* ---------- Save bar collante (.vocnc-save-bar) ---------- */
.vocnc-save-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border-top: 1px solid var(--border);
  margin-top: auto;
}

/* Recette 2026-07-11 — save-bar plaquée en bas de la zone visible de col2.
   Contexte : .vocnc-app__col2 est `position: fixed; height: 100vh;
   display: flex; flex-direction: column; overflow-y: auto` (vocnc_layouts.css).
   La save-bar est `sticky; bottom:0; margin-top:auto`. Bug de recette : quand
   le contenu de col2 est court, `margin-top:auto` ne suffit pas à pousser la
   barre en bas car aucun enfant n'absorbe l'espace libre — elle flottait à
   mi-écran. Correctif : la card « Questions » (dernier bloc de contenu avant la
   save-bar) grandit pour occuper l'espace restant.
   On NE touche PAS au padding-top de col2 (piège vérifié : un padding-top y
   décale l'ancrage du col2-header sticky). */
.vocnc-form-builder__col2 .vocnc-form-builder__fields {
  flex: 1 1 auto;
}

/* #301/#162 — formulaire à 0 question : la carte n'a plus de liste à porter et
   l'étirement ci-dessus la transformait en pavé blanc (mesuré 538 px de carte
   pour 209 px de contenu à 1440x1400). La save-bar reste ancrée en bas par son
   propre `margin-top: auto` — vérifié en capture. */
.vocnc-form-builder__col2 .vocnc-form-builder__fields--empty {
  flex: 0 0 auto;
}

/* Recette 2026-07-11 (bis) — le padding-bottom 72px de col2 (réservation du
   footer in-app) remonte le point d'ancrage sticky de la save-bar : quand le
   footer est HORS écran (document non scrollé), la barre laissait un trou mort
   de 72px au-dessus du bord de la fenêtre. On supprime cette réservation
   uniquement quand une save-bar est présente (:has) : la barre s'ancre alors au
   vrai bas du viewport. Quand le footer entre à l'écran au scroll du document,
   le controller Stimulus `save-bar` la translate au-dessus de lui (le footer,
   z-index 11 > col2 z-index 4, la recouvrirait sinon). */
.vocnc-form-builder__col2:has(.vocnc-save-bar) {
  padding-bottom: 0;
}

.vocnc-save-bar__actions {
  display: flex;
  gap: var(--space-2);
}

/* ---------- Form builder layout helpers ---------- */
.vocnc-form-builder__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.vocnc-form-builder__add-btn {
  margin-top: var(--space-2);
  width: 100%;
}

/* Phase 02.4 FIX-05 (MN-02) — bouton "← Retour à la question" reset des styles
   <button> pour conserver l'apparence d'un eyebrow link (pas de border, pas de
   background, cursor pointer). Évite saut au top de page + URL #. */
.vocnc-form-builder__back-btn {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-align: left;
}

.vocnc-form-builder__back-btn:hover {
  text-decoration: underline;
}

.vocnc-form-builder__picker-grid {
  padding: var(--space-4);
}

/* Grid à 2 colonnes utilitaire — token-driven, BEM (D-43). */
.vocnc-grid--cols-2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}

/* Recette staging (David, chantier 2 — "afficher les graphiques sur toute la
   largeur de leur carte, identique au cockpit") — MÊME correctif déjà appliqué
   à .vocnc-grid--cols-4 ci-dessus : sans min-width:0, un item de grid respecte
   sa taille de contenu intrinsèque (min-width:auto par défaut) — le
   <canvas data-controller="chart"> des 4 charts d'évolution du rapport
   (forms/report.html.erb) empêchait sa colonne 1fr de se réduire à la largeur
   RÉELLE de la carte, laissant un espace résiduel plutôt que de remplir la
   card (le chart "flottait" au lieu de la remplir, contrairement au cockpit
   où .vocnc-cockpit-head/.vocnc-cockpit-perimeter portent déjà min-width:0).
   Cette classe est aussi utilisée par les cards indicateur (pas de canvas) —
   min-width:0 n'a aucun effet visible sur elles (déjà 100% de leur colonne). */
.vocnc-grid--cols-2 > * {
  min-width: 0;
}

/* Recette staging (David, chantier 4 — gauntlet visuel rapport) : aucun
   breakpoint mobile n'existait sur cette grille — sur un viewport 375px, les
   2 colonnes fixes écrasaient chaque card indicateur (forms/report.html.erb)
   ou shared_reports/show.html.erb à ~160px utiles, illisible (texte tronqué,
   graphiques Chart.js compressés). 1 colonne sous 768px, cohérent avec les
   autres breakpoints du fichier (.vocnc-grid--cols-4 bascule déjà à 1024px). */
@media (max-width: 768px) {
  .vocnc-grid--cols-2 {
    grid-template-columns: 1fr;
  }
}

/* ---------- Form list (browse mode + edit mode) ---------- */
.vocnc-form-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ---------- Form name input (Col 2 header edit mode) ---------- */
.vocnc-form-name-input {
  width: 100%;
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
}

/* UAT round 7 (2026-05-13) — règle .vocnc-app__col2-header consolidée dans
   vocnc_layouts.css (single-row sticky aligné sur var(--header-height)).
   .vocnc-app__col2-back garde son style ici (lien retour discret pour form_fields). */
.vocnc-app__col2-back {
  text-decoration: none;
  color: var(--fg-muted);
  font-size: var(--fs-caption);
  /* Recette headers (2026-07-11) — le lien retour peut porter un nom de
     formulaire variable (« ← Formulaire QA riche ») : sans nowrap il wrappait
     sur 2 lignes dans la bande de 64px. Cap à 55% pour laisser la place au
     titre (lui aussi ellipsisé, voir vocnc_layouts.css). */
  flex-shrink: 0;
  max-width: 55%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vocnc-app__col2-back:hover {
  color: var(--fg);
}

/* Recette headers (2026-07-11) — la règle .vocnc-form-builder__col2 est
   supprimée : son `padding: var(--space-3)` écrasait le `padding-top: 0` de
   .vocnc-app__col2 (vocnc_layouts.css), décalant le col2-header sticky à
   12px du haut — bande désalignée avec sidebar__brand / vocnc-header, et
   contenu scrollé visible dans les 12px au-dessus du header. gap et
   overflow-y étaient déjà identiques à la règle de base. */

.vocnc-form-builder__main {
  padding: var(--space-4);
  /* #301/#162 — la colonne doit pouvoir céder sa hauteur à l'état vide. */
  display: flex;
  flex-direction: column;
}

/* #301/#162 — l'éditeur col 3 d'un formulaire à 0 question ne portait que deux
   lignes ; la piste `1fr` de .vocnc-app laissait ~1 030 px de blanc EN DESSOUS.
   L'état vide occupe la piste et se centre : le vide devient symétrique et lu
   comme intentionnel. Mesuré après correctif : 32 px résiduels. */
/* #373 — `:not([hidden])` requis. form_builder_controller.js:27 pose bien
   [hidden] sur l'éditeur quand le mode passe à `add`, mais le `display: flex`
   ci-dessous est une déclaration d'AUTEUR : elle bat le `display: none` que la
   feuille du navigateur associe à [hidden]. L'état vide restait donc peint
   au-dessus du type picker, et le CTA cliquable sans effet. Le cas n'existe
   qu'à 0 question — seul état où .vocnc-empty-state est rendu.
   Mêmes piège et remède : vocnc_layouts.css:753, sidebar_overrides.css:136. */
.vocnc-form-builder__editor:not([hidden]):has(.vocnc-empty-state) {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}


/* ============================================================
   Phase 03.5 Plan 03.5-05 — AUTH-14 — Account / Security UI (2FA)
   QR code inline, secret base32 Mono, backup codes grid, callout
   warning. Consomme tokens vocnc_design.css. D-03 (1 Ember/écran)
   respecté côté ERB (CSS neutre — pas de override Ember).
   ============================================================ */

/* Espacement entre les cards de /account/security. */
.vocnc-account-security__card {
  margin-bottom: var(--space-6);
}

/* Statut "activée" — pill Slate, PAS Ember (D-03). */
.vocnc-account-security__status {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--slate-600);
  padding: var(--space-1) var(--space-3);
  background: var(--slate-100);
  border-radius: var(--radius-full);
}

/* Footer card 2FA on : actions empilées / wrap responsive. */
.vocnc-account-security__actions {
  flex-wrap: wrap;
  gap: var(--space-4);
}

.vocnc-account-security__disable-form {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Texte Mono générique pour secrets / IDs / codes (vocnc-design — un
   secret = identifiant technique = Geist Mono). */
.vocnc-mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* QR code wrapper — centré, largeur bornée pour lisibilité scan. */
.vocnc-qr {
  display: flex;
  justify-content: center;
  margin: var(--space-4) 0;
}

.vocnc-qr svg {
  width: 200px;
  height: 200px;
}

/* Secret base32 fallback — bloc Mono encadré, sélectionnable. */
.vocnc-secret {
  display: inline-block;
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--slate-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-ui);
  letter-spacing: 0.08em;
  word-break: break-all;
  user-select: all;
}

/* Callout informational — variant warning (Slate, PAS Ember : c'est de
   l'information, pas une action). */
.vocnc-callout {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  font-size: var(--fs-ui);
  margin-bottom: var(--space-4);
}

.vocnc-callout p {
  margin: 0;
}

.vocnc-callout--warning {
  background: var(--ember-50);
  border-color: var(--ember-200);
  color: var(--ember-800);
}

/* Backup codes grid — 2 colonnes, Mono, lecture tabulaire. */
.vocnc-backup-codes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--space-4);
  margin: var(--space-4) 0;
}

@media (max-width: 480px) {
  .vocnc-backup-codes {
    grid-template-columns: 1fr;
  }
}

.vocnc-backup-code {
  display: block;
  padding: var(--space-2) var(--space-3);
  background: var(--slate-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-ui);
  letter-spacing: 0.06em;
  text-align: center;
  user-select: all;
}


/* ============================================================
   Phase 4 — Forms & Cockpit (Plan 03 — D-DASH-01bis)
   Bibliothèque de composants Phase 4 : tokens, indicateurs,
   cockpit, génération de liens, bannière AUTH-08.
   Consomme --color-status-* de vocnc_design.css.
   Règle inviolable (INDICATEURS-SPEC §1.5) : aucun hex en dur.
   Règle Ember : exception étoiles Star Rating uniquement.
   ============================================================ */


/* ─── P4-C1 : Bannière avertissement email AUTH-08 ─── */
/* Bandeau persistant sous la topbar — layout application.html.erb uniquement.
   Pas sur survey/legal/auth. Unique élément Ember de la zone topbar quand
   présent (D-03). CSS verbatim UI-SPEC P4-06. */
.vocnc-banner--warning {
  background: var(--ember-50);
  border-bottom: 1px solid var(--ember-200);
  color: var(--slate-800);
  padding: var(--space-2) var(--space-6);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-ui);
}

/* Icône Lucide alert-triangle 16px — Ember 600 (D-03 exception bandeau) */
.vocnc-banner--warning__icon {
  color: var(--accent);
  flex-shrink: 0;
}

/* Lien « Renvoyer le lien de confirmation » — Ember inline (D-03 unique) */
.vocnc-banner--warning__cta {
  color: var(--accent);
  text-decoration: underline;
  font-weight: var(--fw-medium);
  margin-left: var(--space-2);
}


/* ─── P4-C2 : Autocomplete périmètres (scope) ─── */
/* Dropdown organisé 2 sections + création nouvelle entrée.
   Stimulus scope_autocomplete_controller.js. Pas d'Ember (D-03). */
.vocnc-scope-autocomplete {
  position: relative;
}

.vocnc-scope-autocomplete__input-wrapper {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  transition: border-color 120ms ease;
}

.vocnc-scope-autocomplete__input-wrapper:focus-within {
  border-color: var(--border-strong);
}

.vocnc-scope-autocomplete__input-wrapper input {
  border: 0;
  outline: 0;
  flex: 1;
  font-family: var(--font-sans);
  font-size: var(--fs-ui);
  color: var(--fg);
  background: transparent;
}

.vocnc-scope-autocomplete__dropdown {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  right: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  z-index: 50;
  max-height: 320px;
  overflow-y: auto;
}

.vocnc-scope-autocomplete__section-label {
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--fg-muted);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.vocnc-scope-autocomplete__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-size: var(--fs-ui);
  color: var(--fg);
  transition: background 100ms ease;
}

.vocnc-scope-autocomplete__item:hover {
  background: var(--slate-50);
}

.vocnc-scope-autocomplete__item-annotation {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}

.vocnc-scope-autocomplete__create {
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  cursor: pointer;
  transition: background 100ms ease;
}

.vocnc-scope-autocomplete__create:hover {
  background: var(--slate-50);
}

/* Recette 2026-07-11 (TÂCHE 4) — aperçu live de la question avec [périmètre]
   substitué par le nom du périmètre choisi. Le textarea conserve le marqueur ;
   seule cette ligne affiche le rendu final. Encadré discret Slate (pas d'Ember). */
.vocnc-scope-preview {
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--slate-50);
  border-left: 3px solid var(--slate-400);
  border-radius: var(--radius-sm);
  color: var(--fg);
  font-style: italic;
}


/* ─── #346 — L'HISTOGRAMME A DISPARU (arbitrage Mehdi du 31/08) ───────────
   `.vocnc-histogram`, `__bar`, `__bar--critical/neutral/excellent`, `__axis` et
   `__label` habillaient les 11 barres par note du NPS et de l'eNPS. Le détail
   par note a été retiré des SEPT indicateurs : la barre de répartition à trois
   segments et sa clé chiffrée sont la seule restitution de la distribution.
   Aucun appelant ne subsiste — les garder aurait laissé un habillage prêt à
   l'emploi pour une réintroduction silencieuse. Le contre-garde-fou est dans
   spec/views/indicator_card_distribution_parts_spec.rb. */


/* ─── P4-C4 : Barres de distribution horizontales (CSAT 5, CES détail) ─── */
/* Barres horizontales libellé + pourcentage. Couleurs status. Pas d'Ember. */
/* A2b / C8 — la colonne de libellés se dimensionne sur SON bloc.
   Elle valait 3 rem en dur, pour tout le monde. Les libellés CSAT
   (« 1 — Très insatisfait ») y tenaient sur TROIS lignes en drapeau irrégulier :
   défaut de lisibilité, et cause mécanique des 138,7 px d'écart entre la carte
   CSAT et la carte CES du rapport (donc du blanc de pied de la vitrine).
   Élargir la constante aurait réparé CSAT en dégradant CES et Star, dont les
   libellés font un caractère : ils auraient hérité d'une colonne vide de 137 px
   prise sur leur piste.
   La grille résout les deux d'un coup : `max-content` dimensionne la colonne sur
   le PLUS LONG libellé DU BLOC, et le plancher de 3 rem garantit que les blocs
   à libellés courts ne bougent pas d'un pixel. `display: contents` sur la rangée
   fait des trois cellules des enfants directs de la grille — c'est ce qui aligne
   les pistes entre rangées, ce qu'un empilement de flex ne sait pas faire.
   Les deux surcharges inline (`min-width` des motifs FCR et des modalités PMF)
   continuent de fonctionner : elles posent un plancher sur la piste de colonne. */
.vocnc-distribution-bars {
  display: grid;
  grid-template-columns: minmax(3rem, max-content) minmax(0, 1fr) max-content;
  align-items: center;
  row-gap: var(--space-2);
  column-gap: var(--space-3);
}

.vocnc-distribution-bars__row {
  display: contents;
}

.vocnc-distribution-bars__label {
  font-size: var(--fs-caption);
  font-family: var(--font-mono);
  color: var(--fg-muted);
  text-align: right;
}

.vocnc-distribution-bars__track {
  /* La colonne 2 de la grille porte l'élasticité (`minmax(0, 1fr)`) ; la piste
     n'a plus à la réclamer en flex. Le `minmax(0, …)` est ce qui l'autorise à
     rétrécir sous sa taille de contenu quand la card se replie à 375 px. */
  height: 8px;
  background: var(--slate-100);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.vocnc-distribution-bars__fill {
  height: 100%;
  border-radius: var(--radius-full);
  transition: width 300ms ease;
}

.vocnc-distribution-bars__pct {
  width: 2.5rem;
  font-size: var(--fs-caption);
  font-family: var(--font-mono);
  color: var(--fg-muted);
  text-align: left;
}


/* ─── P4-C5 : Section cockpit (encadré fond --bg-subtle) ─── */
/* Encadré de section du cockpit : eNPS, catégories de périmètre.
   Fond --bg-subtle distinctif du reste (fond blanc col 3). */
.vocnc-cockpit-section {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  margin-bottom: var(--space-8);
}

.vocnc-cockpit-section__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}


/* ─── P4-C6 : Caveat pied de card (non supprimable) ─── */
/* Texte metadata systématique sous chaque card agrégée.
   INDICATEURS-SPEC §4.3 : jamais de score agrégé sans caveat.
   padding --space-2 uniquement (UI-SPEC espacement). */
.vocnc-caveat {
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  margin-top: var(--space-3);
}


/* ─── P4-C7 : Formulaire gelé (FORM-IMMUT — 1er token généré) ─── */
/* État read-only des champs après 1er token généré. Fond --slate-50,
   bordure --border, texte --fg (pas de grisé fort). */
.vocnc-form-frozen fieldset,
.vocnc-form-frozen input,
.vocnc-form-frozen textarea,
.vocnc-form-frozen select {
  background: var(--slate-50);
  border-color: var(--border);
  color: var(--fg);
  cursor: default;
  pointer-events: none;
}

.vocnc-form-frozen .vocnc-form-builder__item {
  opacity: 0.75;
  cursor: default;
}

.vocnc-form-frozen .vocnc-form-builder__handle {
  cursor: default;
  pointer-events: none;
}


/* ─── P4-C8 : Rendu lisible des réponses (VERB-02, /responses/:id) ─── */
/* <dl> stylisé pour rendu question→réponse. Résout les clés jsonb vers
   libellés FormField (avec fallback clé brute si champ supprimé). */
.vocnc-response-detail {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}

.vocnc-response-detail dt {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin-bottom: var(--space-1);
}

.vocnc-response-detail dt.is-deleted {
  color: var(--fg-subtle);
  font-size: var(--fs-caption);
}

.vocnc-response-detail dd {
  font-size: var(--fs-body);
  color: var(--fg);
  margin: 0;
  line-height: 1.6;
}


/* ─── P4-C9 : Zone résultats génération de liens (P4-01) ─── */
/* Textarea scrollable + bouton copier header. max-height 240px, overflow-y auto.
   font-mono --fs-caption pour les URLs. Pas d'Ember sur le textarea. */
.vocnc-generation-output {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.vocnc-generation-output__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3);
  background: var(--slate-50);
  border-bottom: 1px solid var(--border);
}

.vocnc-generation-output__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--fg-muted);
}

.vocnc-generation-output__textarea {
  width: 100%;
  max-height: 240px;
  overflow-y: auto;
  padding: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--fg);
  background: var(--bg-surface);
  border: 0;
  resize: none;
  display: block;
  box-sizing: border-box;
}

.vocnc-generation-output__footer {
  display: flex;
  justify-content: flex-end;
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
}


/* ─── P4-C10 : Étoiles Star Rating (exception Ember — UI-SPEC P4-08) ─── */
/* 5 étoiles Lucide `star`. Remplissage Ember au survol et à la sélection.
   Exception Ember autorisée : l'étoile est la métaphore universelle.
   Touch target ≥44px. Stimulus star_rating_controller.js. */
.vocnc-star-rating {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.vocnc-star-rating__star {
  cursor: pointer;
  color: var(--border);
  transition: color 120ms ease, transform 100ms ease;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  padding: 0;
}

/* État survolé et sélectionné — exception Ember pour les étoiles (D-03) */
.vocnc-star-rating__star--hovered,
.vocnc-star-rating__star--selected {
  color: var(--accent);
}

.vocnc-star-rating__star:hover {
  transform: scale(1.1);
}

.vocnc-star-rating__star svg {
  width: 32px;
  height: 32px;
  pointer-events: none;
}


/* ─── P4-C11 : Rangée NPS 0-10 (11 boutons, mobile 2 lignes) ─── */
/* 11 boutons en rangée. Mobile <480px : 2 lignes (0-5, 6-10) via flex-wrap.
   Touch target min 44px. Sélection : --slate-900 fond + --fg-inverse texte.
   Jamais d'Ember sur la sélection (règle indicateurs). */
/* Fix #163 — grille à colonnes égales : en flex-wrap + flex-grow, les modalités
   rejetées en dernière ligne s'étiraient (4-5 deux fois plus larges que 1-2-3 à
   375 px), biaisant l'instrument vers le haut. En grid, chaque cellule occupe
   une colonne identique quelle que soit la ligne. minmax(48px, 1fr) préserve
   la cible tactile ≥ 44 px (SURV-02). */
.vocnc-nps-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
  gap: var(--space-1);
}

.vocnc-nps-row__btn {
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--fs-ui);
  background: var(--bg-surface);
  color: var(--fg);
  transition: background 120ms ease, border-color 120ms ease;
  user-select: none;
}

.vocnc-nps-row__btn input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-nps-row__btn:has(input:checked),
.vocnc-nps-row__btn--selected {
  background: var(--slate-900);
  border-color: var(--slate-900);
  color: var(--fg-inverse);
  font-weight: var(--fw-medium);
}

.vocnc-nps-row__btn:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}

.vocnc-nps-row__labels {
  display: flex;
  justify-content: space-between;
  margin-top: var(--space-1);
}

.vocnc-nps-row__label {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}


/* ─── P4-C12 : Rangée CES 1-7 (7 boutons) ─── */
/* 7 boutons CES en rangée. Touch target min 44px. Même règle Ember (pas d'Ember). */
.vocnc-ces-row {
  display: flex;
  gap: var(--space-1);
}

.vocnc-ces-row__btn {
  flex: 1;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--fs-ui);
  background: var(--bg-surface);
  color: var(--fg);
  transition: background 120ms ease, border-color 120ms ease;
  user-select: none;
}

.vocnc-ces-row__btn input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-ces-row__btn:has(input:checked),
.vocnc-ces-row__btn--selected {
  background: var(--slate-900);
  border-color: var(--slate-900);
  color: var(--fg-inverse);
  font-weight: var(--fw-medium);
}

.vocnc-ces-row__btn:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}


/* ─── P4-C13 : Choix PMF (3 options + option exclue) ─── */
/* 3 boutons radio pleine largeur (48px hauteur). 4e option `Je ne l'utilise plus`
   stylisée gris (--slate-200 fond, --fg-subtle texte), cliquable mais exclue
   du calcul. Pas d'auto-advance sur PMF. */
/* Recette PR #493 (26/08/2026) — CE BLOC N'EST PLUS PROPRE AU PMF.
   `/r/:token` rendait « choisir dans une liste » de DEUX façons : ces grandes
   options cliquables pour le PMF, et une petite case + libellé (.vocnc-radio)
   pour Choix unique, Choix multiple et Oui / Non. Même geste, deux rendus —
   exactement ce que le CLAUDE.md interdit (§Un même contrôle, un même rendu).
   Les quatre partagent désormais CE rendu, d'où le renommage `pmf-choices` →
   `choices`. Seul `--excluded` reste propre au PMF (4e option hors calcul).
   Bénéfice mesurable au passage : 48 px de cible tactile au lieu d'une case de
   18 px, sur un formulaire majoritairement rempli au téléphone. */
.vocnc-choices {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.vocnc-choices__option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--fs-body);
  color: var(--fg);
  background: var(--bg-surface);
  transition: background 120ms ease, border-color 120ms ease;
  user-select: none;
}

.vocnc-choices__option input[type="radio"],
.vocnc-choices__option input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-choices__option:has(input:checked) {
  background: var(--slate-900);
  border-color: var(--slate-900);
  color: var(--fg-inverse);
  font-weight: var(--fw-medium);
}

/* #462 — le radio est masqué (:562) et rien ne le remplaçait : le PMF était le
   SEUL contrôle de /r/:token sans indicateur de focus clavier, là où NPS (:475),
   CES (:530), CSAT (:738), FCR (:782) et Star (:883) en ont un. Même anneau
   qu'eux, au caractère près. WCAG 2.4.7. */
.vocnc-choices__option:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}

/* Option 4e `Je ne l'utilise plus` — exclue du calcul, styled distinct */
.vocnc-choices__option--excluded {
  background: var(--slate-100);
  color: var(--fg-subtle);
  border-color: var(--border);
}

/* #449 — l'écart coché/non coché du 4e choix se réduisait à un pas de gris
   (slate-100 → slate-200) et à une nuance de texte, quand les trois autres
   options passent au noir plein. Le radio natif étant masqué, il ne restait
   aucun autre indice : le répondant qui choisit « Je ne l'utilise plus » croyait
   sa réponse refusée — alors qu'elle est enregistrée et restituée, seulement
   exclue du calcul du score (Sean Ellis). Le traitement secondaire est conservé,
   c'est le sens de --excluded ; l'état coché se lit désormais à la coche et à la
   bordure, plus au seul fond. La gouttière est réservée AU REPOS pour que le
   libellé ne saute pas au clic. */
.vocnc-choices__option--excluded::before {
  content: "";
  width: 1em;
  flex-shrink: 0;
  text-align: center;
}

.vocnc-choices__option--excluded:has(input:checked) {
  background: var(--slate-200);
  color: var(--fg);
  border-color: var(--border-strong);
  font-weight: var(--fw-medium);
}

.vocnc-choices__option--excluded:has(input:checked)::before {
  content: "✓";
}


/* ─── P4-C11 : Variante tabs génération (P4-01) ─── */
/* Segmented control 2-3 onglets equal-width, underline 2 px Ember sur l'actif.
   Distinct du .vocnc-segmented standard (box-shadow) : ici underline Ember.
   Utilise :has() CSS — Chrome 105+, Safari 15.4+, Firefox 121+ (B2B target ok). */
.vocnc-segmented--mode {
  grid-template-columns: repeat(2, 1fr);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  padding: 0;
  border-radius: 0;
  margin-bottom: var(--space-4);
}

.vocnc-segmented--mode.vocnc-segmented--mode-sms {
  grid-template-columns: repeat(3, 1fr);
}

.vocnc-segmented--mode .vocnc-segmented__item {
  border-radius: 0;
  padding: var(--space-3) var(--space-4);
}

.vocnc-segmented--mode .vocnc-segmented__item:has(input:checked) {
  background: transparent;
  box-shadow: none;
  border-bottom: 2px solid var(--accent);
  margin-bottom: -1px;
  color: var(--fg);
  font-weight: var(--fw-medium);
}

/* Panels de mode — CSS-only toggle via :has() sur le wrapper .vocnc-generation */
.vocnc-generation__panel--refs {
  display: none;
}

.vocnc-generation:has(input[name="mode"][value="references"]:checked) .vocnc-generation__panel--count {
  display: none;
}

.vocnc-generation:has(input[name="mode"][value="references"]:checked) .vocnc-generation__panel--refs {
  display: block;
}

/* ─── P4-C12 : Tooltip (auth gate P4-01) ─── */
/* Tooltip positionné en absolu sous le bouton désactivé.
   Activé par tooltip_controller.js (mouseenter/focusin → show, mouseleave/focusout → hide).
   Contient la raison du blocage + ghost "Renvoyer le lien". */
.vocnc-tooltip {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  z-index: 100;
  min-width: 240px;
  max-width: 320px;
  padding: var(--space-3) var(--space-4);
  background: var(--slate-900);
  color: var(--fg-inverse);
  border-radius: var(--radius-md);
  font-size: var(--fs-caption);
  line-height: 1.5;
  box-shadow: var(--shadow-md);
}

.vocnc-tooltip p {
  margin: 0 0 var(--space-2) 0;
}

.vocnc-tooltip .vocnc-btn--ghost {
  color: var(--fg-inverse);
  border-color: var(--slate-600);
  font-size: var(--fs-caption);
  padding: var(--space-1) var(--space-2);
  height: auto;
  min-height: unset;
}

.vocnc-tooltip .vocnc-btn--ghost:hover {
  background: var(--slate-700);
}

/* Checkpoint (audit UX/design, mineur DESIGN-7) — conteneur positionné du
   couple bouton désactivé + .vocnc-tooltip (auth gate P4-01), remplace le
   style inline "position: relative; display: inline-block;" dupliqué à
   chaque occurrence (imports/new.html.erb : mode compteur + mode références). */
.vocnc-tooltip-wrapper {
  position: relative;
  display: inline-block;
}


/* ─── P4-C14 : CSAT 1-5 rangée (Plan 04-12 INDIC-02) ─── */
/* 5 boutons CSAT, labels courts, touch target min 44px.
   Sélection : --slate-900 fond + --fg-inverse texte (jamais Ember — règle indicateurs). */
/* Fix #163 — même correction que .vocnc-nps-row : 5 colonnes égales, plus
   d'étirement de dernière ligne.
   Fix #520 — `minmax(0, 1fr)` et non `1fr` : `1fr` vaut `minmax(auto, 1fr)`,
   dont le plancher est la taille min-content de la piste. Le mot insécable
   « insatisfait » y dépassait la largeur disponible à 375 px, et les cinq
   pistes se calaient dessus — la rangée débordait du cadre. Le calcul du
   commentaire d'origine vérifiait la cible tactile, jamais que le contenu
   tenait dans la piste. `.vocnc-nps-row` y échappait grâce à son plancher
   FIXE (`minmax(48px, …)`), que min-content ne peut pas forcer. */
.vocnc-csat-row {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-1);
}

.vocnc-csat-row__btn {
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  background: var(--bg-surface);
  color: var(--fg);
  transition: background 120ms ease, border-color 120ms ease;
  user-select: none;
  padding: var(--space-2) var(--space-1);
  text-align: center;
}

.vocnc-csat-row__btn input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-csat-row__num {
  font-family: var(--font-mono);
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
}

.vocnc-csat-row__label {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  line-height: 1.3;
  /* Fix #520 — autorise la coupure de « insatisfait » plutôt que le débordement :
     `minmax(0, …)` retire le plancher, encore faut-il que le mot puisse se couper. */
  overflow-wrap: anywhere;
}

.vocnc-csat-row__btn:has(input:checked) {
  background: var(--slate-900);
  border-color: var(--slate-900);
  color: var(--fg-inverse);
  font-weight: var(--fw-medium);
}

.vocnc-csat-row__btn:has(input:checked) .vocnc-csat-row__label {
  color: var(--slate-400);
}

.vocnc-csat-row__btn:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}


/* ─── P4-C15 : FCR Oui/Non rangée (Plan 04-12 INDIC-04) ─── */
/* 2 boutons larges Oui/Non côte-à-côte. Touch target 56px hauteur.
   Sélection : --slate-900 fond (jamais Ember — règle indicateurs). */
.vocnc-fcr-row {
  display: flex;
  gap: var(--space-3);
}

.vocnc-fcr-row__btn {
  flex: 1;
  min-height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  background: var(--bg-surface);
  color: var(--fg);
  transition: background 120ms ease, border-color 120ms ease;
  user-select: none;
}

.vocnc-fcr-row__btn input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-fcr-row__btn:has(input:checked) {
  background: var(--slate-900);
  border-color: var(--slate-900);
  color: var(--fg-inverse);
}

.vocnc-fcr-row__btn:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
}

/* ─── P4-C15b : FCR motif + verbatim conditionnels (Plan 04-17 INDIC-04 gap-closure) ─── */
/* Masque le motif et le verbatim par défaut ; les révèle uniquement si "Non" est coché.
   CSS-only via :has(input[value="false"]:checked) — sans Stimulus (SURV-01 inviolable, RGPD). */
.vocnc-fcr-motif,
.vocnc-fcr-verbatim {
  display: none;
  margin-top: var(--space-4);
}

.vocnc-fcr-container:has(input[value="false"]:checked) .vocnc-fcr-motif,
.vocnc-fcr-container:has(input[value="false"]:checked) .vocnc-fcr-verbatim {
  display: block;
}

/* Barres de motifs FCR dans le rapport indicateur */
.vocnc-indicator-card__fcr-motifs {
  margin-top: var(--space-4);
}

.vocnc-indicator-card__fcr-motifs .vocnc-eyebrow {
  margin-bottom: var(--space-2);
}


/* ─── P4-C16 : Star Survey CSS-only (Plan 04-12 INDIC-07) ─── */
/* 5 étoiles Lucide, CSS-only (sans Stimulus — SURV-01 inviolable).
   Exception Ember : remplissage --accent sur sélection et survol.
   Technique : DOM ordre 5→1 + flex-direction: row-reverse = rendu visuel 1→5.
   :has(input:checked) + ~ sibling selector = fill des étoiles 1 à N choisie. */
.vocnc-star-survey {
  display: flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: var(--space-1);
}

.vocnc-star-survey__star {
  cursor: pointer;
  min-width: 48px;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.vocnc-star-survey__star input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.vocnc-star-survey__icon {
  width: 32px;
  height: 32px;
  color: var(--border);
  fill: none;
  stroke: currentColor;
  transition: color 120ms ease, fill 120ms ease;
  pointer-events: none;
}

/* Sélection : fill Ember sur l'étoile choisie + toutes les étoiles de valeur inférieure
   (= sibling DOM suivants dans l'ordre inversé 5→1 → étoiles visuellement à gauche). */
.vocnc-star-survey__star:has(input:checked) .vocnc-star-survey__icon,
.vocnc-star-survey__star:has(input:checked) ~ .vocnc-star-survey__star .vocnc-star-survey__icon {
  color: var(--accent);
  fill: var(--accent);
  stroke: var(--accent);
}

/* Fix #181 (recette Phase 4, passe du 05/08) — aperçu au survol repris.
   Avant : le reset était déclenché par le survol du GROUPE (donc aussi dans
   les gaps entre étoiles → la sélection se grisait un instant à chaque
   traversée — flicker), et il était plus spécifique que le fill au survol
   (avec 4 étoiles cochées, survoler l'étoile 2 laissait l'étoile 1 grise).
   Maintenant les deux règles ne s'activent que si une ÉTOILE est réellement
   survolée, et le fill (préfixé du même :has) surclasse le reset. */

/* 1) Une étoile est survolée → reset de toutes les icônes (sélection incluse).
   Le .vocnc-star-survey__star intermédiaire est nécessaire à la spécificité :
   (0,5,0), au-dessus des DEUX règles de sélection (0,3,1) et (0,4,1). */
.vocnc-star-survey:has(.vocnc-star-survey__star:hover) .vocnc-star-survey__star .vocnc-star-survey__icon {
  color: var(--border);
  fill: none;
  stroke: var(--border);
}

/* 2) …puis fill Ember de l'étoile survolée + étoiles de valeur inférieure
   (siblings DOM suivants dans l'ordre inversé 5→1). */
.vocnc-star-survey:has(.vocnc-star-survey__star:hover) .vocnc-star-survey__star:hover .vocnc-star-survey__icon,
.vocnc-star-survey:has(.vocnc-star-survey__star:hover) .vocnc-star-survey__star:hover ~ .vocnc-star-survey__star .vocnc-star-survey__icon {
  color: var(--accent);
  fill: var(--accent);
  stroke: var(--accent);
}

.vocnc-star-survey__star:has(input:focus-visible) {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(166, 100, 17, 0.20);
  border-radius: var(--radius-sm);
}


/* ─── P4-C17 : Icône dans .vocnc-alert (Plan 04-12 TOKN-05) ─── */
/* Bandeau anonymat avec icône Lucide lock inline. */
/* #468 — même mise en page pour les deux bandeaux à icône de /r/:token. Deux
   modificateurs et non un seul : `--optout` s'affiche aussi sur un formulaire
   nominatif, où surveys_spec.rb « ne promet l'anonymat nulle part » interdit la
   chaîne « anonym » (#461). */
.vocnc-alert--anonymous,
.vocnc-alert--optout {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}

/* #468, recette du 26/08 — deux bandeaux consécutifs sur /r/:token se
   touchaient : rien n'espace les enfants de .vocnc-card, et .vocnc-alert ne
   porte aucune marge. Scopé à la page publique, où le cas se produit. */
.vocnc-survey .vocnc-alert + .vocnc-alert {
  margin-top: var(--space-3);
}

.vocnc-alert__icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--slate-600);
  margin-top: 2px;
}


/* ─── P4-C18 : Card indicateur (DASH-01bis, Plan 13) ─── */
/* Card de rapport par indicateur : headline + distribution + sparkline + caveats.
   Règle : aucun score sans distribution (T-04-37 — inviolable).
   Couleurs via --color-status-* uniquement (INDICATEURS-SPEC §1.5). */
.vocnc-indicator-card {
  display: flex;
  flex-direction: column;
  /* #346 — le rapport écartait ses blocs de --space-4, le cockpit de --space-2.
     Le cockpit fait référence (directive David du 28/08). */
  gap: var(--space-2);
}

/* Recette staging (David, chantier 4 — gauntlet visuel rapport) : dans une
   grille 2 colonnes, chaque ROW s'aligne sur la card la plus haute (grid
   stretch par défaut) — un indicateur avec moins de contenu (ex. Star sans
   2e verbatim ni motifs) laissait un grand vide blanc sous son caveat pied,
   pendant que la card voisine plus riche (ex. PMF) remplissait toute la
   hauteur. Le pied méthodologique colle désormais au bas de la card
   (`margin-top: auto` sur un flex column) — alignement strict de grille
   (barre de qualité), jamais de vide visuel entre le contenu et le bord. */
/* #346 — L'ANCRAGE PORTE SUR LE DERNIER CAVEAT, PAS SUR LE PIED.
   La règle visait `footer.vocnc-caveat` : elle couvrait les cards qui rendent un
   pied méthodologique, donc toutes… sauf la card MASQUÉE, qui n'en a pas (elle
   rend un `<p class="vocnc-caveat">` en dernier). Ce cas était tenu par
   `.vocnc-perimeter-card > .vocnc-caveat:last-child`, disparue avec sa famille —
   mesuré à la reprise : 219,6 px de vide sous le dernier caveat d'une card eNPS
   masquée, étirée par sa voisine (card_fill_visual_spec, V-D-1).
   Le sélecteur reste resserré sur l'enfant direct ET dernier : une card rend
   aussi une bannière RGPD en tête et un caveat de fiabilité, et plusieurs marges
   `auto` en flexbox se partagent l'espace libre à parts égales — c'est ce qui
   scindait le vide en deux blocs au milieu de la card. */
.vocnc-indicator-card > .vocnc-caveat:last-child {
  margin-top: auto;
}

/* Recette staging (David, chantier 4 — gauntlet visuel rapport) : un nombre
   impair d'indicateurs (7 en MVP — NPS/CSAT/CES/FCR/PMF/Star/eNPS) laisse la
   dernière card seule sur sa ligne dans la grille 2 colonnes
   (forms/report.html.erb, shared_reports/show.html.erb) — grand vide blanc à
   droite, "carte vide" au sens de la barre de qualité (densité maîtrisée).
   La dernière card, quand elle est seule sur une ligne impaire, s'étire sur
   les 2 colonnes plutôt que de laisser un flanc vide. */
.vocnc-grid--cols-2 > .vocnc-indicator-card:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

/* R1 (VIZ-STANDARDS, chantier C) — ligne message titre-message ("SO WHAT"),
   sous l'eyebrow, AU-DESSUS du chiffre-clé. Générée par forms_helper#indicator_message.
   Réutilisée par les head cards cockpit sombres (--inverse) ET les cards
   rapport/perimeter cockpit (fond clair, couleur par défaut). */
.vocnc-indicator-card__message {
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0;
  line-height: 1.3;
}

/* #346 — `.vocnc-indicator-card__message--inverse` a été retirée : la surface
   se déclare UNE fois, sur l'article, et la couleur de titre sur fond sombre est
   portée par `.vocnc-kpi-card--dark .vocnc-indicator-card__message`
   (band_scale.css). Un modificateur d'élément qui répète une information déjà
   portée par le bloc est une seconde source de vérité. */

/* Question label — contexte, démoté sous le message (R1/R2) */
.vocnc-indicator-card__question {
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);
  color: var(--fg-muted);
  margin: 0;
  line-height: 1.4;
}

/* Bandeau de tête : score + delta + n (DASH-01bis) */
.vocnc-indicator-card__bandeau {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Score headline — Geist Mono tabular-nums (UI-SPEC rôle "Score par indicateur").
   #346 — LE SEUL CHIFFRE-CLÉ DU DÉPÔT. Il en existait trois familles ; celle-ci
   survit. La couleur passe par une CLASSE dérivée de `headline_status_suffix`
   (mécanisme du cockpit) et non plus par un style inline dérivé de
   `headline_color_token` (mécanisme du rapport) : même table de seuils, deux
   implémentations, une seule survit. Les surcharges de fond sombre vivent dans
   band_scale.css, sous `.vocnc-kpi-card--dark`. */
.vocnc-indicator-card__score {
  font-family: var(--font-mono);
  font-size: var(--fs-h2);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}

/* E10 — MÊME table de seuils sur les trois surfaces (headline_status_suffix,
   dérivé de forms_helper#headline_color_token) : plus de bandes locales
   divergentes (cockpit 0/30/50/75 vs rapport 0/30/70 pour NPS). */
.vocnc-indicator-card__score--critical  { color: var(--color-status-critical); }
.vocnc-indicator-card__score--warning   { color: var(--color-status-warning); }
.vocnc-indicator-card__score--fair      { color: var(--color-status-fair); }
.vocnc-indicator-card__score--neutral   { color: var(--color-status-neutral); }
.vocnc-indicator-card__score--good      { color: var(--color-status-good); }
.vocnc-indicator-card__score--excellent { color: var(--color-status-excellent); }
.vocnc-indicator-card__score--empty     { color: var(--fg-muted); }

/* E7 — % 4-5 étoiles à côté de la moyenne (Star Rating, "jamais l'un sans l'autre") */
.vocnc-indicator-card__score-secondary {
  font-family: var(--font-mono);
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
  /* D13c (passe C) — au plancher de 360 px, le mot « étoiles » passait SEUL à
     la ligne : « · 83,3 % 4-5 » puis « étoiles ». Un couple valeur/unité ne se
     sépare pas — c'est un seul jeton de lecture. */
  white-space: nowrap;
}

/* B1 / DV-9 — la règle .vocnc-indicator-card__delta a été SUPPRIMÉE avec le chip
   qu'elle habillait : le delta ne vit plus que dans le <h3> (indicator_message),
   identiquement sur les 3 surfaces. */

/* B3 / DV-10 — bloc du contrôle de fenêtre de calcul du cockpit. Il remplace
   trois `style="display: flex…"` inline et deux liens-boutons fantômes ; la
   marge de pied reprend celle qu'ils portaient (--space-6). */
.vocnc-cockpit-window { margin-bottom: var(--space-6); }

/* #346 — `.vocnc-indicator-card__window-empty` et `.vocnc-indicator-card__meta--compact`
   ont été RETIRÉES, faute d'appelant :
   - `__window-empty` habillait le bloc « Aucune réponse sur … » rendu en tête de
     card à n = 0. Le lot de libellés du 15/08 ne garde qu'UNE ligne portant le
     compte de réponses, celle du bandeau — ce bloc y disait la même chose une
     seconde fois, et la card de périmètre l'affichait littéralement deux fois ;
   - `__meta--compact` ramenait le `n` de la head card à 11 px, seule des trois
     surfaces à le faire. Le composant unique n'a plus de raison de le rapetisser
     sur une seule surface. */

/* « 360 réponses » (A1-0 §2.11) — le CHIFFRE en mono tabular-nums (R14), le mot
   en sans : « n = 360 » était du jargon de statisticien. */
.vocnc-indicator-card__meta {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
}
.vocnc-indicator-card__meta-n {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.vocnc-kpi-card--dark .vocnc-indicator-card__meta { color: rgba(255, 255, 255, 0.70); }

/* Marqueur « fiabilité faible » remonté dans le bandeau (A1-0 §2.11). Token
   DURCI --color-status-warning-strong (7,61:1 sur blanc) — jamais
   --color-status-warning (3,66:1, sous le 4,5:1 exigé à 12 px). Même règle que
   .vocnc-table__low-confidence (D-QUID-7), même point de décision côté helper. */
.vocnc-indicator-card__low-confidence-marker {
  color: var(--color-status-warning-strong);
  font-weight: var(--fw-medium);
}
.vocnc-kpi-card--dark .vocnc-indicator-card__low-confidence-marker {
  color: var(--color-status-warning-on-dark);
}

/* ============================================================
   RÉGLETTE DE REPÈRES — .vocnc-band-scale (A1-0, gauntlet visuel)
   Remplace le libellé de bande inline sur les 3 surfaces (DV-1).
   Palette de STATUT uniquement — TOUS les intervalles sont peints depuis
   B2/DV-8, un seul à pleine opacité (voir le paragraphe B2 ci-dessous).
   Fluide en pourcentages : aucun breakpoint de structure, le composant
   rétrécit avec sa card. Cas dimensionnant = piste de 228 px MESURÉE (cockpit
   au VIEWPORT PLANCHER DE 360 px, DV-7 / UI-01 ; c'est la valeur de
   BAND_SCALE_WORST_TRACK_PX). ⚠ Ce commentaire annonçait 243 px (cockpit à
   375 px) : il n'avait pas suivi la bascule au plancher de 360 px, et un dev
   qui recalculait une marge depuis lui se trompait de 15 px — soit 50 fois la
   marge réelle de la paire la plus serrée du produit (CES 90→100 : 0,3 px).
   B2 / DV-8 — TOUS les intervalles sont peints (courant à pleine opacité, les
   autres à 30 %) et la rangée de noms sous la piste a disparu : les noms sont
   RÉVÉLÉS au survol de leur intervalle, ou tous à la fois au focus clavier /
   tactile. Hauteur du bloc : 8 (marqueur) + 10 (piste) + 4 (graduations)
   + 4 + 14 (bornes) = 40 px, marge de pied 12 px → 52 px ajoutés, soit 12 de
   moins qu'avant B2 (84 px gagnés sur un rapport à 7 indicateurs).

   ── DIFFÉRENCIATION D'AVEC LA MICRO-BARRE DE DISTRIBUTION (défaut R5) ───────
   Le code sépare les deux palettes (V15) ; l'œil, lui, voyait deux barres
   horizontales de MÊME couleur, MÊME arrondi et MÊME largeur à 50 px l'une de
   l'autre sur la head card sombre — une échelle de repères et une répartition
   de répondants, rien pour les distinguer. La couleur ne peut pas trancher :
   le segment de la réglette DOIT valoir exactement le token du score (V14) et
   la palette de distribution est hors de cette passe. La différence est donc
   portée par la GÉOMÉTRIE, sur trois canaux mesurables :
     1. hauteur   — piste 10 px vs distbar 6 px (rapport 1,67) ;
     2. forme     — la réglette est une RÈGLE : coins droits (2 px), elle a des
                    bouts ; la distbar reste une PILULE (radius-full), elle a
                    des caps. C'est la différence la plus lisible au premier
                    coup d'œil, et elle survit au daltonisme ;
     3. proximité — la marge passe de la tête au pied : la réglette se colle au
                    score qu'elle qualifie et s'éloigne de la distribution.
                    Hauteur ajoutée : 52 px, MESURÉE sur les 32 relevés de
                    dev/captures-B/mesures-dom.json (V18 : ≤ 52). Ce commentaire
                    disait « inchangée (64 px, V12 ≤ 66) » à 19 lignes d'un
                    autre qui disait 52 : c'est le 64 qui était périmé.
   Un quatrième canal existait : la réglette montrait toujours de la piste
   inactive de part et d'autre de son unique segment. B2 le TRANSFORME au lieu
   de le tuer — la piste est peinte de bout en bout, mais à DEUX niveaux
   d'opacité (un segment plein, N segments à 30 %), là où la distbar est saturée
   sur 100 % de sa largeur. Et B2 en ajoute deux : la réglette porte un rang de
   graduations et un rang de chiffres (la distbar n'en a aucun), et elle porte
   toujours un marqueur triangulaire au-dessus. Les trois canaux sur lesquels R5
   a été signé sont intacts ; la différenciation est remesurée en spec visuel
   dans le cas dur (card NPS du cockpit, même token des deux côtés).
   ============================================================ */
.vocnc-band-scale {
  /* R5 — regroupement par proximité : rien au-dessus (la réglette appartient au
     score), une respiration en dessous (elle n'appartient PAS à la distribution).
     B2 — la respiration de pied passe de --space-2 à --space-3 : c'est elle qui
     absorbe le calque de révélation, qui vit HORS du flux (top: 100 %). Le calque
     fait 14 px pour 12 px de marge : l'intrusion dans le bloc suivant est de 2 px.
     Position relative : le calque s'ancre dessus. */
  position: relative;
  margin-top: 0;
  margin-bottom: var(--space-3);
  width: 100%;
}

/* La réglette est un arrêt de tabulation (DA-21). Son anneau de focus vient de
   la règle GLOBALE `[tabindex]:focus-visible` (composant 16) : aucun troisième
   registre de focus n'est créé (CLAUDE.md § « réutiliser avant de développer »). */

/* Rangée du marqueur — le triangle n'est JAMAIS posé sur la piste (§2.4).
   8 px pour un triangle de 7 px : le pixel rendu à la piste finance sa hauteur. */
.vocnc-band-scale__marker-row {
  position: relative;
  height: 8px;
}

.vocnc-band-scale__marker {
  position: absolute;
  bottom: 0;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 7px solid var(--fg); /* 18,87:1 sur surface claire */
}

/* Piste — les segments inactifs sont un NEUTRE DE SURFACE, jamais un token de
   statut : ce ne sont pas des catégories de données.
   R5 — 10 px et COINS DROITS : une règle graduée a des bouts, pas des caps.
   La micro-barre de distribution garde ses 6 px et son radius-full ; c'est ce
   qui les distingue à couleur identique. Le fond de piste, lui, ne bouge pas :
   les 10 ratios de contraste segment/piste de §2.6 restent ceux qui ont été
   mesurés (warning 3,15:1 et good 3,14:1 n'ont aucune marge). */
.vocnc-band-scale__track {
  position: relative;
  height: 10px;
  background: var(--bg-muted);
  border-radius: 2px;
  overflow: hidden;
}

/* Segments — TOUS peints depuis B2/DV-8. Le token arrive en propriété custom
   inline (--vocnc-band-color) : celui du segment COURANT est identique à celui
   du score du même bandeau (une seule source, aucune seconde table).
   La transparence est le seul ajout CSS de B2, et elle fait DEUX choses :
     1. elle rétablit un écart de luminance entre bandes adjacentes — warning et
        good ont à un millième près la MÊME luminance (1,01:1), donc en niveaux
        de gris, à l'impression N&B et en achromatopsie la frontière entre
        « faible » et « bon » n'existait pas dans la piste ; à 30 % elle vaut
        2,30:1 en clair et 3,55:1 en sombre ;
     2. elle fait ressortir la bande courante sans lui donner de traitement
        dédié (ni bordure, ni hauteur, ni classe de forme). */
.vocnc-band-scale__segment {
  /* D12 (passe C, tour de reprise) — LA RÉGLETTE N'ANNONÇAIT PAS QU'ELLE ÉTAIT
     SURVOLABLE : aucun `cursor` sur les segments, donc rien avant le survol
     lui-même. `help` est le curseur des révélés d'information — jamais
     `pointer`, qui promettrait une navigation ou un réglage (R39/C49 : le
     marqueur était déjà lu comme « un curseur déplaçable, donc un réglage »). */
  cursor: help;
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--vocnc-band-color);
  opacity: 0.3;
}

.vocnc-band-scale__segment--current { opacity: 1; }

/* DV-2 — sous le seuil de fiabilité, aucun intervalle n'est peint : le segment
   subsiste comme ZONE DE SURVOL (les noms restent révélables) mais ne porte
   aucun token de statut et ne peint rien. */
.vocnc-band-scale__segment--unpainted { background: transparent; }

/* Graduations à toutes les bornes (§2.4) — 3,21:1 sur surface claire. */
.vocnc-band-scale__ticks {
  position: relative;
  height: 4px;
}

.vocnc-band-scale__tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  width: 1px;
  height: 4px;
  background: var(--border-strong);
}

/* Bornes chiffrées et noms de bande — positionnement ABSOLU sur une rangée de
   hauteur fixe (jamais de flex : un libellé doit rester centré sur sa
   graduation, un retour à la ligne casserait l'alignement). */
.vocnc-band-scale__bounds {
  position: relative;
  height: 14px;
  overflow: visible;
  margin-top: var(--space-1);
}

/* Calque de révélation — HORS DU FLUX (top: 100 %, donc sous la rangée des
   bornes, donc il n'obscurcit aucun contenu de la réglette : WCAG 1.4.13).
   Hauteur 0 en layout : il ne pousse rien, il n'y a jamais de rangée réservée
   vide au repos. pointer-events: none pour que le survol reste celui du segment
   — c'est le segment qui porte le `title` natif, et il doit continuer de le
   déclencher. */
.vocnc-band-scale__reveal {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 0;
  overflow: visible;
  pointer-events: none;
}

.vocnc-band-scale__bound,
.vocnc-band-scale__name {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 11px;
  line-height: 14px;
  color: var(--fg-muted); /* 7,99:1 sur surface claire */
}

.vocnc-band-scale__bound {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* D13b (passe C) — bornes extrêmes ancrées par leur bord : `transform: none`
   annule le centrage hérité de la règle partagée avec .vocnc-band-scale__name.
   Même mécanique que --start/--end des noms (T1). */
.vocnc-band-scale__bound--start,
.vocnc-band-scale__bound--end {
  transform: none;
}

/* Un nom révélé : fond OPAQUE de la surface de card (il se superpose au bloc
   suivant sur ses 2 derniers pixels), tokens déjà consommés, aucune couleur
   nouvelle. Padding HORIZONTAL seulement : un padding vertical porterait la
   hauteur au-delà des 12 px de marge de pied et l'intrusion dépasserait 2 px.
   Aucune transition (cohérent avec « rien ne justifie d'animer un repère
   statique ») : un prefers-reduced-motion de moins. */
.vocnc-band-scale__name {
  opacity: 0;
  /* D12 (passe C, tour de reprise) — LE RÉVÉLÉ RESSEMBLAIT À UN AXE. Il
     reprenait la police, la taille et la couleur EXACTES des bornes chiffrées
     (11 px, --fg-muted), sans bordure, sans ombre, sans pointe, sur un fond
     (--bg-surface) qui est exactement la couleur de la card : mesuré à 5 px
     sous la ligne des graduations, un libellé révélé y était indiscernable
     d'une seconde ligne d'axe permanente (R113/R114/R115).
     Bordure + ombre portée : DEUX canaux non chromatiques, qui le séparent des
     bornes sans réintroduire la rangée de noms que DV-8 retire. */
  padding: 1px 5px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  box-shadow: 0 1px 3px rgb(0 0 0 / 18%);
}

/* T1 — ANCRAGE SUR LE BORD DU SEGMENT. Le helper ne recentre plus le libellé
   pour satisfaire une contrainte de pire cas : quand sa demi-largeur déborde, il
   l'ancre contre le bord de SON PROPRE segment (offset en % rendu inline). Le
   calque (.vocnc-band-scale__reveal) est étiré left: 0 / right: 0 sur la
   réglette, donc ses pourcentages sont ceux de la piste. Un ancrage est une
   propriété de POSITION, pas de taille : la boîte recouvre son segment à toute
   largeur, quelle que soit sa largeur rendue — c'est ce qui rend la correction
   structurelle. Pour les deux bandes extrêmes l'offset vaut 0.
   `transform: none` annule le centrage hérité de la règle partagée avec
   .vocnc-band-scale__bound, qui reste centré sur SA graduation. */
.vocnc-band-scale__name--start,
.vocnc-band-scale__name--end {
  transform: none;
}

/* Le nom de la bande COURANTE est renforcé par la graisse, pas par la couleur :
   la teinte est déjà prise par le segment, et un second canal chromatique
   n'ajouterait rien à un lecteur daltonien. */
.vocnc-band-scale__name--current {
  font-weight: var(--fw-medium);
  color: var(--fg);
}

/* ── Révélation ─────────────────────────────────────────────────────────────
   Souris : survoler un intervalle révèle SON nom et SES bornes en toutes
   lettres, positionné sur cet intervalle. Vaut pour tous les intervalles, le
   courant compris.

   Clavier / doigt : la réglette entière est un seul arrêt de tabulation, et le
   focus révèle le nom de la BANDE COURANTE — celle où se trouve le score.

   ⚠ ÉCART ASSUMÉ À LA CIBLE, ET IL EST MESURÉ. La cible prescrivait « au focus,
   TOUS les noms révélés simultanément, chacun sur son segment ». C'est
   géométriquement impossible, et la capture le montrait : les libellés portent
   désormais le nom ET les bornes (« critique · inférieur à 60 »), et quatre
   d'entre eux centrés sur leurs segments se chevauchent dès 484 px. Sur eNPS,
   même réduits au seul mot, « faible » et « correct » se chevauchent à 228 px —
   c'est EXACTEMENT la rangée trouée que DV-8 retire, remise au focus. Un empilage
   illisible donne moins que rien.
   Ce qui est rendu au focus est donc le nom de la bande courante : la seule
   information que la rangée retirée garantissait (« le nom de la bande courante
   est TOUJOURS affiché », règle 1 d'A1-0 §2.5). Le reste n'est pas perdu — il
   est dans l'aria-label, servi AU REPOS et en entier, et il s'explore au
   pointeur intervalle par intervalle.

   `:focus` et non `:focus-visible` : un tap ne déclenche pas `:focus-visible`
   dans Chrome. Sans ça, l'équivalent TACTILE exigé par la cible n'existe pas.

   Les règles de survol sont écrites par rang parce que le calque est un FRÈRE
   de la piste (il doit pouvoir déborder de l'overflow: hidden qui découpe les
   segments) : 5 rangs = les 5 bandes du plus fourni des indicateurs (eNPS). */
.vocnc-band-scale:focus .vocnc-band-scale__name--current { opacity: 1; }

.vocnc-band-scale__track:has(.vocnc-band-scale__segment:nth-child(1):hover) ~ .vocnc-band-scale__reveal .vocnc-band-scale__name:nth-child(1),
.vocnc-band-scale__track:has(.vocnc-band-scale__segment:nth-child(2):hover) ~ .vocnc-band-scale__reveal .vocnc-band-scale__name:nth-child(2),
.vocnc-band-scale__track:has(.vocnc-band-scale__segment:nth-child(3):hover) ~ .vocnc-band-scale__reveal .vocnc-band-scale__name:nth-child(3),
.vocnc-band-scale__track:has(.vocnc-band-scale__segment:nth-child(4):hover) ~ .vocnc-band-scale__reveal .vocnc-band-scale__name:nth-child(4),
.vocnc-band-scale__track:has(.vocnc-band-scale__segment:nth-child(5):hover) ~ .vocnc-band-scale__reveal .vocnc-band-scale__name:nth-child(5) {
  opacity: 1;
}

/* ── Surface sombre (.vocnc-kpi-card--dark) ──────────────────────────────────
   Les variantes -on-dark sont celles DÉJÀ dérivées plus bas dans ce fichier
   (color-mix 70 % / blanc) : aucune nouvelle couleur, aucun nouveau color-mix.
   Contrastes du segment courant vs piste inactive : critical 4,11:1 ·
   warning 6,20:1 · fair 5,16:1 · good 6,18:1 · excellent 5,04:1. */
.vocnc-kpi-card--dark .vocnc-band-scale__track      { background: rgba(255, 255, 255, 0.10); }
.vocnc-kpi-card--dark .vocnc-band-scale__marker     { border-top-color: var(--slate-100); }
.vocnc-kpi-card--dark .vocnc-band-scale__tick       { background: rgba(255, 255, 255, 0.45); }
.vocnc-kpi-card--dark .vocnc-band-scale__bound,
.vocnc-kpi-card--dark .vocnc-band-scale__name       { color: rgba(255, 255, 255, 0.70); }
.vocnc-kpi-card--dark .vocnc-band-scale__name--current { color: rgba(255, 255, 255, 0.92); }
/* Le nom révélé se pose sur le fond de SA card — ici la surface sombre. */
.vocnc-kpi-card--dark .vocnc-band-scale__name       { background: var(--slate-900); }

.vocnc-kpi-card--dark .vocnc-band-scale__segment--critical  { background: var(--color-status-critical-on-dark); }
.vocnc-kpi-card--dark .vocnc-band-scale__segment--warning   { background: var(--color-status-warning-on-dark); }
.vocnc-kpi-card--dark .vocnc-band-scale__segment--fair      { background: var(--color-status-fair-on-dark); }
.vocnc-kpi-card--dark .vocnc-band-scale__segment--good      { background: var(--color-status-good-on-dark); }
.vocnc-kpi-card--dark .vocnc-band-scale__segment--excellent { background: var(--color-status-excellent-on-dark); }

/* R21 — texte muet remplaçant un canvas/zone sans donnée (générique,
   consommé par les sparklines vides du rapport ET du cockpit). */
.vocnc-empty {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  font-style: italic;
  margin: 0;
}

.vocnc-indicator-card__chart-empty {
  align-self: flex-start;
}

/* Chart de série 12 mois — promotion de la sparkline résiduelle (chantier 4,
   retour de recette staging David, 04.1-VIZ-INTERACTIVITY-SPEC §I0/I8) :
   conteneur pleine largeur de la card (plus de --chart--micro étroit),
   hauteur cohérente avec les évolutions cockpit (.vocnc-perimeter-card__sparkline,
   même fourchette 120-160px). Le canvas ne porte jamais sa taille (I5). */
/* Passe D, instruction n°2 — HAUTEUR VARIABLE DU GRAPHE.
   La hauteur d'une card est imposée de l'EXTÉRIEUR par l'égalisation de rangée
   (grid stretch, acquis DA-9). Le seul élément capable d'absorber le delta
   entre le contenu d'une card et celui de sa voisine est ce conteneur — tant
   qu'il avait une hauteur FIXE, le delta n'était absorbé par rien et se
   déposait en bloc blanc au-dessus du caveat (mesuré : +123 px sur la card
   FCR, +88 px sur Star, jeu A1-0 à 1440 px).
   Bornes : plancher = la valeur actuelle, déjà arbitrée par I0/I8 (« ≥ 120 px,
   sinon on retombe sur une sparkline muette ») et non rouverte ici ; plafond =
   plancher + 120 px, qui couvre le pire cas mesuré ET s'arrête avant que le
   facteur de forme ne mente : sur une piste de 512 px, 260 px de haut fait un
   rapport 1,97:1, encore dans la zone où 12 points se lisent sans exagération
   de pente. Un chart plus haut ferait lire la MÊME série comme plus volatile
   que dans la card voisine — défaut de dataviz, pas gain de remplissage.
   La composition avec l'ancrage est ce qui rend le dégradé sain : `flex-grow`
   est résolu AVANT les marges `auto`, donc le chart absorbe d'abord tout, et
   ce n'est qu'une fois au plafond que le résidu repart vers la marge `auto` du
   caveat — au-dessus de lui, jamais en dessous ni au milieu.
   I5 est intact : la hauteur reste sur le CONTENEUR, `.vocnc-chart__canvas`
   n'est pas touché (il est en 100 %/100 % et Chart.js se dimensionne sur ce
   parent, `responsive: true` + `maintainAspectRatio: false`). */
/* #346 — UN SEUL CONTENEUR DE GRAPHIQUE. Il en existait trois
   (`vocnc-indicator-card__chart` [140, 260] au rapport,
   `vocnc-kpi-card--dark__sparkline` et `vocnc-perimeter-card__sparkline`
   [120, 240] au cockpit). Les bornes du cockpit survivent — c'est lui la
   référence (directive David du 28/08), et l'écart de plancher n'était pas
   une décision : le rapport portait des cards plus riches (histogramme,
   verbatims) qui n'existent plus. Toutes les cards ont désormais la même
   anatomie, donc la même fourchette. */
.vocnc-indicator-card__chart {
  flex: 1 1 120px;
  min-height: 120px;
  max-height: 240px;
  width: 100%;
  position: relative;   /* obligatoire pour le responsive Chart.js */
  align-self: stretch;
}

/* Zone distribution (type-specific) */
/* #346 — LE CONTENEUR DE RÉPARTITION, UN SEUL. Il en existait deux, identiques
   au `gap` près : `.vocnc-indicator-card__distribution` (rapport, --space-3) et
   `.vocnc-cockpit-dist` (cockpit, --space-2). Le cockpit fait référence
   (directive David du 28/08), donc --space-2 survit. */
.vocnc-indicator-card__distribution {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* #346 — DEUX FAMILLES RETIRÉES, FAUTE D'APPELANT.

   `.vocnc-indicator-card__nps-segments` / `__segment*` : la ligne de segments
   propre au NPS, sous son histogramme. C'était la SECONDE implémentation d'une
   clé de lecture ; D6 l'avait déjà fait passer par le composant unique, l'habillage
   avait survécu à son markup.

   `.vocnc-stacked-bar` / `__track` / `__segment` : la barre 100 % propre au
   rapport (CSAT et FCR). La barre du cockpit lui a survécu et sert les trois
   surfaces (`.vocnc-cockpit-distbar`) — c'est le cockpit qui fait référence
   (directive David du 28/08). `forms/_stacked_bar.html.erb` a disparu avec elle. */

/* Légende HTML directe (R3 : jamais de légende Chart.js) — `.vocnc-stacked-bar__
   legend` / `__legend-item` / `__swatch` ont disparu avec D6 : la légende de
   cette barre EST une clé de lecture de distribution, elle passe par le
   composant unique ci-dessous. */

/* ── D6 (passe C) — CLÉ DE LECTURE D'UNE DISTRIBUTION, composant unique ──────
   Remplace deux formes qui coexistaient : la légende de barre empilée
   (CSAT/FCR) et la ligne de segments NPS. CES, Star et PMF, qui n'avaient
   aucune clé, reçoivent celle-ci ; le cockpit aussi, en variante sans chiffres.

   La COULEUR est portée par la pastille, jamais par le texte. La ligne NPS
   peignait son libellé au token : `--color-status-neutral` (#8D9094) sur fond
   blanc vaut ~2,9:1, sous le seuil AA. Le texte reprend donc --fg. */
.vocnc-distribution-key {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  font-size: var(--fs-caption);
  color: var(--fg);
}

/* ── #346 — LIBELLÉS NUS AU REPOS, CHIFFRES AU SURVOL ET AU FOCUS ───────────

   Arbitrage Mehdi du 31/08 : la clé porte les libellés seuls et révèle compteur
   et pourcentage à la demande. Depuis le retrait du détail par note, c'est la
   seule restitution chiffrée de la répartition.

   ⚠ LE REPLI EST VISUEL, JAMAIS STRUCTUREL. `display: none` et
   `visibility: hidden` sortiraient les chiffres de l'arbre d'accessibilité : la
   card deviendrait muette pour un lecteur d'écran, exactement le défaut É3/B2
   que le dépôt a déjà payé deux fois (« de l'information réservée au survol »).
   `max-width` + `opacity` replient sans retirer. Le garde-fou est dans
   spec/views/distribution_key_spec.rb, qui refuse hidden/aria-hidden/display:none.

   Le focus est porté par la LIGNE, pas par chaque entrée : trois arrêts de
   tabulation par card seraient un impôt sur la navigation, pour une information
   qui se lit d'un bloc. */
.vocnc-distribution-key--revealable {
  cursor: default;
}

.vocnc-distribution-key__figures {
  display: inline-block;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  vertical-align: bottom;
  opacity: 0;
  transition: max-width 150ms ease, opacity 150ms ease;
}

.vocnc-distribution-key--revealable:hover .vocnc-distribution-key__figures,
.vocnc-distribution-key--revealable:focus-visible .vocnc-distribution-key__figures,
.vocnc-distribution-key--revealable:focus-within .vocnc-distribution-key__figures {
  max-width: 14ch;
  opacity: 1;
}

/* Le repli est une commodité de densité, pas une règle : qui a demandé de
   réduire les animations obtient les chiffres en permanence plutôt qu'une
   transition instantanée sur une information qu'il ne verra pas venir. */
@media (prefers-reduced-motion: reduce) {
  .vocnc-distribution-key__figures {
    max-width: 14ch;
    opacity: 1;
    transition: none;
  }
}

/* #346 — le bouton d'ouverture des nuages de mots : aligné à gauche comme les
   autres blocs de la card, jamais étiré par le flex de la card. */
.vocnc-indicator-card__wordcloud {
  align-self: flex-start;
}

.vocnc-distribution-key__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* D13c (passe C) — LES 3 SEGMENTS NPS CASSAIENT EN SÉPARANT UN LIBELLÉ DE SON
     CHIFFRE (et « Passifs » gardait son effectif collé au libellé en renvoyant
     le seul pourcentage à la ligne : trois ruptures, deux motifs différents).
     Un couple libellé/valeur est un seul jeton de lecture : la coupure se fait
     ENTRE les entrées de la clé, jamais dedans. */
  white-space: nowrap;
}

/* Sous le plancher produit (DV-7 : 360 px), la clé passe en colonne : trois
   entrées insécables côte à côte n'y tiennent pas, et `flex-wrap` seul
   produirait 2 + 1 — une rangée bancale plutôt qu'une liste. */
@media (max-width: 479px) {
  .vocnc-distribution-key {
    flex-direction: column;
    align-items: flex-start;
  }
}

.vocnc-distribution-key__swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

/* Trois tokens, jamais plus (cf. l'en-tête de shared/_distribution_key). */
.vocnc-distribution-key__item--critical  .vocnc-distribution-key__swatch { background: var(--color-status-critical); }
.vocnc-distribution-key__item--neutral   .vocnc-distribution-key__swatch { background: var(--color-status-neutral); }
.vocnc-distribution-key__item--excellent .vocnc-distribution-key__swatch { background: var(--color-status-excellent); }

/* Head card du cockpit (Slate 900) : mêmes variantes -on-dark que la micro-barre
   qu'elle légende, et un texte qui tient sur le fond sombre. */
.vocnc-kpi-card--dark .vocnc-distribution-key {
  color: var(--slate-100);
}

.vocnc-kpi-card--dark .vocnc-distribution-key__item--critical  .vocnc-distribution-key__swatch { background: var(--color-status-critical-on-dark); }
.vocnc-kpi-card--dark .vocnc-distribution-key__item--neutral   .vocnc-distribution-key__swatch { background: var(--color-status-neutral-on-dark); }
.vocnc-kpi-card--dark .vocnc-distribution-key__item--excellent .vocnc-distribution-key__swatch { background: var(--color-status-excellent-on-dark); }

/* D-QUID-15/17 — micro-barre 100% de distribution des cards du cockpit (head
   sombre + perimeter clair). Palette de DISTRIBUTION (critical/neutral/excellent),
   JAMAIS la palette de statut (fair/warning/good) — piège G3. Aucun chiffre
   visible : compteurs au survol (title, cf. _cockpit_distribution_bar). Séparateurs
   = gap révélant le fond de card (≥3:1 sur chaque segment, WCAG 1.4.11). */
/* D6 (passe C) — la barre ET sa clé de lecture. La clé est un FRÈRE de la
   barre : le contrat « aucun nœud texte dans .vocnc-cockpit-distbar » tient. */
.vocnc-indicator-card__distribution .vocnc-distribution-key {
  gap: var(--space-1) var(--space-3);
  font-size: var(--fs-caption);
}

.vocnc-cockpit-distbar {
  display: flex;
  gap: 2px;                       /* séparateur = fond de card (contraste, C2-PLAN §Contrastes) */
  width: 100%;
  height: 6px;
  border-radius: var(--radius-full);
  overflow: hidden;
}

.vocnc-cockpit-distbar__seg {
  height: 100%;
}

/* Fond clair (perimeter card, #FFFFFF) */
.vocnc-cockpit-distbar__seg--critical  { background: var(--color-status-critical); }
.vocnc-cockpit-distbar__seg--neutral   { background: var(--color-status-neutral); }
.vocnc-cockpit-distbar__seg--excellent { background: var(--color-status-excellent); }

/* Fond sombre (head card, Slate 900) — variantes -on-dark déjà dérivées
   (vocnc_components.css:3242-3247), AA/1.4.11 garanti sur Slate 900. */
.vocnc-kpi-card--dark .vocnc-cockpit-distbar__seg--critical  { background: var(--color-status-critical-on-dark); }
.vocnc-kpi-card--dark .vocnc-cockpit-distbar__seg--neutral   { background: var(--color-status-neutral-on-dark); }
.vocnc-kpi-card--dark .vocnc-cockpit-distbar__seg--excellent { background: var(--color-status-excellent-on-dark); }

/* Jauge PMF horizontale + seuil 40% (INDICATEURS-SPEC §3.5) */
.vocnc-indicator-card__pmf-gauge {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* A1-0 / DV-4 — la jauge PMF (__pmf-track / __pmf-fill / __pmf-threshold) est
   SUPPRIMÉE : la réglette de repères encode la même information, en tête de
   card, dans le langage visuel commun aux 7 indicateurs. Sa ligne de seuil
   codée en dur (left: 40%) est ce qui rendait la borne 40 « plus dure » que
   toutes les autres bornes du produit — ce que la fiche publique PMF refuse.
   Ne pas la réintroduire : le conteneur __pmf-gauge survit, il ne porte plus
   que le verdict et la note d'inversion sémantique. */

.vocnc-indicator-card__pmf-verdict {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--fg);
  margin: 0;
}

/* Caveat confiance statistique faible (n sous le seuil — INDICATEURS-SPEC §3.7).
   A1-0 (défaut §1.2 n°5 de la cible) : ce caveat consommait
   --color-status-warning = #AA7E2C, soit 3,66:1 sur blanc — sous le 4,5:1 exigé
   pour un texte de 12 px. Il passe au token DURCI --color-status-warning-strong
   (--ember-800, 7,61:1), créé pour ce motif exact (D-QUID-7) et déjà consommé
   par le marqueur du tableau des formulaires actifs.
   Sur fond sombre : --color-status-warning-on-dark (8,03:1 sur Slate 900) — la
   règle vit ici, plus en style inline dans les deux vues du cockpit. */
.vocnc-indicator-card__low-confidence {
  color: var(--color-status-warning-strong);
}
.vocnc-kpi-card--dark .vocnc-indicator-card__low-confidence {
  color: var(--color-status-warning-on-dark);
}

/* Message données insuffisantes eNPS (T-04-36 défense en profondeur) */
.vocnc-indicator-card__insufficient-data {
  font-size: var(--fs-body);
  color: var(--fg-muted);
  font-style: italic;
  margin: 0;
}

/* VERB-02 — Section word-frequency par catégorie (Plan 13) */
.vocnc-indicator-card__verbatim {
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}

.vocnc-indicator-card__verbatim-grid {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-2);
}

.vocnc-indicator-card__verbatim-col {
  flex: 1 1 120px;
}

.vocnc-indicator-card__verbatim-cat {
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 var(--space-1) 0;
}

.vocnc-indicator-card__verbatim-words {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--fs-caption);
  color: var(--fg);
}

.vocnc-indicator-card__verbatim-words .mono {
  color: var(--fg-muted);
}


/* ─── P4-C14 : Head KPI card sombre cockpit (NPS/CSAT Organisation — P4-04) ─── */
/* Background --slate-900. Score 32px Geist Mono tabular-nums.
   Sparkline --slate-400. Caveat pied non supprimable (COCK-04).
   COCK-03 MVP : NPS + CSAT organisation uniquement. Jamais d'Ember dans ces cards. */
.vocnc-kpi-card--dark {
  background: var(--slate-900);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 160px;

  /* Chantier SEL — surcharge des tokens chart sur fond sombre (lus par
     chart_defaults.js via le canvas, cascade CSS normale) :
     - ligne active --slate-100 (16,2:1 sur Slate 900)
     - ligne/ticks estompés --slate-400 (5,9:1 — reste ≥ 4,5:1, AA)
     - gridlines rgba(236,238,241,.15) : l'ancien --slate-100 PLEIN éblouissait
       sur fond Slate 900 (aucune gridline n'a besoin d'un contraste de texte)
     - bordure de tooltip rgba(236,238,241,.25) 1px : le tooltip Slate 900
       (vocncTooltip) était sinon invisible sur une card déjà Slate 900. */
  --chart-line: var(--slate-100);
  --chart-line-dim: var(--slate-400);
  --chart-tick: var(--slate-400);
  --chart-grid: rgba(236, 238, 241, 0.15);
  --chart-tooltip-border: rgba(236, 238, 241, 0.25);
}

/* Chantier SEL, bonus contraste (relevé par l'arbitrage) — le score headline
   en --color-status-critical sur .vocnc-kpi-card--dark ne ratio que 2,8:1
   (Slate 900 en fond) ; --color-status-excellent ne ratio que 3,7:1 : les deux
   sous le seuil AA texte (4,5:1). Variantes -on-dark via color-mix(in srgb,
   token 70%, white) — étendues aux 5 tokens (pas seulement les 2 en défaut)
   car la restructuration cockpit (chantier 4, même lot) fait désormais
   apparaître N'IMPORTE QUEL indicateur en section Organisation (dark), pas
   seulement NPS/CSAT comme au MVP — les 5 teintes doivent rester AA sur fond
   sombre, pas seulement les 2 qui échouaient dans l'usage NPS/CSAT d'origine.
   Ratios recalculés (contraste WCAG texte, fond Slate 900 #0F1117) :
     critical  #9E3B30 → mix 70% #BB766E : 5,33:1 (était 2,81:1)
     warning   #AA7E2C → mix 70% #C4A56B : 8,03:1 (était 5,15:1, déjà AA)
     fair      #6B761F → mix 70% #979F62 : 6,70:1
     neutral   #8D9094 → mix 70% #AFB1B4 : 8,78:1 (était 5,89:1, déjà AA)
     good      #4D9467 → mix 70% #82B495 : 8,01:1 (était 5,16:1, déjà AA)
     excellent #3B7A4F → mix 70% #76A284 : 6,53:1 (était 3,67:1) */
.vocnc-kpi-card--dark {
  --color-status-critical-on-dark:  color-mix(in srgb, var(--color-status-critical) 70%, white);
  --color-status-warning-on-dark:   color-mix(in srgb, var(--color-status-warning) 70%, white);
  --color-status-fair-on-dark:      color-mix(in srgb, var(--color-status-fair) 70%, white);
  --color-status-neutral-on-dark:   color-mix(in srgb, var(--color-status-neutral) 70%, white);
  --color-status-good-on-dark:      color-mix(in srgb, var(--color-status-good) 70%, white);
  --color-status-excellent-on-dark: color-mix(in srgb, var(--color-status-excellent) 70%, white);
}

.vocnc-kpi-card--dark .vocnc-eyebrow {
  /* Eyebrow inverse sur fond sombre */
  color: rgba(255, 255, 255, 0.50);
  font-family: var(--font-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0;
}

/* #346 — LE CHIFFRE-CLÉ, UNE SEULE FAMILLE. Il en existait trois
   (`vocnc-indicator-card__score` au rapport, `vocnc-kpi-card--dark__score` et
   `vocnc-perimeter-card__score` au cockpit) pour un même rôle, avec deux
   mécanismes de couleur concurrents — style inline dérivé de
   `headline_color_token` au rapport, classe dérivée de `headline_status_suffix`
   au cockpit. La classe survit (mécanisme du cockpit, directive David du 28/08).
   La base et les couleurs en fond clair vivent dans forms_cockpit.css ; ce bloc
   ne porte que ce qui est PROPRE au fond sombre.
   La head card garde son chiffre à 32 px : c'est le KPI de tête, sa dominance
   est voulue — le rapport et la card de périmètre partagent 24 px. */
.vocnc-kpi-card--dark .vocnc-indicator-card__score {
  font-size: var(--fs-display-m);
  line-height: 1.20;
}

/* Couleur du score selon statut indicateur (INDICATEURS-SPEC §1.5) — variantes
   -on-dark (chantier SEL, bonus contraste ci-dessus) : AA garanti sur Slate 900. */
.vocnc-kpi-card--dark .vocnc-indicator-card__score--critical  { color: var(--color-status-critical-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--warning   { color: var(--color-status-warning-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--fair      { color: var(--color-status-fair-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--neutral   { color: var(--color-status-neutral-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--good      { color: var(--color-status-good-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--excellent { color: var(--color-status-excellent-on-dark); }
.vocnc-kpi-card--dark .vocnc-indicator-card__score--empty     { color: rgba(255, 255, 255, 0.40); }

/* Le titre-message sur fond sombre. Il portait un modificateur écrit dans la
   vue (`__message--inverse`) : la surface se déclare une fois, sur l'article. */
.vocnc-kpi-card--dark .vocnc-indicator-card__message {
  color: var(--fg-inverse);
}

/* I0/I8 (04.1-VIZ-INTERACTIVITY-SPEC) — évolution 12 mois promue en chart de
   série (axe + tooltip), plus une sparkline muette : hauteur ≥ 120px (était
   32px, dimensionnement sparkline hérité). Conteneur DÉDIÉ (I5) — le canvas
   ne porte jamais sa taille, .vocnc-chart__canvas remplit ce parent. */
/* Passe D, instruction n°2 — même mécanique que .vocnc-indicator-card__chart
   ci-dessus, bornes [120, 240] : le plancher reste la valeur I0/I8, le plafond
   couvre le pire excédent mesuré au cockpit (+61 px sur la head card NPS). */
/* #346 — le conteneur de graphique est `.vocnc-indicator-card__chart` sur les
   trois surfaces (cf. forms_cockpit.css). Il n'en reste ici que la note
   d'implémentation : borderColor --slate-400 injecté côté chart_defaults.js. */

/* Caveat pied de card (héritage .vocnc-caveat sur fond sombre) */
.vocnc-kpi-card--dark .vocnc-caveat {
  border-top-color: rgba(255, 255, 255, 0.15);
  color: rgba(255, 255, 255, 0.45);
}

/* Passe D (gauntlet visuel), instruction n°1 — ANCRAGE DU CAVEAT.
   `margin-top: auto` était posé ci-dessus sur TOUS les .vocnc-caveat
   descendants d'une card sombre — or _cockpit_head_card.html.erb en rend
   jusqu'à trois (bannière RGPD eNPS en tête, caveat de faible fiabilité,
   pied). En flexbox, plusieurs marges `auto` se partagent l'espace libre à
   PARTS ÉGALES : le vide se scindait en deux blocs au milieu de la card au
   lieu d'un seul en bas.
   Ce n'était pas une hypothèse — mesuré sur une card eNPS masquée étirée par
   sa voisine NPS (spec/system/card_fill_visual_spec.rb) : les DEUX caveats
   portaient `margin-top: 140,8 px`, soit 281,6 px de vide coupés en deux,
   dont un bloc juste sous l'eyebrow.
   La règle est donc RESSERRÉE sur l'enfant direct ET dernier de la card, une
   par famille. `margin-top: auto` plutôt que `justify-content: space-between`
   : space-between écarterait AUSSI le score de sa réglette et le chart de sa
   distribution — les regroupements qu'A1-0 a précisément construits. */
/* #346 — l'ancrage est porté une fois pour toutes par
   `.vocnc-indicator-card > .vocnc-caveat:last-child` (forms_cockpit.css), que la
   card soit sombre ou claire, masquée ou non. */

/* Empty state : périmètre sans réponses (score « — »). Le modificateur est
   désormais posé sur la card elle-même, quelle que soit sa surface. */
.vocnc-kpi-card--dark.vocnc-indicator-card--no-data .vocnc-indicator-card__score {
  color: rgba(255, 255, 255, 0.40);
}


/* ─── #346 — LA FAMILLE `.vocnc-perimeter-card__*` A DISPARU ───────────────
   La card de périmètre n'était pas un autre composant : c'était la MÊME card,
   sur fond clair, avec sa propre famille de classes. Chacun de ses éléments
   avait un jumeau `vocnc-indicator-card__*` au rapport et un troisième
   `vocnc-kpi-card--dark__*` sur la head card — trois écritures pour un rôle.
   Tout est passé sur `vocnc-indicator-card__*` (forms_cockpit.css) ; seul le
   fond sombre garde ses surcharges, au-dessus.

   Deux règles n'avaient pas de jumeau et ont été ABSORBÉES, pas supprimées :

   - l'ancrage du pied. La card claire du cockpit était la seule des trois à
     n'en avoir aucun : son caveat suivait le contenu et le vide d'égalisation
     de rangée se déposait EN DESSOUS de lui (mesuré à 31,1 px sur la card Star
     et 19,8 px sur la card CES, jeu A1-0 à 1440 px). Le composant unique rend
     TOUJOURS son pied méthodologique en dernier enfant, donc la règle
     `.vocnc-indicator-card > footer.vocnc-caveat { margin-top: auto }` qui
     existait déjà pour le rapport couvre désormais les trois surfaces. Le
     sélecteur reste resserré sur l'enfant direct ET dernier : une card rend
     aussi une bannière RGPD en tête et un caveat de fiabilité, et plusieurs
     marges `auto` en flexbox se partagent l'espace libre à parts égales —
     c'est ce qui scindait le vide en deux blocs au milieu de la card.

   - le `gap` de --space-2. Le rapport en avait --space-4 : le cockpit fait
     référence (directive David du 28/08), c'est --space-2 qui survit, porté
     par `.vocnc-indicator-card`. */


/* ─── P4-C15 : Grille head cards cockpit ─── */
/* 2 cards sombres en tête, grid 2 colonnes desktop, 1 colonne mobile. */
.vocnc-cockpit-head {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-6);
  margin-bottom: var(--space-8);
}

/* Gauntlet dataviz (chantier E, itération 1) — E4 a fait passer les
   sparklines cockpit de "canvas mort" à "canvas Chart.js réellement rendu" :
   un <canvas> porte une largeur intrinsèque (avant application du
   `responsive:true` de Chart.js) que Grid respecte via son `min-width: auto`
   par défaut sur les items, empêchant la colonne unique mobile de rétrécir
   sous cette largeur intrinsèque — grid blowout horizontal (page entière
   plus large que le viewport 375px, CTA "Créer un formulaire" du topbar
   clippé). Même correctif déjà appliqué à `.vocnc-grid--cols-4` (rapport,
   chantier 4) — cf. https://css-tricks.com/preventing-a-grid-blowout. */
.vocnc-cockpit-head > *,
.vocnc-cockpit-perimeter > * {
  min-width: 0;
}

@media (max-width: 640px) {
  .vocnc-cockpit-head {
    grid-template-columns: 1fr;
  }
}

/* Grille perimeter cards — 2 colonnes desktop, 1 colonne mobile */
.vocnc-cockpit-perimeter {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-8);
}

@media (max-width: 640px) {
  .vocnc-cockpit-perimeter {
    grid-template-columns: 1fr;
  }
}

/* ── #346 (recette du 31/08) — LA POP-IN N'EST PAS SUR LA SURFACE SOMBRE ────

   Deux défauts relevés en recette, tous deux sur la seule head card sombre :
   le libellé du bouton « Nuages de mots » invisible au repos, et le titre de la
   pop-in invisible aussi. Même cause de fond, deux mécanismes.

   1. LE BOUTON EST BIEN SUR LA CARD SOMBRE. `.vocnc-btn--secondary` déclare
      `color: var(--fg)` et une bordure `var(--border)` : du texte sombre et un
      trait clair, pensés pour un fond de surface. Sur Slate 900, le libellé
      disparaît et il ne reste que le survol (`background: --slate-50`) pour le
      révéler — de l'information réservée au survol, une fois de plus.

   2. LA POP-IN, ELLE, N'Y EST PAS. Le `<dialog>` est un DESCENDANT DOM de la
      card, mais `showModal()` le place dans le TOP LAYER du navigateur : il
      s'affiche sur son propre fond clair, hors de la card. Les surcharges
      `-on-dark` continuent pourtant de descendre par la cascade, et peignaient
      son eyebrow en blanc à 50 % sur fond clair.

      C'est un piège structurel de la modale native, pas un oubli ponctuel :
      toute règle `.vocnc-kpi-card--dark X` future retombera dedans. D'où un
      bloc de reset explicite plutôt qu'une correction au cas par cas. */
.vocnc-kpi-card--dark .vocnc-btn--secondary {
  color: var(--fg-inverse);
  border-color: rgba(255, 255, 255, 0.35);
}

.vocnc-kpi-card--dark .vocnc-btn--secondary:hover {
  background: rgba(255, 255, 255, 0.10);
}

.vocnc-kpi-card--dark .vocnc-modal .vocnc-eyebrow {
  color: var(--fg-muted);
}

.vocnc-kpi-card--dark .vocnc-modal .vocnc-caveat {
  color: var(--fg-muted);
  border-top-color: var(--border);
}

.vocnc-kpi-card--dark .vocnc-modal .vocnc-indicator-card__verbatim-cat,
.vocnc-kpi-card--dark .vocnc-modal .vocnc-indicator-card__verbatim-words {
  color: var(--fg);
}

/* Badge méthodo warning (⚠ divergence question/configuration).

   #276 — TROIS CORRECTIONS, une par canal :

   1. CONTRASTE. Le texte était en --color-status-warning (#AA7E2C) sur le fond
      crème #FBF3E1 : 3,32:1 à 12 px, sous les 4,5:1 de la 1.4.3. Le token durci
      --color-status-warning-strong (#7A480B) y vaut 6,89:1. Troisième porteur du
      même défaut après D-QUID-7 et A1-0, corrigé de la même façon.
   2. FORME. La pilule est le vocabulaire des BADGES DE STATUT ; un marqueur
      méthodologique doit s'en distinguer — rectangle à coins légèrement
      arrondis, libellé en capitales avec interlettrage.
   3. RAISON DONNÉE. Elle n'était portée que par la card sombre, via un `title=`
      ; la card de périmètre était muette. Le composant unique la rend sur les
      deux surfaces, et le `tabindex` la rend atteignable au clavier — un `title`
      seul est de l'information réservée au survol, le défaut É3/B2. */
.vocnc-methodo-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  background: #FBF3E1;
  color: var(--color-status-warning-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: 0.08em;
  border: 1px solid var(--color-status-warning-strong);
}

/* Sur fond sombre, le badge garde son fond crème : c'est lui qui porte le
   contraste, et le couple crème/#7A480B est le même des deux côtés. */
.vocnc-kpi-card--dark .vocnc-methodo-badge {
  color: var(--color-status-warning-strong);
}

/* ============================================================
   Cible de clic étendue (fix #201 — groupe #201/#205/#214).
   Le lien porteur (titre) reçoit un overlay ::after couvrant le
   conteneur positionné — jamais d'<a> imbriqué, jamais d'onclick
   (CSP, cf. #170). Les zones d'action (footer) remontent au-dessus
   de l'overlay via __raised. Le lien hérite de la couleur du titre :
   le bleu souligné navigateur disparaît avec.
   ============================================================ */
.vocnc-click-card {
  position: relative;
}

.vocnc-click-card__link {
  color: inherit;
  text-decoration: none;
}

.vocnc-click-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.vocnc-click-card__raised {
  position: relative;
  z-index: 1;
}
/* ============================================================
   voc.nc — Marketing landing (Phase 03 UAT round 2 — sketch 007 V2)
   Consomme les tokens de vocnc_design.css. Port du sketch V2 winner
   (.planning/sketches/007-marketing-landing/index.html) avec naming
   vocnc-marketing__*. Pricing intentionnellement absent (retrait UAT
   round 1 — pricing à re-trancher dans une release ultérieure).
   ============================================================ */

/* ---------- Sticky header marketing ---------- */
.vocnc-marketing__header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
.vocnc-marketing__header-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
  height: 64px;
  display: flex;
  align-items: center;
  gap: var(--space-8);
}
.vocnc-marketing__nav {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex: 1;
}
.vocnc-marketing__nav-link {
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease);
}
.vocnc-marketing__nav-link:hover { color: var(--fg); }
.vocnc-marketing__header-cta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* #282 — sous 480 px, le header (wordmark + 3 ancres + 2 boutons) s'étendait
   jusqu'à 474 px pour un viewport de 375 et faisait défiler toute la page
   horizontalement (99 px mesurés). Sa hauteur est fixe (64 px) : rien ne peut
   passer à la ligne, il faut retirer. Partent les 3 ancres, dupliquées dans la
   colonne « Produit » du pied.

   Recette du 14/08 — « Connexion » RESTE dans le header : le reléguer au pied,
   seul endroit où il subsistait, mettait le retour d'un client existant au bout
   d'une page entière. Les deux boutons tiennent une fois les ancres parties,
   mesuré jusqu'à 320 px sans resserrer les gouttières. */
@media (max-width: 480px) {
  .vocnc-marketing__nav {
    display: none;
  }
}

/* ---------- Container helper ---------- */
.vocnc-marketing__container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
}
.vocnc-marketing__container--narrow { max-width: 880px; }

/* ---------- Section base ---------- */
.vocnc-marketing__section {
  padding: var(--space-24) 0;
}
.vocnc-marketing__section--alt {
  background: var(--bg-subtle);
}
.vocnc-marketing__section--sm {
  padding: var(--space-16) 0;
}

/* ---------- Section header ---------- */
.vocnc-marketing__section-head {
  text-align: center;
  margin-bottom: var(--space-12);
}
.vocnc-marketing__section-head .vocnc-eyebrow {
  margin-bottom: var(--space-3);
  display: block;
}
.vocnc-marketing__section-head h2 {
  font-family: var(--font-sans);
  font-size: 38px;
  font-weight: var(--fw-medium);
  letter-spacing: -0.012em;
  line-height: 1.15;
  margin: 0 auto var(--space-4);
  max-width: 720px;
  color: var(--fg);
}
.vocnc-marketing__section-head p {
  font-size: 17px;
  color: var(--fg-muted);
  max-width: 560px;
  margin: 0 auto;
  line-height: 1.6;
}

/* ============================================================
   HERO — title + sub + CTA + (optionnel) product shot
   ============================================================ */
.vocnc-marketing__hero {
  padding: var(--space-24) 0 var(--space-16);
}
.vocnc-marketing__hero-inner {
  max-width: 880px;
  margin: 0 auto;
  padding: 0 var(--container-gutter);
  text-align: center;
}
.vocnc-marketing__hero .vocnc-eyebrow {
  display: block;
  margin-bottom: 20px;
}
.vocnc-marketing__hero-title {
  font-family: var(--font-sans);
  font-size: 64px;
  line-height: 1.05;
  letter-spacing: -0.020em;
  font-weight: var(--fw-medium);
  margin: 0 0 var(--space-6);
  color: var(--fg);
}
.vocnc-marketing__hero-title em {
  font-style: normal;
  color: var(--accent);
}
.vocnc-marketing__hero-subtitle {
  font-size: 19px;
  line-height: 1.6;
  color: var(--fg-muted);
  margin: 0 auto var(--space-8);
  max-width: 640px;
}
.vocnc-marketing__hero-ctas {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: var(--space-6);
}
.vocnc-marketing__hero-fineprint {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--fg-subtle);
  margin: 0;
}

/* ---------- Hero responsive ---------- */
@media (max-width: 720px) {
  .vocnc-marketing__hero-title { font-size: 44px; }
  .vocnc-marketing__hero-subtitle { font-size: 17px; }
}

/* ============================================================
   PRODUCT SHOT — scène slate-900 portant les captures réelles
   du rapport par indicateur (/forms/:id/report).
   ============================================================ */
.vocnc-marketing__hero-visual {
  margin-top: var(--space-12);
  padding: 0 var(--container-gutter);
  max-width: var(--container-max);
  margin-left: auto;
  margin-right: auto;
}
.vocnc-marketing__product-shot {
  background: var(--slate-900);
  border-radius: var(--radius-lg);
  padding: var(--space-8) 36px;
  box-shadow: 0 24px 60px -24px rgba(15, 17, 23, 0.30),
              0 8px 16px rgba(15, 17, 23, 0.06);
  color: var(--fg-inverse);
  text-align: left;
  max-width: 1080px;
  margin: 0 auto;
}
.vocnc-marketing__product-shot-eyebrow {
  color: var(--fg-inverse-muted);
  margin-bottom: var(--space-2);
}
.vocnc-marketing__product-shot-title {
  font-size: 14px;
  color: var(--fg-inverse);
  margin: 0 0 var(--space-6);
  font-weight: var(--fw-medium);
}
/* Captures réelles du rapport par indicateur, posées sur la scène slate-900.
   Les images sont des cards claires : un liseré très léger suffit à les
   détacher du fond sombre, sans ombre supplémentaire (la scène en porte déjà). */
.vocnc-marketing__product-shot-figures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  align-items: start;
}
.vocnc-marketing__product-shot-img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

@media (max-width: 900px) {
  /* Une seule capture en dessous de 900px : deux cards denses côte à côte
     deviennent illisibles bien avant le viewport mobile. */
  .vocnc-marketing__product-shot-figures { grid-template-columns: minmax(0, 1fr); }
  .vocnc-marketing__product-shot-img--secondary { display: none; }
}

@media (max-width: 720px) {
  .vocnc-marketing__product-shot { padding: var(--space-6) var(--space-4); }
}

/* ============================================================
   METRICS STRIP — SUPPRIMÉE (arbitrage DV-5, 12/08/2026)
   Les 3 « big numbers » qu'elle portait étaient fabriqués (aucune source, dont
   un benchmark sectoriel inventé) : la section HTML est retirée de
   pages/home.html.erb, les 6 règles qui l'habillaient partent avec elle.
   ============================================================ */

/* ============================================================
   FEATURES ALTERNÉES — 2-col LTR/RTL
   ============================================================ */
.vocnc-marketing__feature-alt {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  margin-bottom: var(--space-24);
  max-width: var(--container-max);
  margin-left: auto;
  margin-right: auto;
  padding: 0 var(--container-gutter);
}
.vocnc-marketing__feature-alt:last-child { margin-bottom: 0; }
.vocnc-marketing__feature-alt--reversed { direction: rtl; }
.vocnc-marketing__feature-alt--reversed > * { direction: ltr; }
.vocnc-marketing__feature-alt-text .vocnc-eyebrow {
  display: block;
  margin-bottom: var(--space-3);
}
.vocnc-marketing__feature-alt-text .vocnc-eyebrow--accent { color: var(--accent); }
.vocnc-marketing__feature-alt-text h3 {
  font-family: var(--font-sans);
  font-size: 28px;
  font-weight: var(--fw-medium);
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0 0 14px;
  color: var(--fg);
}
.vocnc-marketing__feature-alt-text p {
  font-size: 16px;
  color: var(--fg-muted);
  line-height: 1.65;
  margin: 0 0 var(--space-4);
}
.vocnc-marketing__feature-alt-text ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.vocnc-marketing__feature-alt-text li {
  font-size: var(--fs-ui);
  color: var(--fg);
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  line-height: 1.5;
}
.vocnc-marketing__feature-alt-text li::before {
  content: "✓";
  color: var(--accent);
  font-weight: var(--fw-medium);
  flex-shrink: 0;
}
.vocnc-marketing__feature-alt-visual {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  aspect-ratio: 4/3;
  /* E §3.5 (repris d'A3 §2.3) — `aspect-ratio` s'applique à la boîte désignée
     par `box-sizing`. En content-box, les 48 px de padding et les 2 px de
     bordure s'AJOUTENT au 4/3 : le ratio rendu tombait à 1,294 et l'étape 03
     était jusqu'à 48,8 % plus petite que ses deux jumelles. Il n'y a pas de
     reset border-box global dans ce dépôt — la déclaration est donc requise
     ici, elle n'est pas redondante. */
  box-sizing: border-box;
  padding: var(--space-6);
  display: flex;
  align-items: center;
  justify-content: center;
  /* E §3.5 (repris d'A3 §2.3) — `--fg-subtle` (slate-400) sur `--bg-subtle`
     donne 2,97:1, sous le 4,5:1 de l'AA. `--fg-muted` (slate-600) donne 7,39:1.
     La variante --dark ci-dessous garde son propre couple de couleurs. */
  color: var(--fg-muted);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  text-align: center;
}
/* A3 réduite §2.4 — L'ÉTAPE 03 SERT UNE CAPTURE RÉELLE DU COCKPIT.
   `.vocnc-marketing__feature-alt-visual--dark` a été SUPPRIMÉE : elle n'avait
   qu'un seul appelant, le placeholder qu'on remplace, et elle posait une
   bordure claire (slate-200) sur un fond slate-900 alors que `--border-inverse`
   existe pour ça. Le défaut disparaît avec la classe.

   La boîte garde sa géométrie (4:3, `box-sizing: border-box`, rayon, bordure) —
   c'est elle qui garantit que les trois étapes restent une rangée cohérente —
   et perd son padding : l'image occupe le cadre. `object-fit: cover` ne rogne
   rien tant que l'asset est un 4:3 exact, ce qu'un point de la barre impose et
   que le spec de production asserte sur l'en-tête du fichier produit. */
.vocnc-marketing__feature-alt-figure {
  margin: 0;
}
.vocnc-marketing__feature-alt-visual--shot {
  display: block;
  padding: 0;
  overflow: hidden;
  cursor: zoom-in;
}
.vocnc-marketing__feature-alt-shot-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.vocnc-marketing__feature-alt-caption {
  display: block;
  margin-top: var(--space-2);
  color: var(--fg-muted);
}

/* La modale existante, en format image. AUCUNE classe de modale n'est créée :
   `.vocnc-modal`, `::backdrop`, `__card`, `__header`, `__close` viennent de
   components/atomiques.css et `modal_controller.js` est inchangé — le `<dialog>`
   natif piège le focus, le restitue à la fermeture et ferme sur Échap sans une
   ligne de JS de plus. Ces trois règles sont des MODIFICATEURS, et elles vivent
   ici parce qu'elles ne servent que la page marketing.

   Le `width: 1200px` FIXE de l'image est délibéré. Au plancher de 360 px
   (DV-7), une capture de tableau de bord réduite à la largeur de l'écran est
   aussi illisible que la vignette : ce n'est pas « en grand », c'est la même
   image avec un voile. Dans une carte `overflow: auto`, le 1200 px permet de
   BALAYER l'image et de lire réellement. Au-delà de ~1264 px de viewport,
   aucun défilement n'apparaît. C'est un pari assumé sur l'usage mobile, pas une
   mesure — le contre-pari (l'image tient dans l'écran) est un clic pour rien.

   A4 — ce pari avait un défaut, relevé à la livraison d'A3 réduite : le bord
   GAUCHE de ces captures est la barre latérale de l'application, et une carte
   qui défile s'ouvre à `scrollLeft = 0`. Au plancher, « agrandir » ouvrait donc
   sur un bandeau noir. Le correctif est dans `modal_controller.js` (cible
   optionnelle `pan`), pas ici : il CENTRE la position de défilement initiale.
   La largeur reste 1200 px — on recentre, on ne rétrécit pas. */
.vocnc-modal--shot {
  width: min(1232px, calc(100vw - 2 * var(--space-4)));
}
.vocnc-modal__card--shot {
  /* A4 — MESURÉ, contre l'estimation notée plus haut : « au-delà de ~1264 px de
     viewport, aucun défilement n'apparaît » est FAUX. À 1440 comme à 2400 px,
     `clientWidth` = 1215 pour un `scrollWidth` de 1232 — il reste 17 px de
     débordement à toute largeur d'écran. Les 17 px sont la BARRE DE DÉFILEMENT
     VERTICALE (l'image fait 900 px de haut dans une modale plus courte), pas le
     padding : `box-sizing: border-box` a été essayé ici et ne change aucune des
     deux mesures. L'image elle-même (1200 px) TIENT dans les 1215 px ; ce qui
     dépasse, ce sont ses deux paddings de 16 px.
     Rien n'est donc corrigé ici : c'est le recentrage de `modal_controller.js`
     qui ne doit pas se déclencher pour ce résidu — sa règle est écrite là-bas. */
  padding: var(--space-4);
  overflow: auto;
}
.vocnc-modal__shot-img {
  display: block;
  width: 1200px;
  max-width: none;
  height: auto;
}

/* E §3.3 — rangée de liens de fiches sous la liste d'une feature.
   `flex-wrap: wrap` (et NON le `nowrap` initial hérité quand ces liens vivaient
   dans un <li> en display:flex) : la rangée passe à la ligne au lieu de
   compresser chaque lien jusqu'à son min-content. C'est ce nowrap qui cassait
   « Star Rating » en deux et fixait le plancher de largeur de page à 483 px.
   Aucun registre de lien nouveau n'est créé : `.vocnc-link` (vocnc_components.css)
   existe, est générique par sa docstring, et sert déjà les vues optouts —
   CLAUDE.md § « Réutiliser avant de développer ». Les règles ci-dessous sont
   donc de DISPOSITION seulement : ni couleur, ni soulignement, ni police. */
.vocnc-marketing__feature-alt-links {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);   /* 4px vertical, 12px horizontal */
  margin: var(--space-3) 0 0;
  font-size: var(--fs-ui);
  line-height: 1.5;
  color: var(--fg-muted);
}
/* Le libellé occupe TOUJOURS sa propre ligne (E §3.4, divergence assumée avec
   A3 §2.1) : les 7 liens démarrent alors au même bord gauche que le <h3> et le
   <p> de l'étape, à toutes les largeurs. C'est la correction directe de
   « l'alignement bancal », et elle ne coûte aucune ligne de hauteur — aux deux
   largeurs de référence la rangée wrappait de toute façon ; le libellé en ligne
   ne produisait qu'une première ligne courte et ragée. */
.vocnc-marketing__feature-alt-links-label {
  flex: 0 0 100%;
  margin-bottom: var(--space-1);
}
.vocnc-marketing__feature-alt-links .vocnc-link {
  display: inline-block;
  padding-block: 2px;    /* cible tactile ≥ 24 px de haut (WCAG 2.5.8) */
  white-space: nowrap;   /* « Star Rating » ne se coupe jamais */
}

@media (max-width: 720px) {
  .vocnc-marketing__feature-alt {
    grid-template-columns: 1fr;
    gap: var(--space-8);
    margin-bottom: var(--space-12);
  }
  .vocnc-marketing__feature-alt--reversed { direction: ltr; }
}

/* ============================================================
   USE CASES — 3 cards horizontales
   ============================================================ */
.vocnc-marketing__use-cases {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
}
.vocnc-marketing__use-case {
  background: var(--bg-page);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: border-color var(--duration-fast) var(--ease),
              transform var(--duration-fast) var(--ease);
}
.vocnc-marketing__use-case:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
}
.vocnc-marketing__use-case .vocnc-eyebrow {
  display: block;
  color: var(--accent);
}
.vocnc-marketing__use-case h3 {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: var(--fw-medium);
  letter-spacing: -0.005em;
  margin: 0;
  color: var(--fg);
}
.vocnc-marketing__use-case p {
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  line-height: 1.6;
  margin: 0;
}
/* .vocnc-marketing__use-case-quote / __use-case-author : SUPPRIMÉES avec les
   trois témoignages fabriqués (arbitrage DV-5, 12/08/2026). Les cartes de cas
   d'usage restent — elles décrivent des usages du produit, pas des clients. */

@media (max-width: 900px) {
  .vocnc-marketing__use-cases { grid-template-columns: 1fr; }
}

/* ============================================================
   FAQ — accordion natif <details>/<summary>
   ============================================================ */
.vocnc-marketing__faq {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 var(--container-gutter);
}
.vocnc-marketing__faq-item {
  border-bottom: 1px solid var(--border);
}
.vocnc-marketing__faq-q {
  padding: 18px 0;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: var(--fw-medium);
  color: var(--fg);
  list-style: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  line-height: 1.5;
  position: relative;
}
.vocnc-marketing__faq-q::-webkit-details-marker { display: none; }
.vocnc-marketing__faq-q::after {
  content: "+";
  font-family: var(--font-mono);
  font-size: 22px;
  color: var(--fg-muted);
  flex-shrink: 0;
  line-height: 1;
  transition: transform var(--duration-fast) var(--ease);
}
.vocnc-marketing__faq-item[open] .vocnc-marketing__faq-q::after {
  transform: rotate(45deg);
}
.vocnc-marketing__faq-a {
  padding: 0 0 18px;
  font-size: var(--fs-ui);
  color: var(--fg-muted);
  line-height: 1.6;
}

/* ============================================================
   FOOTER MARKETING — slate-900 dark, 4 cols + RESILIENCE bottom
   ============================================================ */
.vocnc-marketing__footer {
  background: var(--slate-900);
  color: #fff;
  padding: 80px 0 var(--space-8);
  margin-top: var(--space-24);
}
.vocnc-marketing__footer-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
}
.vocnc-marketing__footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--space-12);
  margin-bottom: 64px;
}
.vocnc-marketing__footer-brand .vocnc-wordmark {
  display: inline-block;
  margin-bottom: var(--space-4);
  font-size: 22px;
  color: #fff;
}
.vocnc-marketing__footer-brand .vocnc-wordmark-link {
  display: inline-block;
}
.vocnc-marketing__footer-brand p {
  font-size: var(--fs-ui);
  color: rgba(255, 255, 255, 0.66);
  line-height: 1.6;
  margin: 0;
  max-width: 360px;
}
.vocnc-marketing__footer-col h4 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  margin: 0 0 var(--space-4);
  font-weight: var(--fw-medium);
}
.vocnc-marketing__footer-col ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.vocnc-marketing__footer-col a {
  font-size: var(--fs-ui);
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
  line-height: 2;
  display: block;
}
.vocnc-marketing__footer-col a:hover { color: var(--accent); }

.vocnc-marketing__footer-bottom {
  padding-top: var(--space-6);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
}
.vocnc-marketing__footer-resilience {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.5);
  margin: 0;
}
.vocnc-marketing__footer-resilience a {
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
}
.vocnc-marketing__footer-resilience a:hover { color: var(--accent); }

@media (max-width: 900px) {
  .vocnc-marketing__footer-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-8);
  }
  .vocnc-marketing__footer-brand { grid-column: span 2; }
}
@media (max-width: 540px) {
  .vocnc-marketing__footer-grid { grid-template-columns: 1fr; }
  .vocnc-marketing__footer-brand { grid-column: auto; }
}
/*
 * Manifest compilé vers application.css.
 * Phase 02.3 (D-08) — ordre déterministe : tokens → layouts → components → self.
 * Source canonique tokens : .claude/skills/vocnc-design/colors_and_type.css
 *
 * Directive auto-tree retirée (Pitfall 1 RESEARCH §1) : ordre alphabétique
 * non déterministe. Tout fichier CSS futur doit être ajouté explicitement.
 *
 * #317 — `vocnc_components.css` (3 871 lignes) a été scindé en 8 fichiers
 * `components/*` aux frontières de ses bannières de section. L'ORDRE ci-dessous
 * est celui de l'ancien fichier : la concaténation des 8 lui est byte-identique,
 * donc la cascade est inchangée. Ne pas réordonner.
 *












 */
