/* ============================================================
   TYPEFACES — self-hosted, latin + latin-ext only (fr/en/es/it).
   Space Grotesk (display) carries the technical, engineered
   character of the brand; Inter (body) stays neutral and legible;
   IBM Plex Mono (data) marks anything that reads like a record —
   SRN codes, XSD versions, kickers, counters — the actual material
   of a regulatory data product, not a decorative accent.
   ============================================================ */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url('fonts/spacegrotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url('fonts/spacegrotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/plexmono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/plexmono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/plexmono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/plexmono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================
   DESIGN TOKENS
   Single source of truth. Every colour, size and shadow in this
   file resolves to one of these — never a raw hex value.
   ============================================================ */
:root {
  --font-display: 'Space Grotesk', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  /* Brand scale — calm institutional medical blue, less saturated,
     anchored on hospital/authority sites rather than SaaS marketing. */
  --c-brand-50:  #f0f6fa;
  --c-brand-100: #dfecf5;
  --c-brand-200: #c2dbec;
  --c-brand-300: #93bedd;
  --c-brand-400: #5c9ac6;
  --c-brand-500: #2f79ab;
  --c-brand-600: #1d6295;
  --c-brand-700: #175078;
  --c-brand-800: #123f5e;
  --c-brand-900: #0d2f47;
  --c-brand-950: #081f30;

  /* Verification accent — restrained clinical green, used sparingly */
  --c-teal-100: #ddefe6;
  --c-teal-300: #8cc7ab;
  --c-teal-400: #57b991;
  --c-teal-500: #2f7d5b;
  --c-teal-700: #1f5c42;

  /* Neutrals, blue-undertoned so they sit with the brand */
  --c-ink-50:  #f6f8fa;
  --c-ink-100: #eef2f6;
  --c-ink-200: #dfe6ed;
  --c-ink-300: #c7d3de;
  --c-ink-400: #94a5b5;
  --c-ink-500: #5c6f81;
  --c-ink-600: #485a6b;
  --c-ink-700: #344450;
  --c-ink-800: #1f2c37;
  --c-ink-900: #131d25;

  /* Status */
  --c-danger:      #c22b26;
  --c-danger-bg:   #fdecec;
  --c-danger-line: #f0b9b7;
  --c-warn:        #a96a06;
  --c-warn-bg:     #fff5e2;
  --c-warn-line:   #edcd94;
  --c-ok:          #12795b;
  --c-ok-bg:       #e6f5ef;
  --c-ok-line:     #a4d8c3;

  /* Couleurs du logo. Le logo étant un SVG en ligne et non une image, il
     suffit de les redéfinir pour le retourner sur une surface sombre — voir
     « Couleurs du logo » en fin de fichier. */
  --logo-encre:    var(--c-brand-900);   /* la grille, le chevron, « EUDA » */
  --logo-accent:   var(--c-brand-500);   /* la cellule mappée, « MED »      */
  --logo-lets:     var(--c-brand-600);   /* « Let's »                       */

  /* Semantic surface / text roles — themed below */
  --bg:            var(--c-ink-50);
  --bg-sunken:     var(--c-ink-100);
  --card:          #ffffff;
  --card-raised:   #ffffff;
  --text:          var(--c-ink-800);
  --text-muted:    var(--c-ink-500);
  /* Valeur littérale, PAS c-ink-400 (#94a5b5) : ce dernier ne tient que
     2.53:1 sur fond blanc, alors que --text-faint porte du texte réel
     (libellés KPI, compteurs, horodatages, messages d'état vide -- pas
     seulement des glyphes décoratifs) en petite taille (--fs-2xs/-xs),
     donc soumis au seuil normal AA 4.5:1, pas au seuil "grand texte"
     3:1. Mesuré (audit contraste, 2026-07-28) : 4.97:1 sur blanc, 4.67:1
     sur --bg. */
  --text-faint:    #5f7284;
  --border:        var(--c-ink-200);
  --border-strong: var(--c-ink-300);

  /* Legacy aliases — keep existing pages working, now on the new scale */

  /* --- Jetons hérités, redevenus effectifs (2026-08-14) -------------------
     Même défaut que celui décrit juste en dessous pour `--primary`, revenu à
     plus grande échelle : sept jetons étaient appelés SANS être definis nulle
     part -- ni ici, ni dans un gabarit, ni en JavaScript. Un `var()` non
     résolu ne retombe pas sur une valeur par défaut : la déclaration entière
     est ignorée. Cent vingt-six règles ne faisaient donc rien du tout, avec
     pour effet des fonds restés transparents et des textes revenus à la
     couleur héritée au lieu d'être atténués.

     Les définir en alias plutôt que remplacer 126 appels garde le correctif en
     un seul endroit, et laisse le thème sombre fonctionner : un alias est
     résolu au moment de l'usage, il suit donc la valeur que sa cible prend
     dans le thème courant.

     Ces noms restent des ALIAS de compatibilité. Pour tout nouveau code,
     utiliser directement la cible : --card, --bg-sunken, --primary-ink,
     --text-muted. */
  --surface:     var(--card);       /* 41 fonds de cartes et de panneaux */
  --surface-alt: var(--bg-sunken);  /* 27 fonds en retrait */
  --bg-surface:  var(--card);       /* 1 fond de case à cocher */
  --muted:       var(--text-muted); /* 4 textes secondaires */
  --c-warning:   var(--c-warn);     /* 2 pastilles d'avertissement */
  --fs-md:       var(--fs-h3);      /* 1 titre de sous-section, donc l'échelle h3 */
  /* --accent sert 8 fois en `color` et 4 fois en bordure, une seule fois en
     fond plein : c'est donc de l'encre, pas un aplat. Il pointe sur
     --primary-ink, qui s'éclaircit en thème sombre. L'unique fond plein est
     corrigé sur place vers --primary, sans quoi du texte blanc s'y poserait
     sur un bleu clair. */
  --accent:      var(--primary-ink);

  /* --- Marque : les trois seuls jetons à utiliser pour l'accent ----------
     `--primary` était appelé 74 fois sans avoir JAMAIS été défini : toutes
     ces règles étaient sans effet (fonds transparents, textes retombant sur
     la couleur héritée). Un seul jeton ne suffit pas, car l'accent sert à
     deux choses opposées :
       --primary       fond plein, du blanc se pose dessus  -> reste foncé
       --primary-ink   texte/glyphe posé sur une teinte      -> s'éclaircit
                                                                en thème sombre
       --primary-tint  la teinte douce sous ce texte
     Séparer les trois est ce qui permet au thème sombre d'inverser l'un
     sans casser l'autre. */
  --primary:      #1d6295;
  --primary-ink:  #1d6295;
  --primary-tint: #e8f0f7;

  --eu-blue-darkest: var(--c-brand-900);
  --eu-blue-dark:    var(--c-brand-600);
  --eu-blue:         var(--c-brand-500);
  --eu-blue-light:   var(--c-brand-50);
  --accent-amber:    var(--c-warn);
  --accent-amber-bg: var(--c-warn-bg);
  --danger:          var(--c-danger);
  --danger-bg:       var(--c-danger-bg);
  --success:         var(--c-ok);
  --success-bg:      var(--c-ok-bg);

  /* Type scale — institutional: strong but never spectacular. */
  --fs-display: clamp(2.3rem, 4.4vw, 3.6rem);
  --fs-h1:      clamp(1.75rem, 2.9vw, 2.35rem);
  --fs-h2:      clamp(1.45rem, 2.2vw, 1.85rem);
  --fs-h3:      1.1rem;
  --fs-lead:    clamp(1rem, 1.3vw, 1.12rem);
  --fs-body:    0.95rem;
  --fs-sm:      0.855rem;
  --fs-xs:      0.775rem;
  --fs-2xs:     0.70rem;

  --tracking-display: -0.03em;
  --tracking-tight:   -0.012em;
  --tracking-wide:    0.1em;

  /* Spacing */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 22px;  --sp-6: 28px;  --sp-7: 36px;  --sp-8: 46px;
  --sp-9: 58px;  --sp-10: 72px; --sp-11: 88px; --sp-12: 108px;

  /* Radii — institutional: crisp corners, nothing pill-shaped but controls */
  --r-xs:   4px;
  --r-sm:   6px;
  --r-md:   8px;
  --r-lg:   10px;
  --r-xl:   14px;
  --r-full: 999px;
  --radius: var(--r-md);

  /* Elevation — barely-there, structure comes from borders not shadows */
  --sh-xs: 0 1px 2px rgba(13, 47, 71, .05);
  --sh-sm: 0 1px 4px rgba(13, 47, 71, .06);
  --sh-md: 0 3px 10px rgba(13, 47, 71, .07);
  --sh-lg: 0 8px 22px rgba(13, 47, 71, .08);
  --sh-xl: 0 14px 36px rgba(13, 47, 71, .10);

  /* Layout */
  --page-max: 1200px;
  --gutter:   clamp(20px, 4vw, 34px);

  --ease: cubic-bezier(.4, 0, .2, 1);
}

* { box-sizing: border-box; }

/* Les sections ancrées s'arrêtent sous le header sticky, pas derrière. */
[id] { scroll-margin-top: 92px; }
/* Defilement adouci vers les ancres -- la navigation publique en utilise
   sept. Sous garde de mouvement reduit : pour qui a demande moins
   d'animations, un saut net est le comportement attendu, pas un defaut. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ============================================================
   SIGNATURE MOTIF — "precision instrument for regulatory data"
   Applied with restraint to the 2-3 moments that actually show the
   product (hero, validation demo): a registration frame on opposite
   corners, a faint blueprint grid, a live status dot. Everywhere
   else stays quiet so these moments keep their weight.
   ============================================================ */
/* A literal element, not a ::before — several sections already carry
   a "kill legacy pseudo-element" override from an earlier redesign
   pass, so a real node is the robust way to layer this in. */
.blueprint-layer {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(var(--grid-line, rgba(47, 121, 171, .08)) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line, rgba(47, 121, 171, .08)) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: radial-gradient(ellipse 70% 65% at 55% 35%, #000 0%, transparent 78%);
          mask-image: radial-gradient(ellipse 70% 65% at 55% 35%, #000 0%, transparent 78%);
}
:root[data-theme="dark"] .blueprint-layer { --grid-line: rgba(122, 176, 219, .10); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .blueprint-layer { --grid-line: rgba(122, 176, 219, .10); } }

/* Registration marks on opposite corners only — a full 4-corner frame
   reads as decoration, two corners read as "this is being measured." */
.instrument-frame { position: relative; }
.instrument-frame::before,
.instrument-frame::after {
  content: ""; position: absolute;
  width: 22px; height: 22px;
  pointer-events: none;
}
.instrument-frame::before {
  top: -9px; left: -9px;
  border-top: 2px solid var(--c-brand-400);
  border-left: 2px solid var(--c-brand-400);
}
.instrument-frame::after {
  bottom: -9px; right: -9px;
  border-bottom: 2px solid var(--c-brand-400);
  border-right: 2px solid var(--c-brand-400);
}
/* On a marine background the mid-tone brand blue disappears — lighten it. */
.market-validation .instrument-frame::before, .market-validation .instrument-frame::after,
.deployment-section .instrument-frame::before, .deployment-section .instrument-frame::after {
  border-color: var(--c-brand-200);
}

/* Directional glow behind a featured product visualization — soft,
   never a hard-edged blob. */
.instrument-glow { position: relative; }
.instrument-glow::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -15%;
  background: radial-gradient(ellipse 60% 55% at 50% 35%, color-mix(in srgb, var(--c-brand-400) 30%, transparent), transparent 70%);
  filter: blur(6px);
}

.live-dot {
  display: inline-block; flex: none;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--c-ok);
  animation: live-pulse 2.6s ease-in-out infinite;
}
/* ---------- Motion: entrance + scroll reveal ---------- */
.market-hero-copy > * { opacity: 0; animation: hero-rise .6s var(--ease) both; }
.market-hero-copy > .market-kicker    { animation-delay: .04s; }
.market-hero-copy > h1                { animation-delay: .13s; }
.market-hero-copy > .market-hero-lead { animation-delay: .24s; }
.market-hero-copy > .market-hero-actions { animation-delay: .35s; }
.market-hero-copy > .market-hero-note { animation-delay: .44s; }
.market-hero-media { opacity: 0; animation: hero-rise-media .7s .22s var(--ease) both; }
@keyframes hero-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes hero-rise-media { from { opacity: 0; transform: translateY(18px) scale(.985); } to { opacity: 1; transform: none; } }

/* Scoped to .js (set by an inline script in <head> the instant it
   runs): only script ever adds .is-visible, so hiding [data-reveal]
   unconditionally would leave it at opacity:0 forever for anyone
   without JavaScript. Under .js the CSS below still hides it first —
   the JS-driven IntersectionObserver is what brings it back. */
.js [data-reveal] {
  opacity: 0; transform: translateY(18px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
[data-reveal].is-visible { opacity: 1; transform: none; }

/* Staggered group reveal: direct children of a [data-reveal-group]
   step in one after another instead of the whole track fading as a
   single block — a workflow track "builds itself" left to right, a
   card grid settles in row by row. */
.js [data-reveal-group] > * {
  opacity: 0; transform: translateY(16px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
[data-reveal-group].is-visible > * { opacity: 1; transform: none; }
[data-reveal-group] > *:nth-child(1) { transition-delay: 0ms; }
[data-reveal-group] > *:nth-child(2) { transition-delay: 70ms; }
[data-reveal-group] > *:nth-child(3) { transition-delay: 140ms; }
[data-reveal-group] > *:nth-child(4) { transition-delay: 210ms; }
[data-reveal-group] > *:nth-child(5) { transition-delay: 280ms; }
[data-reveal-group] > *:nth-child(6) { transition-delay: 350ms; }
[data-reveal-group] > *:nth-child(7) { transition-delay: 420ms; }
[data-reveal-group] > *:nth-child(8) { transition-delay: 490ms; }

/* Flow diagram (Excel -> Contrôle -> XML): connectors draw themselves
   and nodes pop in left to right, riding the same .is-visible trigger
   as the section's [data-reveal] heading — a diagram-specific
   entrance instead of a flat fade of the whole SVG. */
.js .flow-diagram .flow-node { opacity: 0; transform: scale(.82); transform-box: fill-box; transform-origin: center; }
.js .flow-diagram .flow-label { opacity: 0; }
.js .flow-diagram .flow-arrow { opacity: 0; stroke-dasharray: 60; stroke-dashoffset: 60; }
@keyframes flow-pop  { from { opacity: 0; transform: scale(.82); } to { opacity: 1; transform: scale(1); } }
@keyframes flow-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes flow-draw { from { opacity: 1; stroke-dashoffset: 60; } to { opacity: 1; stroke-dashoffset: 0; } }
[data-reveal].is-visible .flow-diagram .flow-node  { animation: flow-pop .45s var(--ease) both; }
[data-reveal].is-visible .flow-diagram .flow-label { animation: flow-fade .35s var(--ease) both; }
[data-reveal].is-visible .flow-diagram .flow-arrow { animation: flow-draw .4s var(--ease) both; }
[data-reveal].is-visible .flow-diagram > *:nth-child(1)  { animation-delay: .05s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(2)  { animation-delay: .30s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(3)  { animation-delay: .40s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(4)  { animation-delay: .48s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(5)  { animation-delay: .60s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(6)  { animation-delay: .85s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(7)  { animation-delay: .95s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(8)  { animation-delay: 1.03s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(9)  { animation-delay: 1.15s; }
[data-reveal].is-visible .flow-diagram > *:nth-child(10) { animation-delay: 1.40s; }
@media (prefers-reduced-motion: reduce) {
  [data-reveal-group] > * { transition-delay: 0ms !important; }
  .flow-diagram .flow-node, .flow-diagram .flow-label, .flow-diagram .flow-arrow { animation: none !important; opacity: 1; transform: none; stroke-dashoffset: 0; }
}
@keyframes live-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--c-ok) 45%, transparent); }
  50%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--c-ok) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-size: 15px;
}

a { color: var(--eu-blue); }

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 700; }

/* ---------------- Header ---------------- */
.lang-switch { display: flex; gap: 2px; }
.lang-switch a {
  padding: 2px 7px;
  border-radius: 4px;
  font-weight: 600;
  letter-spacing: 0.03em;
}
.lang-switch a.active { background: rgba(255,255,255,0.18); color: #fff; }

.topbar {
  background: var(--card);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
.brand {
  font-size: 1.08rem;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 12px;
  white-space: nowrap;
}
.brand-logo { display:block; flex-shrink:0; }
.brand small { display: block; font-weight: 400; font-size: 0.72rem; color: var(--text-muted); margin-top: 1px; }

.topbar-nav { display: flex; align-items: center; gap: 10px; }
.user-chip { color: var(--text-muted); font-size: 0.85rem; padding-right: 6px; }

/* Modules dropdown */
.modules-bar {
  background: var(--eu-blue-dark);
}
.modules-bar-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  align-items: stretch;
}
.dropdown { position: relative; }
.dropdown-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  color: #fff;
  font-size: 0.9rem;
  font-weight: 600;
  padding: 13px 18px;
  cursor: pointer;
  font-family: inherit;
}
.dropdown-trigger:hover, .modules-bar a.top-link:hover { background: rgba(255,255,255,0.08); }
.dropdown-trigger .chevron { transition: transform 0.15s ease; font-size: 0.7rem; }
.dropdown.open .dropdown-trigger .chevron { transform: rotate(180deg); }
.modules-bar a.top-link {
  color: #fff;
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  padding: 13px 18px;
  display: flex;
  align-items: center;
}

.dropdown-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-top: 3px solid var(--eu-blue);
  box-shadow: 0 12px 30px rgba(4, 33, 61, 0.18);
  min-width: 620px;
  padding: 10px;
  z-index: 40;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.dropdown.open .dropdown-panel { display: grid; }
.dropdown-item {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 9px 12px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text);
}
.dropdown-item:hover { background: var(--eu-blue-light); }
.dropdown-item .dd-icon { flex-shrink: 0; color: var(--eu-blue); margin-top: 2px; }
.dropdown-item .dd-label { font-weight: 600; font-size: 0.86rem; line-height: 1.35; }

@media (max-width: 760px) {
  .dropdown-panel { grid-template-columns: 1fr; min-width: 280px; left: -10px; }
}

/* ---------------- Breadcrumb ---------------- */
.breadcrumb {
  max-width: 1180px;
  margin: 0 auto;
  padding: 14px 24px 0;
  font-size: 0.82rem;
  color: var(--text-muted);
}
.breadcrumb a { color: var(--text-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--eu-blue); text-decoration: underline; }
.breadcrumb .sep { margin: 0 6px; }
.breadcrumb .current { color: var(--text); font-weight: 600; }

/* ---------------- Layout ---------------- */
.page {
  flex: 1;
  max-width: 1180px;
  width: 100%;
  margin: 0 auto;
  padding: 18px 24px 60px;
}

.footer {
  background: var(--eu-blue-darkest);
  color: #a9c2dc;
  margin-top: 40px;
}
.footer-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 30px 24px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px;
  font-size: 0.82rem;
}
.footer-col h4 { color: #fff; font-size: 0.85rem; margin: 0 0 10px; }
.footer-col a { color: #a9c2dc; text-decoration: none; display: block; margin-bottom: 6px; }
.footer-col a:hover { color: #fff; }
.footer-bottom {
  text-align: center;
  padding: 12px;
  font-size: 0.75rem;
  background: #031627;
  color: #7d97b3;
}

h1 { font-size: 1.55rem; margin: 4px 0 6px; color: var(--text); }
h2 { font-size: 1.1rem; margin: 0 0 4px; }
p.subtitle { color: var(--text-muted); margin-top: 0; margin-bottom: 24px; }

/* ---------------- Search ---------------- */
.search-box { position: relative; margin-bottom: 24px; }
.search-box input {
  width: 100%;
  padding: 12px 16px 12px 42px;
  border-radius: 10px;
  border: 1px solid var(--border);
  font-size: 0.95rem;
  background: var(--card) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%235c6b7a' stroke-width='2' viewBox='0 0 24 24'><circle cx='11' cy='11' r='7'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>") no-repeat 14px center;
}
.search-empty { display: none; color: var(--text-muted); font-size: 0.9rem; padding: 20px 0; }

/* ---------------- Cards / tiles ---------------- */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 16px;
}

/* Dashboard module card: a compact clickable row, not a padded panel —
   icon, name, operation count, an independent ⓘ button, a clickable hint.
   The description that used to sit here now lives in the ⓘ help panel only. */
.tile {
  position: relative;
  display: flex; align-items: center; gap: var(--sp-3);
  background: var(--card-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4);
  color: var(--text);
  transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.tile:hover {
  box-shadow: 0 8px 20px rgba(11, 95, 168, 0.14);
  border-color: var(--eu-blue);
  transform: translateY(-2px);
}
.tile-link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.tile-icon {
  width: 40px; height: 40px; flex: none;
  border-radius: 9px;
  background: var(--eu-blue-light);
  color: var(--eu-blue);
  display: flex; align-items: center; justify-content: center;
}
.tile-index {
  display: inline-block;
  background: var(--eu-blue);
  color: white;
  font-weight: 700;
  font-size: 0.72rem;
  padding: 2px 8px;
  border-radius: 999px;
  margin-right: 8px;
  vertical-align: middle;
}
.tile-copy { flex: 1; min-width: 0; }
.tile-copy h3 { font-size: 0.9rem; line-height: 1.3; margin: 0; }
.tile-info-btn {
  position: relative; z-index: 2; flex: none;
  width: 28px; height: 28px; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--card); color: var(--text-faint); cursor: pointer;
}
.tile-info-btn:hover, .tile-info-btn:focus-visible { border-color: var(--c-brand-300); color: var(--c-brand-600); outline: 0; }
.tile-arrow { flex: none; color: var(--c-brand-500); font-size: 1.1rem; }

.op-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.op-list a {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 15px 18px;
  text-decoration: none;
  color: var(--text);
}
.op-list a:hover { border-color: var(--eu-blue); background: var(--eu-blue-light); }
.op-icon { color: var(--eu-blue); flex-shrink: 0; margin-top: 2px; }
.op-title { font-weight: 600; display: block; }
/* Résumé de l'opération, sous le titre. Tronqué à deux lignes : la carte doit
   renseigner d'un coup d'œil, pas devenir un pavé. Le texte entier reste
   accessible par le bouton d'information, qui n'est plus le SEUL moyen de
   savoir ce que fait l'opération. `align-items: center` sur la carte suffit à
   recentrer le bloc, aucune autre règle n'est à toucher. */
.op-summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin-top: 3px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.45;
}

.back-link { display: inline-flex; align-items: center; gap: 5px; margin-bottom: 14px; color: var(--text-muted); text-decoration: none; font-size: 0.87rem; }
.back-link:hover { color: var(--primary-ink); }

form.stack { display: flex; flex-direction: column; gap: 14px; max-width: 480px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
label { font-weight: 600; font-size: 0.87rem; display: block; margin-bottom: 6px; }
.form-label { font-weight: 600; font-size: 0.87rem; display: block; margin-bottom: 6px; }
/* input[type=search] et les champs temporels : des types que les navigateurs
   habillent avec un rendu natif propre (chrome de recherche, sélecteur de
   date OS) qui, sans appearance:none, ignore une partie du CSS ci-dessous
   (bordure/rayon notamment) -- bug réel mesuré sur le champ de recherche
   admin (Demandes d'accès) : bordure "2px inset" grise et rayon 0, valeurs
   par défaut du navigateur, jamais définies par ce fichier, alors que le
   select juste à côté recevait bien border:1px solid + rayon 8px. Ajouté
   aux DEUX types manquants ici, pas seulement au cas signalé -- toute autre
   page utilisant l'un ou l'autre (dates Statistiques/Activité/Journal,
   recherche Support/Journal...) partage exactement le même défaut. */
/* datetime-local a rejoint cette liste le 2026-08-23, sur le même défaut et
   pour la troisième fois : aucune page n'employait ce type quand search et
   date y ont été ajoutés, il est donc resté dehors. Un champ absent de cette
   liste ne reçoit RIEN — ni largeur, ni hauteur, ni bordure, ni police —
   d'où les deux champs d'horodatage de « Modifier l'annonce », plus courts,
   plus fins et dans la police par défaut du navigateur, à côté de deux menus
   correctement habillés. tests/test_champs_saisie_habilles.py interdit
   désormais qu'un quatrième type suive le même chemin. */
input[type=text], input[type=email], input[type=password], input[type=file],
input[type=tel], input[type=number], input[type=search], input:is([type=date],[type=datetime-local]),
textarea, select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.95rem;
  background: #fff;
  color: var(--text);
  font-family: inherit;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
}
/* Flèche descendante explicite : appearance:none retire l'indicateur natif
   des listes. Tous les select simples conservent maintenant le même repère,
   aligné à droite et centré verticalement. */
select:not([multiple]) {
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%237d91a5' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 12px 8px;
}
/* appearance:none seul retire aussi l'icône calendrier native sur certains
   navigateurs (Firefox) -- ::-webkit-calendar-picker-indicator (Chrome/Edge)
   reste géré nativement même avec appearance:none sur le champ lui-même,
   donc toujours visible et cliquable là où c'est le cas. */
input[type=search]::-webkit-search-cancel-button { -webkit-appearance: none; }
input:is([type=date],[type=datetime-local])::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .6; }
input:is([type=date],[type=datetime-local])::-webkit-calendar-picker-indicator:hover { opacity: 1; }
input:focus, select:focus { outline: 2px solid var(--eu-blue); outline-offset: 1px; }

.btn {
  display: inline-block;
  background: var(--eu-blue);
  color: white;
  /* Bordure transparente plutôt qu'absente : les variantes qui en ajoutent
     une visible (.btn-ghost, .btn-outline) occupent alors exactement le même
     espace. Avec « border:none », un bouton bordé mesurait 2 px de plus que
     son voisin plein, sur toute l'application. */
  border: 1px solid transparent;
  padding: 11px 22px;
  border-radius: 8px;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  /* Hauteur identique quel que soit l'élément porteur : un <button> hérite
     de line-height:normal (~1.15) alors qu'un <a> hérite de celle du corps
     de page (1.5). Sans cette valeur explicite, deux boutons côte à côte
     n'ont pas la même hauteur — constaté sur « Exporter le XML » (38 px)
     face à « Importer un autre fichier » (46 px). */
  line-height: 1.4;
}
.btn:hover { background: var(--eu-blue-dark); }
.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
}
.btn-ghost:hover { color: var(--text); border-color: var(--text-muted); background: transparent; }
/* align-items:center — sans cela, un bouton enveloppé dans un <form> (donc
   dont le <form> est l'élément flex) et un lien direct ne s'étirent pas de
   la même façon et se retrouvent décalés verticalement. */
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.nav-link { padding: 8px 14px; border-radius: 8px; font-size: 0.87rem; text-decoration: none; }

.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  background: #fff;
  color: #3c4043;
  border: 1px solid #dadce0;
  padding: 11px 18px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
}
.btn-google:hover { background: #f7f8f8; }
.or-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-muted);
  font-size: 0.78rem;
  margin: 18px 0;
}
.or-divider::before, .or-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.flash-stack,
.public-flash-stack {
  position: fixed;
  top: 76px;
  right: 22px;
  z-index: 1500;
  width: min(460px, calc(100vw - 32px));
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
}
.flash {
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) 30px;
  align-items: center;
  gap: 10px;
  min-height: 58px;
  padding: 12px 12px 12px 14px;
  overflow: hidden;
  border: 1px solid rgba(110, 231, 183, .42);
  border-radius: 12px;
  background: rgba(7, 69, 51, .91);
  color: #d8fff0;
  box-shadow: 0 16px 38px rgba(0, 20, 30, .26);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  font-size: .9rem;
  line-height: 1.45;
  pointer-events: auto;
  animation: flash-enter .26s ease-out both;
}
.flash-indicator {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(110, 231, 183, .18);
  color: #6ee7b7;
  font-weight: 800;
}
.flash-message { min-width: 0; overflow-wrap: anywhere; }
.flash-close {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: currentColor;
  font-size: 1.25rem;
  cursor: pointer;
  opacity: .72;
}
.flash-close:hover,
.flash-close:focus-visible { background: rgba(255,255,255,.12); opacity: 1; outline: none; }
.flash-error {
  border-color: rgba(248, 113, 113, .55);
  background: rgba(91, 25, 25, .94);
  color: #fee2e2;
}
.flash-error .flash-indicator { background: rgba(248,113,113,.17); color: #fca5a5; }
.flash-warning { border-color: rgba(251,191,36,.5); background: rgba(93,58,8,.93); color: #fef3c7; }
.flash-warning .flash-indicator { background: rgba(251,191,36,.17); color: #fcd34d; }
/* La barre de decompte est verte par defaut : sur un fond ambre elle jurerait,
   et une barre verte sous un avertissement envoie le mauvais signal. */
.flash-warning .flash-timer { background: rgba(251,191,36,.75); }
.flash-timer {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 3px;
  background: rgba(110,231,183,.75);
  transform-origin: left;
  animation: flash-life 8s linear forwards;
}
.flash.is-paused .flash-timer { animation-play-state: paused; }
.flash.is-leaving { animation: flash-leave .26s ease-in both; }
@keyframes flash-enter { from { opacity: 0; transform: translate3d(22px,-6px,0); } }
@keyframes flash-leave { to { opacity: 0; transform: translate3d(24px,0,0); } }
@keyframes flash-life { to { transform: scaleX(0); } }
@media (max-width: 640px) {
  .flash-stack, .public-flash-stack { top: 66px; right: 16px; left: 16px; width: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .flash, .flash-timer, .flash.is-leaving { animation: none; }
}

/* Bandeau d'annonces plateforme (Administration > Maintenance) — affiché en
   pleine largeur au-dessus du contenu, sous l'en-tête. Chaque annonce se
   masque individuellement (voir _announcement_banner.html) donc elles
   s'empilent verticalement sans jamais se chevaucher. */
.announcement-banner-stack { display: flex; flex-direction: column; }
.announcement-banner {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px var(--sp-6, 24px); font-size: var(--fs-sm, 0.855rem);
  border-bottom: 1px solid var(--border);
}
/* SANS CETTE LIGNE, LA CROIX NE FERME RIEN. Le script de fermeture pose
   l'attribut ``hidden``, dont l'effet (display:none) vient de la feuille du
   NAVIGATEUR — que n'importe quelle règle d'auteur écrase, y compris le
   `display:flex` ci-dessus. Le bandeau restait donc affiché, et l'annonce
   déjà « fermée » revenait à chaque page. Même piège partout ailleurs où un
   élément masquable reçoit un display explicite. */
.announcement-banner[hidden] { display: none; }
.announcement-banner-icon { flex: none; margin-top: 1px; }
.announcement-banner-text { flex: 1; margin: 0; line-height: 1.5; overflow-wrap: anywhere; }
.announcement-banner-close {
  flex: none; border: 0; background: transparent; cursor: pointer; line-height: 1;
  font-size: 1.1rem; padding: 0 4px; color: inherit; opacity: 0.7;
}
.announcement-banner-close:hover, .announcement-banner-close:focus-visible { opacity: 1; }
.announcement-banner.is-info { background: var(--eu-blue-light); color: var(--primary-ink); }
.announcement-banner.is-warning { background: var(--c-warn-bg); color: var(--c-warn); }
/* 'maintenance' = le niveau le plus sévère (non fermable) : un rouge plein
   et assumé, pas la même teinte pastel que les erreurs de formulaire —
   doit se voir immédiatement, sur les deux thèmes. */
.announcement-banner.is-maintenance { background: #b91c1c; color: #fff; border-bottom-color: #7f1d1d; }
.announcement-banner.is-maintenance .announcement-banner-icon { color: #fff; }

.support-attachment-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.support-attachment-list li { display: flex; align-items: center; gap: 6px; font-size: var(--fs-sm); flex-wrap: wrap; }
.support-attachment-list a { word-break: break-all; }
.support-attachment-name { word-break: break-all; font-weight: 600; }
.support-thread-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.support-thread-header h2 { margin: 0; }
.support-thread-filter { margin: 0; }

/* ---------------- Zone de dépôt des pièces jointes (Support) ---------------- */
.attachments-dropzone {
  border: 1.5px dashed var(--border); border-radius: var(--r-md);
  padding: 14px; background: var(--card); transition: border-color .12s, background .12s;
}
.attachments-dropzone.is-dragover { border-color: var(--c-brand-500); background: var(--c-brand-50); }
.attachments-dropzone input[type="file"] { display: block; width: 100%; max-width: 100%; }
.attachments-dropzone-hint {
  margin: 8px 0 0; font-size: var(--fs-2xs); color: var(--text-muted);
}
.attachments-dropzone-hint:empty { display: none; }
.attachments-filelist { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.attachments-filelist:empty { margin: 0; }
.attachments-file-item {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 10px; border-radius: var(--r-sm); background: var(--bg-sunken); font-size: var(--fs-sm);
}
.attachments-file-item.is-rejected { background: var(--c-danger-bg); }
.attachments-file-name { font-weight: 600; word-break: break-all; }
.attachments-file-meta { color: var(--text-muted); font-size: var(--fs-2xs); white-space: nowrap; }
.attachments-file-warning { color: var(--c-danger); font-size: var(--fs-2xs); flex-basis: 100%; }
.attachments-file-remove {
  margin-left: auto; border: none; background: transparent; color: var(--text-muted); cursor: pointer;
  font-size: 1.1rem; line-height: 1; padding: 2px 6px; border-radius: var(--r-sm);
}
.attachments-file-remove:hover { color: var(--c-danger); background: var(--c-danger-bg); }
.attachments-file-remove:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 1px; }

/* ---------------- DataGrid — composant tableau unique ----------------
   Chrome partagé par TOUS les tableaux (voir _datagrid.html) : barre
   d'outils, menu colonnes, barre d'actions groupées, poignées de
   redimensionnement/réorganisation. Les styles de cellules restent ceux
   du design system (.datatable, .col-*, badges). */
.dg-topbar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border); background: var(--card-raised); flex-wrap: wrap; min-height: 44px; }
/* `overflow-y: visible` était écrit ici pour laisser l'en-tête collant
   s'échapper vers le haut. La spécification l'en empêchait : dès qu'un axe
   n'est pas `visible`, l'autre passe à `auto`. Le cadre devenait donc un
   CONTENEUR DE DÉFILEMENT, et un `position: sticky` à l'intérieur s'ancre
   sur lui, pas sur la fenêtre — l'en-tête défilait avec la table au lieu de
   rester sous la barre de filtres. L'intention d'origine était bonne, elle
   était simplement inatteignable par ce moyen.

   Le cadre ne devient donc défilant QUE lorsqu'il déborde réellement. La
   classe est posée par app-shell.js (updateScrollableRegion), et sa
   détection — scrollWidth > clientWidth — reste valide sans overflow :
   mesuré à 700px de large, 1283 contre 674 dans les deux modes.

   MAIS CETTE CLASSE SE POSAIT À TORT ET NE SE RETIRAIT JAMAIS, et c'est ce
   qui a produit les captures du 2026-08-24 : sur /admin/fichiers en 1920 px,
   la table TIENT — 1448 contre 1448, mesuré après retrait de la classe — et
   le cadre était pourtant défilant, donc l'en-tête se posait 167 px SOUS le
   haut du cadre, en plein milieu des lignes. Correctif côté app-shell.js.

   Quand le débordement est RÉEL (/admin/messages en 1920 px : 1813 contre
   1448), on ne peut pas avoir les deux — un en-tête ancré à la fenêtre ne
   survit pas à un ancêtre défilant. Il se colle alors au haut du cadre :
   voir la règle `top: 0` posée avec les en-têtes collants plus bas. */
.dg-table-scroll { overflow: visible; }
.dg-table-scroll.is-horizontally-scrollable { overflow-x: auto; }
.dg-topbar-right { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; flex-wrap: wrap; }
.dg-bulkbar { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); }
.dg-bulkbar[hidden] { display: none; }
.dg-bulk-count { font-weight: 700; color: var(--primary-ink); }
.dg-bulkbar select { min-height: 30px; font-size: var(--fs-xs); padding: 4px 8px; }

.dg-colmenu { position: relative; }
.dg-colmenu-panel { right: 0; left: auto; min-width: 220px; max-height: calc(100vh - 96px); overflow-y: auto; padding: var(--sp-2); display: none; flex-direction: column; gap: 4px; }
.dg-colmenu.open .dg-colmenu-panel { display: flex; }
.dg-colmenu-item { display: flex; align-items: center; gap: 9px; width:100%; min-height:38px; box-sizing:border-box; margin:0; border:0; background:transparent; color:inherit; text-align:left; font:inherit; font-size: var(--fs-sm); font-weight: 500; padding: 8px 9px; border-radius: var(--r-sm); cursor: pointer; user-select:none; }
.dg-colmenu-item:hover { background: var(--bg-sunken); }
.dg-colmenu-item:focus-visible { outline:2px solid var(--c-brand-500); outline-offset:-2px; }
.dg-colcheck { position:relative; flex:0 0 14px; width:14px; height:14px; box-sizing:border-box; border:1px solid var(--border-strong); border-radius:2px; background:var(--bg-surface); }
.dg-colmenu-item[aria-checked="true"] .dg-colcheck { border-color:var(--c-brand-500); background:var(--c-brand-500); }
.dg-colmenu-item[aria-checked="true"] .dg-colcheck::after { content:""; position:absolute; left:3px; top:3px; width:6px; height:3px; border-left:2px solid #fff; border-bottom:2px solid #fff; transform:rotate(-45deg); }
/* Trois actions distinctes (Tout afficher / Réinitialiser l'affichage /
   Enregistrer comme affichage par défaut) empilées en pleine largeur,
   séparées visuellement de la liste de colonnes par une bordure -- une
   ligne horizontale de 3 boutons déborderait le panneau (220px) vu la
   longueur de "Enregistrer comme affichage par défaut". */
.dg-colmenu-actions { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--border); }
.dg-colmenu-actions .btn { width: 100%; justify-content: center; }

.dg-th-inner { display: flex; align-items: center; gap: 4px; position: relative; padding-right: 10px; }
.dg-sort { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 3px; }
.dg-sort .sort-icon { opacity: 0; transition: opacity .12s, transform .12s; }
.dg-sort.is-sorted .sort-icon { opacity: 1; }
.dg-sort.is-desc .sort-icon { transform: rotate(180deg); }
th:hover .dg-sort .sort-icon { opacity: .5; }
.dg-drag { opacity: 0; cursor: grab; font-size: 10px; letter-spacing: -2px; color: var(--text-faint); user-select: none; }
th:hover .dg-drag { opacity: 1; }
th.dg-dragging { opacity: .5; }
th.dg-dropover { box-shadow: inset 3px 0 0 var(--eu-blue); }
.dg-resize { position: absolute; top: -10px; bottom: -10px; right: -8px; width: 12px; cursor: col-resize; }
.dg-resize:hover { background: linear-gradient(to right, transparent 5px, var(--c-brand-300) 5px, var(--c-brand-300) 7px, transparent 7px); }
body.dg-resizing { cursor: col-resize; user-select: none; }

.badge-muted { background: var(--bg-sunken); color: var(--text-muted); }

/* Cloche de notifications (en-têtes utilisateur et admin) — composant
   unique _notif_bell.html, menu déroulant d'aperçu rapide. */
.notif-bell-trigger { position: relative; }
.notif-badge {
  position: absolute; top: -4px; right: -6px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 999px; background: var(--danger); color: #fff; font-size: 10px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}
.notif-row-unread td { font-weight: 600; }
.notif-row-unread td.cell-muted, .notif-row-unread .cell-muted { font-weight: 400; }

.notif-dropdown-panel {
  grid-template-columns: 1fr; width: 340px; min-width: 340px;
  max-width: min(340px, calc(100vw - 2 * var(--sp-4)));
  right: 0; left: auto; padding: 0; gap: 0; overflow: hidden;
}
.notif-dropdown-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border);
}
.notif-dropdown-header strong { font-size: var(--fs-sm); color: var(--text); }
.notif-dropdown-markall {
  border: 0; background: none; color: var(--c-brand-600); font: inherit; font-size: var(--fs-2xs);
  font-weight: 700; cursor: pointer; padding: 0;
}
.notif-dropdown-markall:hover { text-decoration: underline; }
.notif-dropdown-list { list-style: none; margin: 0; padding: 0; max-height: 360px; overflow-y: auto; }
.notif-dropdown-item {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4); text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--border);
}
.notif-dropdown-item:hover { background: var(--bg-sunken); }
.notif-dropdown-list li:last-child .notif-dropdown-item { border-bottom: 0; }
.notif-dropdown-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; background: transparent; }
.notif-dropdown-list li.is-unread .notif-dropdown-dot { background: var(--c-brand-500); }
.notif-dropdown-copy { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.notif-dropdown-title { font-size: var(--fs-xs); font-weight: 600; color: var(--text); }
.notif-dropdown-list li.is-unread .notif-dropdown-title { font-weight: 700; }
.notif-dropdown-body {
  margin: 0; font-size: var(--fs-xs); color: var(--text-muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.notif-dropdown-time { font-size: var(--fs-2xs); color: var(--text-faint); }
.notif-dropdown-empty { margin: 0; padding: var(--sp-5) var(--sp-4); text-align: center; color: var(--text-muted); font-size: var(--fs-xs); }
.notif-dropdown-seeall {
  display: block; text-align: center; padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border); font-size: var(--fs-xs); font-weight: 700;
  color: var(--c-brand-600); text-decoration: none;
}
.notif-dropdown-seeall:hover { background: var(--bg-sunken); }
@media (max-width: 1023.98px) {
  /* La cloche n'est pas l'élément le plus à droite de l'en-tête (thème,
     langue, compte suivent) : un panneau ancré `right:0` à SA position
     déborderait à gauche de l'écran dès que la largeur ne laisse plus
     340px à sa gauche (mesuré : débordement réel jusqu'à ~1000px).
     Position fixe, calée sur le viewport, comme le menu Modules
     (voir base.html) qui a le même problème avec le même mécanisme. */
  .notif-dropdown-panel {
    position: fixed; top: var(--header-h, 60px); left: var(--sp-3); right: var(--sp-3);
    width: auto; min-width: 0; max-width: none;
  }
}

/* Note interne Support : visuellement distincte d'une vraie réponse */
.support-internal-note { background: var(--c-warn-bg) !important; border-style: dashed !important; }

.stats-delta { font-size: var(--fs-xs); font-weight: 700; margin-left: 6px; vertical-align: middle; }
.stats-delta.is-up { color: var(--success); }
.stats-delta.is-down { color: var(--danger); }
.dg-selcol { width: 36px; text-align: center; }
.dg-selcol input { width: auto; margin: 0; }
.dg-table td.dg-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 280px; }
@media (max-width: 720px) {
  .dg-table td.dg-ellipsis { max-width: none; white-space: normal; }
  .dg-drag, .dg-resize { display: none; } /* tactile : pas de drag/resize, le serveur reste maître */
}

.login-wrap { display: flex; justify-content: center; padding-top: 30px; }
.login-card { width: 100%; max-width: 400px; }
.login-hint { font-size: 0.8rem; color: var(--text-muted); margin-top: 16px; text-align: center; }

.result-list { list-style: none; padding: 0; margin: 18px 0; display: flex; flex-direction: column; gap: 8px; }
.result-list li {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.9rem;
}
.result-list a { color: var(--primary-ink); font-weight: 600; text-decoration: none; }

.warn-box {
  background: var(--accent-amber-bg);
  border: 1px solid #f0d29c;
  color: #7a5108;
  border-radius: 10px;
  padding: 16px 18px;
  margin-bottom: 20px;
  font-size: 0.9rem;
}

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 760px) { .two-col { grid-template-columns: 1fr; } }

/* auto-fit et non repeat(4) : une 5e tuile apparaît quand des avertissements
   ont été acceptés, et une grille figée à 4 la renvoyait seule sur une
   deuxième ligne, au quart de la largeur. */
.validation-summary { display:grid; grid-template-columns:repeat(auto-fit, minmax(130px, 1fr)); gap:1px; border-radius:10px; overflow:hidden; border:1px solid var(--border); margin:20px 0; }
.validation-summary > div { padding:18px 16px; background:var(--card); display:flex; flex-direction:column; gap:4px; }
.validation-summary strong { font-size:1.5rem; line-height:1; color:var(--eu-blue); }
.validation-summary span { font-size:.78rem; color:var(--text-muted); }
.validation-summary-error { border-color:#e8aaaa; }
.validation-summary-error > div { background:var(--danger-bg); }
.validation-summary-error strong { color:var(--danger); }
.validation-summary-success { border-color:#a9d7bf; }
.validation-summary-success > div { background:var(--success-bg); }
.validation-summary-success strong { color:var(--success); }
.validation-blocker { margin-top:18px; }
.success-box { padding:14px 16px; border-radius:8px; background:var(--success-bg); color:var(--success); }
.section-heading { display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:14px; }
.section-heading h2 { margin:0; }
.table-wrap { overflow-x:auto; }
.validation-table { width:100%; border-collapse:collapse; font-size:.86rem; }
.validation-table th, .validation-table td { text-align:left; vertical-align:top; padding:11px 10px; border-bottom:1px solid var(--border); }
.validation-table th { color:var(--text-muted); font-size:.75rem; text-transform:uppercase; letter-spacing:.03em; }
.validation-table code { font-size:.8rem; white-space:nowrap; }
.validation-pill { display:inline-block; padding:3px 7px; border-radius:999px; font-size:.72rem; font-weight:700; white-space:nowrap; }
.validation-pill.error { background:var(--danger-bg); color:var(--danger); }
.validation-pill.warning { background:#fff2cc; color:#855c00; }
.empty-state { color:var(--text-muted); margin:8px 0; }

/* Correction assistée : saisir la valeur corrigée directement dans le
   tableau des anomalies, puis relancer l'analyse. */
.validation-rule-id { font-size:.74rem; color:var(--text-muted); white-space:nowrap; }
.correction-input { width:100%; min-width:130px; padding:6px 8px; font-size:.82rem;
  border:1px solid var(--border); border-radius:6px; background:var(--surface); color:var(--text); }
.correction-input:focus { outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent); }
.correction-choice-notice { display:block; margin-top:6px; max-width:28ch; color:var(--text-muted);
  font-size:.68rem; line-height:1.35; }
.correction-clear-group { margin-top:8px; white-space:normal; text-align:left; color:var(--danger); }
.correction-not-applicable { color:var(--text-muted); }
.correction-template-link { display:inline-flex; align-items:center; gap:5px; white-space:nowrap;
  font-size:.8rem; font-weight:600; color:var(--accent); text-decoration:none; }
.correction-template-link:hover { text-decoration:underline; }
.correction-bar { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:12px; margin-top:14px; padding-top:14px; border-top:1px solid var(--border); }
.correction-hint { margin:0; color:var(--text-muted); font-size:.82rem; max-width:62ch; }

/* Acceptation d'un avertissement : « vu et assumé ». La ligne acceptée reste
   VISIBLE et simplement atténuée — la faire disparaître donnerait l'illusion
   que l'anomalie n'a jamais existé, alors que la décision est justement ce
   que le rapport de conformité doit pouvoir montrer. */
.validation-pill.accepted { background:var(--success-bg); color:var(--success); }
.validation-table tr.is-accepted > td { opacity:.62; }
.validation-table tr.is-accepted .validation-pill.accepted { opacity:1; }
.acceptance-cell { min-width:150px; }
.acceptance-check { display:inline-flex; align-items:center; gap:6px; font-size:.8rem;
  font-weight:600; cursor:pointer; white-space:nowrap; }
.acceptance-check input { width:15px; height:15px; accent-color:var(--accent); cursor:pointer; }
.acceptance-trace { display:block; margin-top:4px; color:var(--text-muted); font-size:.68rem;
  line-height:1.3; overflow-wrap:anywhere; }
/* Cible de la soumission implicite (touche Entrée) : neutre, invisible, hors
   du parcours clavier. Sans elle, « Entrée » activerait le premier bouton du
   tableau au lieu de relancer l'analyse. */
.submit-par-defaut { position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  border:0; overflow:hidden; clip:rect(0 0 0 0); pointer-events:none; }

@media (max-width: 760px) {
  .validation-summary { grid-template-columns:repeat(2, minmax(0, 1fr)); }
  .correction-bar { flex-direction:column; align-items:stretch; }
}

.help-text {
  background: var(--eu-blue-light);
  border-left: 3px solid var(--eu-blue);
  padding: 12px 14px;
  border-radius: 6px;
  color: #0a3d6e;
  font-size: 0.9rem;
  /* Marge HAUTE indispensable : sans elle, un encadré posé sous une
     rangée de boutons les touche — constaté sur /recuperation, et vrai
     partout puisque la règle est globale. Les marges adjacentes se
     fusionnent, elle ne s'ajoute donc pas là où l'élément précédent
     porte déjà une marge basse. */
  margin: 14px 0 16px;
}
.steps p { font-size: 0.88rem; color: var(--text-muted); margin: 8px 0; }
.steps strong { color: var(--text); }

.btn-outline {
  background: transparent;
  color: var(--primary-ink);
  border: 1px solid var(--eu-blue);
}
.btn-outline:hover { background: var(--eu-blue); color: white; }

.doc-panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 18px;
}
.doc-panel-title {
  font-weight: 700;
  font-size: 1.02rem;
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--primary-ink);
}
.doc-body { color: var(--text-muted); font-size: 0.92rem; line-height: 1.65; }
.doc-body ul { margin: 10px 0; padding-left: 20px; }
.doc-body li { margin: 6px 0; }
.doc-body strong { color: var(--text); }
.doc-body a { color: var(--primary-ink); }
.doc-inline-link { font-weight: 600; text-decoration: none; }

table.admin-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
table.admin-table th, table.admin-table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); }
table.admin-table th { color: var(--text-muted); font-weight: 600; font-size: 0.75rem; text-transform: uppercase; }
.badge { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
.badge-ok { background: var(--success-bg); color: var(--success); }
.badge-error { background: var(--danger-bg); color: var(--danger); }
.badge-expired { background: var(--danger-bg); color: var(--danger); }
.badge-admin { background: var(--eu-blue-light); color: var(--primary-ink); }
.badge-warning { background: var(--c-warn-bg); color: var(--c-warn); }
.badge-icon { display: inline-flex; align-items: center; gap: 4px; }
.badge-icon svg { width: 11px; height: 11px; flex: none; }
/* --eu-blue-light/--eu-blue-dark sont tous deux assombris en thème sombre
   (voir tokens ci-dessus) : les utiliser ensemble comme fond clair + texte
   foncé y devient quasi illisible (texte/fond de luminance presque égale).
   Même correctif que .admin-sidebar-link.is-active. */
:root[data-theme="dark"] .badge-admin { background: color-mix(in srgb, var(--c-brand-600) 22%, transparent); color: var(--c-brand-500); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .badge-admin { background: color-mix(in srgb, var(--c-brand-600) 22%, transparent); color: var(--c-brand-500); } }
.inline-form { display: inline-flex; gap: 6px; align-items: center; }
.inline-form input { width: 70px; padding: 6px 8px; font-size: 0.85rem; }
.btn-sm { padding: 6px 12px; font-size: 0.8rem; }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }
.btn-danger:hover { color: #fff; background: var(--danger); border-color: var(--danger); }

/* Console d'administration des demandes d'accès */
.eyebrow { color: var(--eu-blue); font-size: 0.75rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; margin: 0; }
.metric-pill, .status-pill {
  display: inline-flex; align-items: center; gap: 6px; border-radius: 999px;
  padding: 6px 10px; font-size: .76rem; font-weight: 650; white-space: nowrap; flex-shrink: 0;
}
.metric-pill { color: var(--primary-ink); background: var(--eu-blue-light); }
.metric-pill strong { font-size: .95rem; }
.status-ok { background: var(--success-bg); color: var(--success); }
.status-warning { background: var(--accent-amber-bg); color: var(--accent-amber); }
.status-danger { background: var(--danger-bg); color: var(--danger); }
.admin-section { margin: 0 0 34px; }
.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin-bottom: 14px; }
.section-heading h2 { margin: 0 0 4px; font-size: 1.15rem; }
.section-heading p { margin: 0; color: var(--text-muted); font-size: .88rem; max-width: 760px; }
.request-list { display: grid; gap: 16px; }
.access-request-card {
  background: var(--card); border: 1px solid var(--border); border-radius: 14px;
  overflow: hidden;
}
.request-identity { padding: 20px 22px 16px; display: grid; grid-template-columns: 42px 1fr auto; gap: 12px; align-items: center; }
.avatar-placeholder { width: 42px; height: 42px; display: grid; place-items: center; border-radius: 50%; background: var(--eu-blue-light); color: var(--primary-ink); font-weight: 800; }
.request-identity h3 { margin: 0 0 2px; font-size: 1rem; }
.request-identity a { font-size: .88rem; color: var(--eu-blue); text-decoration: none; }
.request-company { margin: 4px 0 0; color: var(--text-muted); font-size: .84rem; }
.request-timestamp { color: var(--text-muted); font-size: .8rem; text-align: right; }
.request-summary-grid { display: grid; grid-template-columns: 1fr 1fr 1.6fr; gap: 12px; padding: 0 22px 18px; }
.request-summary-grid > div { border: 1px solid var(--border); border-radius: 9px; padding: 11px 12px; min-height: 61px; }
.request-summary-grid span { display: block; color: var(--text-muted); font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; margin-bottom: 5px; }
.request-summary-grid strong { display: block; font-size: .86rem; font-weight: 600; }
.grant-access-form { background: color-mix(in srgb, var(--eu-blue-light) 38%, var(--card)); border-top: 1px solid var(--border); padding: 18px 22px; }
.grant-form-heading { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.grant-form-heading h4 { margin: 0 0 3px; font-size: .95rem; }
.grant-form-heading p { margin: 0; font-size: .82rem; color: var(--text-muted); }
.grant-basics { display: flex; align-items: end; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; }
.grant-basics label { display: flex; flex-direction: column; gap: 5px; font-size: .8rem; font-weight: 650; color: var(--text-muted); }
.grant-basics input { width: 150px; padding: 8px 10px; }
.grant-basics input[type=number] { width: 106px; }
.grant-separator { color: var(--text-muted); font-size: .82rem; padding-bottom: 8px; }
.module-selector { margin: 0; border: 0; padding: 0; }
.module-selector legend { font-size: .8rem; font-weight: 700; margin-bottom: 8px; }
.module-selector-grid { display: flex; flex-wrap: wrap; gap: 7px; }
.module-selector-grid label { display: flex; align-items: center; gap: 6px; padding: 7px 9px; background: var(--card); border: 1px solid var(--border); border-radius: 7px; font-size: .78rem; cursor: pointer; }
.module-selector-grid label:hover { border-color: var(--eu-blue); }
.module-selector-grid input { width: auto; }
.module-selector-grid strong { color: var(--primary-ink); }
.request-actions { margin-top: 16px; }
.request-secondary-actions { padding: 12px 22px; border-top: 1px solid var(--border); }
.empty-admin-state { border: 1px dashed var(--border); border-radius: 12px; padding: 28px; display: flex; flex-direction: column; gap: 5px; color: var(--text-muted); background: var(--card); }
.empty-admin-state strong { color: var(--text); }
.history-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; background: var(--card); }
.history-table { min-width: 1000px; }
.history-table th, .history-table td { vertical-align: middle; }
.table-muted { color: var(--text-muted); font-size: .8rem; }
.history-actions { display: flex; flex-wrap: wrap; gap: 6px; min-width: 250px; }

/* Connexion et espace personnel : sobre, centré et orienté produit SaaS. */
.auth-page { display: grid; place-items: start center; padding: 52px 18px 72px; min-height: 620px; }
.auth-card-modern { width: 100%; max-width: 450px; text-align: center; }
.auth-card-wide { max-width: 590px; }
.auth-brand { display: inline-block; color: var(--text); font-size: 1.45rem; font-weight: 850; text-decoration: none; letter-spacing: -.03em; margin-bottom: 24px; }
.auth-brand span { color: var(--eu-blue); }
.auth-card-modern h1 { font-size: 1.72rem; margin: 0 0 10px; letter-spacing: -.025em; }
.auth-intro { margin: 0 auto 24px; max-width: 390px; color: var(--text-muted); line-height: 1.5; }
.auth-provider-btn { width: 100%; min-height: 48px; border: 1px solid var(--border); border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; gap: 10px; color: var(--text); text-decoration: none; font-weight: 700; background: var(--card); transition: border-color .15s, box-shadow .15s; }
.auth-provider-btn:hover { border-color: var(--eu-blue); box-shadow: 0 3px 12px rgba(10, 61, 110, .12); }
.auth-divider { display: flex; align-items: center; gap: 12px; margin: 22px 0; color: var(--text-muted); font-size: .78rem; }
.auth-divider::before, .auth-divider::after { content: ""; height: 1px; background: var(--border); flex: 1; }
.auth-info { background: var(--accent-amber-bg); color: #8a570f; padding: 12px; border-radius: 8px; font-size: .86rem; margin-bottom: 18px; text-align: left; }
.auth-form { display: flex; flex-direction: column; gap: 10px; }
.auth-form-left { text-align: left; gap: 15px; }
.auth-form-left > label:not(.auth-input-wrap), .auth-two-columns label { display: flex; flex-direction: column; gap: 6px; color: var(--text-muted); font-size: .82rem; font-weight: 650; }
.auth-form-left input:not([type=radio]) { width: 100%; padding: 11px 12px; }
.auth-form-left input[readonly] { background: var(--bg); color: var(--text-muted); }
.auth-two-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.auth-choice-group { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.auth-choice-group legend { width: 100%; padding: 0; margin-bottom: 3px; color: var(--text-muted); font-size: .82rem; font-weight: 650; }
.auth-choice-group label { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border); border-radius: 7px; padding: 9px 11px; color: var(--text); font-size: .86rem; cursor: pointer; }
.auth-choice-group input { width: auto; }
.auth-form-left label span { font-weight: 400; }
.auth-form-actions { text-align: center; padding-top: 3px; }
.auth-success-card { max-width: 490px; }
.auth-success-icon { width: 48px; height: 48px; display: grid; place-items: center; margin: 0 auto 16px; border-radius: 50%; color: var(--success); background: var(--success-bg); font-size: 1.8rem; font-weight: 800; }
.auth-submit-link { display: inline-flex; align-items: center; text-decoration: none; margin-top: 6px; }
.auth-input-wrap { display: flex; align-items: center; gap: 10px; height: 52px; background: var(--card); border: 1px solid #aeb9c5; border-radius: 7px; padding: 0 14px; transition: border-color .15s, box-shadow .15s; }
.auth-form-left > label.auth-input-wrap { display: flex; flex-direction: row; align-items: center; gap: 10px; margin: 0; color: inherit; font-size: inherit; }
.auth-input-wrap:focus-within { border-color: var(--eu-blue); box-shadow: 0 0 0 3px rgba(11, 95, 168, .13); }
.auth-input-icon { width: 20px; height: 20px; flex: 0 0 20px; color: var(--primary-ink); display: grid; place-items: center; }
.auth-input-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.auth-form-left .auth-input-wrap input, .auth-input-wrap input { width: 100%; min-width: 0; padding: 0; margin: 0; border: 0; outline: 0; background: transparent; color: var(--text); font-size: .98rem; }
.auth-forgot-link { align-self: center; color: var(--eu-blue); font-size: .88rem; font-weight: 600; margin: 2px 0 8px; }
.auth-submit { align-self: center; border: 0; border-radius: 8px; background: var(--eu-blue); color: #fff; padding: 12px 32px; font-size: .98rem; font-weight: 750; cursor: pointer; transition: background .15s; }
.auth-submit:hover { background: var(--eu-blue-dark); }
.auth-signup { margin: 24px 0 0; color: var(--text-muted); }
.auth-signup a { color: var(--eu-blue); font-weight: 700; }
.account-header { display: flex; justify-content: space-between; gap: 20px; margin: 6px 0 28px; }
.account-header h1 { margin: 2px 0 8px; }
.account-access-summary { min-width: 175px; background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; display: flex; flex-direction: column; gap: 5px; font-size: .78rem; color: var(--text-muted); }
.account-access-summary strong { font-size: .87rem; }
.account-settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; max-width: 960px; }
.module-checkbox-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 14px; margin-top: 6px; }
.module-checkbox { display: flex; align-items: center; gap: 7px; font-weight: 400; font-size: 0.85rem; }
.module-checkbox input { width: auto; }
.inline-checkbox { display: flex; align-items: center; gap: 8px; font-weight: 400; }
.inline-checkbox input { width: auto; }
.access-inline-form { align-items: flex-end; flex-wrap: wrap; margin-top: 10px; }
.access-inline-form label { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; font-weight: 600; }
.access-inline-form input { width: 130px; }
.danger-panel { border-color: var(--danger); }
.danger-panel .account-panel-heading h2 { color: var(--danger); }
.directory-search { margin-bottom: 12px; max-width: 380px; }
.user-detail-link { font-weight: 600; text-decoration: none; }
.user-detail-link:hover { text-decoration: underline; }
.account-panel { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 21px; }
.account-panel-wide { grid-column: 1 / -1; }
.account-panel-heading { margin-bottom: 18px; }
.account-panel-heading h2 { font-size: 1.04rem; margin: 0 0 5px; }
.account-panel-heading p { margin: 0; color: var(--text-muted); font-size: .86rem; line-height: 1.45; }
.profile-name-grid, .password-settings-form { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.profile-name-grid label, .account-panel .stack label, .password-settings-form label { display: flex; flex-direction: column; gap: 6px; color: var(--text-muted); font-size: .8rem; font-weight: 650; }
.account-panel input { padding: 9px 10px; }
.auth-method-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; border-top: 1px solid var(--border); padding: 13px 0; }
.auth-method-row:first-of-type { border-top: 0; padding-top: 0; }
.auth-method-row div { display: flex; flex-direction: column; gap: 3px; }
.auth-method-row span:not(.status-pill) { color: var(--text-muted); font-size: .82rem; }
.oidc-link-form { display: flex; align-items: center; justify-content: flex-end; gap: 7px; flex-wrap: wrap; }
.account-panel .oidc-link-form input { width: 175px; padding: 7px 9px; font-size: .8rem; }
@media (max-width: 720px) {
  .auth-method-row { align-items: flex-start; flex-direction: column; }
  .oidc-link-form { width: 100%; justify-content: flex-start; }
  .account-panel .oidc-link-form input { flex: 1; width: auto; min-width: 150px; }
}
.password-settings-form > div { grid-column: 1 / -1; }

.company-name { font-weight: 700; color: var(--text); }
.company-secondary { display: block; color: var(--text-muted); font-size: .78rem; margin-top: 3px; }
.company-table-actions, .actor-row-actions { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }
.actor-create-card, .actor-codes-card { margin-top: var(--sp-5); }
.actor-code-form { display: grid; grid-template-columns: minmax(190px, 1fr) minmax(220px, 1.2fr) minmax(220px, 1.2fr) auto; gap: var(--sp-4); align-items: end; }
/* Meme raison que pour .admin-users-page : sans cela, le champ « Echeance »
   sortait du formulaire de 49 px a 390 px, bien que la grille passe pourtant
   a une seule colonne a 760 px. Les controles natifs (date, select) portent
   une largeur intrinseque qu'il faut brider explicitement. */
.actor-code-form > * { min-width: 0; }
.actor-code-form input, .actor-code-form select, .actor-code-form textarea { max-width: 100%; }
/* Deux boutons cote a cote dans une boite de 102 px : sans repli, le second
   partait a x=457 sur un ecran de 390 — hors du formulaire ET hors de l'ecran.
   Mesure sur /recuperation a 390 px, ou l'action porte « Obtenir le modele
   Excel » et « Analyser sans telecharger ». */
.actor-form-action { padding-bottom: 1px; flex-wrap: wrap; }
.actor-codes-card .table-wrap { overflow-x: auto; }
.actor-codes-table { min-width: 1060px; table-layout: fixed; }
.actor-codes-table th:nth-child(1) { width: 22%; }
.actor-codes-table th:nth-child(2) { width: 23%; }
.actor-codes-table th:nth-child(3) { width: 23%; }
.actor-codes-table th:nth-child(4) { width: 13%; }
.actor-codes-table th:nth-child(5) { width: 19%; }
.actor-codes-table select, .actor-codes-table input { box-sizing: border-box; width: 100%; min-width: 0; min-height: 40px; margin: 0; }
.actor-codes-table td { vertical-align: middle; padding-top: 12px; padding-bottom: 12px; }
.actor-row-actions { min-width: 184px; justify-content: flex-start; }
.actor-codes-table tr.is-inactive { opacity: .68; background: var(--bg-sunken); }

@media (max-width: 1080px) {
  .actor-code-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .actor-form-action { grid-column: span 2; }
}

@media (max-width: 760px) {
  .request-identity { display: flex; flex-direction: column; }
  .request-timestamp { text-align: left; }
  .request-summary-grid { grid-template-columns: 1fr; }
  .account-header, .account-settings-grid { display: flex; flex-direction: column; }
  .profile-name-grid, .password-settings-form { grid-template-columns: 1fr; }
  .auth-two-columns { grid-template-columns: 1fr; }
  /* Neuf gabarits figent « grid-template-columns » en style EN LIGNE, ce qui
     bat toute media query : le repli mobile ne s'appliquait donc pas.
     Mesure sur /recuperation a 390 px : le bouton « Analyser sans
     telecharger » partait a x=457, hors du formulaire (343) et hors de
     l'ecran (390). Un plancher responsive ne doit pas etre annulable par un
     choix de mise en page propre a une page — d'ou le !important, ici
     justifie. Corriger les neuf gabarits un a un laisserait le piege ouvert
     pour le dixieme. */
  .actor-code-form { grid-template-columns: 1fr !important; }
  .actor-form-action { grid-column: auto; padding-bottom: 0; }
}

/* Recent jobs */
.recent-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.recent-list li {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--card); border: 1px solid var(--border); border-radius: 8px;
  padding: 11px 15px; font-size: 0.86rem;
}
.recent-list .rec-meta { color: var(--text-muted); font-size: 0.78rem; }

/* Thème sombre : appliqué automatiquement selon les préférences système,
   ou explicitement forcé via data-theme sur <html> par le bouton de
   bascule (voir le script dans base.html, stocké en localStorage). Les
   règles data-theme="dark" et [data-theme="light"] gagnent toujours face
   à la préférence système grâce à :not([data-theme="light"]) ci-dessous. */
:root[data-theme="dark"] {
  --eu-blue-darkest: #04213d;
  --eu-blue-dark: #123a5e;
  --eu-blue: #2f7dc4;
  --eu-blue-light: #17293b;
  --bg: #101820;
  --card: #17212b;
  --text: #e7edf2;
  --text-muted: #90a0ae;
  --border: #29343e;
  --danger-bg: #3a1f1c;
  --success-bg: #16302a;
  --accent-amber-bg: #3a2c14;
  /* Le logo se retourne avec le thème : encre claire, accent éclairci. */
  --logo-encre: #f2f7fb;
  --logo-accent: var(--c-brand-400);
  --logo-lets: var(--c-brand-400);
}
:root[data-theme="dark"] input[type=text], :root[data-theme="dark"] input[type=email],
:root[data-theme="dark"] input[type=password], :root[data-theme="dark"] input[type=file],
:root[data-theme="dark"] input[type=tel], :root[data-theme="dark"] input[type=number],
:root[data-theme="dark"] input[type=search], :root[data-theme="dark"] input:is([type=date],[type=datetime-local]),
:root[data-theme="dark"] textarea,
:root[data-theme="dark"] select { background-color: #101820; color: var(--text); }
:root[data-theme="dark"] input:is([type=date],[type=datetime-local]) { color-scheme: dark; }
:root[data-theme="dark"] .search-box input { background-color: var(--card); }
:root[data-theme="dark"] .help-text { color: #cfe3f5; }
:root[data-theme="dark"] .btn-google { background: #1b232c; color: #e7edf2; border-color: #333f4a; }
:root[data-theme="dark"] .btn-google:hover { background: #232d38; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --eu-blue-darkest: #04213d;
    --eu-blue-dark: #123a5e;
    --eu-blue: #2f7dc4;
    --eu-blue-light: #17293b;
    --bg: #101820;
    --card: #17212b;
    --text: #e7edf2;
    --text-muted: #90a0ae;
    --border: #29343e;
    --danger-bg: #3a1f1c;
    --success-bg: #16302a;
    --accent-amber-bg: #3a2c14;
    /* Le logo se retourne avec le thème : encre claire, accent éclairci. */
    --logo-encre: #f2f7fb;
    --logo-accent: var(--c-brand-400);
    --logo-lets: var(--c-brand-400);
  }
  :root:not([data-theme="light"]) input[type=text], :root:not([data-theme="light"]) input[type=email],
  :root:not([data-theme="light"]) input[type=password], :root:not([data-theme="light"]) input[type=file],
  :root:not([data-theme="light"]) input[type=tel], :root:not([data-theme="light"]) input[type=number],
  :root:not([data-theme="light"]) input[type=search], :root:not([data-theme="light"]) input:is([type=date],[type=datetime-local]),
  :root:not([data-theme="light"]) textarea,
  :root:not([data-theme="light"]) select { background-color: #101820; color: var(--text); }
  :root:not([data-theme="light"]) input:is([type=date],[type=datetime-local]) { color-scheme: dark; }
  :root:not([data-theme="light"]) .search-box input { background-color: var(--card); }
  :root:not([data-theme="light"]) .help-text { color: #cfe3f5; }
  :root:not([data-theme="light"]) .btn-google { background: #1b232c; color: #e7edf2; border-color: #333f4a; }
  :root:not([data-theme="light"]) .btn-google:hover { background: #232d38; }
}

.theme-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 8px;
  background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); cursor: pointer;
}
.theme-toggle:hover { color: var(--text); border-color: var(--eu-blue); }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: block; }
}

/* Coques applicatives : une navigation produit pour les utilisateurs et une
   navigation de pilotage distincte pour les administrateurs. */
.modules-panel { top:100%; left:0; min-width: 0; width: 280px; max-width: calc(100vw - 2 * var(--sp-4)); grid-template-columns: 1fr; padding: 6px; }
/* Détaché vers <body> par le script de base.html pour échapper au mask-image
   de .app-header-nav (voir commentaire JS) : positionnement calculé en JS,
   visibilité pilotée par .is-open plutôt que par le sélecteur .dropdown.open. */
.modules-panel-detached { position: fixed; display: none; z-index: 60; }
.modules-panel-detached.is-open { display: grid; }

/* Menu hamburger générique (static/responsive-nav.js) : bascule quand le
   contenu de l'en-tête déborde RÉELLEMENT (scrollWidth > clientWidth),
   jamais un seul point de rupture arbitraire — reste correct au zoom, en
   langue plus longue, avec une police agrandie. Un seul composant partagé
   par les en-têtes public/connecté/admin. */
[data-responsive-nav] { position: relative; }
.is-nav-collapsed [data-responsive-inline] { display: none !important; }
/* Sans ça, le wrapper .dropdown (display:block par défaut) traite son
   bouton inline-flex comme une boîte inline ordinaire et lui réserve de
   l'espace d'ascendant au-dessus (line-height du bloc englobant) -- mesuré
   réellement : wrapper 41px pour un bouton de 36px, tout l'écart au-dessus,
   ce qui décale le centre visuel du bouton de 2.5px sous le logo/les
   autres contrôles alignés par .app-header-inner { align-items:center }.
   flex ici fait que le wrapper épouse exactement la boîte du bouton. */
.responsive-nav-dropdown { display: inline-flex; align-items: center; }
.responsive-nav-toggle {
  display: none; flex-direction: column; justify-content: center; align-items: center; gap: 4px;
  flex: none; width: 36px; height: 36px; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--card); color: var(--text-muted); cursor: pointer;
}
.is-nav-collapsed .responsive-nav-toggle { display: inline-flex; }

/* ── Repli de l'en-tête utilisateur : point de rupture CSS d'abord ─────────
   L'en-tête d'administration se replie sur une media query ; celui du public
   et de l'espace connecté ne se repliait que par JavaScript, via un
   ResizeObserver mesurant le débordement réel. La mesure est plus fine -- elle
   suit le zoom, la langue et la taille de police -- mais elle ne s'applique
   qu'une fois le script exécuté. Entre-temps, un mobile affiche une navigation
   pleine largeur et défile horizontalement. C'est aussi ce qui a fait
   diagnostiquer trois faux débordements pendant l'audit du 2026-08-14 : en
   onglet masqué, l'observateur ne se déclenche jamais.

   Le point de rupture ci-dessous reprend EXACTEMENT le seuil que le script
   applique déjà pour cet en-tête (1180 px), de sorte que les deux ne peuvent
   pas se contredire. Le CSS assure le cas courant sans dépendre de rien ; le
   script reste utile au-dessus du seuil, là où seule une mesure réelle peut
   voir qu'un libellé traduit fait déborder la barre. Aucune capacité perdue,
   une dépendance en moins. */
@media (max-width: 1180px) {
  #appHeaderResponsive [data-responsive-inline] { display: none; }
  #appHeaderResponsive .responsive-nav-toggle { display: inline-flex; }
}
.responsive-nav-toggle:hover { border-color: var(--c-brand-300); color: var(--c-brand-600); }
.responsive-nav-toggle:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.responsive-nav-toggle .burger-bar { display: block; width: 16px; height: 2px; border-radius: var(--r-full); background: currentColor; transition: transform .18s var(--ease), opacity .18s var(--ease); }
.responsive-nav-dropdown.open .responsive-nav-toggle .burger-bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.responsive-nav-dropdown.open .responsive-nav-toggle .burger-bar:nth-child(2) { opacity: 0; }
.responsive-nav-dropdown.open .responsive-nav-toggle .burger-bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
/* Position fixe (pas absolue) : évite d'être rogné par un ancêtre avec
   overflow/mask-image, même problème que la cloche (voir notif-dropdown-
   panel). left/width sont recalculés par static/responsive-nav.js à
   chaque ouverture pour caler le bord gauche sur le bouton déclencheur
   (bord gauche du logo/hamburger, pas un point fixe côté viewport) --
   les valeurs ci-dessous ne sont qu'un repli avant le premier calcul JS,
   le panneau restant de toute façon masqué jusqu'à .open. */
.responsive-nav-panel {
  position: fixed; top: var(--header-h, 60px); left: var(--sp-3); right: var(--sp-3);
  /* min-width:0 casse le min-width:620px de .dropdown-panel (pensé pour le
     méga-menu 2 colonnes) -- sans lui, mesuré réellement : le panneau
     rendait à 620px quel que soit le width:360px imposé en JS par
     positionPanel(), qui calculait alors un offset "left" pour un panneau
     360px de large tout en en affichant un de 620 -- déborde silencieusement
     à droite dès que la fenêtre est trop étroite pour absorber l'écart. */
  width: auto; min-width: 0; max-width: 360px; margin-left: auto;
  max-height: calc(100vh - var(--header-h, 76px)); overflow-y: auto;
  grid-template-columns: 1fr; gap: 0; padding: var(--sp-2);
}
.responsive-nav-section { display: flex; flex-direction: column; padding: var(--sp-2) 0; }
.responsive-nav-section + .responsive-nav-section { border-top: 1px solid var(--border); }
.responsive-nav-link {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm);
  color: var(--text); text-decoration: none; font-size: var(--fs-sm); font-weight: 600;
  background: none; border: 0; text-align: left; cursor: pointer; width: 100%;
}
.responsive-nav-link:hover { background: var(--bg-sunken); }
.responsive-nav-link.is-active { color: var(--c-brand-600); }
.responsive-nav-link.is-logout { color: var(--c-danger); }
/* Groupe repliable ("Modules") : mêmes visite/hover que les liens plats,
   chevron qui pivote à l'ouverture -- même convention que .dropdown-trigger
   .chevron, appliquée ici à aria-expanded plutôt qu'à une classe .open
   puisqu'il n'y a pas de wrapper .dropdown autour de ce disclosure. */
.responsive-nav-group-toggle { justify-content: space-between; }
.responsive-nav-group-toggle .chevron { font-size: .6rem; opacity: .7; transition: transform .15s ease; }
.responsive-nav-group-toggle[aria-expanded="true"] .chevron { transform: rotate(180deg); }
.responsive-nav-subgroup { display: flex; flex-direction: column; padding-left: var(--sp-4); }
.responsive-nav-subgroup[hidden] { display: none; }
.responsive-nav-sublink { font-weight: 500; font-size: var(--fs-sm); color: var(--text-muted); }
.responsive-nav-langs { display: flex; gap: 6px; padding: var(--sp-2) var(--sp-3); }
.responsive-nav-langs a {
  flex: 1; text-align: center; padding: 6px 0; border-radius: var(--r-sm);
  border: 1px solid var(--border); color: var(--text-muted); text-decoration: none;
  font-size: var(--fs-2xs); font-weight: 700;
}
.responsive-nav-langs a.is-active { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }

/* ============================================================
   CONNECTED APP — single compact header (replaces the old
   utility-bar + product-header + workspace-nav stack). Functional
   and dense rather than a copy of the marketing header: no hero-style
   spacing, tab-style active state instead of a pill button.
   ============================================================ */
.app-header {
  position: sticky; top: 0; z-index: 40;
  background: var(--card); border-bottom: 1px solid var(--border);
  box-shadow: var(--sh-sm);
}
.app-header-inner {
  max-width: 1180px; margin: 0 auto; padding: 0 var(--gutter);
  min-height: 56px; display: flex; align-items: center; gap: var(--sp-6);
}
.app-header-brand { display: inline-flex; align-items: center; flex: none; }
/* Pas de overflow-x:auto/min-width:0 ici (contrairement à une version
   antérieure) : un défilement interne absorbait le débordement AVANT qu'il
   n'atteigne [data-responsive-nav] (.app-header-inner), qui ne détectait
   donc jamais scrollWidth > clientWidth -- le hamburger ne se déclenchait
   alors JAMAIS sur l'en-tête connecté, à aucune largeur. Même principe que
   .marketing-links (en-tête public), qui n'a jamais eu ce problème : la nav
   garde sa largeur de contenu (flex-shrink limité par son min-width auto),
   ce qui fait déborder l'en-tête entier une fois la place insuffisante --
   c'est précisément ce débordement que static/responsive-nav.js mesure. */
.app-header-nav { display: flex; align-items: stretch; gap: 2px; flex: 1; }
.app-nav-link {
  display: inline-flex; align-items: center; gap: 5px;
  height: 56px; padding: 0 var(--sp-4);
  color: var(--text-muted); background: transparent; border: 0;
  font: inherit; font-size: var(--fs-sm); font-weight: 700;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  position: relative;
}
.app-nav-link:hover, .app-nav-link:focus-visible { color: var(--text); background: var(--bg-sunken); outline: 0; }
.app-nav-link::after {
  content: ""; position: absolute; left: var(--sp-4); right: var(--sp-4); bottom: 0; height: 2px;
  background: var(--c-brand-500); transform: scaleX(0); transition: transform .15s var(--ease);
}
.app-nav-link.is-active { color: var(--c-brand-600); }
.app-nav-link.is-active::after { transform: scaleX(1); }
.app-nav-link .chevron { font-size: .6rem; opacity: .7; }
.app-header-actions { display: flex; align-items: center; gap: var(--sp-2); flex: none; }
.app-header-actions.public-quick-actions,
.app-header-actions.admin-quick-actions { margin-left: auto; }
/* Sélecteur de langue unique (sprint UX/UI) : un seul composant partagé
   par les 3 gabarits (base.html, admin_base.html, public_base.html) —
   voir templates/_lang_switcher.html. Ne réutilise pas .util-btn pour la
   variante avec code visible : sa largeur fixe 36px/display:grid est
   pensée pour une icône seule (voir .lang-switcher-trigger-icon, qui elle
   réutilise volontairement ce même gabarit de taille). */
.lang-switcher { position: relative; }
.lang-switcher-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  height: 38px; padding: 0 var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-full);
  background: var(--card); color: var(--text-muted);
  font: inherit; font-size: var(--fs-xs); font-weight: 700;
  cursor: pointer;
}
.lang-switcher-trigger:hover, .lang-switcher-trigger:focus-visible { border-color: var(--border-strong); color: var(--text); }
.lang-switcher-trigger:hover { outline: 0; }
.lang-switcher-trigger:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.lang-switcher-trigger .chevron { font-size: .6rem; opacity: .6; }
/* Variante icône seule (en-tête public, à côté du bouton thème) : mêmes
   dimensions que .util-btn pour un alignement parfait avec ses voisins. */
.lang-switcher-trigger-icon {
  width: 36px; height: 36px; padding: 0; justify-content: center; gap: 0;
  border-radius: var(--r-sm);
}
.lang-switcher-panel {
  grid-template-columns: repeat(4, 1fr); gap: 4px;
  width: max-content; min-width: 236px;
  max-width: min(260px, calc(100vw - 2 * var(--sp-4)));
  right: 0; left: auto; padding: 6px;
}
@media (max-width: 1023.98px) {
  /* Même problème que la cloche et le hamburger (voir notif-dropdown-panel,
     responsive-nav-panel) : sur le site public, le sélecteur de langue
     n'est pas l'élément le plus à droite de l'en-tête (Se connecter et
     Démarrer un projet suivent) — un panneau ancré `right:0` à SA position
     déborderait à gauche de l'écran. */
  .lang-switcher-panel {
    position: fixed; top: var(--header-h, 60px); left: auto; right: var(--sp-3);
    width: max-content; min-width: 236px;
    max-width: min(260px, calc(100vw - 2 * var(--sp-3)));
  }
}
/* Puces de code compactes : largeur/hauteur identiques pour chaque
   langue (FR/EN/ES/IT) — plus de décalage entre libellés de longueurs
   différentes (Français/English/Español/Italiano). */
.lang-switcher-item {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 52px; height: 34px; padding: 0 10px;
  font-weight: 700; font-size: var(--fs-xs); text-align: center;
}
.lang-switcher-item-code { line-height: 1; }
.lang-switcher-item svg { color: var(--c-brand-500); flex: none; }
.lang-switcher-item.is-active { background: var(--bg-sunken); color: var(--text); }

.account-menu { position: relative; }
/* Avatar seul, jamais de nom/société/email en clair dans le bandeau --
   36px pour rester à la même hauteur que .util-btn et .responsive-nav-toggle
   (alignement vertical réel avec les autres contrôles de l'en-tête). */
.account-trigger {
  display: inline-grid; place-items: center;
  width: 36px; height: 36px; padding: 0;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--bg-sunken); color: var(--text); font: inherit;
  cursor: pointer;
}
.account-trigger:hover, .account-trigger:focus-visible { border-color: var(--border-strong); }
.account-trigger:hover { outline: 0; }
.account-trigger:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.account-avatar {
  display: grid; place-items: center; flex: none;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--c-brand-500); color: #fff; font-size: var(--fs-2xs); font-weight: 800;
}
.account-panel { right: 0; left: auto; min-width: 216px; grid-template-columns: 1fr; }
.account-panel-info { padding: var(--sp-3) var(--sp-3) var(--sp-3); border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.account-panel-info strong { display: block; color: var(--text); font-size: var(--fs-sm); }
.account-panel-info span { display: block; color: var(--text-muted); font-size: var(--fs-xs); margin-top: 2px; }
.dropdown-item-logout { color: var(--c-danger); }
.dropdown-logout-form { display: block; margin: 0; }
button.dropdown-item { width: 100%; background: none; border: none; font: inherit; text-align: left; cursor: pointer; }
@media (max-width: 620px) {
  .app-header-inner { padding: 0 14px; gap: var(--sp-3); }
  .app-nav-link { padding: 0 var(--sp-2); font-size: var(--fs-xs); }
  /* En-tête ADMIN : le badge « Administration » + 4 actions ne tiennent pas
     sur 375 px (débordement horizontal réel mesuré, 492 px) — le badge est
     masqué en mobile, le contexte admin restant évident (sidebar, contenu).
     Sélecteur composé : la définition de base de .admin-mode-badge arrive
     PLUS BAS dans ce fichier et gagnerait sinon à spécificité égale. */
  .app-header .admin-mode-badge { display: none; }
  .app-header .admin-quick-actions { gap: var(--sp-2); }
  .app-header .lang-switcher-trigger .chevron { display: none; }
}

/* La bascule clair/sombre du logo est regroupée en fin de fichier, section
   « Variante sombre du logo ». Elle vivait ici, dispersée à côté de chaque
   ancienne plaque blanche : deux surfaces (pied de page marketing, pages
   d'authentification) n'avaient jamais eu de plaque, donc n'ont jamais reçu
   de bascule non plus. Un seul endroit, la liste complète des surfaces. */

/* ---------- Connected app: minimal footer ---------- */
.app-footer { margin-top: var(--sp-10); padding: var(--sp-6) var(--gutter); border-top: 1px solid var(--border); background: var(--bg-sunken); }
.app-footer-inner {
  max-width: 1180px; margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-5);
}
.app-footer-brand { display: inline-flex; position: relative; }
.app-footer-links { display: flex; gap: var(--sp-5); }
.app-footer-links a { color: var(--text-muted); font-size: var(--fs-xs); font-weight: 700; text-decoration: none; }
.app-footer-links a:hover { color: var(--text); text-decoration: underline; }
.app-footer-notice { margin-left: auto; color: var(--text-faint); font-size: var(--fs-2xs); }
@media (max-width: 620px) {
  .app-footer-inner { gap: var(--sp-3); }
  .app-footer-notice { margin-left: 0; width: 100%; }
}

/* ---------- Dashboard cleanup: no raw keys, real CTA per card ---------- */
.dashboard-access-note {
  display: flex; align-items: center; gap: 6px;
  margin: -8px 0 var(--sp-5); color: var(--text-faint); font-size: var(--fs-xs);
}
.tile-op-count { display: block; margin-top: 3px; color: var(--text-faint); font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.stat-num-xsd { font-size: 1.3rem !important; font-family: var(--font-mono); }

/* ============================================================
   PUBLIC SITE — brand surface, landing, auth
   Token-driven only: no raw hex below this line.
   ============================================================ */

/* ---------- Public header ---------- */
.marketing-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--card);
  border-bottom: 1px solid var(--border);
}
/* Bande « version bêta » sous l'en-tête public (public_base.html) :
   informative, jamais sticky — elle défile avec la page. */
.beta-banner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--sp-2) var(--sp-3);
  padding: .55rem var(--gutter);
  background: var(--c-brand-50);
  border-bottom: 1px solid var(--border);
  font-size: .92rem; color: var(--text); text-align: center;
}
.beta-banner-tag {
  padding: .14rem .62rem; border-radius: 999px;
  /* Meme raison que la pastille des phases : le jeton 600 devient clair en
     sombre et laissait du blanc sur bleu pale (2,28:1). Bleu marque
     litteral, sombre dans les deux themes. */
  background: #2f79ab; color: #fff;
  font-weight: 700; font-size: .74rem;
  letter-spacing: .05em; text-transform: uppercase; white-space: nowrap;
}
.beta-banner a { color: var(--c-brand-600); font-weight: 600; }

/* ---- Composants de la section Clients (admin) ----
   Accent dédié : `--primary` n'existe dans AUCUN thème de cette feuille
   (51 usages orphelins, à traiter à part) et `--eu-blue-dark` s'assombrit
   en thème sombre — illisible dès qu'il porte du texte. Même stratégie que
   la bande bêta : une valeur claire par thème, déclarée une seule fois. */
:root {
  --client-accent: var(--c-brand-600);
  --client-accent-on: #ffffff;   /* ce qui se pose SUR l'accent */
  --client-accent-tint: #dbe7f0; /* fond discret, accent lisible dessus */
  --client-accent-line: #a9c8de;
}
/* En thème sombre l'accent devient CLAIR : ce qui se pose dessus doit
   redevenir foncé, sinon la pastille d'étape courante tombe à 1.97:1. */
:root[data-theme="dark"] {
  --primary:      #1f6aa5;
  --primary-ink:  #93bedd;
  --primary-tint: #16354b;
  --client-accent: var(--c-brand-300); --client-accent-on: #0b1c2b;
  --client-accent-tint: #14324a; --client-accent-line: #2a5d80;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --primary:      #1f6aa5;
  --primary-ink:  #93bedd;
  --primary-tint: #16354b;
  --client-accent: var(--c-brand-300); --client-accent-on: #0b1c2b;
    --client-accent-tint: #14324a; --client-accent-line: #2a5d80;
  }
}

/* ---- Fiche client (admin) : frise du parcours + checklist des jalons ---- */
.client-stepper {
  display: flex; flex-wrap: wrap; gap: 6px 0;
  list-style: none; margin: 0 0 18px; padding: 0;
}
.client-stepper li {
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 auto; min-width: 150px; padding: 8px 10px;
  border-bottom: 3px solid var(--border);
  color: var(--text-muted); font-size: .86rem; font-weight: 600;
}
.client-stepper .step-dot {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--border); color: var(--text-muted);
  font-size: .74rem; font-weight: 700;
}
.client-stepper li.is-done { border-bottom-color: var(--client-accent-line); color: var(--text); }
.client-stepper li.is-done .step-dot { background: var(--client-accent-tint); color: var(--client-accent); }
.client-stepper li.is-current { border-bottom-color: var(--client-accent); color: var(--client-accent); }
.client-stepper li.is-current .step-dot { background: var(--client-accent); color: var(--client-accent-on); }
.client-checklist { display: grid; gap: 8px; margin: 0 0 12px; }
.client-checklist .jalon {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) 175px;
  gap: 10px; align-items: center; padding: 8px 12px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface);
}
.client-checklist .jalon-etat {
  width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--border); color: var(--text-muted);
  font-size: .78rem; font-weight: 700;
}
.client-checklist .jalon.is-done { border-color: var(--client-accent-line); }
.client-checklist .jalon.is-done .jalon-etat { background: var(--client-accent-tint); color: var(--client-accent); }
.client-checklist .jalon label { font-weight: 600; font-size: .9rem; margin: 0; }
.client-checklist input:is([type="date"],[type="datetime-local"]) { width: 100%; }
@media (max-width: 640px) {
  .client-checklist .jalon { grid-template-columns: 26px 1fr; }
  .client-checklist input:is([type="date"],[type="datetime-local"]) { grid-column: 2; }
}

/* ---- Suggestions déduites de l'activité réelle ---- */
.client-suggestions {
  display: grid; gap: 8px; margin: 0 0 var(--sp-5);
  padding: 12px 16px; border-radius: 11px;
  border: 1px solid var(--client-accent-line);
  background: var(--client-accent-tint);
}
.client-suggestions > strong { display: flex; align-items: center; gap: 7px; font-size: .9rem; color: var(--client-accent); }
.client-suggestion { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; font-size: .9rem; }
.client-suggestion > span { flex: 1 1 320px; }
.client-suggestion em { font-style: normal; font-weight: 600; }

/* ---- Pièces du dossier ---- */
.client-pieces { list-style: none; margin: 12px 0; padding: 0; display: grid; gap: 6px; }
.client-pieces li {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 9px; background: var(--surface);
}
.client-pieces .piece-nom { font-weight: 600; font-size: .9rem; flex: 1 1 220px; overflow-wrap: anywhere; }
.client-pieces form { margin: 0; }

/* Kit et connecteur produits depuis la fiche client. Les deux boutons se
   replient d'eux-memes : sans flex-wrap, ils sortaient de l'ecran en etroit —
   c'est le defaut qu'on vient de corriger sur /recuperation, autant ne pas le
   reintroduire ici. */
.client-kit-actions { margin-bottom: var(--sp-5); }
.client-kit-boutons { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0; }
.client-kit-boutons form { margin: 0; }
/* État du travail de fond (fabrication du connecteur). Absent tant qu'il n'y
   a rien à dire — d'où l'absence de hauteur réservée. */
.client-kit-etat {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
  font-size: 13px; color: var(--text-muted);
}
.client-kit-etat.is-termine { color: var(--success); }
.client-kit-etat.is-echec { color: var(--danger); }

/* ---- Chronologie ---- */
.client-note-form { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 14px; }
.client-note-form textarea { flex: 1; resize: vertical; }
.client-timeline { list-style: none; margin: 0; padding: 0 0 0 4px; display: grid; gap: 2px; }
.client-timeline li {
  display: grid; grid-template-columns: 92px minmax(0, 1fr) auto;
  gap: 12px; align-items: start;
  padding: 10px 0 10px 16px; margin-left: 8px;
  border-left: 2px solid var(--border); position: relative;
}
.client-timeline li::before {
  content: ""; position: absolute; left: -5px; top: 15px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--border);
}
.client-timeline li.is-note::before { background: var(--client-accent); }
.client-timeline .tl-date { font-size: .8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.client-timeline .tl-corps p { margin: 0 0 2px; font-size: .9rem; white-space: pre-wrap; overflow-wrap: anywhere; }
.client-timeline form { margin: 0; }
/* Relances : chaque message se déplie, champ par champ, avec un bouton
   « copier ». Zimbra n'acceptant aucun paramètre d'URL pour sa fenêtre de
   rédaction, la recopie est le seul chemin — autant la rendre exacte. */
.client-mail-templates { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: 14px; }
.client-mail-templates .help-text { margin: 0; }
.client-mail-gabarit { border: 1px solid var(--border); border-radius: 8px; }
.client-mail-gabarit > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); cursor: pointer; font-weight: 600;
}
.client-mail-champs {
  display: grid; gap: var(--sp-3);
  padding: 0 var(--sp-3) var(--sp-3); border-top: 1px solid var(--border);
  padding-top: var(--sp-3);
}
.client-mail-champ { display: grid; gap: 4px; min-width: 0; }
.client-mail-champ > span:first-child { font-size: 12px; color: var(--text-muted); }
/* « align-items: start » et non « center » : sur la zone de texte, un bouton
   centré verticalement se retrouve au milieu de huit lignes. */
.client-mail-copie { display: flex; align-items: start; gap: var(--sp-2); min-width: 0; }
.client-mail-copie input, .client-mail-copie textarea { flex: 1; min-width: 0; }
.client-mail-copie textarea { resize: vertical; font-family: inherit; }
.client-mail-copie .btn { flex: none; }
.client-mail-copie .btn.is-copie { color: var(--success); border-color: var(--success); }
.client-mail-champs > .btn { justify-self: start; }
@media (max-width: 640px) {
  .client-mail-copie { flex-wrap: wrap; }
  .client-mail-copie input, .client-mail-copie textarea { flex-basis: 100%; }
}
@media (max-width: 640px) {
  .client-timeline li { grid-template-columns: 1fr auto; }
  .client-timeline .tl-date { grid-column: 1 / -1; }
}

/* ---- Pipeline (colonnes par étape) ---- */
.client-pipeline {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(215px, 1fr);
  gap: 12px; overflow-x: auto; padding-bottom: 8px; align-items: start;
}
.pipeline-col { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 10px; }
.pipeline-col > header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.pipeline-col > header h2 { font-size: .88rem; margin: 0; flex: 1; border: 0; padding: 0; }
.pipeline-col-index {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--border); color: var(--text-muted); font-size: .72rem; font-weight: 700;
}
.pipeline-col-count { font-size: .78rem; color: var(--text-muted); font-weight: 700; }
.pipeline-card {
  display: grid; gap: 5px; padding: 10px 12px; margin-bottom: 8px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--card);
  text-decoration: none; color: var(--text);
}
.pipeline-card:hover { border-color: var(--client-accent); }
.pipeline-card.is-late { border-left: 3px solid var(--danger); }
.pipeline-card strong { font-size: .88rem; overflow-wrap: anywhere; }
.pipeline-action { font-size: .8rem; color: var(--text-muted); overflow-wrap: anywhere; }
.pipeline-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: .76rem; color: var(--text-muted); }
.pipeline-modules { display: flex; flex-wrap: wrap; gap: 4px; }
.pipeline-empty { text-align: center; color: var(--text-muted); font-size: .8rem; padding: 10px 0; margin: 0; }
:root[data-theme="dark"] .beta-banner { background: var(--c-brand-950); }
:root[data-theme="dark"] .beta-banner a { color: var(--c-brand-300); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .beta-banner { background: var(--c-brand-950); }
  :root:not([data-theme="light"]) .beta-banner a { color: var(--c-brand-300); }
}
.marketing-header-inner {
  /* Volontairement plus large que --page-max (1200px, utilisé par le
     contenu marketing en dessous) : la barre d'en-tête peut s'étendre
     davantage qu'une section de contenu sans nuire à la lecture — mesuré
     réellement débordant de ~100px à 1200px avec le nombre d'éléments
     actuel (voir static/responsive-nav.js, sprint UX/UI). */
  max-width: 1400px; min-height: 76px; margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex; align-items: center; gap: var(--sp-5);
}
.marketing-brand, .marketing-footer-logo {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  color: var(--text); text-decoration: none;
  font-size: 1.05rem; font-weight: 800;
  letter-spacing: var(--tracking-tight); white-space: nowrap;
}
.marketing-brand b, .marketing-footer-logo b { color: var(--c-brand-500); }
.marketing-brand small {
  display: block; margin-top: 1px;
  color: var(--text-muted); font-size: var(--fs-2xs);
  font-weight: 500; letter-spacing: 0;
}
.marketing-links { display: flex; align-items: center; gap: var(--sp-4); margin-left: auto; }
.marketing-links a {
  position: relative; color: var(--text-muted); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 600; white-space: nowrap;
  transition: color .15s var(--ease);
}
.marketing-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 2px;
  border-radius: var(--r-full); background: var(--c-brand-500);
  transform: scaleX(0); transition: transform .18s var(--ease);
}
.marketing-links a:hover { color: var(--c-brand-600); }
.marketing-links a:hover::after { transform: scaleX(1); }
.marketing-links a:focus-visible,
.marketing-cta:focus-visible, .marketing-login:focus-visible, .marketing-brand:focus-visible {
  outline: 2px solid var(--c-brand-500); outline-offset: 3px; border-radius: var(--r-xs);
}

.marketing-actions, .public-quick-actions { display: flex; align-items: center; gap: var(--sp-4); }
/* Hauteur harmonisée avec .marketing-login/.marketing-cta (42px) : .util-btn
   et .lang-switcher-trigger-icon valent 36px par défaut (partagés avec les
   en-têtes connecté/admin, plus bas, où 36px reste correct) -- seul l'en-
   tête public, plus haut (76px), a besoin de ses boutons utilitaires à la
   même hauteur que Se connecter/Démarrer un projet. Bornée à >540px : à
   deux sélecteurs de classe, cette règle est plus spécifique que le repli
   mobile ci-dessous (32px, un seul sélecteur) et le battrait même dans son
   propre média-query si elle s'appliquait aussi là -- le repli mobile doit
   rester la seule règle active en dessous de 540px. */
@media (min-width: 540.02px) {
  .marketing-actions .util-btn, .marketing-actions .lang-switcher-trigger-icon { width: 42px; height: 42px; }
}
.marketing-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 42px; padding: 0 var(--sp-5);
  border-radius: var(--r-sm);
  background: linear-gradient(155deg, var(--c-brand-500), var(--c-brand-700)); color: #fff; text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700; white-space: nowrap;
  box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--c-brand-600) 55%, transparent);
  transition: background .15s var(--ease), transform .15s var(--ease), box-shadow .15s var(--ease);
}
.marketing-cta:hover {
  background: linear-gradient(155deg, var(--c-brand-400), var(--c-brand-600)); color: #fff;
  transform: translateY(-1px); box-shadow: 0 10px 22px -8px color-mix(in srgb, var(--c-brand-600) 65%, transparent);
}
/* Bouton secondaire : même gabarit (hauteur/rayon/police) que .marketing-cta,
   couleur différente — reprend le duo primaire/fantôme déjà établi par
   .market-primary/.market-ghost dans le hero, à la taille compacte de
   l'en-tête. */
.marketing-login {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 42px; padding: 0 var(--sp-5);
  border: 1px solid var(--c-brand-300); border-radius: var(--r-sm);
  /* brand-600 et non brand-700 : le 700 ne bascule pas avec le theme, si
     bien qu'en sombre « Se connecter » restait bleu nuit sur fond nuit --
     1,89:1, mesure et vu le 2026-08-11. Meme correction que .market-ghost,
     meme cause. */
  background: transparent; color: var(--c-brand-600); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700; white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease), color .15s var(--ease),
              transform .15s var(--ease), box-shadow .15s var(--ease);
}
.marketing-login:hover {
  border-color: var(--c-brand-600); background: var(--c-brand-50); color: var(--c-brand-800);
  transform: translateY(-1px);
  box-shadow: 0 6px 14px -8px color-mix(in srgb, var(--c-brand-600) 45%, transparent);
}
.marketing-login:active, .marketing-cta:active { transform: translateY(0); }
.compact-cta { min-height: 38px; }
.public-quick-actions > a:first-child {
  color: var(--c-brand-600); font-weight: 700;
  text-decoration: none; font-size: var(--fs-sm);
}

/* Header utilities: theme toggle + language */
.public-utils { display: flex; align-items: center; gap: var(--sp-2); }
.util-btn {
  display: inline-grid; place-items: center;
  width: 36px; height: 36px; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--card); color: var(--text-muted);
  cursor: pointer; transition: all .15s var(--ease);
}
.util-btn:hover { border-color: var(--c-brand-300); color: var(--c-brand-600); }
.util-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; }

/* ---------- Hero ---------- */
.market-hero {
  position: relative; overflow: hidden;
  /* Institutionnel : bande bleu pâle unie, structure par bordure, pas de photo. */
  background: linear-gradient(180deg, var(--c-brand-50) 0%, var(--bg) 100%);
  border-bottom: 1px solid var(--border);
}
.market-hero-shade { display: none; }
.market-hero-inner {
  position: relative;
  max-width: var(--page-max); margin: 0 auto;
  padding: var(--sp-11) var(--gutter) var(--sp-11);
  display: grid; grid-template-columns: 1.05fr .95fr;
  align-items: center; gap: var(--sp-9);
}
/* Kickers read as field labels on a data sheet: monospace, tracked,
   prefixed with a tick mark rather than a plain dash — the same
   vocabulary as the instrument annotations elsewhere on the page. */
.market-kicker {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  margin: 0 0 var(--sp-4);
  color: var(--c-brand-600); font-family: var(--font-mono); font-size: var(--fs-2xs);
  font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
.market-kicker span {
  position: relative;
  width: 14px; height: 14px; border-radius: 0;
  background: none;
}
.market-kicker span::before, .market-kicker span::after {
  content: ""; position: absolute; border-color: var(--c-brand-500); border-style: solid; width: 6px; height: 6px;
}
.market-kicker span::before { top: 0; left: 0; border-width: 1.5px 0 0 1.5px; }
.market-kicker span::after { bottom: 0; right: 0; border-width: 0 1.5px 1.5px 0; }
/* La variante .light est réservée aux bandes bleu marine restantes
   (déploiement, validation, approche, CTA final, connexion) : encre
   claire lisible sur fond sombre — WCAG AA. */
/* Valeurs littérales : la gamme brand est inversée par le thème sombre,
   or ces kickers restent posés sur des bandes marine dans les deux thèmes. */
.market-kicker.light { color: #c2dbec; }
.market-kicker.light span::before, .market-kicker.light span::after { border-color: #93bedd; }
/* La mesure de 15ch était taillée pour l'ancien titre (« Vos données
   produit, prêtes pour EUDAMED »). Le message central de la marque est plus
   long : à 15ch il cassait en quatre lignes avec un mot orphelin isolé
   (« suffisent. » seul sur sa ligne). 22ch rend la colonne au titre, et
   text-wrap: balance répartit les mots également entre les lignes au lieu
   de remplir la première puis d'abandonner le reste — c'est exactement le
   cas que cette propriété existe pour traiter. */
/* Le message central tient en deux phrases, séparées par un <br>. L'objectif
   typographique est donc UNE ligne par phrase : c'est ce qui donne au titre
   sa cadence. Deux réglages l'empêchaient. Le plafond de 22ch (hérité d'un
   titre plus court) bridait la ligne avant la colonne ; et --fs-display,
   dimensionné pour trois mots, faisait déborder une phrase de vingt-neuf
   caractères. La mesure est rendue à la colonne, l'échelle recalée pour que
   la plus longue des deux phrases tienne d'un trait au-delà de 1200px.
   text-wrap: balance reste le filet en dessous, quand le repli est
   inévitable : il répartit les mots au lieu de laisser un orphelin. */
.market-hero h1 {
  max-width: none; margin: 0;
  color: var(--c-brand-950);
  font-size: clamp(2rem, 3.4vw, 3rem); line-height: 1.08;
  letter-spacing: var(--tracking-display); font-weight: 800;
  text-wrap: balance;
}
.market-hero h1 em {
  font-style: normal;
  color: var(--c-brand-600);
}
.market-hero-lead {
  max-width: 52ch; margin: var(--sp-5) 0 0;
  color: var(--text-muted); font-size: var(--fs-lead); line-height: 1.6;
}
.market-hero-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-7); }
.market-primary {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  min-height: 46px; padding: 0 var(--sp-6);
  border-radius: var(--r-sm);
  background: linear-gradient(155deg, var(--c-brand-500), var(--c-brand-700));
  color: #fff; text-decoration: none;
  font-size: var(--fs-body); font-weight: 700;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--c-brand-900) 25%, transparent),
              0 10px 22px -10px color-mix(in srgb, var(--c-brand-600) 60%, transparent);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), background .16s var(--ease);
}
.market-primary:hover {
  background: linear-gradient(155deg, var(--c-brand-400), var(--c-brand-600));
  color: #fff; transform: translateY(-1px);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--c-brand-900) 28%, transparent),
              0 16px 30px -10px color-mix(in srgb, var(--c-brand-600) 70%, transparent);
}
.market-primary:active { transform: translateY(0); }
.market-primary span { display: inline-flex; transition: transform .16s var(--ease); }
.market-primary:hover span { transform: translateX(3px); }
.market-ghost {
  display: inline-flex; align-items: center;
  min-height: 46px; padding: 0 var(--sp-6);
  border: 1px solid var(--c-brand-300); border-radius: var(--r-sm);
  /* brand-600 et non brand-700 : le 700 ne bascule pas avec le theme. En
     sombre, l'intitule restait bleu nuit sur fond nuit -- 2,11:1, mesure et
     VU le 2026-08-11 : le bouton secondaire du heros avait l'air desactive.
     Le 600 bascule (6,10:1 en clair, 7,91:1 en sombre), c'est deja la
     couleur des kickers et des numeros de regle. */
  background: transparent; color: var(--c-brand-600); text-decoration: none;
  font-size: var(--fs-body); font-weight: 700;
  transition: background .16s var(--ease), border-color .16s var(--ease), color .16s var(--ease);
}
.market-ghost:hover { border-color: var(--c-brand-600); background: var(--c-brand-50); color: var(--c-brand-700); }
.market-hero-note {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
  margin-top: var(--sp-6); color: var(--text-muted); font-size: var(--fs-xs);
}
/* Chaque coche + libellé forme une unité insécable : la coche ne peut
   plus se détacher de son texte au retour à la ligne. */
.market-hero-note .note-item {
  display: inline-flex; align-items: center; gap: var(--sp-2);
}
/* Le lien vers le chemin inverse, sous les coches du hero. Souligne et
   discret : c'est une PISTE vers une section, pas une quatrieme action --
   deux boutons suffisent au-dessus, un troisieme diluerait la demande
   d'acces. */
.hero-return-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
  color: var(--primary-ink);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.hero-return-link span { transition: transform .2s var(--ease); }
.hero-return-link:hover span { transform: translateX(4px); }
/* Le lien est souligne AU REPOS (c'est une piste de lecture, pas un
   bouton) : le survol ne peut donc pas ajouter un soulignement. Seule la
   fleche avancait -- un signal trop discret pour se remarquer. La teinte
   change aussi, et le trait s'epaissit. */
.hero-return-link { transition: color .18s var(--ease); }
.hero-return-link:hover { color: var(--c-brand-500); text-decoration-thickness: 2px; }
.market-hero-note .note-item i {
  display: inline-grid; place-items: center; flex: none;
  width: 17px; height: 17px;
  border-radius: var(--r-full);
  background: var(--c-teal-100); color: var(--c-teal-700);
  font-style: normal; font-size: var(--fs-2xs); font-weight: 800;
}

/* ---------- Hero product card ---------- */
.market-analysis-card {
  display: grid; gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-xl);
  background: var(--card-raised);
  box-shadow: var(--sh-xl);
}
.analysis-card-header {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-sm);
}
.analysis-brand-dot {
  width: 8px; height: 8px; border-radius: var(--r-full);
  background: var(--c-ok);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--c-ok) 16%, transparent);
}
.analysis-card-header strong { color: var(--text); }
.analysis-card-header small {
  margin-left: auto; padding: 3px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--c-ok-bg); color: var(--c-ok);
  font-size: var(--fs-2xs); font-weight: 800;
}
/* La carte affiche des chiffres de démonstration (score, erreurs) : ce
   sont des valeurs d'exemple, pas une mesure produit — l'étiquette évite
   toute confusion pour un lecteur pressé. */
.analysis-example-tag {
  margin: 0 0 var(--sp-2); padding: 0 var(--sp-2);
  color: var(--text-faint); font-size: var(--fs-2xs);
  font-style: italic;
}
.analysis-file {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border-radius: var(--r-md);
  background: var(--bg-sunken);
}
.analysis-file > span {
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: var(--r-sm);
  background: var(--c-ok); color: #fff;
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .02em;
}
.analysis-file strong { display: block; font-size: var(--fs-sm); color: var(--text); }
.analysis-file small { color: var(--text-muted); font-size: var(--fs-xs); }
.analysis-score {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-4); border-radius: var(--r-md);
  background: var(--c-brand-700);
  color: #fff;
}
.analysis-score > div:first-child > span {
  display: block; font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: color-mix(in srgb, #fff 72%, transparent);
}
.analysis-score strong { font-family: var(--font-mono); font-size: 2.3rem; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.analysis-score strong small { font-size: 1rem; font-weight: 700; margin-left: 2px; }
.score-ring {
  display: grid; place-items: center;
  width: 62px; height: 62px; border-radius: var(--r-full);
  background: conic-gradient(var(--c-teal-300) 0% 96%, color-mix(in srgb, #fff 22%, transparent) 96% 100%);
  color: #fff; font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 600;
}
.score-ring span {
  display: grid; place-items: center;
  width: 48px; height: 48px; border-radius: var(--r-full);
  background: var(--c-brand-700);
}
.analysis-findings { display: grid; gap: var(--sp-2); }
.analysis-findings > div {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--card);
}
.analysis-findings i {
  display: grid; place-items: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--r-full);
  font-style: normal; font-size: var(--fs-xs); font-weight: 900;
}
.finding-error   { background: var(--c-danger-bg); color: var(--c-danger); }
.finding-warning { background: var(--c-warn-bg);   color: var(--c-warn); }
.finding-ok      { background: var(--c-ok-bg);     color: var(--c-ok); }
.analysis-findings strong { display: block; font-size: var(--fs-sm); color: var(--text); }
.analysis-findings small { color: var(--text-muted); font-size: var(--fs-xs); }

/* ---------- Capability strip ---------- */
.market-capabilities {
  max-width: var(--page-max); margin: calc(var(--sp-8) * -1) auto 0;
  position: relative; z-index: 2;
  padding: 0 var(--gutter);
  /* Quatre colonnes depuis l'ajout du compteur de règles. auto-fit plutôt
     qu'un nombre figé : à 240 px minimum, la rangée passe d'elle-même à
     2 × 2 puis à une colonne, sans point de rupture à entretenir. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--sp-4);
}
.market-capabilities article {
  display: flex; align-items: flex-start; gap: var(--sp-4);
  padding: var(--sp-6);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card-raised); box-shadow: var(--sh-md);
}
.market-capabilities strong {
  color: var(--c-brand-500); font-family: var(--font-mono); font-size: 2.1rem; font-weight: 600;
  line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
/* h3 : ce sont des légendes de chiffre-clé, pas des titres de section —
   éviter de polluer le plan de navigation par titres (lecteurs d'écran). */
.market-capabilities h2 { margin: 0 0 var(--sp-1); font-size: var(--fs-sm); font-weight: 700; color: var(--text); }
.market-capabilities p { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.55; }

/* ---------- Generic section ---------- */
.market-section { max-width: var(--page-max); margin: 0 auto; padding: var(--sp-12) var(--gutter); }
.market-section-heading { max-width: 62ch; }
.market-section-heading.centered { margin: 0 auto; text-align: center; }
.market-section-heading.centered .market-kicker { justify-content: center; }
.market-section h2, .market-validation h2, .approach-copy h2, .market-final-cta h2 {
  margin: 0; color: var(--text);
  font-size: var(--fs-h2); line-height: 1.14;
  letter-spacing: var(--tracking-display); font-weight: 800;
}
.market-section-heading p:last-child {
  margin: var(--sp-4) 0 0; color: var(--text-muted);
  font-size: var(--fs-body); line-height: 1.65;
}

/* ---------- Workflow ---------- */
.workflow-track {
  margin-top: var(--sp-8);
  display: grid; grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr;
  align-items: stretch; gap: var(--sp-3);
}
.workflow-track article {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-5);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.workflow-track article:hover {
  transform: translateY(-3px); border-color: var(--c-brand-200); box-shadow: var(--sh-md);
}
.workflow-number {
  color: var(--text-faint); font-family: var(--font-mono); font-size: var(--fs-2xs);
  font-weight: 600; letter-spacing: .04em;
}
.workflow-icon {
  display: grid; place-items: center;
  width: 44px; height: 44px; margin-bottom: var(--sp-1);
  border-radius: var(--r-md);
  background: var(--c-brand-50); color: var(--c-brand-600);
  font-size: var(--fs-2xs); font-weight: 800;
}
.workflow-track h3 { margin: 0; font-size: var(--fs-h3); color: var(--text); letter-spacing: var(--tracking-tight); }
.workflow-track p { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.6; }
.workflow-arrow { align-self: center; color: var(--c-brand-300); font-size: 1.2rem; }

/* Excel -> XML transformation: a real, honest excerpt (field names and
   XML tags are pulled straight from Convertisseur_EUDAMED_V2.py / the
   MDR export), not a generic mockup — grounding "workflow" in the
   product's actual output. */
.transform-illustration {
  position: relative; z-index: 1;
  margin-top: var(--sp-10);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--sp-5);
}
.transform-pane {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card); box-shadow: var(--sh-lg);
  overflow: hidden;
}
.transform-pane-head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border); background: var(--bg-sunken);
  color: var(--text-faint);
}
.transform-pane-head strong { font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600; color: var(--text-muted); }
.transform-grid { padding: var(--sp-3); }
.transform-grid > div {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-2);
  padding: var(--sp-2); border-radius: var(--r-sm);
  font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text);
}
.transform-grid > div span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.transform-grid-head { color: var(--text-faint); font-weight: 800; letter-spacing: .03em; text-transform: uppercase; font-size: 9px; }
.cell-flag { border-radius: 4px; padding: 0 4px; margin: -1px -4px; }
.cell-flag.error { color: var(--c-danger); background: var(--c-danger-bg); }
.cell-flag.warning { color: var(--c-warn); background: var(--c-warn-bg); }
.transform-arrow {
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: var(--r-full);
  background: var(--c-brand-50); color: var(--c-brand-600);
}
.transform-code {
  margin: 0; padding: var(--sp-4);
  font-family: var(--font-mono); font-size: var(--fs-2xs); line-height: 1.7;
  white-space: pre; overflow-x: auto; color: var(--text-muted);
}
.xml-tag { color: var(--c-brand-500); }
.xml-val { color: var(--c-teal-700); }
:root[data-theme="dark"] .xml-val { color: var(--c-teal-300); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .xml-val { color: var(--c-teal-300); } }
@media (max-width: 760px) {
  .transform-illustration { grid-template-columns: 1fr; }
  .transform-arrow { transform: rotate(90deg); justify-self: center; }
}

/* ---------- Validation showcase ---------- */
.market-validation {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; grid-template-columns: 1.05fr .95fr;
  align-items: stretch;
  background:
    linear-gradient(180deg, var(--c-brand-900) 0%, transparent 10%, transparent 90%, var(--c-brand-900) 100%),
    var(--c-brand-800);
}
.market-validation::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 18%, #000 82%, transparent);
          mask-image: linear-gradient(180deg, transparent, #000 18%, #000 82%, transparent);
}
.validation-showcase { display: grid; place-items: center; padding: var(--sp-12) var(--gutter); }
.validation-window-wrap { width: 100%; max-width: 560px; }
.validation-window {
  width: 100%;
  border-radius: var(--r-lg); overflow: hidden;
  background: var(--card); box-shadow: var(--sh-xl);
}
.validation-window-top {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border); background: var(--bg-sunken);
}
.validation-window-top span {
  width: 10px; height: 10px; border-radius: var(--r-full); background: var(--border-strong);
}
.validation-window-top .live-dot { width: 7px; height: 7px; background: var(--c-ok); margin-right: 1px; }
.validation-window-top strong {
  margin-left: var(--sp-2); color: var(--text-muted);
  font-size: var(--fs-xs); font-weight: 700;
}
.validation-table-demo { padding: var(--sp-2); font-size: var(--fs-xs); }
.validation-table-demo > div {
  display: grid; grid-template-columns: 120px 44px 144px 1fr;
  align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border-radius: var(--r-sm);
  color: var(--text);
}
.validation-table-demo > div:not(.validation-demo-head):hover { background: var(--bg-sunken); }
.validation-demo-head {
  color: var(--text-faint) !important;
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .04em;
}
.validation-table-demo > div span:nth-child(2),
.validation-table-demo > div span:nth-child(3) { color: var(--text-muted); font-family: var(--font-mono); font-size: .92em; font-variant-numeric: tabular-nums; }
.validation-table-demo > div span:last-child { color: var(--text-muted); }
.demo-badge {
  display: inline-block; padding: 3px var(--sp-2);
  border-radius: var(--r-full);
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600; font-style: normal;
  text-transform: uppercase; letter-spacing: .03em;
}
.demo-badge.error   { background: var(--c-danger-bg); color: var(--c-danger); }
.demo-badge.warning { background: var(--c-warn-bg);   color: var(--c-warn); }
.demo-badge.ok      { background: var(--c-ok-bg);     color: var(--c-ok); }
.validation-copy {
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--sp-12) max(var(--gutter), 5vw);
  color: #fff;
}
.validation-copy h2 { color: #fff; }
.validation-copy > p {
  max-width: 54ch; margin: var(--sp-4) 0 0;
  color: color-mix(in srgb, #fff 74%, transparent);
  font-size: var(--fs-body); line-height: 1.68;
}
.validation-copy ul { margin: var(--sp-7) 0 0; padding: 0; list-style: none; display: grid; gap: var(--sp-4); }
.validation-copy li { display: flex; gap: var(--sp-3); }
.validation-copy li > span {
  display: grid; place-items: center;
  width: 24px; height: 24px; flex: none;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-teal-300) 24%, transparent);
  color: var(--c-teal-300); font-size: var(--fs-2xs); font-weight: 900;
}
.validation-copy li strong { display: block; color: #fff; font-size: var(--fs-sm); }
.validation-copy li small {
  color: color-mix(in srgb, #fff 62%, transparent);
  font-size: var(--fs-xs); line-height: 1.55;
}
/* Les identifiants des regles qui produisent ce controle. En mono, sous
   le texte : une reference, pas une decoration. Sur l'aplat marine, une
   couleur litterale -- les jetons de marque basculent avec le theme du
   site alors que cette bande, elle, reste marine dans les deux. */
.control-rules {
  display: block; margin-top: var(--sp-2);
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: .06em;
  color: #9fc7e4;
}

/* ---------- Modules: interactive explorer ----------
   Replaces the old flat card grid (counts you couldn't open). Left
   rail is a real tablist over the 7 categories from CATEGORIES in
   eudamed_core.py; the panel lists that category's actual operations
   — nothing invented, nothing summarized away. Progressive
   enhancement: every panel renders open by default (see the
   :not(.is-interactive) rule below) so the full content is readable
   with zero JavaScript; script.js only tightens it into tabs once it
   proves it can run. */
.modules-section { position: relative; overflow: hidden; }
.module-explorer {
  position: relative; z-index: 1;
  margin-top: var(--sp-9);
  display: grid; grid-template-columns: 336px minmax(0, 1fr);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--sh-lg);
  overflow: hidden;
}
.module-explorer-nav {
  display: flex; flex-direction: column; gap: var(--sp-1);
  padding: var(--sp-3);
  background: var(--bg-sunken);
  border-right: 1px solid var(--border);
}
/* Grid, not flex: the count badge is a DOM sibling of the label (not
   nested under it), so only explicit grid placement can reliably put
   it on its own row under the label regardless of how many lines the
   label wraps to. */
.module-nav-item {
  display: grid; grid-template-columns: 36px 1fr; column-gap: var(--sp-3); row-gap: 3px;
  width: 100%; padding: var(--sp-3);
  border: 1px solid transparent; border-radius: var(--r-md);
  background: transparent; cursor: pointer;
  text-align: left; font: inherit; color: var(--text-muted);
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.module-nav-item:hover { background: var(--card); color: var(--text); }
.module-nav-item:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 1px; }
.module-nav-item.is-active { background: var(--card); border-color: var(--border); color: var(--text); box-shadow: var(--sh-md); }
.module-nav-icon {
  grid-column: 1; grid-row: 1;
  display: grid; place-items: center;
  width: 36px; height: 36px; border-radius: var(--r-md);
  background: var(--c-brand-50); color: var(--c-brand-600);
  transition: background .15s var(--ease), color .15s var(--ease);
}
.module-nav-item.is-active .module-nav-icon { background: var(--c-brand-500); color: #fff; }
.module-nav-text { grid-column: 2; grid-row: 1; min-width: 0; align-self: center; }
.module-nav-text strong { display: block; font-size: var(--fs-sm); font-weight: 700; color: inherit; line-height: 1.3; }
/* text-muted et non text-faint : sur la bande teintee de « modules », le
   faint tombait a 4,42:1 -- sous le seuil AA de 4,5 pour un texte de 12 px,
   et c'est le seul endroit de la page publique qui restait en dessous apres
   la passe du 2026-08-11. Le muted donne 5,2:1 sans changer la hierarchie :
   le compte reste plus discret que l'intitule du module, qui est en gras. */
.module-nav-count { grid-column: 2; grid-row: 2; font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-muted); }
.module-nav-item.is-active .module-nav-count { color: var(--c-brand-500); }

.module-explorer-panels { position: relative; padding: var(--sp-8); min-height: 380px; }
/* Scoped to the no-JS fallback only: the sibling combinator matches by
   DOM position regardless of display:none, so without this guard every
   panel except the first-in-DOM would inherit this spacing even in
   single-tab mode, pushing the active panel down by ~92px depending on
   which module was selected. */
.module-explorer:not(.is-interactive) .module-panel + .module-panel { margin-top: var(--sp-8); padding-top: var(--sp-8); border-top: 1px dashed var(--border); }
.module-explorer.is-interactive .module-panel { display: none; }
.module-explorer.is-interactive .module-panel.is-active {
  display: block; animation: module-panel-in .35s var(--ease) both;
}
@keyframes module-panel-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.module-panel-head { display: flex; gap: var(--sp-4); align-items: flex-start; padding-bottom: var(--sp-5); border-bottom: 1px solid var(--border); }
.module-panel-icon {
  display: grid; place-items: center; flex: none;
  width: 48px; height: 48px; border-radius: var(--r-md);
  background: var(--c-brand-50); color: var(--c-brand-600);
}
.module-panel-head h3 { margin: 0 0 var(--sp-2); font-size: var(--fs-h3); color: var(--text); }
.module-panel-head p { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6; max-width: 62ch; }
.module-ops-kicker {
  margin: var(--sp-6) 0 var(--sp-3);
  color: var(--text-faint); font-size: var(--fs-2xs); font-weight: 800;
  text-transform: uppercase; letter-spacing: .05em;
}
.module-ops-list { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--sp-2); }
.module-ops-list li {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-sunken);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.module-ops-list li:hover { border-color: var(--c-brand-200); background: var(--card); }
.module-op-status { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--c-ok); }
.module-ops-list li > div { flex: 1; min-width: 0; }
.module-ops-list li strong { display: block; font-size: var(--fs-sm); color: var(--text); }
.module-ops-list li small { display: block; margin-top: 2px; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.5; }
.module-op-badge {
  flex: none; padding: 3px var(--sp-2); border-radius: var(--r-full);
  background: var(--c-ok-bg); color: var(--c-ok);
  font-size: var(--fs-2xs); font-weight: 700;
}
/* Operations cascade in as a panel activates — "results populating",
   not a flat block appearing. Gated on the explorer's own .is-visible
   (added once it scrolls into view): without this, the first panel's
   items would animate immediately on page load, off-screen, and have
   nothing left to show once the user actually scrolls to it. */
.module-explorer.is-visible .module-panel.is-active .module-ops-list li { opacity: 0; animation: module-op-in .4s var(--ease) both; }
@keyframes module-op-in { from { opacity: 0; transform: translateX(6px); } to { opacity: 1; transform: none; } }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(1) { animation-delay: .08s; }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(2) { animation-delay: .13s; }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(3) { animation-delay: .18s; }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(4) { animation-delay: .23s; }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(5) { animation-delay: .28s; }
.module-explorer.is-visible .module-panel.is-active .module-ops-list li:nth-child(6) { animation-delay: .33s; }
@media (max-width: 900px) {
  .module-explorer { grid-template-columns: 1fr; }
  .module-explorer-nav {
    flex-direction: row; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
    border-right: none; border-bottom: 1px solid var(--border);
  }
  .module-nav-item { flex: none; width: 188px; grid-template-columns: 28px 1fr; }
  .module-nav-icon { width: 28px; height: 28px; }
  .module-nav-text strong {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: var(--fs-xs);
  }
  .module-explorer-panels { padding: var(--sp-6) var(--sp-5); }
}
@media (max-width: 540px) {
  .module-panel-head { flex-direction: column; }
  /* Grid instead of flex: at this width the dot + text + badge no
     longer fit one row without squeezing the text into a sliver, so
     the badge drops to its own row under the description instead of
     floating at the vertical center of a tall wrapped paragraph. */
  .module-ops-list li { display: grid; grid-template-columns: 18px 1fr; column-gap: var(--sp-3); row-gap: var(--sp-2); align-items: start; }
  .module-op-status { grid-column: 1; grid-row: 1; margin-top: 7px; }
  .module-ops-list li > div { grid-column: 2; grid-row: 1; }
  .module-op-badge { grid-column: 2; grid-row: 2; justify-self: start; }
}

/* ---------- Approach ---------- */
.market-approach {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  background: var(--c-brand-900); color: #fff;
}
/* No stock photography: a dark instrument panel with a few of the
   product's own record tags settling into place — the actual material
   of the product, not generic office imagery. */
/* Les étiquettes et la légende forment UN bloc, centré. Elles étaient
   toutes deux en position absolue — les étiquettes au centre du panneau,
   la légende collée en bas — ce qui laissait, vu à l'écran le 2026-08-11,
   300 px de bleu vide au-dessus des étiquettes et 200 px entre elles et la
   légende. Deux objets aux deux bouts d'un panneau ne composent pas. Elles
   reviennent dans le flux, dans une colonne flex centrée : la légende suit
   les étiquettes, à distance choisie. */
.approach-image {
  position: relative;
  min-height: 560px;
  display: flex; flex-direction: column;
  justify-content: center; align-items: center;
  gap: var(--sp-8);
  padding: var(--sp-10) clamp(24px, 5vw, 72px);
  background: radial-gradient(ellipse 70% 60% at 30% 20%, var(--c-brand-700), var(--c-brand-900) 70%);
  border-right: 1px solid var(--c-brand-900);
}
.approach-copy { display: flex; flex-direction: column; justify-content: center; padding: var(--sp-12) max(var(--gutter), 4vw); }
.approach-copy h2 { color: #fff; }
.approach-copy > p {
  max-width: 54ch; margin: var(--sp-4) 0 0;
  color: color-mix(in srgb, #fff 72%, transparent);
  font-size: var(--fs-body); line-height: 1.68;
}
.approach-points { margin-top: var(--sp-8); display: grid; gap: var(--sp-3); }
.approach-points > div {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, #fff 14%, transparent);
  border-radius: var(--r-md);
  background: color-mix(in srgb, #fff 6%, transparent);
}
/* brand-300 et non brand-200 : cette bande est marine dans LES DEUX themes,
   alors que le 200 bascule et devient bleu nuit en sombre. Les numeros
   passaient a 1,49:1 sur le fond marine -- mesure le 2026-08-11. Le 300 ne
   bascule pas : il reste clair des deux cotes, 6,8:1 ici. */
.approach-points strong { color: var(--c-brand-300); font-size: var(--fs-sm); font-weight: 800; }
.approach-points span { color: color-mix(in srgb, #fff 84%, transparent); font-size: var(--fs-sm); }

/* ---------- Final CTA ---------- */
/* ---------- Réassurance (faits vérifiables, ton calme) ----------
   La classe .market-trust portait un dégradé radial. Il ne s'affichait
   plus : depuis le rythme des bandes, la règle #confiance (sélecteur d'id,
   donc plus spécifique) impose un aplat par-dessus. Vérifié dans le
   navigateur — background-image calculé : none. Retiré plutôt que laissé en
   place à décrire une intention que la page ne montre pas. */
/* Les engagements : une liste de définitions, pas deux grilles de cartes.
   Mesuré le 2026-08-11 : huit cartes de 271 px, soit environ 35 caractères
   par ligne pour des explications de trois phrases — la carte imposait sa
   largeur au texte au lieu de la suivre. L'intitulé tient désormais la
   colonne de gauche, l'explication occupe la largeur de lecture, un filet
   sépare. Ni boîte, ni ombre, ni soulèvement au survol : la charte de marque
   les exclut, et une carte qui décolle sous la souris n'apprend rien. */
.trust-group-label {
  max-width: 52rem; margin: var(--sp-8) auto 0;
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--c-brand-600);
}
/* 52rem : la mesure de la liste « problème ». Deux blocs de texte suivi sur
   la même page n'ont aucune raison d'avoir deux largeurs de lecture. */
.trust-rows { max-width: 52rem; margin: var(--sp-4) auto 0; }
.trust-rows > div {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-6);
  padding: var(--sp-5) 0;
  border-top: 1px solid var(--border);
}
.trust-rows > div:last-child { border-bottom: 1px solid var(--border); }
.trust-rows dt {
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-h3);
  color: var(--text); line-height: 1.35;
}
/* fs-body, pas fs-sm : le corps de texte était réduit parce que la carte
   ne faisait que 271 px. La colonne fait maintenant sa largeur de lecture,
   le texte peut reprendre sa taille normale. */
.trust-rows dd { margin: 0; color: var(--text-muted); font-size: var(--fs-body); line-height: 1.65; }
@media (max-width: 780px) {
  .trust-rows > div { grid-template-columns: 1fr; gap: var(--sp-2); }
}

/* Les 4 messages piliers (objection -> message -> preuve).
   Deux colonnes, pas quatre : chaque bloc porte une phrase entière qu'on
   doit pouvoir lire sans casser les mots. Le filet vertical à gauche tient
   lieu d'accent — pas d'ombre portée, la charte de marque les exclut
   (« instrument de précision, filets fins, aucune ombre portée lourde »).
   Il s'épaissit au survol : le seul mouvement de la carte, et il indique
   quelle carte est lue plutôt que de décorer. */
/* Rythme des bandes. Mesuré le 2026-08-11 : les cinq premières sections
   (héros, capacités, problème, piliers, démonstration) étaient TOUTES sur
   fond transparent — 3 693 px, presque quatre écrans, sans un seul
   changement de sol avant la bande marine du déploiement. D'où l'impression
   de mur, que ni la palette ni les animations ne pouvaient corriger.
   Une teinte très pâle un rang sur deux suffit à donner une cadence : l'oeil
   sait qu'il change de sujet. Pas de bordure ni d'ombre — la charte de
   marque ne veut que des aplats et des filets fins. */
/* Les bandes teintées, dans l'ordre de la page. Deux murs avaient été
   mesurés : les cinq premières sections (3 693 px) et les sections 9 à 12
   (bénéfices, modules, pour-qui, confiance : 3 425 px), chacune sans un
   seul changement de sol. Un rang sur deux est teinté pour que l'oeil sache
   qu'il change de sujet. Les bandes marine existantes (déploiement,
   validation, approche, appel final) sont conservées telles quelles : elles
   ponctuent déjà la page, c'est entre elles que le rythme manquait. */
.market-capabilities,
.market-section.market-pillars,
#retour,
#modules,
#confiance {
  background: var(--c-brand-50);
}
/* Sur fond teinté, une carte en var(--card) se confondrait avec le sol :
   elle repasse en blanc plein pour rester détachée. Ne reste que
   l'explorateur de modules : « confiance » n'a plus de carte du tout, et le
   bandeau de couverture n'a plus de caisson — des filets sur la teinte. */
#modules .module-panel {
  background: var(--surface);
}
@media (prefers-color-scheme: dark) {
  .market-capabilities,
  .market-section.market-pillars,
  #retour,
  #modules,
  #confiance { background: rgba(255, 255, 255, .022); }
}
:root[data-theme="dark"] .market-capabilities,
:root[data-theme="dark"] .market-section.market-pillars,
:root[data-theme="dark"] #retour,
:root[data-theme="dark"] #modules,
:root[data-theme="dark"] #confiance { background: rgba(255, 255, 255, .022); }
:root[data-theme="light"] .market-capabilities,
:root[data-theme="light"] .market-section.market-pillars,
:root[data-theme="light"] #retour,
:root[data-theme="light"] #modules,
:root[data-theme="light"] #confiance { background: var(--c-brand-50); }

/* « Le problème » : liste numérotée, pas une grille. La page enchaînait une
   dizaine de damiers de cartes ; ces cinq constats ont un ordre de lecture,
   et une liste le dit mieux. Aucune boîte — un filet horizontal sépare, le
   numéro en mono ancre la colonne de gauche. C'est le vocabulaire des
   annotations d'instrument déjà utilisé par les kickers. */
.problem-list {
  list-style: none; margin: var(--sp-8) 0 0; padding: 0;
  max-width: 52rem; margin-inline: auto;
}
.problem-list li {
  display: grid; grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: var(--sp-4);
  padding: var(--sp-5) 0;
  border-top: 1px solid var(--border);
}
.problem-list li:last-child { border-bottom: 1px solid var(--border); }
/* brand-600, pas brand-400 : mesuré le 2026-08-11, le 400 donne 2,86:1 sur
   fond clair — sous le seuil AA de 4,5:1, pour un numéro qui est le seul
   repère de position dans la liste. Le 600 est la couleur que les kickers
   utilisent déjà (6,10:1 en clair, 7,91:1 en sombre : le jeton bascule avec
   le thème, aucune surcharge n'est nécessaire). */
.problem-index {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  font-weight: 600; color: var(--c-brand-600);
  letter-spacing: .04em; padding-top: .15rem;
}
.problem-list h3 {
  margin: 0 0 var(--sp-2); font-size: var(--fs-h3);
  color: var(--c-brand-900); line-height: 1.3;
}
.problem-list p { margin: 0; color: var(--text-muted); }
@media (prefers-color-scheme: dark) { .problem-list h3 { color: var(--c-brand-300); } }
:root[data-theme="dark"] .problem-list h3 { color: var(--c-brand-300); }
:root[data-theme="light"] .problem-list h3 { color: var(--c-brand-900); }
@media (max-width: 620px) {
  .problem-list li { grid-template-columns: 2.5rem minmax(0, 1fr); gap: var(--sp-3); }
}

/* Les quatre objections : un échange, pas un damier. Chaque bloc est une
   réplique — quelqu'un objecte, on répond, on prouve. Quatre cartes de
   largeur égale donnaient le même poids à l'objection et à la réponse, et
   le dialogue s'y perdait. L'objection tient la colonne de gauche, alignée
   à DROITE contre le filet ; la réponse part de l'autre bord. La disposition
   dit qui parle, sans qu'on ait à l'écrire. */
.pillar-exchanges {
  max-width: 58rem; margin: var(--sp-8) auto 0;
  display: grid; gap: 0;
}
.pillar-exchange {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: var(--sp-6);
  padding: var(--sp-6) 0;
  border-top: 1px solid var(--border);
}
.pillar-exchange:last-child { border-bottom: 1px solid var(--border); }
.pillar-objection {
  margin: 0; padding: 0;
  border: 0; /* neutralise le style <blockquote> par défaut du navigateur */
  text-align: right;
  color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6;
}
.pillar-answer h3 {
  margin: 0 0 var(--sp-2); font-size: var(--fs-h3);
  color: var(--c-brand-900); line-height: 1.3;
}
.pillar-proof { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.65; }
@media (prefers-color-scheme: dark) { .pillar-answer h3 { color: var(--c-brand-300); } }
:root[data-theme="dark"] .pillar-answer h3 { color: var(--c-brand-300); }
:root[data-theme="light"] .pillar-answer h3 { color: var(--c-brand-900); }
@media (max-width: 780px) {
  .pillar-exchange { grid-template-columns: 1fr; gap: var(--sp-3); }
  .pillar-objection { text-align: left; }
}
@media (prefers-color-scheme: dark) {
  .pillar-card h3 { color: var(--c-brand-300); }
}
:root[data-theme="dark"] .pillar-card h3 { color: var(--c-brand-300); }
:root[data-theme="light"] .pillar-card h3 { color: var(--c-brand-900); }
@media (max-width: 860px) {
  .pillar-grid { grid-template-columns: 1fr; }
}

/* Page « À propos » : une colonne étroite, parce que c'est un texte suivi.
   La mesure est plafonnée autour de 68 caractères — au-delà, l'oeil perd
   la ligne suivante en revenant à la marge. */
/* 38rem, pas 46 : mesuré au navigateur, 46rem donnait ~92 caractères par
   ligne. Au-delà d'environ 75, l'oeil rate la ligne suivante en revenant à
   la marge gauche, et un texte de cinq paragraphes se lit mal. La taille de
   police est fixée ici plutôt que via --fs-lead, dont le clamp retombe sur
   son minimum à cette largeur de colonne. */
.about-page { max-width: 38rem; margin-inline: auto; }
.about-page h1 { font-size: var(--fs-h1); line-height: 1.2; margin: 0; }
.about-story { margin-top: var(--sp-7); }
.about-story p {
  margin: 0 0 var(--sp-5); color: var(--text);
  font-size: clamp(1.02rem, 1.05vw, 1.12rem); line-height: 1.7;
}
.about-story p:last-child { margin-bottom: 0; }
/* Titres de section du recit. Discrets : ils servent de reperes dans une
   colonne de lecture, pas de titres de chapitre -- d'ou le calibre proche
   du texte et la couleur de marque plutot qu'une taille imposante. Le
   premier ne prend pas de marge haute, il suit deja le titre de page. */
.about-story h2 {
  margin: var(--sp-8) 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: 1.25;
  color: var(--text);
}
.about-story h2:first-child { margin-top: 0; }

/* Les faits verifiables. Chaque ligne : un reperage, une affirmation, sa
   justification, et le lien vers la page qui en porte la preuve. */
.about-facts { max-width: 38rem; margin: var(--sp-10) auto 0; padding-top: var(--sp-8); border-top: 1px solid var(--border); }
.about-facts h2 { margin: 0; font-family: var(--font-display); font-size: var(--fs-h3); color: var(--text); }
.about-facts-intro { margin: var(--sp-3) 0 var(--sp-6); color: var(--text-muted); line-height: 1.65; }
.about-facts ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-5); }
.about-facts li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-4); }
.about-fact-mark { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px; background: var(--c-brand-50); color: var(--c-brand-700); }
:root[data-theme="dark"] .about-fact-mark { background: rgba(75, 156, 224, .14); color: var(--c-brand-300); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .about-fact-mark { background: rgba(75, 156, 224, .14); color: var(--c-brand-300); }
}
.about-facts strong { display: block; color: var(--text); font-size: var(--fs-body); }
.about-facts small { display: block; margin-top: var(--sp-1); color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6; }
.about-facts a { display: inline-block; margin-top: var(--sp-2); color: var(--primary-ink); font-size: var(--fs-sm); font-weight: 700; text-decoration: none; transition: color .18s var(--ease); }
.about-facts a:hover { color: var(--c-brand-500); text-decoration: underline; text-underline-offset: 3px; }
.about-facts a span { display: inline-block; transition: transform .2s var(--ease); }
.about-facts a:hover span { transform: translateX(4px); }
/* La déclaration d'indépendance se détache du fil du récit : un filet et
   un fond très pâle suffisent, aucune boîte d'alerte -- ce n'est pas un
   avertissement, c'est une réponse. */
.about-independence {
  margin-top: var(--sp-6); padding: var(--sp-5);
  border-left: 2px solid var(--c-brand-400);
  background: var(--c-brand-50); border-radius: 0 var(--r-md) var(--r-md) 0;
  font-size: var(--fs-body) !important;
}
.about-actions {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
  margin-top: var(--sp-8);
}
@media (prefers-color-scheme: dark) {
  .about-independence { background: rgba(31, 106, 165, .12); }
}
:root[data-theme="dark"] .about-independence { background: rgba(31, 106, 165, .12); }
:root[data-theme="light"] .about-independence { background: var(--c-brand-50); }

/* Sur grand écran, la colonne unique cessait d'être une colonne de lecture :
   mesurés au navigateur en 1600x900, 540 px de texte posés au milieu de
   1585 px laissaient 522 px de vide de chaque côté. La page ne paraissait
   pas sobre, elle paraissait faite pour un téléphone et affichée sur un PC.
   Le récit ne s'élargit pas pour autant — ce sont les TITRES qui partent à
   gauche, et qui y restent pendant que le texte défile. Une seule règle de
   composition, tenue par les deux blocs : le repère à gauche, le contenu à
   droite.
   1080 px : c'est la largeur où 21rem + gouttière + 40rem tiennent encore
   dans la page (1012 px de contenu) ; en dessous, la colonne unique reste
   la bonne réponse et rien de ce qui suit ne s'applique. */
@media (min-width: 1080px) {
  .about-page {
    /* Un seul gabarit de pistes, décliné par les deux blocs. La rangée des
       faits occupe toute la largeur de la grille — soit exactement
       21rem + gouttière + 40rem — donc les mêmes pistes y retombent
       d'aplomb sur celles du récit, sans subgrid.
       40rem de lecture, pas 38 : mesuré au navigateur, 38rem tombait à 58
       signes par ligne, sous les ~66 d'un texte suivi confortable. 40rem
       en donne ~69, toujours loin des 75 au-delà desquels l'oeil rate la
       ligne suivante en revenant à la marge. */
    --about-rail: minmax(0, 21rem) minmax(0, 40rem);
    --about-gutter: clamp(3.5rem, 5vw, 5.5rem);
    max-width: var(--page-max);
    display: grid;
    grid-template-columns: var(--about-rail);
    column-gap: var(--about-gutter);
    /* Les pistes sont plafonnées : sans cela la grille s'étalerait sur
       1132 px et la mesure repartirait à ~120 signes. */
    justify-content: center;
    /* start, et pas la valeur par défaut : un élément collant étiré sur
       toute la hauteur de sa rangée n'a aucune course pour glisser. */
    align-items: start;
  }
  /* Titre + récit : la boîte qui borne la course du titre collant. Sans
     elle, mesuré, le titre continuait sa descente jusqu'à se poser sur le
     titre « Les faits » — le navigateur ne s'arrête pas à la rangée de
     grille. */
  .about-lede {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: var(--about-rail);
    column-gap: var(--about-gutter);
    align-items: start;
  }
  /* Le titre de page tient lieu de repère : il reste en vue tant que le
     récit défile, et s'en va avec lui. */
  .about-page .market-section-heading {
    grid-column: 1;
    max-width: none;
    position: sticky;
    /* L'en-tête public est collant et mesure 77 px : on se pose dessous. */
    top: calc(77px + var(--sp-7));
  }
  .about-story { grid-column: 2; margin-top: 0; }
  /* Pleine largeur, donc son filet court sur les deux colonnes : c'est lui
     qui sépare le récit de sa vérification, il ne peut pas s'arrêter au
     milieu de la page. */
  .about-facts {
    grid-column: 1 / -1;
    max-width: none;
    display: grid;
    grid-template-columns: var(--about-rail);
    column-gap: var(--about-gutter);
    align-items: start;
  }
  .about-facts-head { grid-column: 1; }
  .about-facts-intro { margin-bottom: 0; }
  .about-facts ul { grid-column: 2; }
  .about-actions { grid-column: 2; }
}
/* Bandeau maintenance : un fait vérifiable (version XSD réelle,
   passée par app.py depuis xsd_version_check.reference_version(), pas
   un texte i18n figé), jamais une date ou un pourcentage inventés.
   Bande, plus caisson : le titre de section et son intro sont déjà de la
   prose centrée ; un troisième bloc centré et ombré par-dessus n'ajoutait
   qu'une boîte. Deux filets, le fait à gauche, la donnée à droite. */
/* Pleine largeur du contenu, et non 62rem : vu a l'ecran, une bande de
   992 px posee au-dessus d'un explorateur de 1132 px donnait deux bords
   gauches decales de 67 px -- l'oeil y lit un defaut d'alignement, pas une
   composition. Les filets tombent desormais d'aplomb sur ceux du panneau.
   Le texte, lui, garde sa largeur de lecture (62ch). */
.coverage-strip {
  max-width: none; margin: var(--sp-7) auto 0;
  padding: var(--sp-5) 0;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-5) var(--sp-7); align-items: center;
  text-align: left;
}
.coverage-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px var(--sp-3); border-radius: var(--r-full);
  background: var(--c-ok-bg); color: var(--c-ok);
  font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
}
.coverage-strip p { margin: var(--sp-3) 0 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6; max-width: 62ch; }
.coverage-value-line { color: var(--text); font-weight: 600; }
.coverage-xsd {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 !important;
  padding-left: var(--sp-7); border-left: 1px solid var(--border);
  font-family: var(--font-mono); font-size: var(--fs-xs);
}
.coverage-xsd span { color: var(--text-faint); }
.coverage-xsd strong { color: var(--c-brand-600); font-weight: 700; }
.coverage-xsd-generic { color: var(--text-faint); font-family: var(--font-body); font-style: italic; max-width: 24rem; }
@media (max-width: 860px) {
  .coverage-strip { grid-template-columns: 1fr; }
  .coverage-xsd { padding-left: 0; border-left: 0; }
}

/* Option secondaire, jamais un concurrent visuel de la plateforme en
   ligne : padding réduit, pas de fond de couleur, pas de gros bouton
   primaire — juste un lien. */
.desktop-secondary { padding: clamp(48px, 6vw, 76px) var(--gutter); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.desktop-secondary-inner {
  max-width: var(--page-max); margin: 0 auto;
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr);
  gap: clamp(32px, 5vw, 64px); align-items: center;
}
.desktop-secondary-copy h2 { margin: var(--sp-2) 0 0; font-size: var(--fs-h3); color: var(--text); max-width: 34ch; }
.desktop-secondary-copy > p { margin: var(--sp-3) 0 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.65; max-width: 58ch; }
.desktop-secondary-case-kicker { margin: var(--sp-5) 0 var(--sp-2) !important; color: var(--text-faint); font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; }
.desktop-secondary-cases { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.desktop-secondary-cases li { position: relative; padding-left: 18px; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.55; }
.desktop-secondary-cases li::before { content: ""; position: absolute; left: 0; top: 9px; width: 6px; height: 6px; border-radius: 50%; background: var(--border-strong); }
.desktop-secondary-link {
  display: inline-block; margin-top: var(--sp-5);
  color: var(--c-brand-600); font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
}
.desktop-secondary-link:hover { text-decoration: underline; }
.desktop-secondary-link:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 3px; border-radius: var(--r-xs); }
.desktop-secondary-visual { display: flex; justify-content: center; }
.desktop-mini-window {
  width: 100%; max-width: 220px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--sh-md);
  overflow: hidden;
}
.desktop-mini-top { display: flex; gap: 5px; padding: var(--sp-2) var(--sp-3); background: var(--bg-sunken); border-bottom: 1px solid var(--border); }
.desktop-mini-top span { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); }
.desktop-mini-body { padding: var(--sp-5) var(--sp-4); display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); }
.desktop-mini-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--r-md); background: var(--c-brand-50); color: var(--c-brand-600); }
.desktop-mini-line { width: var(--w, 70%); height: 7px; border-radius: var(--r-full); background: var(--border); }
.desktop-mini-btn { width: 60%; height: 20px; margin-top: 4px; border-radius: var(--r-sm); background: var(--c-brand-200); }
@media (max-width: 760px) {
  .desktop-secondary-inner { grid-template-columns: 1fr; }
  .desktop-secondary-visual { order: -1; }
  .desktop-mini-window { max-width: 180px; }
}

.market-final-cta {
  max-width: var(--page-max);
  margin: var(--sp-12) auto; padding: var(--sp-10) var(--sp-9);
  border-radius: var(--r-lg);
  background: var(--c-brand-800);
  color: #fff;
  display: grid; grid-template-columns: 1.3fr auto; align-items: center; gap: var(--sp-8);
}
.market-final-cta h2 { color: #fff; max-width: 22ch; }
.market-final-cta > div:last-child { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-5); }
.market-final-cta a:not(.market-primary) {
  color: color-mix(in srgb, #fff 86%, transparent);
  font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
}
.market-final-cta a:not(.market-primary):hover { color: #fff; text-decoration: underline; }
.light-button { background: #fff; color: var(--c-brand-700); }
.light-button:hover { background: #fff; color: var(--c-brand-800); }

/* ---------- Footer ---------- */
.marketing-footer {
  margin-top: auto;
  padding: var(--sp-10) var(--gutter) var(--sp-5);
  background: var(--c-brand-950); color: color-mix(in srgb, #fff 78%, transparent);
}
.marketing-footer-grid {
  max-width: var(--page-max); margin: 0 auto;
  display: grid; grid-template-columns: 1.7fr .9fr .9fr 1.1fr; gap: var(--sp-8);
  padding-bottom: var(--sp-8);
}
.marketing-footer h2 { margin: 0 0 var(--sp-4); color: #fff; font-size: var(--fs-sm); }
.marketing-footer a {
  display: block; margin-bottom: var(--sp-3);
  color: color-mix(in srgb, #fff 66%, transparent);
  text-decoration: none; font-size: var(--fs-xs);
  transition: color .15s var(--ease);
}
.marketing-footer a:hover { color: #fff; text-decoration: underline; }
.marketing-footer-logo, .marketing-footer-logo b { color: #fff; }
.marketing-footer-brand p {
  max-width: 42ch; margin: var(--sp-4) 0 0;
  font-size: var(--fs-xs); line-height: 1.6;
  color: color-mix(in srgb, #fff 62%, transparent);
}
.marketing-footer-note, .marketing-footer-contact {
  color: color-mix(in srgb, #fff 52%, transparent) !important;
  font-size: var(--fs-2xs) !important; line-height: 1.55;
}
.marketing-footer-bottom {
  max-width: var(--page-max); margin: 0 auto;
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-4);
  padding-top: var(--sp-5);
  border-top: 1px solid color-mix(in srgb, #fff 14%, transparent);
  color: color-mix(in srgb, #fff 50%, transparent); font-family: var(--font-mono); font-size: var(--fs-2xs);
}

/* ---------- Auth (login / register) ---------- */
.auth-market-layout { display: grid; grid-template-columns: 1fr 1fr; min-height: calc(100vh - 76px); }

/* Nous contacter — en-tête compact (titre + intro + « que se passe-t-il
   ensuite ») suivi d'une zone formulaire (colonne principale) + informations
   (colonne latérale), pour que le formulaire reste visible sans défiler
   toute la page sur un desktop standard. */
.contact-hero-compact {
  position: relative; isolation: isolate;
  max-width: 1180px; margin: 0 auto; padding: var(--sp-4) var(--gutter) 0;
}
.contact-hero-compact::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  top: 0; left: 50%; width: 100vw; height: 320px; transform: translateX(-50%);
  background: radial-gradient(ellipse 55% 100% at 50% 0%, var(--c-brand-50), transparent 72%);
}
.contact-hero-compact h1 { margin: var(--sp-2) 0 0; max-width: 34ch; color: var(--text); font-size: var(--fs-h2); line-height: 1.15; letter-spacing: var(--tracking-display); }
.contact-intro { max-width: 70ch; margin: var(--sp-2) 0 0; color: var(--text-muted); line-height: 1.55; font-size: var(--fs-sm); }

.contact-layout {
  display: grid; grid-template-columns: 1.15fr .85fr; gap: var(--sp-8);
  align-items: start;
  max-width: 1180px; margin: 0 auto; padding: var(--sp-4) var(--gutter) var(--sp-12);
}
.contact-subtitle { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-sm); font-weight: 700; color: var(--text); }
.contact-subtitle:first-child { margin-top: 0; }
.contact-list { margin: 0; padding-left: 20px; color: var(--text-muted); line-height: 1.6; font-size: var(--fs-sm); }
.contact-list li::marker { color: var(--c-brand-500); }
.contact-chips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-5); }
.contact-chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4); border: 1px solid var(--c-brand-200);
  border-radius: var(--r-full); background: var(--c-brand-50);
  color: var(--c-brand-700); font-size: var(--fs-xs); font-weight: 650;
}
/* Champs signalés en erreur seulement après interaction de l'utilisateur
   (:user-invalid), jamais au premier affichage. */
.contact-card :is(input, select, textarea):user-invalid {
  border-color: var(--c-danger);
  background: var(--c-danger-bg);
}
.contact-card {
  padding: var(--sp-6); border: 1px solid var(--border); border-radius: var(--r-xl);
  background: var(--card-raised); box-shadow: var(--sh-md);
}
.contact-card h2 { margin: 0 0 var(--sp-3); font-size: var(--fs-h3); color: var(--text); }
.contact-card label { display: flex; flex-direction: column; gap: 5px; font-size: var(--fs-xs); font-weight: 650; color: var(--text); }
.contact-card textarea { resize: vertical; }
.contact-card label.contact-consent { flex-direction: row; align-items: flex-start; gap: 10px; font-weight: 450; color: var(--text-muted); }
.contact-card .contact-consent input { width: auto; min-height: 0; margin-top: 3px; }
.hp-field { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; opacity: 0; }
.contact-checklist { list-style: none; padding-left: 0; }
.contact-checklist li { position: relative; padding-left: 26px; margin: 6px 0; }
.contact-checklist li::before {
  content: "\2713"; position: absolute; left: 0; top: 0;
  width: 18px; height: 18px; display: grid; place-items: center;
  border-radius: var(--r-full); background: var(--c-brand-50);
  color: var(--c-brand-600); font-size: 11px; font-weight: 800;
}
.contact-email-alt { margin-top: var(--sp-5); color: var(--text-muted); font-size: var(--fs-xs); }
/* brand-600 ici seulement : le bleu de lien par defaut (brand-500) tient
   sur un fond blanc, mais cette adresse est posee sur le fond teinte de la
   page contact, ou il tombe a 4,44:1 -- le dernier element du site public
   sous le seuil AA apres la passe du 2026-08-11. Correction locale : le
   bleu de lien general reste inchange, il est mesure conforme ailleurs. */
.contact-email-alt a { font-weight: 650; color: var(--c-brand-600); }
.contact-success { text-align: center; padding: var(--sp-10) var(--sp-7); }
.contact-success-icon {
  display: inline-grid; place-items: center; width: 56px; height: 56px;
  margin-bottom: var(--sp-4); border-radius: var(--r-full);
  background: var(--c-ok-bg); color: var(--c-ok); font-size: 1.6rem; font-weight: 800;
}
.contact-success p { max-width: 46ch; margin: var(--sp-3) auto var(--sp-6); color: var(--text-muted); line-height: 1.65; }
.message-detail-grid { display: grid; grid-template-columns: 1.4fr .6fr; gap: 18px; align-items: start; }
.message-detail-grid .account-panel { margin: 0; }
.message-full-text {
  padding: var(--sp-5); border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: var(--text); line-height: 1.7; white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.message-meta { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; margin: 0; }
.message-meta dt { color: var(--text-muted); font-size: var(--fs-xs); font-weight: 650; }
.message-meta dd { margin: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }
.message-preview a { color: var(--text-muted); text-decoration: none; }
.message-preview a:hover { color: var(--c-brand-700); }
.message-row-new td:first-child { box-shadow: inset 3px 0 0 var(--c-brand-500); }
@media (max-width: 900px) {
  .message-detail-grid { grid-template-columns: 1fr; }
}
@media (min-width: 901px) {
  /* La colonne d'information reste visible pendant que le formulaire
     (plus long) défile — évite qu'elle paraisse abandonnée en haut de
     page une fois son propre contenu épuisé. */
  .contact-context { position: sticky; top: calc(76px + var(--sp-5)); }
}
@media (max-width: 900px) {
  .contact-layout { grid-template-columns: 1fr; gap: var(--sp-7); }
}
.auth-market-context {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--sp-12) max(var(--gutter), 4vw);
  background: var(--c-brand-800);
  color: #fff;
}
.auth-market-context::after { display: none; }
.auth-market-context h1 {
  max-width: 16ch; margin: 0; color: #fff;
  font-size: var(--fs-h1); line-height: 1.1;
  letter-spacing: var(--tracking-display); font-weight: 800;
}
.auth-market-context > p {
  max-width: 46ch; margin: var(--sp-4) 0 0;
  color: color-mix(in srgb, #fff 72%, transparent);
  font-size: var(--fs-body); line-height: 1.65;
}
.auth-overline { font-family: var(--font-mono); }
.auth-context-list { margin-top: var(--sp-8); display: grid; gap: var(--sp-3); }
/* Small workspace preview: the same instrument-panel language as the
   homepage hero, sized down — a glimpse of what's behind the login. */
.auth-preview {
  position: relative;
  margin-top: var(--sp-9); max-width: 300px;
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, #fff 16%, transparent);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--c-brand-900) 55%, transparent);
  backdrop-filter: blur(2px);
}
.auth-preview-top {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-4); padding-bottom: var(--sp-3);
  border-bottom: 1px solid color-mix(in srgb, #fff 14%, transparent);
  color: color-mix(in srgb, #fff 78%, transparent);
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
}
.auth-preview-row { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.auth-preview-icon {
  flex: none; display: grid; place-items: center;
  width: 34px; height: 22px;
  border-radius: var(--r-xs);
  background: color-mix(in srgb, #fff 10%, transparent);
  color: color-mix(in srgb, #fff 82%, transparent);
  font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
}
.auth-preview-bar { flex: 1; height: 7px; border-radius: var(--r-full); background: color-mix(in srgb, #fff 10%, transparent); overflow: hidden; }
.auth-preview-bar::after {
  content: ""; display: block; width: var(--w, 60%); height: 100%;
  border-radius: var(--r-full); background: linear-gradient(90deg, var(--c-teal-300), var(--c-brand-300));
}
.auth-preview-stat {
  display: flex; align-items: baseline; gap: var(--sp-2);
  margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid color-mix(in srgb, #fff 14%, transparent);
}
.auth-preview-stat strong { font-family: var(--font-mono); font-size: 1.5rem; font-weight: 700; color: #fff; }
.auth-preview-stat span { color: color-mix(in srgb, #fff 68%, transparent); font-size: var(--fs-xs); }
.auth-context-list span {
  display: flex; align-items: center; gap: var(--sp-3);
  color: color-mix(in srgb, #fff 82%, transparent); font-size: var(--fs-sm);
}
.auth-context-list i {
  display: grid; place-items: center;
  width: 22px; height: 22px; flex: none;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-teal-300) 24%, transparent);
  color: var(--c-teal-300); font-style: normal; font-size: var(--fs-2xs); font-weight: 900;
}
.auth-market-form { display: grid; place-items: center; padding: var(--sp-10) var(--gutter); background: var(--bg); }
.auth-market-card {
  width: 100%; max-width: 424px;
  padding: var(--sp-8);
  border: 1px solid var(--border); border-radius: var(--r-xl);
  background: var(--card-raised); box-shadow: var(--sh-lg);
}
.auth-overline {
  margin: 0 0 var(--sp-2);
  color: var(--c-brand-600); font-size: var(--fs-2xs);
  font-weight: 800; letter-spacing: var(--tracking-wide); text-transform: uppercase;
}
.auth-market-card h2 {
  margin: 0; color: var(--text);
  font-size: 1.65rem; letter-spacing: var(--tracking-display); font-weight: 800;
}
.auth-intro { margin: var(--sp-2) 0 var(--sp-6); color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.55; }
.auth-provider-btn {
  width: 100%; min-height: 48px;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--card); color: var(--text);
  text-decoration: none; font-weight: 700; font-size: var(--fs-sm);
  transition: all .15s var(--ease);
}
.auth-provider-btn:hover { border-color: var(--c-brand-400); box-shadow: var(--sh-sm); }
.auth-provider-btn-disabled,
.auth-provider-btn-disabled:hover {
  border-style: dashed;
  border-color: var(--border-strong);
  box-shadow: none;
  color: var(--text-muted);
  cursor: not-allowed;
  opacity: .78;
}
.auth-provider-btn-disabled > span { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.auth-provider-btn-disabled small { margin-top: 3px; color: var(--text-faint); font-size: .68rem; font-weight: 600; }
.auth-divider {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-5) 0; color: var(--text-faint); font-size: var(--fs-xs);
}
.auth-divider::before, .auth-divider::after { content: ""; height: 1px; flex: 1; background: var(--border); }
.auth-info {
  margin-bottom: var(--sp-5); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--c-brand-200); border-radius: var(--r-sm);
  background: var(--c-brand-50); color: var(--c-brand-700);
  font-size: var(--fs-xs); line-height: 1.5;
}
.auth-form { display: grid; gap: var(--sp-3); }
.auth-input-wrap {
  position: relative; display: block; height: 50px;
}
.auth-input-icon {
  position: absolute; left: var(--sp-4); top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; color: var(--text-faint); pointer-events: none;
}
.auth-input-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.auth-input-wrap input {
  width: 100%; height: 100%;
  padding: 0 var(--sp-4) 0 calc(var(--sp-4) * 2 + 18px);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--card); color: var(--text); font-size: var(--fs-sm);
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.auth-input-wrap input:focus {
  outline: none; border-color: var(--c-brand-500);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-500) 24%, transparent);
}
.auth-form-meta { display: flex; justify-content: flex-end; }
.auth-form-meta a { color: var(--c-brand-600); font-size: var(--fs-xs); font-weight: 700; text-decoration: none; }
.auth-form-meta a:hover { text-decoration: underline; }
.auth-submit {
  min-height: 50px; padding: 0 var(--sp-6);
  border: 0; border-radius: var(--r-sm);
  background: var(--c-brand-500); color: #fff;
  font-size: var(--fs-sm); font-weight: 750; cursor: pointer;
  box-shadow: var(--sh-sm);
  transition: background .15s var(--ease), transform .15s var(--ease), box-shadow .15s var(--ease);
}
.auth-submit:hover { background: var(--c-brand-600); transform: translateY(-1px); box-shadow: var(--sh-md); }
.auth-submit-wide { width: 100%; margin-top: var(--sp-1); }
.auth-submit.is-loading, .btn.is-loading { display: flex; align-items: center; justify-content: center; gap: var(--sp-2); cursor: progress; opacity: .88; }
.auth-submit.is-loading::before, .btn.is-loading::before {
  content: ""; width: 15px; height: 15px; flex: none;
  border: 2px solid color-mix(in srgb, #fff 35%, transparent);
  border-top-color: #fff; border-radius: 50%;
  animation: auth-spin .7s linear infinite;
}
@keyframes auth-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .auth-submit.is-loading::before { animation-duration: 1.4s; } }
.auth-signup { margin: var(--sp-5) 0 0; text-align: center; color: var(--text-muted); font-size: var(--fs-xs); }
.auth-signup a { color: var(--c-brand-600); font-weight: 700; text-decoration: none; }
.auth-signup a:hover { text-decoration: underline; }
.auth-two-columns { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.register-layout { min-height: calc(100vh - 76px); }

/* ============================================================
   DARK THEME
   Re-points the semantic roles and the low end of the brand
   scale; every rule above inherits it with no duplication.
   ============================================================ */
:root[data-theme="dark"] {
  --bg:            #0f171e;
  --bg-sunken:     #0a1116;
  --card:          #17222b;
  --card-raised:   #1c2831;
  --text:          #e8eef4;
  --text-muted:    #9db0be;
  /* Idem note ci-dessous -- #6d8394 ne tenait que 4.10:1 sur --card
     (#17222b), sous le seuil AA 4.5:1 pour du texte réel en petite
     taille. Mesuré : 4.75:1 sur --card, 5.32:1 sur --bg. */
  --text-faint:    #798e9e;
  --border:        #293843;
  --border-strong: #374855;

  /* Tinted surfaces must darken, not lighten, in dark mode */
  --c-brand-50:  #122a3e;
  --c-brand-100: #16354b;
  --c-brand-200: #1d4763;
  --c-danger-bg: #3a1e1d;
  --c-warn-bg:   #382b16;
  --c-ok-bg:     #10322a;
  --c-teal-100:  #10332f;

  /* Lift text-bearing accents for contrast on dark surfaces */
  --c-brand-500: #4b9ce0;
  --c-brand-600: #6cb2ea;
  --c-danger:    #f08a85;
  --c-warn:      #e0ac5a;
  --c-ok:        #5cc79f;

  --sh-xs: 0 1px 2px rgba(0, 0, 0, .34);
  --sh-sm: 0 2px 6px rgba(0, 0, 0, .38);
  --sh-md: 0 6px 18px rgba(0, 0, 0, .44);
  --sh-lg: 0 14px 34px rgba(0, 0, 0, .5);
  --sh-xl: 0 26px 60px rgba(0, 0, 0, .58);
}
/* Deep-navy panels are already dark; keep them from washing out */
:root[data-theme="dark"] .market-hero {
  background:
    radial-gradient(1100px 520px at 78% -8%, #16354b 0%, transparent 62%),
    linear-gradient(168deg, #122a3e 0%, var(--bg) 58%);
}
:root[data-theme="dark"] .market-hero h1 { color: var(--text); }
:root[data-theme="dark"] .market-validation { background: #0e1e2b; }
:root[data-theme="dark"] .market-approach   { background: #0c1c2b; }
:root[data-theme="dark"] .marketing-footer  { background: #0a1420; }
:root[data-theme="dark"] .auth-market-context { background: linear-gradient(155deg, #12283a, #0a1622); }
:root[data-theme="dark"] .analysis-score { background: linear-gradient(135deg, #17456d, #0e2c47); }
:root[data-theme="dark"] .score-ring span { background: #17456d; }
:root[data-theme="dark"] .market-final-cta { background: #123f5e; }
:root[data-theme="dark"] .light-button { background: #e8eef4; color: #0e3b6a; }

/* Same rules when the OS prefers dark and the user has not chosen light */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #0f171e;
    --bg-sunken:     #0a1116;
    --card:          #17222b;
    --card-raised:   #1c2831;
    --text:          #e8eef4;
    --text-muted:    #9db0be;
    /* Idem note du :root clair -- #6d8394 ne tenait que 4.10:1 sur --card
       (#17222b), sous le seuil AA 4.5:1 pour du texte réel en petite
       taille. Mesuré : 4.75:1 sur --card, 5.32:1 sur --bg. */
    --text-faint:    #798e9e;
    --border:        #293843;
    --border-strong: #374855;
    --c-brand-50:  #122a3e;
    --c-brand-100: #16354b;
    --c-brand-200: #1d4763;
    --c-danger-bg: #3a1e1d;
    --c-warn-bg:   #382b16;
    --c-ok-bg:     #10322a;
    --c-teal-100:  #10332f;
    --c-brand-500: #4b9ce0;
    --c-brand-600: #6cb2ea;
    --c-danger:    #f08a85;
    --c-warn:      #e0ac5a;
    --c-ok:        #5cc79f;
    --sh-xs: 0 1px 2px rgba(0, 0, 0, .34);
    --sh-sm: 0 2px 6px rgba(0, 0, 0, .38);
    --sh-md: 0 6px 18px rgba(0, 0, 0, .44);
    --sh-lg: 0 14px 34px rgba(0, 0, 0, .5);
    --sh-xl: 0 26px 60px rgba(0, 0, 0, .58);
  }
  :root:not([data-theme="light"]) .market-hero {
    background:
      radial-gradient(1100px 520px at 78% -8%, #16354b 0%, transparent 62%),
      linear-gradient(168deg, #122a3e 0%, var(--bg) 58%);
  }
  :root:not([data-theme="light"]) .market-hero h1 { color: var(--text); }
  :root:not([data-theme="light"]) .market-validation { background: #0e1e2b; }
  :root:not([data-theme="light"]) .market-approach   { background: #0c1c2b; }
  :root:not([data-theme="light"]) .marketing-footer  { background: #0a1420; }
  :root:not([data-theme="light"]) .auth-market-context { background: linear-gradient(155deg, #12283a, #0a1622); }
  :root:not([data-theme="light"]) .analysis-score { background: linear-gradient(135deg, #17456d, #0e2c47); }
  :root:not([data-theme="light"]) .score-ring span { background: #17456d; }
  :root:not([data-theme="light"]) .market-final-cta { background: #123f5e; }
  :root:not([data-theme="light"]) .light-button { background: #e8eef4; color: #0e3b6a; }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1080px) {
  .market-hero-inner { grid-template-columns: 1fr .85fr; gap: var(--sp-7); }
  /* Deux colonnes, plus une seule : la rangée compte quatre chiffres depuis
     l'ajout des règles, et les empiler ferait quatre cartes pleine largeur
     juste sous le héros. Elle repasse à une colonne à 540 px. */
  .market-capabilities { grid-template-columns: repeat(2, 1fr); margin-top: var(--sp-8); }
  .workflow-track { grid-template-columns: repeat(2, 1fr); }
  .workflow-arrow { display: none; }
  .market-validation, .market-approach, .auth-market-layout { grid-template-columns: 1fr; }
  .approach-image { min-height: 380px; }
  .market-final-cta { grid-template-columns: 1fr; }
}
@media (max-width: 820px) {
  .marketing-header-inner { gap: var(--sp-4); }
  .market-hero-inner { padding: var(--sp-9) var(--gutter); grid-template-columns: 1fr; }
  .market-hero h1 { max-width: none; }
  .market-analysis-card { max-width: 470px; }
  .market-section { padding: var(--sp-10) var(--gutter); }
  .validation-showcase, .validation-copy, .approach-copy { padding: var(--sp-9) var(--gutter); }
  /* 116px, not 78px: "AVERTISSEMENT" in mono/uppercase needs the room
     — the narrower column let it collide with the row-number cell. */
  .validation-table-demo > div { grid-template-columns: 116px 34px 1fr; }
  .validation-table-demo > div span:last-child { display: none; }
  .marketing-footer-grid { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
  .auth-market-context { padding: var(--sp-9) var(--gutter); }
  .market-final-cta { margin: var(--sp-9) var(--gutter); padding: var(--sp-8) var(--sp-6); }
}
@media (max-width: 540px) {
  .marketing-brand small { display: none; }
  /* En dessous de 540 px, le logo + le hamburger seuls ne laissent plus
     assez de place pour thème/langue/Se connecter/Démarrer un projet sur
     la même ligne (ces contrôles ne doivent JAMAIS être masqués dans le
     hamburger — voir _responsive_nav.html). Plutôt que de les cacher, la
     barre passe sur deux lignes : logo+hamburger, puis les actions en
     dessous, alignées à droite comme sur desktop. */
  .marketing-header-inner { flex-wrap: wrap; gap: var(--sp-2); padding: var(--sp-2) var(--sp-4); min-height: 0; }
  .marketing-brand { font-size: var(--fs-body); gap: var(--sp-2); }
  .marketing-actions { flex-basis: 100%; justify-content: flex-end; gap: var(--sp-2); margin-left: 0; }
  .util-btn, .lang-switcher-trigger-icon { width: 32px; height: 32px; }
  .marketing-cta, .marketing-login { padding: 0 var(--sp-3); }
  .workflow-track { grid-template-columns: 1fr; }
  .market-hero-actions { flex-direction: column; align-items: stretch; }
  .market-primary, .market-ghost { width: 100%; justify-content: center; }
  .marketing-footer-grid { grid-template-columns: 1fr; }
  .auth-market-card { padding: var(--sp-6); }
  .auth-two-columns { grid-template-columns: 1fr; }
  .market-capabilities { grid-template-columns: 1fr; }
  .market-capabilities article { flex-direction: column; gap: var(--sp-3); }
}
/* À 390 px (375 px utiles avec la barre de défilement), les libellés
   français des deux actions dépassaient encore la largeur disponible de
   26 px. On conserve les quatre commandes visibles et tactiles, mais on
   réduit uniquement leur espacement horizontal sur les très petits écrans. */
@media (max-width: 420px) {
  .marketing-actions { gap: 4px; }
  .marketing-actions .marketing-cta { padding-inline: 8px; font-size: .75rem; }
  .marketing-actions .marketing-login { padding-inline: 8px; font-size: .8rem; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ============================================================
   AUTHENTICATED PAGES — inherit the same system
   ============================================================ */
.page { padding-top: var(--sp-6); }
.card, .tile, .stat-box, .doc-panel { border-color: var(--border); box-shadow: var(--sh-sm); }
.card, .doc-panel { border-radius: var(--r-lg); }
.tile { border-radius: var(--r-lg); }
.tile:hover { box-shadow: var(--sh-md); border-color: var(--c-brand-200); }
.tile-icon { border-radius: var(--r-md); background: var(--c-brand-50); color: var(--c-brand-600); }
.search-box input { min-height: 48px; border-radius: var(--r-md); border-color: var(--border-strong); box-shadow: var(--sh-xs); }
.dashboard-intro { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-7); margin: var(--sp-1) 0 var(--sp-6); }
.dashboard-intro h1 { margin: var(--sp-1) 0 var(--sp-2); font-size: var(--fs-h2); letter-spacing: var(--tracking-display); }
.dashboard-intro .subtitle { margin: 0; }
.dashboard-intro-status {
  min-width: 245px; display: flex; flex-direction: column; align-items: flex-end; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--sh-xs);
}
.dashboard-intro-status .status-ok { font-size: var(--fs-2xs); }
.dashboard-intro-status small { color: var(--text-muted); font-size: var(--fs-2xs); text-align: right; }
table.admin-table th { background: var(--bg-sunken); }
table.admin-table tbody tr:hover { background: var(--bg-sunken); }
input[type=text], input[type=email], input[type=password], input[type=file],
input[type=tel], input[type=number], input[type=search], input:is([type=date],[type=datetime-local]), select {
  border-color: var(--border-strong); min-height: 43px;
}
textarea { border-color: var(--border-strong); }
input[type=text]:focus, input[type=email]:focus, input[type=password]:focus,
input[type=tel]:focus, input[type=number]:focus, input[type=search]:focus,
input:is([type=date],[type=datetime-local]):focus, textarea:focus, select:focus {
  outline: none; border-color: var(--c-brand-400);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-400) 18%, transparent);
}
.btn { box-shadow: var(--sh-xs); }
.btn-ghost, .btn-outline { box-shadow: none; }
@media (max-width: 760px) {
  .dashboard-intro { flex-direction: column; }
  .dashboard-intro-status { width: 100%; align-items: flex-start; }
  .dashboard-intro-status small { text-align: left; }
}

/* ============================================================
   PRODUCT-WIDE REFINEMENT — public, user workspace and admin
   ============================================================ */
:root {
  --fs-body: 1rem;
  --fs-sm: .92rem;
  --fs-xs: .84rem;
  --fs-2xs: .76rem;
}

body { font-size: 16px; line-height: 1.5; }
body.user-shell,
body.admin-shell { --sticky-toolbar-top: 77px;
  background:
    radial-gradient(700px 360px at 10% 0%, color-mix(in srgb, var(--c-brand-100) 52%, transparent), transparent 68%),
    var(--bg);
}
.user-shell .page,
.admin-shell .page { width: 100%; padding-bottom: var(--sp-10); }
/* Généralisation validée du gabarit pilote (.admin-page-framed, ex-scopé à
   Demandes d'accès seul) à TOUTES les pages admin -- demande explicite :
   sur grand écran le contenu (KPI/filtre/tableau) doit rester centré dans
   la fenêtre plutôt que s'étirer collé à la sidebar jusqu'au bord de
   l'écran. max-width: 1600px reste large (les tableaux denses gardent leur
   place) ; en dessous de ce seuil, aucun changement visible (la plupart des
   écrans n'atteignent jamais la largeur de blocage). */
.admin-shell .page { width: 90%; max-width: 1600px; margin: 0 auto; padding-left: var(--sp-5); padding-right: var(--sp-5); }
@media (min-width: 1600px) { .admin-shell .page { width: 80%; } }
@media (max-width: 1024px) { .admin-shell .page { width: 95%; } }
@media (max-width: 900px) { .admin-shell .page { padding-left: var(--sp-3); padding-right: var(--sp-3); } }
@media (max-width: 640px) { .admin-shell .page { width: 100%; } }

/* Navigation */
.product-header .topbar-inner { min-height: 76px; }
.brand { gap: var(--sp-3); }
.brand > span { font-size: 1.02rem; letter-spacing: var(--tracking-tight); }
.brand small { font-size: var(--fs-2xs); font-weight: 500; }

/* Universal product page hero */
.app-page-hero {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8);
  margin: var(--sp-2) 0 var(--sp-7); padding: var(--sp-8);
  border: 1px solid var(--c-brand-100); border-radius: var(--r-xl);
  background:
    radial-gradient(360px 180px at 92% 6%, color-mix(in srgb, var(--c-teal-100) 72%, transparent), transparent 72%),
    linear-gradient(135deg, var(--card-raised), var(--c-brand-50));
  box-shadow: var(--sh-md);
}
.app-page-hero::after {
  content: ""; position: absolute; right: -54px; bottom: -80px;
  width: 210px; height: 210px; border-radius: var(--r-full);
  border: 1px solid color-mix(in srgb, var(--c-brand-300) 38%, transparent);
  box-shadow: 0 0 0 34px color-mix(in srgb, var(--c-brand-100) 32%, transparent);
  pointer-events: none;
}
.app-page-hero-copy { position: relative; z-index: 1; max-width: 760px; }
.app-page-kicker,
.section-kicker {
  margin: 0 0 var(--sp-2); color: var(--c-brand-600);
  font-size: var(--fs-2xs); line-height: 1.2; font-weight: 800;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
}
.app-page-hero h1 {
  margin: 0; max-width: 24ch;
  color: var(--text); font-size: var(--fs-h1);
  line-height: 1.08; letter-spacing: var(--tracking-display);
}
.app-page-hero-copy > p:last-of-type {
  max-width: 65ch; margin: var(--sp-3) 0 0;
  color: var(--text-muted); font-size: var(--fs-body); line-height: 1.65;
}
.app-page-meta {
  display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5);
  margin-top: var(--sp-5);
}
.app-page-meta span {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  color: var(--text-muted); font-size: var(--fs-xs); font-weight: 650;
}
.app-page-meta svg { color: var(--c-brand-500); }
.app-page-hero-aside {
  position: relative; z-index: 1;
  width: min(290px, 100%); flex: none;
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2);
  padding: var(--sp-5);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--card-raised) 92%, transparent);
  box-shadow: var(--sh-sm);
}
.app-page-hero-aside strong { color: var(--text); font-size: var(--fs-sm); }
.app-page-hero-aside small { color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.55; }
.aside-icon {
  display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: var(--r-md); background: var(--c-brand-50); color: var(--c-brand-600);
}
.live-indicator {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  color: var(--c-ok); font-size: var(--fs-2xs); font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em;
}
.live-indicator i {
  width: 8px; height: 8px; border-radius: var(--r-full); background: var(--c-ok);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--c-ok) 16%, transparent);
}

/* Section headings and empty states */
.polished-heading {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-5);
  margin: var(--sp-8) 0 var(--sp-4);
}
.polished-heading h2 { margin: 0; color: var(--text); font-size: var(--fs-h3); letter-spacing: var(--tracking-tight); }
.polished-heading p:not(.section-kicker) { margin: var(--sp-2) 0 0; color: var(--text-muted); font-size: var(--fs-xs); }
/* --eu-blue-dark DEVIENT PLUS FONCE en theme sombre (#123a5e) : c'est un jeton
   de FOND, et il reste juste pour cet usage. Employe comme couleur de TEXTE il
   posait du bleu tres fonce sur un fond quasi noir -- 1,38:1 mesure sur les
   liens de documentation, 1,79:1 sur les boutons a contour, contre 4,5 exiges.
   Les douze usages en texte pointent desormais sur --primary-ink, qui
   s'eclaircit en sombre. En theme clair les deux jetons valent #1d6295 : le
   changement est donc invisible en clair et ne repare que le sombre.
   Les trois usages en fond restent sur --eu-blue-dark, c'est son role. */

/* .text-action est pose aussi bien sur <a> que sur <button> (selection en masse
   des tableaux). Sa couleur, --c-brand-600, s'adapte bien au theme : le defaut
   etait ailleurs. Sur un bouton, sans reinitialisation, le fond gris par defaut
   du navigateur subsiste -- indetectable en clair, mais en sombre il place un
   bleu clair sur un gris clair : 2:1 mesure. Un element style comme un lien
   doit etre depouille comme un lien. */
.text-action {
  color: var(--c-brand-600);
  font-size: var(--fs-xs);
  font-weight: 750;
  text-decoration: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
}
.text-action:hover { text-decoration: underline; }
.section-count {
  display: inline-flex; align-items: center; min-height: 32px; padding: 0 var(--sp-3);
  border-radius: var(--r-full); background: var(--c-brand-50); color: var(--c-brand-600);
  font-size: var(--fs-2xs); font-weight: 800; white-space: nowrap;
}
.app-empty-state {
  min-height: 190px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); padding: var(--sp-7); text-align: center; color: var(--text-muted);
}
/* This component is now also toggled dynamically via the `hidden` attribute
   (admin.js datatable filters) rather than only through {% if %}. An
   unconditional `display: flex` on the class always wins over the browser's
   default [hidden] { display: none }, since author CSS beats the UA
   stylesheet regardless of the attribute — so the override must be explicit. */
.app-empty-state[hidden] { display: none; }
.app-empty-state > svg { color: var(--c-brand-400); }
.app-empty-state strong { color: var(--text); font-size: var(--fs-sm); }
.app-empty-state span { max-width: 52ch; font-size: var(--fs-xs); }
.app-empty-state .btn { margin-top: var(--sp-3); }

.app-search-box {
  position: relative; display: flex; align-items: center; max-width: none;
  margin: 0 0 var(--sp-5);
}
.app-search-box > span { position: absolute; left: var(--sp-4); z-index: 1; color: var(--text-faint); display: grid; }
.app-search-box input { width: 100%; padding-left: 48px; font-size: var(--fs-sm); }
.grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); }
.tile-copy h3 { font-size: var(--fs-sm); line-height: 1.4; }
.recent-heading { margin-top: var(--sp-9); }
.recent-card,
.activity-table-card { margin-top: 0; padding: 0; overflow: hidden; }
.recent-list li { padding: var(--sp-4) var(--sp-5); }

/* Dashboard: a compact working tool, not a marketing hero — title and
   stats share one shallow row instead of a big decorative card. */
.dashboard-topbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-5); margin-bottom: var(--sp-6);
}
.dashboard-topbar-copy h1 { margin: 0; font-size: var(--fs-h3); color: var(--text); }
.dashboard-topbar-copy p { margin: 4px 0 0; color: var(--text-muted); font-size: var(--fs-sm); }
/* Carte KPI unique de la plateforme (_kpi_card.html) : valeur en haut,
   intitulé en dessous, sub optionnelle -- remplace .dashboard-stat (tableau
   de bord utilisateur) ET .stat-card (admin, 5 gabarits qui recopiaient le
   même HTML indépendamment avant ce chantier). min-height fixe : toutes les
   cartes d'une même grille ont la même hauteur que la carte ait ou non une
   ligne sub (le contenu est centré verticalement dans cet espace réservé,
   jamais recalculé par le contenu réel). */
/* margin-bottom aligné sur .admin-toolbar (même rythme vertical que
   l'espace filtre → tableau déjà en place) -- annulé ci-dessous pour le
   contexte tableau de bord (.dashboard-topbar-stats), où cette grille est
   un slot flex à côté du titre et n'a pas de bloc suivant à espacer. */
.kpi-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-2); }
.kpi-card {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  min-height: 96px; padding: var(--sp-3) var(--sp-4); box-sizing: border-box;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: inherit; text-decoration: none;
}
/* .kpi-card-copy regroupe libellé + sub (voir _kpi_card.html) : par défaut
   un simple ré-empilement identique à avant (la carte elle-même reste
   flex-direction:column, .kpi-card-copy n'ajoute qu'un niveau neutre) --
   seule la variante --wide ci-dessous exploite ce regroupement pour mettre
   la valeur à gauche et le libellé+sub à droite sur une seule ligne basse. */
.kpi-card-copy { display: flex; flex-direction: column; min-width: 0; }
a.kpi-card:hover, a.kpi-card:focus-visible { border-color: var(--c-brand-300); background: var(--bg-sunken); outline: 0; }
a.kpi-card:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
/* Taille adaptative par nombre de chiffres (0-99 / 100-999 / 1000-9999 /
   10000+) -- calculée une fois dans la macro, jamais par largeur de
   conteneur (pas de container query : la valeur ne doit jamais changer
   d'échelle au redimensionnement, seulement selon son propre nombre de
   chiffres, pour rester prévisible dans une grille de cartes homogènes). */
.kpi-card-value { color: var(--c-brand-600); font-weight: 800; letter-spacing: var(--tracking-tight); line-height: 1.15; }
.kpi-card-value--xl { font-size: 2rem; }
.kpi-card-value--lg { font-size: 1.6rem; }
.kpi-card-value--md { font-size: 1.3rem; }
.kpi-card-value--sm { font-size: 1.05rem; }
.kpi-card-value--na { font-size: var(--fs-sm); font-weight: 700; color: var(--text-faint); }
/* Suffixe d'unité (Statistiques : "s", "%", "Go"…) -- délibérément plus
   petit et non-gras, jamais compté dans le calcul de taille adaptative
   ci-dessus (voir le paramètre `unit`, séparé de `value`, dans _kpi_card.html). */
.kpi-card-unit { margin-left: 2px; font-size: 0.6em; font-weight: 700; color: var(--text-muted); }
/* overflow-wrap:anywhere -- un libellé long et SANS espace ("Administrateurs")
   ne peut pas se répartir sur 2 lignes à une largeur de carte étroite (2
   colonnes en dessous de 760px) sans un point de coupure autorisé à
   l'intérieur du mot lui-même ; sans ça la carte déborde réellement de son
   conteneur (mesuré via getBoundingClientRect sur .stat-card-label, bug
   jumeau corrigé ci-dessous -- même composant visuel, même risque). */
.kpi-card-label { color: var(--text-faint); font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .02em; overflow-wrap: anywhere; }
.kpi-card-sub { color: var(--text-muted); font-size: var(--fs-2xs); margin-top: 1px; overflow-wrap: anywhere; }
.kpi-card.is-warning { border-left-width: 3px; border-left-color: var(--c-warn); }
.kpi-card.is-danger { border-left-width: 3px; border-left-color: var(--c-danger); }
.kpi-card.is-ok { border-left-width: 3px; border-left-color: var(--c-ok); }
@media (max-width: 760px) {
  .kpi-card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Cartes KPI (Nouveau/En cours/Traité/Archivé, etc.) sur les pages admin
   (Messages/Support/Journal) qui utilisent le modèle par défaut (pas
   --wide) : réduites d'environ moitié (hauteur mini, padding, taille des
   chiffres) par rapport au modèle non scopé ci-dessus -- demande explicite,
   ce modèle repris tel quel du tableau de bord ressortait trop grand une
   fois vu sur ces pages, qui affichent ces cartes comme un simple résumé
   chiffré, pas comme le point d'entrée principal de la page (contrairement
   au tableau de bord). Non scopé aux cartes --wide (déjà compactes,
   inchangées) ni au tableau de bord (pas de .admin-shell sur cette page). */
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card { min-height: 48px; padding: 6px 8px; gap: 1px; border-left-width: 3px; }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card-value--xl { font-size: 1.1rem; }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card-value--lg { font-size: 0.95rem; }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card-value--md { font-size: 0.85rem; }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card-value--sm { font-size: 0.75rem; }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card-label { font-size: 0.62rem; }
/* Liseré de couleur à gauche de chaque carte, même dégradé que la variante
   --wide (repris tel quel, pas de nouvelle palette) -- demande explicite
   ("rajouter les couleurs à gauche de chaque"). Exclu des cartes à ton
   sémantique (is-warning/is-danger/is-ok, ex. « Nouveau » ou « Échecs ») :
   ce signal (alerte réelle) doit rester prioritaire sur la couleur
   purement décorative du cycle. */
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+1) { border-left-color: var(--c-brand-300); }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+2) { border-left-color: var(--c-brand-400); }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+3) { border-left-color: var(--c-brand-500); }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+4) { border-left-color: var(--c-brand-600); }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+5) { border-left-color: var(--c-brand-400); }
.kpi-card-grid:not(.kpi-card-grid--wide) .kpi-card:not(.is-warning):not(.is-danger):not(.is-ok):nth-child(6n+6) { border-left-color: var(--c-brand-600); }

/* Variante "wide" (vue d'ensemble admin) : des rectangles bas et larges au
   lieu de cartes empilées verticalement -- même typographie/rayons/bordures
   que .kpi-card par défaut (voir règles ci-dessus, non dupliquées), seules
   la disposition interne (ligne valeur+libellé au lieu d'empilement centré)
   et la largeur minimale de colonne changent.
   auto-fit, jamais auto-fill : auto-fill réserve une colonne vide dès que
   la largeur le permet, rétrécissant chaque carte réelle sans raison --
   mesuré avant ce correctif : 7 colonnes de 157px pour 6 cartes réelles à
   1440px (une carte fantôme volait de la largeur aux 6 vraies). */
/* Espace après la grille KPI avant ce qui suit (barre d'outils/filtre, ou
   puces de statut sur Support) -- sans lui, la grille et la barre suivante
   se touchaient (0px mesuré), rendant les cartes KPI visuellement écrasées
   contre le filtre. Valeur alignée sur .admin-toolbar margin-bottom (même
   rythme vertical que l'espace filtre → tableau, déjà en place). */
.kpi-card-grid--wide { grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); margin-bottom: var(--sp-2); }
.kpi-card-grid--wide .kpi-card {
  flex-direction: row; align-items: center; gap: var(--sp-3);
  min-height: 0; padding: 12px var(--sp-4);
  border-left-width: 3px;
}
.kpi-card-grid--wide .kpi-card-copy { justify-content: center; gap: 1px; }
/* flex-shrink AUTORISÉ (pas flex:none) -- avec grid-template-columns en
   auto-fit/1fr, un value flex-shrink:0 associé à un texte long non
   numérique (dates, libellés d'état) fait dérailler l'algorithme de
   dimensionnement des lignes de la grille dans Chromium : mesuré à 3
   cartes texte (bloc Email), la ligne de grille explosait à 354px alors
   qu'aucune carte prise seule ou en paire ne dépassait 64-83px -- reproduit
   et confirmé en neutralisant isolément flex-shrink (354px -> 81px), pas
   liée au nombre de colonnes (confirmé en forçant 3 colonnes explicites,
   toujours 354px). flex-grow reste à 0 : la valeur ne doit pas s'étirer
   pour remplir l'espace, seulement pouvoir rétrécir si nécessaire. */
.kpi-card-grid--wide .kpi-card-value { flex: 0 1 auto; line-height: 1.05; }
/* Paliers réduits par rapport aux valeurs par défaut (2rem/1.6rem/...) : la
   variante wide gagne sa lisibilité par la largeur de la ligne, pas par une
   valeur géante qui pousserait la hauteur de la carte -- vérifié à 0 / 8 /
   54 / 999 / 12500 / indisponible, jamais de troncature ni de changement de
   hauteur entre ces cas. */
.kpi-card-grid--wide .kpi-card-value--xl { font-size: 1.5rem; }
.kpi-card-grid--wide .kpi-card-value--lg { font-size: 1.3rem; }
.kpi-card-grid--wide .kpi-card-value--md { font-size: 1.15rem; }
.kpi-card-grid--wide .kpi-card-value--sm { font-size: 1.02rem; }
.kpi-card-grid--wide .kpi-card-value--na { font-size: var(--fs-xs); }
.kpi-card-grid--wide .kpi-card-label { white-space: normal; }
/* Nuances de bleu -- un seul procédé (liseré + fond très légèrement teinté)
   appliqué identiquement aux 6 cartes, seule la nuance --c-brand-* tourne
   par position : même structure partout, jamais une couleur hors de
   l'échelle de marque. Les paliers 300-600 restent lisibles en thème sombre
   (soit inchangés et déjà utilisés ailleurs pour des bordures, soit
   explicitement rehaussés par la surcharge --c-brand-500/600 du thème
   sombre) -- volontairement pas de 700+ ici, qui deviendrait trop proche du
   fond de carte sombre et perdrait son rôle de repère visuel. */
.kpi-card-grid--wide .kpi-card:nth-child(6n+1) { border-left-color: var(--c-brand-300); background: color-mix(in srgb, var(--c-brand-100) 45%, var(--card)); }
.kpi-card-grid--wide .kpi-card:nth-child(6n+2) { border-left-color: var(--c-brand-400); background: color-mix(in srgb, var(--c-brand-100) 55%, var(--card)); }
.kpi-card-grid--wide .kpi-card:nth-child(6n+3) { border-left-color: var(--c-brand-500); background: color-mix(in srgb, var(--c-brand-100) 65%, var(--card)); }
.kpi-card-grid--wide .kpi-card:nth-child(6n+4) { border-left-color: var(--c-brand-600); background: color-mix(in srgb, var(--c-brand-200) 45%, var(--card)); }
.kpi-card-grid--wide .kpi-card:nth-child(6n+5) { border-left-color: var(--c-brand-400); background: color-mix(in srgb, var(--c-brand-200) 60%, var(--card)); }
.kpi-card-grid--wide .kpi-card:nth-child(6n+6) { border-left-color: var(--c-brand-600); background: color-mix(in srgb, var(--c-brand-200) 70%, var(--card)); }
/* Le signal warning/danger (ex. demandes en attente > 0) reste prioritaire
   sur la rotation décorative -- même règle de spécificité (0,3,0) que les
   nth-child ci-dessus, donc l'ordre dans la feuille tranche : ces deux
   règles doivent rester APRÈS le bloc nth-child pour gagner la cascade.
   Fond réinitialisé à --card (pas de teinte bleue) pour que le liseré
   sémantique ne soit jamais dilué par la décoration de couleur. */
.kpi-card-grid--wide .kpi-card.is-warning { border-left-color: var(--c-warn); background: var(--card); }
.kpi-card-grid--wide .kpi-card.is-danger { border-left-color: var(--c-danger); background: var(--card); }
.kpi-card-grid--wide .kpi-card.is-ok { border-left-color: var(--c-ok); background: var(--card); }
/* Spécificité (0,3,1) > (0,3,0) des règles nth-child ci-dessus -- sans ce
   correctif, le survol/focus (déjà géré par la règle générique a.kpi-card:hover
   plus bas, spécificité 0,2,0) resterait invisible sur cette variante, la
   teinte bleue de fond par position l'emportant silencieusement dessus. */
.kpi-card-grid--wide a.kpi-card:hover,
.kpi-card-grid--wide a.kpi-card:focus-visible { background: var(--bg-sunken); }
@media (max-width: 760px) {
  .kpi-card-grid--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  .kpi-card-grid--wide { grid-template-columns: 1fr; }
}

/* Admin-only compact XSD compliance indicator — a status line, not a
   dashboard block. Colour communicates the state; the only click target
   is the actual verification source, not the whole card. */
.admin-xsd-indicator {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: 0 0 var(--sp-6); padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border); border-left-width: 3px; border-radius: var(--r-md);
  background: var(--card);
}
.admin-xsd-icon { flex: none; display: grid; place-items: center; color: var(--text-faint); }
.admin-xsd-copy { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.admin-xsd-copy strong { font-size: var(--fs-xs); font-weight: 800; color: var(--text); }
.admin-xsd-status { font-size: var(--fs-xs); color: var(--text-muted); }
.admin-xsd-copy small { color: var(--text-faint); font-size: var(--fs-2xs); }
.admin-xsd-source-link { flex: none; font-size: var(--fs-2xs); font-weight: 700; color: var(--c-brand-600); text-decoration: none; white-space: nowrap; }
.admin-xsd-source-link:hover { text-decoration: underline; }
.admin-xsd-indicator.is-synced { border-left-color: var(--c-ok); }
.admin-xsd-indicator.is-synced .admin-xsd-icon { color: var(--c-ok); }
.admin-xsd-indicator.is-mismatch { border-left-color: var(--c-danger); }
.admin-xsd-indicator.is-mismatch .admin-xsd-icon { color: var(--c-danger); }
.admin-xsd-indicator.is-error { border-left-color: var(--c-warn); }
.admin-xsd-indicator.is-error .admin-xsd-icon { color: var(--c-warn); }
.admin-xsd-indicator.is-never { border-left-color: var(--border-strong); }
.admin-xsd-indicator.is-stale { border-left-color: var(--c-warn); }
.admin-xsd-indicator.is-stale .admin-xsd-icon { color: var(--c-warn); }

/* Composant unique de conformité XSD (_version_badge.html), utilisé sur
   toutes les pages qui affichaient jusqu'ici des présentations différentes
   (bandeau tableau de bord, carte admin, texte brut génération/documentation).
   Une seule forme visuelle, deux gabarits : 'full' (bloc avec sous-ligne) et
   'compact' (pastille en ligne pour un en-tête de page). */
.version-badge {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  border: 1px solid var(--border); border-left-width: 3px; border-radius: var(--r-md);
  background: var(--card); color: inherit; text-decoration: none;
}
.version-badge-full { padding: var(--sp-2) var(--sp-4); width: 100%; box-sizing: border-box; margin-bottom: var(--sp-4); }
.version-badge-compact { padding: 6px var(--sp-3); gap: var(--sp-2); font-size: var(--fs-xs); }
.version-badge-icon { flex: none; display: grid; place-items: center; color: var(--text-faint); }
.version-badge-copy { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; min-width: 0; }
.version-badge-compact .version-badge-copy { flex-direction: row; }
/* Une seule ligne compacte quand la largeur le permet (« Synchronisée ·
   Version X · Dernière vérification : … ») — ne repasse en colonne que
   sous 540px, voir media query plus bas. */
.version-badge-full .version-badge-copy { flex-direction: row; align-items: baseline; }
.version-badge-label { font-size: var(--fs-sm); font-weight: 700; color: var(--text); }
.version-badge-status { font-size: var(--fs-sm); color: var(--text-muted); }
.version-badge-compact .version-badge-status { font-size: var(--fs-xs); font-weight: 600; }
.version-badge-sub { color: var(--text-faint); font-size: var(--fs-2xs); }
.version-badge-sep { color: var(--text-faint); }
a.version-badge:hover { border-color: var(--border-strong); }
.version-badge.is-ok .version-badge-icon { color: var(--c-ok); }
.version-badge.is-ok { border-left-color: var(--c-ok); }
.version-badge.is-danger .version-badge-icon { color: var(--c-danger); }
.version-badge.is-danger { border-left-color: var(--c-danger); }
.version-badge.is-warning .version-badge-icon { color: var(--c-warn); }
.version-badge.is-warning { border-left-color: var(--c-warn); }
.version-badge.is-muted { border-left-color: var(--border-strong); }
@media (max-width: 540px) {
  .version-badge-full .version-badge-copy { flex-direction: column; align-items: flex-start; gap: 1px; }
  .version-badge-full .version-badge-sep { display: none; }
}

/* Ligne compacte "Conformité XSD" de la vue d'ensemble admin : le badge
   partagé (variant='full', inchangé) + le bouton de vérification manuelle,
   côte à côte sur une seule ligne quand la largeur le permet, jamais une
   grande carte. .version-badge-full a `width:100%` par défaut (pensé pour
   un bloc seul) -- ici il doit plutôt partager l'espace avec le bouton. */
.admin-xsd-overview-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-6); }
/* flex-grow:0 est essentiel ici : un flex-grow:1 ferait grandir le badge
   pour occuper tout l'espace disponible (mesuré réellement : 859px sur une
   rangée de 1013px), ne laissant plus assez de place pour le bouton sur la
   même ligne malgré le flex-wrap -- le badge doit garder sa largeur de
   contenu naturelle, pas s'étirer. */
.admin-xsd-overview-row .version-badge-full { width: auto; flex: 0 1 auto; margin-bottom: 0; max-width: 100%; }
.admin-xsd-overview-row form { flex: none; }
@media (max-width: 480px) {
  .admin-xsd-overview-row .version-badge-full { flex-basis: 100%; }
}

/* Grille KPI dans la barre supérieure du tableau de bord : 3 cartes
   compactes côte à côte, pas le comportement auto-fill générique de
   .kpi-card-grid (pensé pour un bloc pleine largeur comme la vue
   d'ensemble admin) -- ce contexte est un simple slot flex à côté du
   titre, la grille ne doit pas essayer de s'étirer pour remplir l'espace
   disponible de façon imprévisible. */
.dashboard-topbar-stats .kpi-card-grid { grid-template-columns: repeat(3, minmax(96px, auto)); gap: var(--sp-2); margin-bottom: 0; }
.dashboard-topbar-stats .kpi-card { min-height: 64px; padding: 6px var(--sp-3); }
.dashboard-topbar-stats .kpi-card-value--xl,
.dashboard-topbar-stats .kpi-card-value--lg { font-size: var(--fs-h3); }
@media (max-width: 760px) {
  .dashboard-topbar { flex-direction: column; align-items: stretch; }
  .dashboard-topbar-stats .kpi-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .admin-xsd-copy { flex-direction: column; align-items: flex-start; gap: 2px; }
}

/* Modules and operations: the zone the user actually came for — the
   search is part of it, not an isolated box above it. */
.dashboard-modules-zone > h2 { margin: 0 0 var(--sp-4); font-size: var(--fs-h3); color: var(--text); }
.dashboard-search {
  position: relative; margin-bottom: var(--sp-2);
}
.dashboard-search > span { position: absolute; left: var(--sp-4); top: 50%; transform: translateY(-50%); color: var(--text-faint); display: grid; }
.dashboard-search input {
  width: 100%; min-height: 52px; padding: 0 var(--sp-4) 0 48px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--card); color: var(--text); font: inherit; font-size: var(--fs-sm);
  box-shadow: var(--sh-xs);
}
.dashboard-search input:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 1px; }
.dashboard-modules-zone .grid { margin-top: var(--sp-6); }

/* Documentation */
.documentation-page-hero { justify-content: flex-start; }

/* Row-split confirmation */
.split-page-hero { align-items: center; }

/* Modules and operations */
.module-guidance-panel { margin: 0 0 var(--sp-7); }
.operation-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
.operation-card {
  position: relative;
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card-raised); color: var(--text);
  box-shadow: var(--sh-xs); transition: all .18s var(--ease);
}
.operation-card-link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.operation-card:hover { transform: translateY(-2px); border-color: var(--c-brand-300); box-shadow: var(--sh-md); }
.operation-card .op-icon {
  width: 34px; height: 34px; margin: 0; display: grid; place-items: center;
  border-radius: var(--r-md); background: var(--c-brand-50);
}
.operation-card-copy { min-width: 0; }
.operation-card .op-title { margin-top: 1px; font-size: var(--fs-sm); line-height: 1.4; }
.operation-card-arrow { align-self: center; color: var(--c-brand-500); font-size: 1.1rem; }

/* Compact module header — a title bar, not a decorative hero card. */
.module-header-compact {
  display: flex; align-items: flex-start; flex-wrap: wrap; gap: var(--sp-4);
  padding: var(--sp-4) 0 var(--sp-5); margin-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.module-header-icon {
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px; border-radius: var(--r-md);
  background: var(--c-brand-50); color: var(--c-brand-600);
}
.module-header-copy { flex: 1; min-width: 0; }
.module-header-copy h1 { margin: 0; font-size: var(--fs-h3); color: var(--text); line-height: 1.3; }
.module-header-copy p { margin: 2px 0 0; color: var(--text-faint); font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }

/* Contextual help: one reusable panel pattern for modules and
   operations alike — a small trigger, a slide-in sheet, content
   already in the page (server-rendered), no separate fetch. */
.info-trigger {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 8px 14px; border: 1px solid var(--border); border-radius: var(--r-full);
  background: var(--card); color: var(--c-brand-600); font-size: var(--fs-xs); font-weight: 700;
  cursor: pointer; white-space: nowrap;
}
.info-trigger:hover, .info-trigger:focus-visible { border-color: var(--c-brand-300); background: var(--c-brand-50); outline: 0; }
/* UN SEUL bouton d'information dans toute l'application : celui des cartes
   d'opération. Les champs de formulaire avaient le leur, en 20 px, sur la
   ligne d'étiquette — deux objets pour la même promesse (« il y a une
   explication ici »), de deux tailles, à deux places. */
.op-info-btn, .field-info-btn {
  position: relative; z-index: 2; flex: none; align-self: center;
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--card); color: var(--text-faint); cursor: pointer;
}
.op-info-btn:hover, .op-info-btn:focus-visible,
.field-info-btn:hover, .field-info-btn:focus-visible { border-color: var(--c-brand-300); color: var(--c-brand-600); outline: 0; }
@media (max-width: 760px) {
  /* Sous 760px, .module-header-copy (flex:1, min-width:0) peut se
     réduire assez pour que le badge XSD / le parcours d'étapes semblent
     toujours « tenir » sur la même ligne flex que lui d'après l'algo de
     wrap, alors que le titre y déborde déjà sur plusieurs lignes en
     dessous — recouvrement visuel réel constaté (titre masqué par le
     badge). Chaque bloc prend sa propre ligne pleine largeur : plus
     aucune ambiguïté possible. Placé après les règles de base : même
     spécificité, une déclaration antérieure serait sinon écrasée. */
  .module-header-compact { flex-wrap: wrap; }
  .module-header-compact .module-header-copy,
  .module-header-compact .version-badge,
  .module-header-compact .process-mini-flow,
  .module-header-compact .validation-score-pill { flex-basis: 100%; }
  .info-trigger { flex-basis: 100%; margin-top: var(--sp-1); }
}
.help-panel { position: fixed; inset: 0; z-index: 300; }
.help-panel[hidden] { display: none; }
.help-panel-backdrop { position: absolute; inset: 0; background: rgba(10, 22, 34, .38); }
.help-panel-sheet {
  position: absolute; top: 0; right: 0; height: 100%; width: min(420px, 92vw);
  background: var(--card); box-shadow: var(--sh-xl);
  display: flex; flex-direction: column;
  animation: help-slide-in .22s var(--ease) both;
}
@keyframes help-slide-in { from { opacity: 0; transform: translateX(24px); } to { opacity: 1; transform: none; } }
.help-panel-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-5); border-bottom: 1px solid var(--border);
}
.help-panel-head h3 { margin: 0; font-size: var(--fs-h3); color: var(--text); }
.help-panel-close {
  display: grid; place-items: center; flex: none;
  width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 50%;
  background: var(--card); color: var(--text-muted); cursor: pointer;
}
.help-panel-close:hover, .help-panel-close:focus-visible { border-color: var(--border-strong); color: var(--text); outline: 0; }
.help-panel-body { padding: var(--sp-5); overflow-y: auto; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.65; }
.help-panel-body :is(p, ul, ol) { margin: 0 0 var(--sp-3); }
.help-panel-body p:last-child { margin-bottom: 0; }
.help-block + .help-block { margin-top: var(--sp-5); }
.help-block h4 { margin: 0 0 var(--sp-2); color: var(--text); font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .03em; }
body.help-panel-open { overflow: hidden; }
@media (max-width: 620px) { .help-panel-sheet { width: 100%; } }
@media (prefers-reduced-motion: reduce) { .help-panel-sheet { animation: none; } }

.support-intro { margin: 0 0 var(--sp-6); color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.65; max-width: 62ch; }
.support-temp-notice {
  display: flex; align-items: flex-start; gap: 6px;
  margin-top: var(--sp-6); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-sunken);
  color: var(--text-faint); font-size: var(--fs-xs); line-height: 1.55;
}
.support-temp-notice svg { flex: none; margin-top: 2px; }

/* Generation */
.generation-page-hero { align-items: flex-end; }
.process-mini-flow { position: relative; z-index: 1; display: flex; align-items: center; gap: var(--sp-2); flex: none; }
.process-mini-flow span {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-full); background: var(--card); color: var(--text-muted);
  font-size: var(--fs-2xs); font-weight: 750;
}
.process-mini-flow b {
  display: grid; place-items: center; width: 20px; height: 20px;
  border-radius: var(--r-full); background: var(--bg-sunken); color: var(--text-muted);
}
.process-mini-flow .is-current { border-color: var(--c-brand-300); color: var(--c-brand-700); }
.process-mini-flow .is-current b { background: var(--c-brand-600); color: #fff; }
.process-mini-flow .is-done { border-color: var(--c-ok); color: var(--c-ok); }
.process-mini-flow .is-done b { background: var(--c-ok); color: #fff; }
.process-mini-flow .is-done svg { display: block; }
.compact-steps { flex: none; }

/* Compact header reused on generation/validation pages — same base as
   .module-header-compact (category.html); border-bottom tints with the
   validation outcome instead of a big colored hero. */
.operation-header-compact.has-errors { border-bottom-color: var(--c-danger); }
.operation-header-compact.is-ready { border-bottom-color: var(--c-ok); }
.validation-score-pill {
  flex: none; padding: 6px 14px; border-radius: var(--r-full); font-size: var(--fs-sm); font-weight: 800;
  border: 1px solid var(--border); background: var(--card);
}
.validation-score-pill.is-ok { border-color: var(--c-ok); color: var(--c-ok); }
.validation-score-pill.is-error { border-color: var(--c-danger); color: var(--c-danger); }

/* Form help hierarchy: short essential hint under a field (.field-hint,
   plain text, no box) vs. a detail worth a click but not everyone's time
   (.field-info-btn, opens the existing .help-panel) vs. a technical
   metadata line (.field-hint-meta). Replaces .help-text/.warn-box-style
   callouts that gave every hint the same visual weight as a warning. */
.field-label-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.field-label-row label { margin: 0; }
/* La saisie et son bouton d'aide sur une même ligne, le bouton centré sur la
   saisie — exactement la place qu'il occupe sur une carte d'opération. Il a
   été essayé sur la ligne d'étiquette : collé au libellé il tombait à une
   abscisse différente par champ ; poussé au bord droit il se retrouvait
   contre l'étiquette de la colonne VOISINE et semblait lui appartenir.
   Contre sa propre saisie, la question ne se pose plus. */
/* Champ de date : la saisie garde le format du modèle (JJ/MM/AAAA) et le
   bouton ouvre le calendrier du navigateur — voir static/date-field.js. Le
   sélecteur natif est posé sous le bouton, invisible, pour que le calendrier
   s'ouvre à l'aplomb de l'icône plutôt qu'au coin de la page. */
.date-field { position: relative; }
.date-field > input[type=text] { width: 100%; padding-right: 38px; }
.date-field-picker {
  position: absolute; top: 0; right: 0; width: 38px; height: 100%;
  display: grid; place-items: center; padding: 0;
  border: 0; background: none; color: var(--text-faint); cursor: pointer;
}
.date-field-picker:hover, .date-field-picker:focus-visible { color: var(--c-brand-600); outline: 0; }
.date-field-native {
  position: absolute; right: 14px; bottom: 2px; width: 1px; height: 1px;
  min-height: 0; padding: 0; border: 0; opacity: 0; pointer-events: none;
}

.field-control { display: grid; grid-template-columns: minmax(0, 1fr) 28px; gap: 8px; align-items: center; }
.field-control > :first-child { min-width: 0; }
/* La colonne du bouton existe même quand le champ n'a rien à expliquer : sans
   elle, une saisie sans « i » serait 36 px plus large que sa voisine et les
   bords droits d'une rangée cesseraient de s'aligner. */
.field-hint { margin: 6px 0 0; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.5; }
.field-hint-meta { display: flex; align-items: center; gap: 6px; color: var(--text-faint); font-family: var(--font-mono); font-size: var(--fs-2xs); }
.field-hint-meta svg { flex: none; color: var(--c-brand-500); }
/* Même métadonnée, mais posée DANS une ligne de texte ou à côté d'un bouton.
   Sans cette variante, la version « flex » passe à la ligne et casse
   l'alignement — et l'ancienne solution (.help-text sur un <span>) peignait un
   panneau à padding vertical par-dessus le contenu voisin : un padding
   vertical sur une boîte inline ne pousse rien, il déborde. */
.field-hint-meta.is-inline { display: inline-flex; vertical-align: middle; }

/* ------------------------------------------------------------------
   MENU DÉROULANT CHERCHABLE (.combo — voir static/combo.js)
   Un seul habillage pour les deux cas que le navigateur rendait mal : un
   <select> trop long pour être parcouru à l'œil (248 pays), et un
   <input list> dont la bulle native, hors charte, recouvrait l'aide du
   champ. La saisie reprend EXACTEMENT le gabarit des autres champs — même
   hauteur, même bordure, même anneau de focus — pour qu'un formulaire ne
   se lise pas comme deux formulaires selon le champ regardé.
   ------------------------------------------------------------------ */
.combo { position: relative; display: block; }
/* LE <select> RESTE DANS LE FLUX, et c'est le point important : il porte les
   règles de SON contexte — 150 px de large et 32 px de haut dans une barre de
   filtres d'administration, pleine largeur et 43 px dans un formulaire — et
   l'enveloppe s'y ajuste donc toute seule. Le sortir du flux obligeait à
   redécrire ces dimensions pour chaque écran, et un menu converti se
   retrouvait de deux tailles différentes dans la même barre.
   Il garde aussi son `required` : c'est lui que le navigateur signale quand
   l'envoi part vide. Seul son TEXTE est masqué — la saisie posée par-dessus
   le remplace, sans rien changer à la boîte. */
/* SPÉCIFICITÉ VOULUE — la classe de l'enveloppe est répétée pour porter la
   règle à trois classes. La feuille repeint les champs par thème et par
   coquille (``:root[data-theme=dark] select``,
   ``body.user-shell :is(input,…,select)``, deux classes chacune, et déclarée
   plus bas) : à égalité, c'est la dernière qui gagne, et le texte du menu
   masqué réapparaissait SOUS la saisie, en double. */
.combo.combo-closed > select.combo-native {
  color: transparent; -webkit-text-fill-color: transparent;
  pointer-events: none; margin: 0;
}
/* Police et retraits sont recopiés du <select> par combo.js : ils ne peuvent
   pas être hérités (le champ d'origine est un frère, pas un parent) et
   varient d'un contexte à l'autre. */
.combo.combo-closed > input.combo-input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; border: 0; background: none; box-shadow: none; min-height: 0;
  color: var(--text);
}
.combo.combo-closed > input.combo-input:focus { outline: 0; box-shadow: none; }
/* L'anneau de focus est peint sur la boîte visible, donc sur le <select>. */
.combo.combo-closed:focus-within > select.combo-native {
  border-color: var(--c-brand-400);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-400) 18%, transparent);
}
.combo-open > .combo-input { width: 100%; padding-right: 34px; }
.combo-toggle {
  position: absolute; top: 0; right: 0; width: 34px; height: 100%;
  display: grid; place-items: center; padding: 0;
  border: 0; background: none; color: var(--text-faint); cursor: pointer;
}
/* En mode fermé, le chevron est déjà celui du <select> dessous : en dessiner
   un second en donnerait deux. Le bouton n'est plus qu'une zone de clic. */
.combo-open > .combo-toggle::before {
  content: ""; width: 12px; height: 8px; background-color: currentColor;
  -webkit-mask: var(--combo-chevron) center / 12px 8px no-repeat;
  mask: var(--combo-chevron) center / 12px 8px no-repeat;
}
:root { --combo-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.combo-open.is-open .combo-toggle { color: var(--c-brand-600); transform: rotate(180deg); }
.combo.combo-closed.is-invalid > select.combo-native { border-color: var(--c-danger); }
/* `fixed` : la liste est ancrée au viewport et placée par le script (voir
   combo.js/placer). Posée dans le flux, elle était rognée par tout ancêtre en
   overflow:hidden — une cellule de tableau, une barre de filtres, une carte
   à coins arrondis — et ne montrait alors que ses premières lignes. */
.combo-list {
  /* Au-dessus des panneaux d'aide (.help-panel, z-index 300) : un menu ouvert
     DANS une modale doit se voir. Ailleurs, la question ne se pose pas — une
     liste se ferme dès que le champ perd le focus, donc dès qu'une modale
     s'ouvre. */
  position: fixed; z-index: 400;
  max-height: 264px; overflow-y: auto; margin: 0; padding: 4px;
  list-style: none; border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--sh-md, 0 12px 28px rgba(10, 22, 34, .18));
}
.combo-item {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-size: var(--fs-xs); color: var(--text); line-height: 1.35;
}
/* La valeur ne se coupe JAMAIS en deux lignes : « FR-MF-000000001 » scindé
   après le tiret ne se lit plus comme un code. C'est le complément qui cède
   la place et s'abrège. */
.combo-item-label {
  flex: 0 1 auto; min-width: 0; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* Le complément (rôle d'un code acteur, désignation d'un site) reste lisible
   mais ne dispute pas la place au code lui-même, qui est ce qu'on choisit. */
.combo-item-detail {
  flex: 0 1 auto; margin-left: auto; max-width: 52%;
  color: var(--text-muted); font-size: var(--fs-2xs); text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.combo-item.is-active { background: var(--c-brand-50); color: var(--c-brand-700); }
.combo-item.is-current { font-weight: 700; }
.combo-none { padding: 10px; color: var(--text-muted); font-size: var(--fs-xs); text-align: center; }
:root[data-theme="dark"] .combo-item.is-active { background: color-mix(in srgb, var(--c-brand-600) 22%, transparent); color: var(--text); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .combo-item.is-active { background: color-mix(in srgb, var(--c-brand-600) 22%, transparent); color: var(--text); }
}

.generation-layout { display: grid; grid-template-columns: .92fr 1.08fr; gap: var(--sp-5); align-items: start; }
.generation-layout > .card { margin: 0; padding: var(--sp-7); }
.card-heading-icon,
.resource-card-icon {
  width: 46px; height: 46px; display: grid; place-items: center;
  margin-bottom: var(--sp-4); border-radius: var(--r-md);
  background: var(--c-brand-50); color: var(--c-brand-600);
}
.card-section-heading h2 { margin: 0; font-size: var(--fs-h3); color: var(--text); }
.instruction-list { display: grid; gap: var(--sp-3); margin-top: var(--sp-5); }
.instruction-list > div { display: grid; grid-template-columns: 32px 1fr; gap: var(--sp-3); align-items: start; }
.instruction-list b {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-full); background: var(--c-brand-50); color: var(--c-brand-600);
  font-size: var(--fs-2xs);
}
.instruction-list p { margin: 2px 0 0; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.55; }
.template-downloads { display: grid; gap: var(--sp-3); margin-top: var(--sp-6); }
.template-download-card {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-sunken); color: var(--text); text-decoration: none;
  transition: all .16s var(--ease);
}
.template-download-card:hover { border-color: var(--c-brand-300); background: var(--c-brand-50); }
.template-download-card > span { color: var(--c-brand-600); display: grid; }
.template-download-card strong,
.template-download-card small { display: block; }
.template-download-card strong { font-size: var(--fs-xs); }
.template-download-card small { margin-top: 2px; color: var(--text-muted); font-size: var(--fs-2xs); }
.upload-card .stack { margin-top: var(--sp-5); }
.upload-card .stack > div:not(.btn-row) > label:first-child { margin-bottom: var(--sp-2); font-size: var(--fs-xs); }
.preparation-card { border-top: 3px solid var(--c-brand-500); }

/* Écran de résultat du module 6 : la suite du parcours. Bordure haute
   comme .preparation-card — c'est une étape à faire, pas un résumé. */
.result-next-step-card { border-top: 3px solid var(--c-brand-500); }
.result-next-step-card .instruction-list { margin-top: var(--sp-4); }

/* ---- Saisie d'une requête de téléchargement (module 6) -------------------
   Le module 6 formule une RECHERCHE : quelques champs, tous facultatifs pris
   un par un. Les groupes séparent ce qui restreint la recherche de ce qui
   découpe seulement la réponse — la confusion entre les deux est la première
   cause de requête qui revient vide. */
.pullform-group {
  margin: var(--sp-5) 0 0; padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-sunken);
}
.pullform-group > legend {
  padding: 0 var(--sp-2); font-size: var(--fs-xs); font-weight: 600; color: var(--text);
}
.pullform-field { margin-top: var(--sp-4); }
.pullform-field:first-of-type { margin-top: var(--sp-2); }
.pullform-field > label { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-xs); }
.pullform-field input, .pullform-field select { width: 100%; }

/* ------------------------------------------------------------------
   SAISIE D'UNE REQUÊTE — une page, un formulaire (voir pull_form.html)
   Les critères tiennent en grille plutôt qu'en colonne : une requête de
   sept critères occupait deux hauteurs d'écran, et il fallait faire
   défiler pour voir ce qu'il restait à remplir. La largeur mini de 240px
   est celle d'un champ encore lisible — en dessous, la grille retombe
   d'elle-même à moins de colonnes, jusqu'à une seule sur téléphone.
   ------------------------------------------------------------------ */
.pullform-page { display: block; }
.pullform-page > .card { margin: 0; padding: var(--sp-6) var(--sp-7); }
/* Titre de carte à gauche, « Comment ça marche » à droite : la même ligne
   sur tous les modules, pour que le mode d'emploi se cherche toujours au
   même endroit (voir _how_it_works.html). */
.card-head-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-2);
}
.card-head-row .card-section-heading { margin: 0; }
.card-head-row > h2 { margin: 0; }
.pullform-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp-3) var(--sp-4); align-items: start;
}
/* La marge verticale des champs est portée par la grille : conservée par
   champ, elle doublerait l'écart entre deux rangées. */
.pullform-grid > .pullform-field,
.pullform-grid > .pullform-field:first-of-type { margin-top: 0; }
/* Les blocs venus d'ailleurs (classeur à télécharger, zone de dépôt) portent
   leurs propres marges hautes : dans la grille, elles décalaient leur cellule
   par rapport à sa voisine. L'espacement des rangées appartient à la grille. */
.pullform-grid :is(.template-downloads, .file-upload-field) { margin: 0; }
.pullform-grid > div { min-width: 0; }
.pullform-grid .field-label-row { margin-bottom: 4px; }
/* UNE SEULE ÉTIQUETTE POUR TOUT LE FORMULAIRE. Les champs viennent de trois
   sources — la grille elle-même, le partiel de code acteur, le partiel de
   version XSD — et chacune avait sa typographie : 0,87 rem/600 ici, 12 px/800
   là. Sur une même rangée, un libellé sortait plus gras et plus petit que son
   voisin sans qu'aucune information ne le justifie. */
.pullform-grid :is(label, .form-label) { font-size: var(--fs-xs); font-weight: 600; margin: 0; }
.pullform-grid > * > .field-label-row { margin-bottom: 4px; }
/* Le choix de version XSD est dessiné en panneau teinté sur la page d'import,
   où il est seul et doit se remarquer. Rangé dans une grille de champs, ce
   cadre en faisait le seul champ encadré de l'écran — un accent là où il n'y
   a plus rien à distinguer. Il reprend ici le gabarit de ses voisins. */
.pullform-grid .xsd-profile-field {
  margin: 0; padding: 0; border: 0; background: none; gap: 0;
}
/* Aussi précis que la règle du partiel (`.xsd-profile-field > label`,
   12 px/800), déclarée plus bas et qui gagnerait sinon à égalité. */
.pullform-grid .xsd-profile-field .field-label-row label { font-size: var(--fs-xs); font-weight: 600; }
/* Un champ dont le contenu est un message (« aucun code acteur configuré »)
   ne doit pas étirer la rangée : le message se lit dans la hauteur d'une
   saisie, et les champs voisins restent alignés sur leur première ligne. */
.pullform-grid .warn-box { margin: 0; font-size: var(--fs-2xs); line-height: 1.45; }
.pullform-actions {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5);
}
.pullform-actions .btn { display: inline-flex; align-items: center; gap: 8px; }
.help-steps { margin: var(--sp-3) 0 0; padding-left: var(--sp-5); }
.help-steps li { margin-bottom: var(--sp-3); }
.help-steps strong { display: block; color: var(--text); }
.help-steps span { display: block; margin-top: 2px; }
@media (max-width: 720px) {
  .pullform-actions { flex-direction: column-reverse; align-items: stretch; }
  .pullform-actions .btn { justify-content: center; }
}
.pullform-optional {
  margin-left: var(--sp-2); color: var(--text-muted);
  font-size: var(--fs-2xs); font-weight: 400;
}
.pullform-required {
  margin-left: var(--sp-2); color: var(--c-brand-600);
  font-size: var(--fs-2xs); font-weight: 600;
}
/* « Au moins un critère » : l'indice passe au vert dès qu'un critère est
   renseigné, et au rouge si l'envoi est tenté sans aucun. Purement visuel —
   sans JavaScript, l'indice reste neutre et c'est le serveur qui tranche. */
#pullform-criteria-hint.is-satisfied { color: var(--c-success-700, #1a7f37); }
#pullform-criteria-hint.is-missing { color: var(--c-danger-700, #b42318); font-weight: 600; }
.file-upload-field { margin-top: var(--sp-1); }
.file-upload-field > label:first-child { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-xs); }
.file-drop-zone {
  position: relative; min-height: 170px;
  display: flex !important; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); padding: var(--sp-6);
  border: 1.5px dashed var(--c-brand-300); border-radius: var(--r-lg);
  background: var(--c-brand-50); color: var(--c-brand-600); text-align: center;
  cursor: pointer; transition: all .16s var(--ease);
}
.file-drop-zone:hover { border-color: var(--c-brand-500); background: var(--c-brand-100); }
.file-drop-zone strong { color: var(--text); font-size: var(--fs-sm); }
.file-drop-zone small { color: var(--text-muted); font-size: var(--fs-xs); }
.file-drop-zone input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.generation-submit .btn { width: 100%; min-height: 50px; justify-content: center; }
.generation-submit .btn span { margin-left: var(--sp-2); }

/* Validation, split and result */
.validation-page-hero.has-errors { border-color: var(--c-danger-line); }
.validation-page-hero.is-ready { border-color: var(--c-ok-line); }
.validation-score-card {
  position: relative; z-index: 1; width: 210px; flex: none;
  padding: var(--sp-5); border-radius: var(--r-lg);
  background: var(--c-brand-800); color: #fff;
  box-shadow: var(--sh-md);
}
.validation-score-card > span { display: block; color: color-mix(in srgb, #fff 72%, transparent); font-size: var(--fs-xs); }
.validation-score-card strong { display: block; margin-top: var(--sp-1); font-size: 2.45rem; line-height: 1; }
.validation-score-card small { font-size: var(--fs-sm); }
.score-progress { height: 6px; margin-top: var(--sp-4); overflow: hidden; border-radius: var(--r-full); background: color-mix(in srgb, #fff 20%, transparent); }
.score-progress i { display: block; height: 100%; border-radius: inherit; background: var(--c-teal-300); }
.validation-summary {
  margin: 0 0 var(--sp-5); gap: var(--sp-3); border: 0; overflow: visible; background: transparent;
}
.validation-summary > div {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card-raised); box-shadow: var(--sh-xs);
}
.validation-summary-error > div,
.validation-summary-success > div { background: var(--card-raised); }
.result-alert {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  margin: var(--sp-5) 0; padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border); border-radius: var(--r-md);
}
.result-alert > span {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center;
  border-radius: var(--r-full); font-weight: 900;
}
.result-alert strong { display: block; color: var(--text); font-size: var(--fs-sm); }
.result-alert p { margin: var(--sp-1) 0 0; color: var(--text-muted); font-size: var(--fs-xs); }
.result-alert-danger { border-color: var(--c-danger-line); background: var(--c-danger-bg); }
.result-alert-danger > span { background: color-mix(in srgb, var(--c-danger) 14%, transparent); color: var(--c-danger); }
.result-alert-success { border-color: var(--c-ok-line); background: var(--c-ok-bg); }
.result-alert-success > span { background: color-mix(in srgb, var(--c-ok) 14%, transparent); color: var(--c-ok); }
.result-alert-warning { border-color: var(--c-warn-line); background: var(--c-warn-bg); }
.result-alert-warning > span { background: color-mix(in srgb, var(--c-warn) 14%, transparent); color: var(--c-warn); }
.validation-issues-card { margin-top: var(--sp-5); padding: var(--sp-5); }
.validation-issues-card .polished-heading { margin: 0 0 var(--sp-4); }
.validation-issues-card .btn { display: inline-flex; align-items: center; gap: var(--sp-2); }
.validation-table th, .validation-table td { padding: var(--sp-3); font-size: var(--fs-xs); }
.page-action-bar {
  margin-top: var(--sp-5); padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card-raised);
}
.result-download-card { padding: var(--sp-5); }
.result-download-card .polished-heading { margin: 0 0 var(--sp-4); }
.result-list { margin: 0; }
.result-list li { padding: var(--sp-4); border-radius: var(--r-md); }
.result-list li > span { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.result-list li > span strong { overflow-wrap: anywhere; font-size: var(--fs-xs); }
.result-list li i {
  padding: 5px var(--sp-2); border-radius: var(--r-sm);
  background: var(--c-brand-600); color: #fff; font-size: var(--fs-2xs); font-style: normal; font-weight: 800;
}
.result-list a { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); }
.split-visual {
  position: relative; z-index: 1; flex: none; display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card);
}
.split-visual span, .split-visual strong { padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); font-size: var(--fs-sm); }
.split-visual span { background: var(--c-warn-bg); color: var(--c-warn); }
.split-visual strong { background: var(--c-brand-50); color: var(--c-brand-600); }
.split-visual i { color: var(--text-faint); font-style: normal; }

/* Documentation */
.resource-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); margin-bottom: var(--sp-7); }
.resource-card {
  min-height: 260px; display: flex; flex-direction: column; padding: var(--sp-6);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card-raised); box-shadow: var(--sh-sm);
}
.resource-card h2 { margin: 0; font-size: var(--fs-h3); line-height: 1.35; }
.resource-card p:not(.section-kicker) { margin: var(--sp-3) 0 var(--sp-5); color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.6; }
.resource-card a {
  display: inline-flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  margin-top: auto; padding-top: var(--sp-4); border-top: 1px solid var(--border);
  color: var(--c-brand-600); font-size: var(--fs-xs); font-weight: 750; text-decoration: none;
}
.resource-card a:hover { color: var(--c-brand-700); }
.featured-doc-panel { border-left: 4px solid var(--c-brand-500); }
.documentation-table-panel .doc-body { padding-top: var(--sp-2); }
.documentation-modules-heading { margin-top: var(--sp-9); }
.doc-panel { margin-bottom: var(--sp-4); overflow: hidden; }
.doc-panel-title { padding: var(--sp-4) var(--sp-5); font-size: var(--fs-sm); }
/* Chaque carte de module EST une entrée du sommaire de gauche : son titre
   doit se lire comme un titre, pas comme l'étiquette d'un encadré. Il reste
   sous le titre de section (« Modules »), qui les contient tous. */
.doc-module-card > .doc-panel-title {
  font-size: var(--fs-md); font-weight: 750; color: var(--text);
  border-bottom: 1px solid var(--border);
}
.doc-body { padding: var(--sp-5); font-size: var(--fs-sm); }

/* Documentation — nouvelle mise en page : navigation latérale sticky +
   recherche + contenu en sections ancrées, reconstruite à partir des
   fonctionnalités réellement disponibles (voir <system-reminder> / rapport). */
.doc-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: var(--sp-7); align-items: start; }
.doc-sidebar {
  position: sticky; top: 72px; max-height: calc(100vh - 90px); overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--sp-1); padding-right: var(--sp-2);
}
.doc-sidebar-group { margin-bottom: var(--sp-3); }
.doc-sidebar-group-title { font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); padding: 4px var(--sp-3); }
.doc-sidebar-link {
  display: block; padding: 6px var(--sp-3); border-radius: var(--r-sm);
  color: var(--text-muted); text-decoration: none; font-size: var(--fs-xs); font-weight: 600;
  border-left: 2px solid transparent;
}
.doc-sidebar-link:hover, .doc-sidebar-link:focus-visible { color: var(--text); background: var(--bg-sunken); }
.doc-sidebar-link.is-active { color: var(--c-brand-600); border-left-color: var(--c-brand-500); background: var(--c-brand-50); }
:root[data-theme="dark"] .doc-sidebar-link.is-active { background: color-mix(in srgb, var(--c-brand-600) 16%, transparent); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .doc-sidebar-link.is-active { background: color-mix(in srgb, var(--c-brand-600) 16%, transparent); } }
.doc-sidebar-sub { padding-left: var(--sp-5); }
.doc-search-box { position: relative; margin-bottom: var(--sp-5); }
.doc-search-box span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-faint); pointer-events: none; }
.doc-search-box input {
  width: 100%; box-sizing: border-box; padding: 10px 14px 10px 38px; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--card); color: var(--text); font-size: var(--fs-sm);
}
.doc-content { min-width: 0; }
.doc-section { scroll-margin-top: 76px; margin-bottom: var(--sp-9); }
/* HIÉRARCHIE DES TITRES DE LA DOCUMENTATION.
   Chaque entrée du sommaire de gauche mène à un titre de section. Ces titres
   étaient rendus à var(--fs-h3), soit EXACTEMENT la taille et la graisse des
   sous-titres (.doc-subsection h3, var(--fs-md) : les deux valent 1.1rem).
   Résultat : passé les deux premières sections, plus rien ne distinguait
   « Compte et sécurité » — une entrée du menu — de « Mot de passe », un
   paragraphe à l'intérieur. On arrivait sur l'ancre sans voir où la section
   commençait. Le titre de section reprend donc la même échelle que celui du
   parcours rapide en haut de page (.doc-command-heading h2), et le
   sous-titre redescend d'un cran. Le filet supérieur marque la coupure :
   c'est lui qui répond au clic dans le menu. */
.doc-section-heading { margin: 0 0 var(--sp-4); }
.doc-section-heading h2 {
  margin: 0; font: 750 clamp(1.2rem, 1.9vw, 1.55rem)/1.15 var(--font-display);
  letter-spacing: -.025em; color: var(--text);
}
.doc-section-heading p { margin: 4px 0 0; color: var(--text-muted); font-size: var(--fs-xs); }
.doc-content > .doc-section + .doc-section > .doc-section-heading,
.doc-module-workbooks > .doc-section-heading {
  padding-top: var(--sp-6); border-top: 1px solid var(--border);
}
.doc-subsection { margin: var(--sp-6) 0; }
.doc-subsection h3 { font-size: var(--fs-sm); font-weight: 700; margin: 0 0 var(--sp-2); color: var(--text); }
.doc-callout {
  display: flex; gap: var(--sp-3); padding: var(--sp-4); border-radius: var(--r-md);
  border: 1px solid var(--border); border-left-width: 3px; margin: var(--sp-4) 0; background: var(--card);
}
.doc-callout-icon { flex: none; color: var(--text-faint); }
.doc-callout p, .doc-callout ul { margin: 0; font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.6; }
.doc-callout strong { color: var(--text); }
.doc-callout.is-info { border-left-color: var(--c-brand-500); }
.doc-callout.is-info .doc-callout-icon { color: var(--c-brand-500); }
.doc-callout.is-warning { border-left-color: var(--c-warn); }
.doc-callout.is-warning .doc-callout-icon { color: var(--c-warn); }
.doc-callout.is-tip { border-left-color: var(--c-ok); }
.doc-callout.is-tip .doc-callout-icon { color: var(--c-ok); }
.doc-module-card { margin-bottom: var(--sp-5); }
.doc-module-meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--sp-4); margin-top: var(--sp-3); }
.doc-module-meta-grid dt { font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); margin: 0; }
.doc-module-meta-grid dd { margin: 3px 0 0; font-size: var(--fs-xs); color: var(--text); }
.doc-op-list { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.doc-op-list li { padding: var(--sp-3); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg-sunken); }
.doc-op-list strong { display: block; font-size: var(--fs-xs); color: var(--text); }
.doc-op-list span { display: block; margin-top: 2px; font-size: var(--fs-2xs); color: var(--text-muted); line-height: 1.5; }
.doc-op-list li > div { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.doc-op-list li > div a { flex:0 0 auto; color:var(--accent); font-size:var(--fs-2xs); font-weight:700; text-decoration:none; }
.doc-op-list li > div a:hover,.doc-structure-link:hover { text-decoration:underline; }
.doc-template-versions { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:12px; margin-top:14px; }
.doc-template-version { overflow:hidden; border:1px solid var(--border); border-radius:10px; background:var(--surface); }
.doc-template-version-head { display:flex; align-items:center; gap:9px; min-height:42px; padding:9px 11px; border-bottom:1px solid var(--border); background:var(--surface-alt); }
.doc-template-version-head b { font-size:13px; color:var(--text); }
.doc-template-version-head small { margin:0; color:var(--text-muted); text-transform:capitalize; }
.doc-template-version-head > span { display:inline-flex; margin:0 0 0 auto; padding:3px 7px; border-radius:999px; color:#52d7ac; background:rgba(34,197,139,.12); font-size:10px; font-weight:700; }
.doc-template-version-links { display:grid; grid-template-columns:1fr 1fr; gap:8px; padding:9px; }
.doc-template-download { display:flex; align-items:flex-start; gap:7px; min-width:0; padding:9px; border:1px solid var(--border); border-radius:8px; color:var(--accent); text-decoration:none; background:var(--bg-sunken); }
.doc-template-download:hover { border-color:var(--accent); background:var(--surface-alt); }
.doc-template-download > span { min-width:0; margin:0; }
.doc-template-download b { display:block; color:var(--text); font-size:12px; }
.doc-template-download small { display:block; margin-top:2px; color:var(--text-muted); font-size:10px; line-height:1.35; }
@media(max-width:640px){.doc-template-version-links{grid-template-columns:1fr}}
.doc-catalog-jump { display:inline-flex; align-items:center; gap:6px; width:max-content; margin-top:10px; }
.doc-model-catalog { scroll-margin-top:90px; }
.doc-catalog-legend { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 14px; }
.doc-catalog-legend span { padding:7px 10px; border:1px solid var(--border); border-radius:999px; color:var(--text-muted); background:var(--surface-alt); font-size:11px; }
.doc-catalog-scroll { overflow-x:auto; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
.doc-catalog-table { width:100%; min-width:0; table-layout:fixed; border-collapse:separate; border-spacing:0; }
.doc-catalog-table th,.doc-catalog-table td { padding:12px; border-right:1px solid var(--border); border-bottom:1px solid var(--border); text-align:left; vertical-align:top; }
.doc-catalog-table th:last-child,.doc-catalog-table td:last-child { border-right:0; }
.doc-catalog-table tbody tr:last-child td { border-bottom:0; }
.doc-catalog-table thead th { color:var(--text); background:var(--bg-sunken); font-size:11px; text-transform:uppercase; letter-spacing:.03em; }
.doc-catalog-table thead th:first-child { width:22%; }
.doc-catalog-table thead th:nth-child(2) { width:26%; }
.doc-catalog-table thead tr:first-child th { position:sticky; top:0; z-index:2; }
.doc-catalog-table th small { display:block; margin-top:3px; color:var(--text-muted); font-size:9px; text-transform:none; }
.doc-catalog-table td:first-child { width:22%; }
.doc-catalog-table td:nth-child(2) { width:26%; }
.doc-catalog-module-cell { vertical-align:middle !important; background:var(--surface-alt); }
.doc-catalog-module-cell .tile-index { display:inline-flex; margin-bottom:8px; }
.doc-catalog-table td b { display:block; color:var(--text); font-size:12px; }
.doc-catalog-table td small { display:block; margin-top:4px; color:var(--text-muted); font-size:10px; line-height:1.45; }
.doc-catalog-table tr[id] { scroll-margin-top:100px; }
.doc-catalog-table tr:target td { background:rgba(51,145,225,.12); animation:doc-target-fade 2.4s ease-out forwards; }
.doc-catalog-download { display:inline-flex; align-items:center; gap:5px; max-width:100%; white-space:nowrap; color:var(--accent); font-size:10px; font-weight:700; text-decoration:none; }
.doc-catalog-download:hover { text-decoration:underline; }
.doc-structure-link { color:var(--accent); font-weight:700; text-decoration:none; }
.doc-module-workbooks-table td:first-child { width:26%; }
.doc-module-workbooks-table td:nth-child(2) { width:30%; }
.doc-module-workbooks-full { margin-top:var(--sp-7); scroll-margin-top:90px; }
.doc-module-workbooks-full .doc-module-workbooks-table { min-width:900px; }

.template-download-heading { display:flex; align-items:center; flex-wrap:wrap; gap:8px; }
.template-xsd-badge {
  display:inline-flex; align-items:center; width:max-content; padding:3px 8px;
  border:1px solid color-mix(in srgb, var(--accent) 55%, var(--border)); border-radius:999px;
  color:var(--accent); background:color-mix(in srgb, var(--accent) 12%, transparent);
  font-size:10px; font-weight:800; line-height:1.3; white-space:nowrap;
}

.operation-structure-hero { margin-bottom:16px; }
.operation-structure-hero .section-kicker { margin:0 0 4px; color:var(--accent); font-size:10px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; }
.operation-structure-toolbar { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; margin-bottom:16px; padding:14px 16px; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
.operation-structure-version { display:grid; gap:6px; min-width:300px; }
.operation-structure-version label { color:var(--text-muted); font-size:11px; font-weight:700; }
/* `background-color` : la forme condensée emporterait le chevron dessiné par
   `select:not([multiple])` — même piège qu'à `body.user-shell`. */
.operation-structure-version select,.operation-structure-filters input { min-height:40px; padding:8px 11px; border:1px solid var(--border); border-radius:8px; color:var(--text); background-color:var(--surface-alt); }
.operation-structure-actions { display:flex; flex-wrap:wrap; justify-content:flex-end; gap:8px; }
.operation-structure-summary { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; margin-bottom:16px; }
.operation-structure-summary article { display:grid; gap:3px; padding:14px; border:1px solid var(--border); border-top:3px solid var(--accent); border-radius:11px; background:var(--surface); }
.operation-structure-summary article.is-required { border-top-color:#d9534f; }
.operation-structure-summary article.is-conditional { border-top-color:#d99a37; }
.operation-structure-summary article.is-optional { border-top-color:#4098df; }
.operation-structure-summary article.is-automatic { border-top-color:#778392; }
.operation-structure-summary span { color:var(--text-muted); font-size:10px; font-weight:800; letter-spacing:.04em; text-transform:uppercase; }
.operation-structure-summary strong { color:var(--text); font-size:25px; line-height:1; }
.operation-structure-summary small { color:var(--text-muted); font-size:10px; line-height:1.35; }
.operation-structure-panel { margin-bottom:24px; }
.operation-structure-note { margin-bottom:14px; }
.operation-structure-filters { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px; }
.operation-structure-filters input { flex:1 1 320px; }
.operation-structure-filters > div { display:flex; flex-wrap:wrap; justify-content:flex-end; gap:6px; }
.operation-structure-filters button { padding:7px 10px; border:1px solid var(--border); border-radius:999px; color:var(--text-muted); background:var(--surface-alt); font-size:10px; font-weight:700; cursor:pointer; }
/* Seul fond PLEIN de tout le fichier utilisant l'accent, et du blanc s'y pose :
   il lui faut --primary, qui reste foncé en thème sombre. L'alias --accent
   pointe sur --primary-ink, qui s'y éclaircit -- du blanc sur bleu clair ne
   passerait pas le contraste. */
.operation-structure-filters button.is-active { border-color:var(--primary); color:#fff; background:var(--primary); }
.operation-structure-table-wrap { max-height:68vh; overflow:auto; border:1px solid var(--border); border-radius:10px; }
.operation-structure-table { width:100%; min-width:1080px; border-collapse:separate; border-spacing:0; font-size:11px; }
.operation-structure-table th,.operation-structure-table td { padding:10px; border-right:1px solid var(--border); border-bottom:1px solid var(--border); text-align:left; vertical-align:top; }
.operation-structure-table th { position:sticky; top:0; z-index:2; color:var(--text); background:var(--bg-sunken); font-size:10px; letter-spacing:.03em; text-transform:uppercase; }
.operation-structure-table th:last-child,.operation-structure-table td:last-child { border-right:0; }
.operation-structure-table tbody tr:last-child td { border-bottom:0; }
.operation-structure-table tbody tr:hover td { background:rgba(51,145,225,.05); }
.operation-structure-table td:nth-child(1) { width:54px; text-align:center; }
.operation-structure-table td:nth-child(2) { width:240px; }
.operation-structure-table td:nth-child(3) { width:150px; }
.operation-structure-table td:nth-child(4) { width:180px; }
.operation-structure-table td:nth-child(6) { width:210px; }
.operation-structure-table code { overflow-wrap:anywhere; color:var(--text); font-size:10px; }
.operation-structure-table td small { display:block; margin-top:5px; color:var(--text-muted); font-size:9px; }
.structure-status { display:inline-flex; padding:4px 7px; border-radius:999px; font-size:9px; font-weight:800; white-space:nowrap; }
.structure-status.is-required { color:#9c0006; background:#fde9e7; }
.structure-status.is-conditional { color:#7f6000; background:#fff2cc; }
.structure-status.is-optional { color:#1f4e78; background:#eaf4fb; }
.structure-status.is-automatic { color:#475569; background:#e7eef5; }
.operation-structure-empty { display:none; margin-top:12px; }

@media(max-width:960px){
  .operation-structure-summary{grid-template-columns:repeat(2,minmax(0,1fr))}
  .operation-structure-toolbar,.operation-structure-filters{align-items:stretch;flex-direction:column}
  .operation-structure-version{min-width:0}
  .operation-structure-actions,.operation-structure-filters>div{justify-content:flex-start}
}
@media(max-width:640px){
  .doc-op-list li>div{display:grid}
  .operation-structure-summary{grid-template-columns:1fr}
}
@keyframes doc-target-fade { from{background:rgba(51,145,225,.25)} to{background:transparent} }
.doc-no-results { display: none; }
@media (max-width: 900px) {
  .doc-layout { grid-template-columns: 1fr; }
  .doc-sidebar { position: static; max-height: none; flex-direction: row; flex-wrap: wrap; overflow-x: auto; padding-bottom: var(--sp-2); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-5); }
  .doc-sidebar-group { margin-bottom: 0; display: flex; align-items: center; gap: 2px; }
  .doc-sidebar-group-title { display: none; }
  .doc-sidebar-sub { padding-left: var(--sp-2); }
}

/* Account */
.account-header { margin: var(--sp-2) 0 var(--sp-7); }
.account-access-summary {
  position: relative; z-index: 1; min-width: 220px; padding: var(--sp-5);
  border-radius: var(--r-lg); background: var(--card-raised); box-shadow: var(--sh-sm);
}
.account-settings-grid { max-width: none; gap: var(--sp-5); }
.account-panel { padding: var(--sp-6); border-radius: var(--r-lg); box-shadow: var(--sh-sm); }

/* Administration */
.metric-pill {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 40px; padding: 0 var(--sp-4); border: 1px solid var(--border);
  border-radius: var(--r-full); background: var(--card-raised);
  color: var(--text-muted); font-size: var(--fs-xs); white-space: nowrap; box-shadow: var(--sh-xs);
}
.metric-pill strong { color: var(--c-brand-600); font-size: var(--fs-sm); }
.admin-filter-bar {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: var(--sp-5); padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card-raised);
}
.admin-filter-bar > span { display: inline-flex; align-items: center; gap: var(--sp-2); margin-right: auto; color: var(--text); font-size: var(--fs-xs); font-weight: 750; }
.admin-filter-bar select { min-width: 220px; }
.admin-activity-card { padding: 0; overflow: hidden; }
.database-context {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: var(--sp-4); padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card);
  color: var(--text-muted); font-size: var(--fs-xs);
}
.database-context code { color: var(--c-brand-600); overflow-wrap: anywhere; }
.database-warning { margin-bottom: var(--sp-6); }
.access-request-card { border-radius: var(--r-lg); box-shadow: var(--sh-sm); }
.request-summary-grid { border-radius: var(--r-md); background: var(--bg-sunken); }
.grant-access-form { border-radius: var(--r-md); }
.history-table-wrap { border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; background: var(--card); }

/* Public audience section and stronger photography */
.market-audience {
  display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
  align-items: stretch; min-height: 650px;
  background: var(--bg-sunken);
}
.market-audience-visual {
  position: relative; min-height: 560px;
  background:
    linear-gradient(0deg, color-mix(in srgb, var(--c-brand-900) 55%, transparent), transparent 40%),
    url("images/regulatory-workflow-team-v2.jpg") center / cover no-repeat;
}
.audience-visual-caption {
  position: absolute; left: clamp(24px, 5vw, 72px); right: clamp(24px, 5vw, 72px); bottom: var(--sp-8);
  padding: var(--sp-5); border: 1px solid var(--c-brand-700);
  border-radius: var(--r-md); background: var(--c-brand-900);
  color: #fff;
}
.audience-visual-caption span { display: block; margin-bottom: var(--sp-2); color: var(--c-brand-200); font-size: var(--fs-2xs); font-weight: 800; letter-spacing: var(--tracking-wide); text-transform: uppercase; }
.audience-visual-caption strong { display: block; max-width: 25ch; font-size: var(--fs-h3); line-height: 1.4; }
.market-audience-copy { display: flex; flex-direction: column; justify-content: center; padding: var(--sp-12) max(var(--gutter), 5vw); }
.market-audience-copy h2 { max-width: 17ch; margin: 0; font-size: var(--fs-h2); line-height: 1.14; letter-spacing: var(--tracking-display); }
.market-audience-copy > p:not(.market-kicker) { max-width: 58ch; margin: var(--sp-4) 0 0; color: var(--text-muted); line-height: 1.68; }
.audience-cards { display: grid; gap: var(--sp-3); margin-top: var(--sp-7); }
.audience-cards article {
  display: grid; grid-template-columns: 104px 1fr; gap: var(--sp-1) var(--sp-4);
  padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card);
}
.audience-cards b { grid-row: 1 / 3; align-self: center; color: var(--c-brand-600); font-size: var(--fs-xs); }
.audience-cards h3 { margin: 0; font-size: var(--fs-sm); }
.audience-cards p { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.5; }
.approach-disclaimer {
  margin-top: var(--sp-6) !important; padding-top: var(--sp-5);
  border-top: 1px solid color-mix(in srgb, #fff 14%, transparent);
  color: color-mix(in srgb, #fff 56%, transparent) !important; font-size: var(--fs-xs) !important;
}

/* Responsive product layer */
@media (max-width: 1020px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .generation-layout { grid-template-columns: 1fr; }
  .process-mini-flow { flex-wrap: wrap; }
  .resource-grid { grid-template-columns: 1fr 1fr; }
  .resource-card:last-child { grid-column: 1 / -1; min-height: 220px; }
  .market-audience { grid-template-columns: 1fr; }
  .market-audience-visual { min-height: 520px; }
}
@media (max-width: 760px) {
  :root { --fs-body: .96rem; --fs-sm: .89rem; --fs-xs: .82rem; }
  .app-page-hero { flex-direction: column; align-items: stretch; padding: var(--sp-6); }
  .app-page-hero-aside,
  .validation-score-card,
  .account-access-summary { width: 100%; min-width: 0; }
  .operation-grid,
  .resource-grid { grid-template-columns: 1fr; }
  .resource-card:last-child { grid-column: auto; }
  .validation-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .admin-filter-bar { align-items: stretch; }
  .admin-filter-bar > span { width: 100%; }
  .admin-filter-bar select { width: 100%; min-width: 0; }
  .audience-cards article { grid-template-columns: 1fr; }
  .audience-cards b { grid-row: auto; }
}
@media (max-width: 520px) {
  .grid { grid-template-columns: 1fr; }
  .validation-summary { grid-template-columns: 1fr; }
  .app-page-hero,
  .generation-layout > .card { padding: var(--sp-5); }
  .app-page-meta { flex-direction: column; gap: var(--sp-2); }
  .process-mini-flow span { width: 100%; }
  .operation-card { grid-template-columns: auto 1fr auto; }
  .operation-card-arrow { display: none; }
  .market-audience-visual { min-height: 440px; }
  .page-action-bar { align-items: stretch; }
  .page-action-bar .btn,
  .page-action-bar form,
  .page-action-bar form .btn { width: 100%; }
}

/* ============================================================
   PHOTO-LED MARKETING — restores the immersive visual direction
   ============================================================ */
.market-hero {
  background:
    radial-gradient(900px 460px at 12% 8%, var(--card) 0%, transparent 68%),
    linear-gradient(150deg, var(--c-brand-50), var(--bg));
  border-bottom: 0;
}
/* Le panneau de démonstration recevait PLUS de place que le titre
   (.94fr contre 1.06fr, avec un plancher de 500px). Conséquence mesurée à
   1440px : la colonne de texte tombait à 498px, le titre cassait en quatre
   lignes, et le bas du héros restait vide. C'est l'inverse de la hiérarchie
   voulue — la promesse se lit avant la capture d'écran qui l'illustre.
   La colonne de texte passe devant, le panneau garde un plancher suffisant
   pour rester lisible, et la mesure totale s'élargit à 1320px pour rendre
   au titre les lignes qui lui manquaient. */
.market-hero-inner {
  max-width: 1320px;
  grid-template-columns: minmax(0, 1.12fr) minmax(420px, .88fr);
  gap: clamp(32px, 4.5vw, 64px);
  padding-top: var(--sp-9);
  padding-bottom: var(--sp-10);
}
.market-hero-media {
  /* Panneau institutionnel : bleu pâle uni, bordure fine, contenu empilé. */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  min-height: 0;
  padding: clamp(22px, 2.6vw, 36px);
  border: 1px solid var(--c-brand-200);
  border-radius: var(--r-lg);
  background: var(--c-brand-50);
  box-shadow: none;
}
.market-hero-media::after { display: none; }
.hero-photo-label {
  position: static;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--c-brand-200);
  border-radius: var(--r-sm);
  background: var(--card);
  color: var(--c-brand-700);
  font-size: var(--fs-2xs);
  font-weight: 800;
}
.hero-photo-label i { color: var(--c-brand-500); font-style: normal; }
.market-hero-media .market-analysis-card {
  position: static;
  width: 100%;
  background: var(--card);
}
.market-hero-media .analysis-findings {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.market-hero-media .analysis-findings > div {
  align-items: flex-start;
  min-height: 104px;
  padding: var(--sp-3);
}
.market-hero-media .analysis-findings > div > span { min-width: 0; }
.market-hero-media .analysis-findings strong {
  font-size: var(--fs-2xs);
  line-height: 1.35;
}
.market-hero-media .analysis-findings small {
  margin-top: 3px;
  font-size: .68rem;
  line-height: 1.35;
}
.market-audience-visual {
  background:
    linear-gradient(0deg, color-mix(in srgb, var(--c-brand-900) 55%, transparent), transparent 40%),
    url("images/regulatory-workflow-team-v2.jpg") 34% center / cover no-repeat;
}
.approach-image {
  position: relative;
  overflow: hidden;
  z-index: 1;
  margin-right: -2px;
  min-height: 640px;
}
.approach-image::after { display: none; }
.approach-tags {
  display: flex; flex-wrap: wrap; justify-content: center;
  align-items: center; gap: 14px;
  max-width: 34rem;
}
.approach-tag {
  display: inline-flex; align-items: center;
  padding: 7px 14px;
  border: 1px solid color-mix(in srgb, #fff 20%, transparent);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--c-brand-800) 60%, transparent);
  color: color-mix(in srgb, #fff 88%, transparent);
  font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: 500;
  white-space: nowrap;
  box-shadow: 0 10px 24px -8px rgba(0, 0, 0, .35);
  transform: rotate(var(--r, 0deg));
}
.approach-copy {
  position: relative;
  z-index: 2;
  margin-left: -2px;
  border-left: 0;
}
.approach-image-caption {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 34rem;
  padding: var(--sp-5);
  border: 1px solid var(--c-brand-700);
  border-radius: var(--r-md);
  background: var(--c-brand-900);
  color: #fff;
}
.approach-image-caption span {
  display: block;
  margin-bottom: var(--sp-2);
  color: var(--c-brand-200);
  font-size: var(--fs-2xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.approach-image-caption strong {
  display: block;
  max-width: 28ch;
  font-size: var(--fs-h3);
  line-height: 1.42;
}
.auth-market-context { background: var(--c-brand-800); }
:root[data-theme="dark"] .auth-market-context,
:root:not([data-theme="light"]) .auth-market-context { background: var(--c-brand-900); }
@media (max-width: 1080px) {
  .market-hero-inner { grid-template-columns: minmax(0, 1fr) minmax(420px, .88fr); }
  .approach-image { margin-right: 0; }
  .approach-copy { margin-left: 0; }
}
@media (max-width: 820px) {
  .market-hero-inner { grid-template-columns: 1fr; }
  .market-hero-media .market-analysis-card { max-width: none; }
  .approach-image { min-height: 500px; }
}
@media (max-width: 540px) {
  .market-hero-media { border-radius: var(--r-md); }
  .market-hero-media .analysis-findings { grid-template-columns: 1fr; }
  .market-hero-media .analysis-findings > div { min-height: 0; }
  .approach-image { min-height: 460px; }
  .approach-image-caption { padding: var(--sp-4); }
}

/* Auth fields: one clean control, never a nested double border. */
.auth-form .auth-input-wrap,
.auth-form-left > label.auth-input-wrap {
  position: relative;
  display: block;
  height: 52px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.auth-form .auth-input-wrap:focus-within {
  border: 0;
  box-shadow: none;
}
.auth-form .auth-input-wrap input,
.auth-form-left .auth-input-wrap input {
  width: 100%;
  height: 52px;
  min-height: 52px;
  margin: 0;
  padding: 0 var(--sp-4) 0 48px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  outline: 0;
  background: var(--card);
  color: var(--text);
  font-size: var(--fs-sm);
}
.auth-form .auth-input-wrap input:focus {
  border-color: var(--c-brand-500);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-500) 24%, transparent);
}
.auth-form .auth-input-icon {
  position: absolute;
  z-index: 1;
  left: var(--sp-4);
  top: 50%;
  width: 18px;
  height: 18px;
  transform: translateY(-50%);
  color: var(--c-brand-600);
  pointer-events: none;
}

/* Traitement institutionnel : bandes plates, cartes opaques, pas de photo
   derrière le texte. */
.market-intro {
  position: relative;
  isolation: isolate;
  max-width: none;
  overflow: hidden;
  padding-left: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter)));
  padding-right: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter)));
  background: var(--c-brand-50);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.market-intro::before { display: none; }
.market-intro > * {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: calc(var(--page-max) - (2 * var(--gutter)));
  margin-left: auto;
  margin-right: auto;
}
.market-intro .market-section-heading {
  margin-left: auto;
  margin-right: auto;
}
.market-intro .workflow-track article {
  background: var(--card);
  border-color: var(--border);
  box-shadow: var(--sh-xs);
}

.market-approach {
  background: var(--c-brand-900);
}

@media (max-width: 820px) {
  .market-intro {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }
}

/* Le logo. C'est un SVG en ligne (webapp/templates/_logo.html, engendré par
   outils/generer_logo.py) et non une image : ses trois couleurs sont des
   variables, redéfinies par thème et par surface. Voir « Couleurs du logo »
   en fin de fichier. */
.brand-logo {
  display: block;
  width: auto;
  max-width: none;
  position: relative; z-index: 1;
}
.brand .brand-logo {
  width: auto;
  height: 43px;
}
.brand-tagline {
  display: block;
  max-width: 140px;
  color: var(--text-muted);
  font-size: var(--fs-2xs);
  font-weight: 600;
  line-height: 1.35;
  white-space: normal;
}
.marketing-brand {
  gap: var(--sp-3);
}
/* Ici on fixe la LARGEUR, pas la hauteur : c'est l'encombrement horizontal qui
   commande. La barre publique est une grille à trois colonnes dont celle du
   milieu, la navigation, réclame 907 px à partir de 1600 px de large (sept
   liens à 16 px, séparés de --sp-5). Le reste de la ligne se partage entre la
   marque et les actions ; au-delà de ~140 px, la marque prend sur la nav, qui
   déborde alors sur ses deux voisins — mesuré le 2026-08-15 à -11 px de
   chaque côté quand le logo passait à 171 px.
   Les paliers plus étroits expriment eux aussi une largeur (128, 118, 102) :
   même raison, même unité. */
.marketing-brand .brand-logo {
  width: 134px;
  height: auto;
}
.marketing-brand > .brand-tagline {
  max-width: 112px;
  margin: 0;
  color: var(--text-muted);
  font-size: .68rem;
  line-height: 1.3;
}
/* Plus de cartouche blanc : le logo a désormais une variante claire, servie
   ici en permanence (voir « Variante sombre du logo » en fin de fichier). Le
   cartouche existait parce que l'actif PNG était bleu foncé et que ce pied de
   page est sombre — il ne compensait plus rien, il restait juste visible. */
.marketing-footer-logo {
  display: inline-flex;
  margin-bottom: var(--sp-4);
}
.marketing-footer-logo .brand-logo {
  width: auto;
  height: 44px;
}
.auth-brand {
  display: inline-flex;
  align-items: center;
  margin-bottom: var(--sp-6);
}
.auth-brand .brand-logo {
  width: auto;
  height: 54px;
}

@media (max-width: 720px) {
  .marketing-brand > .brand-tagline,
  .brand > .brand-tagline {
    display: none;
  }
  .brand .brand-logo { height: 38px; }
}

/* Hero institutionnel : surface plate héritée de la définition de base,
   aucun traitement photo. Le thème sombre suit les tokens. */
.market-hero::before { display: none; }
.market-hero-inner { position: relative; z-index: 1; }
:root[data-theme="dark"] .market-hero {
  background: linear-gradient(180deg, #10222f 0%, var(--bg) 100%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .market-hero {
    background: linear-gradient(180deg, #10222f 0%, var(--bg) 100%);
  }
}

/* Results in the analysis card are three equal, self-contained panels. */
.market-hero-media .market-analysis-card {
  width: min(492px, 86%);
  padding: 18px;
}
/* Trois colonnes étaient imposées jusqu'à 540 px. Entre 541 px et environ
   1100 px, chaque colonne tombait à ~86 px, dont 60 px de texte utile : les
   mots se coupaient en plein milieu (« 2 erreurs bloquante / s »), ce que
   overflow-wrap ci-dessous ne faisait qu'autoriser au lieu de l'éviter.
   auto-fit + minmax supprime le point de rupture : les colonnes ne restent
   côte à côte que s'il reste 8,5 rem pour chacune, sinon elles se replient
   d'elles-mêmes. La largeur de la carte dépend du gabarit du héros, pas
   seulement du viewport — un seuil en media query ne pouvait donc pas
   couvrir tous les cas. */
.market-hero-media .analysis-findings {
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  /* Le panneau mesure ~348px : trois colonnes y tiendraient à 104px chacune,
     mais « XML structurellement prêt » recouperait alors son mot le plus
     long. Deux colonnes, et la troisième carte prend toute la largeur —
     composition assumée plutôt qu'une carte orpheline laissée seule dans sa
     rangée. Le sélecteur ne s'applique QUE s'il y a exactement trois cartes
     et que la troisième est la dernière : à quatre cartes la grille
     redevient un damier régulier, sans règle à retoucher. */
  gap: 10px;
  align-items: stretch;
}
.market-hero-media .analysis-findings > div {
  display: flex;
  flex-direction: column;
  /* CONTENU CENTRÉ dans chaque carré. Aligné à gauche, il laissait une marge
     droite inégale d'un carré à l'autre — « 4 avertissements » tient sur une
     ligne, « XML structurellement prêt » sur deux — et les trois pastilles
     d'icône ne tombaient pas sur le même axe que leur texte. Centré, chaque
     carré se lit comme une vignette : l'icône au-dessus, la mention dessous,
     sur le même axe. */
  align-content: center;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 8px;
  min-height: 146px;
  padding: 12px;
  overflow: hidden;
}
.market-hero-media .analysis-findings > div:nth-child(3):last-child {
  grid-column: 1 / -1;
  min-height: 0;
}
.market-hero-media .analysis-findings > div > span { display: block; min-width: 0; width: 100%; text-align: center; }
.market-hero-media .analysis-findings strong {
  min-width: 0;
  font-size: .76rem;
  line-height: 1.34;
  overflow-wrap: break-word; hyphens: auto;
}
.market-hero-media .analysis-findings small {
  display: block;
  margin-top: 5px;
  font-size: .7rem;
  line-height: 1.42;
  overflow-wrap: break-word; hyphens: auto;
}
@media (max-width: 820px) {
  .market-hero-media .market-analysis-card { width: auto; padding: var(--sp-4); }
}
@media (max-width: 540px) {
  .market-hero-media .analysis-findings { grid-template-columns: 1fr; }
  .market-hero-media .analysis-findings > div { min-height: 0; }
}

/* Démonstration : le diagramme est le seul point focal, plus de
   colonne de texte concurrente — la preuve du produit avant tout
   discours. */
.market-demo { padding: clamp(72px, 9vw, 130px) var(--gutter); }
/* Le texte à gauche, le schéma à droite et à sa taille. Tout tenait avant
   dans une colonne de 640 px centrée, le schéma relégué sous la liste avec
   250 px de vide de part et d'autre — alors que ce schéma est la promesse du
   produit : Excel entre, contrôlé, ressort en XML. */
.market-demo-inner {
  max-width: var(--page-max); margin: 0 auto;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
  gap: clamp(32px, 6vw, 80px); align-items: center;
}
.market-demo-inner h2 {
  margin: var(--sp-3) 0 0; color: var(--text);
  font-size: var(--fs-h2); line-height: 1.16; letter-spacing: var(--tracking-display);
}
.demo-copy > p { max-width: 52ch; margin: var(--sp-4) 0 0; color: var(--text-muted); line-height: 1.72; }
/* 280 px : le schéma vertical n'a pas besoin de toute la colonne, et une
   figure plus étroite que sa colonne se centre mieux face au texte. */
.market-demo-diagram { width: 100%; max-width: 280px; margin: 0 auto; }
@media (max-width: 900px) {
  .market-demo-inner { grid-template-columns: 1fr; gap: var(--sp-8); }
  .market-demo-diagram { max-width: 220px; }
}
/* Signature diagram: the same transformation the product performs,
   drawn once as an illustration instead of only described in prose. */
.flow-diagram { overflow: visible; }
.flow-node rect, .flow-node circle {
  fill: var(--card); stroke: var(--c-brand-400); stroke-width: 1.6;
}
.flow-node path { stroke: var(--c-brand-500); fill: none; }
.flow-node-accent circle { stroke: var(--c-teal-500); }
.flow-node-accent path { stroke: var(--c-teal-500); }
.flow-arrow { stroke: var(--border-strong); fill: none; }
.flow-label {
  fill: var(--text-muted); font-family: var(--font-mono); font-size: 9px;
  font-weight: 600; letter-spacing: .05em;
}
/* Aligne a gauche, et non centre : le titre de cette section est cale a
   gauche (elle n'utilise pas .centered), tandis que la colonne de 760 px
   etait centree dans les 1 132 px de contenu. Vu a l'ecran le 2026-08-11 :
   les cinq phases commencaient 186 px a droite du titre qui les annonce.
   Deux axes de lecture pour un meme bloc. La colonne garde sa largeur --
   c'est du texte suivi -- mais part du meme bord que le titre. */
.approach-method .project-phases { max-width: 760px; margin: clamp(30px, 4vw, 54px) 0 0; }
.project-phases {
  position: relative;
  display: grid;
  gap: 14px;
}
.project-phases::before {
  content: "";
  position: absolute;
  top: 28px;
  bottom: 28px;
  left: 20px;
  width: 1px;
  background: linear-gradient(var(--c-brand-200), var(--c-teal-300));
}
.project-phases article {
  position: relative;
  min-height: 148px;
  padding: 25px 26px 25px 82px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--card-raised);
  box-shadow: var(--sh-sm);
}
.project-phases article > span {
  position: absolute;
  top: 22px;
  left: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 41px;
  height: 41px;
  border: 5px solid var(--bg);
  border-radius: 50%;
  /* Bleu de marque litteral, pas var(--c-brand-600) : ce jeton bascule et
     devient CLAIR en sombre, ce qui laissait du blanc sur bleu pale --
     2,28:1, mesure le 2026-08-11. La pastille porte du blanc dans les deux
     themes, son fond doit donc rester sombre dans les deux. #2f79ab est le
     bleu marque de la charte : blanc dessus, 4,77:1. */
  background: #2f79ab;
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: .02em;
}
.project-phases h3 {
  margin: 0 0 9px;
  color: var(--text);
  font-size: 1.17rem;
}
.project-phases p {
  max-width: 66ch;
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.62;
}
.integration-journey { padding-top: clamp(72px, 9vw, 132px); }
.integration-phases {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-top: clamp(30px, 4vw, 54px);
}
.integration-phases article {
  position: relative;
  min-height: 246px;
  padding: 26px 24px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--card-raised);
  box-shadow: var(--sh-sm);
}
.integration-phases article > span {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 30px;
  border-radius: 8px;
  background: var(--c-brand-50);
  color: var(--c-brand-700);
  font-size: var(--fs-xs);
  font-weight: 850;
}
.integration-phases h3 { margin: 24px 0 10px; font-size: 1.08rem; color: var(--text); }
.integration-phases p { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6; }
/* Soft top/bottom falloff instead of a hard colour-block edge, plus
   the same blueprint texture used on the hero — one recurring motif
   instead of an isolated flat panel. */
.deployment-section {
  position: relative; isolation: isolate; overflow: hidden;
  padding: clamp(64px, 8vw, 110px) max(var(--gutter), calc((100% - var(--page-max)) / 2));
  background:
    linear-gradient(180deg, var(--c-brand-900) 0%, transparent 7%, transparent 93%, var(--c-brand-900) 100%),
    var(--c-brand-800);
  color: #fff;
}
.deployment-section::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 15%, #000 85%, transparent);
          mask-image: linear-gradient(180deg, transparent, #000 15%, #000 85%, transparent);
}
.deployment-heading { max-width: 720px; }
.deployment-heading h2 { margin: 12px 0 16px; max-width: 24ch; color: #fff; font-size: var(--fs-h2); }
.deployment-heading > p:last-child { max-width: 64ch; margin: 0; color: #cae1f1; line-height: 1.72; }
.deployment-index { color: var(--c-brand-200); font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: 600; letter-spacing: .06em; }
/* Plateforme en ligne : une liste de bénéfices concrets, pas une carte
   de comparaison — un seul produit mis en avant, sans alternative
   égale à côté. */
/* Deux colonnes, et non une de 620 px : vu a l'ecran le 2026-08-11, la
   bande marine fait 1 200 px de contenu et tout s'y entassait a gauche --
   titre, intro, les quatre rangs, les boutons. Plus de 600 px de bande
   restaient vides a droite, texture bleue et rien d'autre. Le titre et
   l'intro gardent leur largeur de lecture, c'est leur role ; les quatre
   rangs, eux, n'avaient aucune raison de s'arreter au milieu. */
.platform-feature-list {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px;
  max-width: none; margin: 38px 0 0;
  padding: 0; list-style: none;
}
@media (max-width: 860px) {
  .platform-feature-list { grid-template-columns: 1fr; }
}
.platform-feature-list li {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, #fff 14%, transparent);
  border-radius: var(--r-md);
  background: color-mix(in srgb, #fff 6%, transparent);
}
/* #cae1f1, not var(--c-brand-200): this section's navy background is
   fixed regardless of site theme, but --c-brand-200 is theme-adaptive
   and remaps to a near-black shade in dark mode — invisible here. The
   same literal already used just above for .deployment-heading text. */
.platform-feature-list li svg { flex: none; color: #cae1f1; }
.platform-feature-list li span { color: #fff; font-size: var(--fs-sm); font-weight: 600; line-height: 1.5; }
.platform-primary-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-5); margin-top: var(--sp-8); }
.platform-login-link { color: #cae1f1; font-size: var(--fs-sm); font-weight: 700; text-decoration: none; }
.platform-login-link:hover { color: #fff; text-decoration: underline; }
.platform-login-link:focus-visible { outline: 2px solid #cae1f1; outline-offset: 3px; border-radius: var(--r-xs); }
@media (max-width: 900px) {
  .integration-phases { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .market-demo { padding-top: 56px; padding-bottom: 56px; }
  .project-phases article { min-height: 0; padding: 23px 20px 23px 65px; }
  .project-phases article > span { width: 35px; height: 35px; border-width: 4px; }
  .project-phases::before { left: 17px; }
  .integration-phases { grid-template-columns: 1fr; }
  .integration-phases article { min-height: 0; }
}

/* ============================================================
   PAGE APPLICATION DESKTOP (/solutions/application-desktop)
   ============================================================ */
/* Maquette de la fenêtre de l'application (fidèle à l'app Tkinter réelle :
   mini-flow 1-2-3, champs du formulaire, bouton Générer, ligne de licence). */
.desktop-window {
  width: 100%; max-width: 470px;
  border-radius: var(--r-lg); overflow: hidden;
  background: var(--card); box-shadow: var(--sh-xl);
  border: 1px solid var(--border);
}
.desktop-window-body { padding: var(--sp-5); }
.desktop-window-flow {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-4);
  margin-bottom: var(--sp-5); font-size: var(--fs-xs); color: var(--text-muted); font-weight: 600;
}
.desktop-window-flow b {
  display: inline-grid; place-items: center;
  width: 20px; height: 20px; margin-right: 5px;
  border-radius: var(--r-xs);
  background: var(--bg-sunken); color: var(--text-muted);
  font-size: var(--fs-2xs);
}
.desktop-window-flow .is-current { color: var(--text); }
.desktop-window-flow .is-current b { background: var(--c-brand-500); color: #fff; }
.desktop-window-field { margin-bottom: var(--sp-3); }
.desktop-window-field label {
  display: block; margin-bottom: 4px;
  color: var(--text-muted); font-size: var(--fs-2xs); font-weight: 700;
}
.desktop-window-field > div {
  padding: 8px 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--card); color: var(--text);
  font-family: var(--font-mono); font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.desktop-window-cta {
  margin-top: var(--sp-4); text-align: center;
}
.desktop-window-cta span {
  display: inline-block; padding: 10px 26px;
  border-radius: var(--r-sm);
  background: var(--c-brand-500); color: #fff;
  font-size: var(--fs-sm); font-weight: 700;
  box-shadow: var(--sh-sm);
}
.desktop-window-licence {
  margin: var(--sp-4) 0 0; text-align: center;
  color: var(--c-teal-500); font-size: var(--fs-2xs); font-weight: 600;
}
/* Cartes de faits : réutilise market-capabilities, avec des glyphes plus petits
   que les grands chiffres de l'accueil. */
/* Les faits desktop portent une icône SVG (plus d'émoji dépendant de l'OS) :
   pastille carrée aux couleurs de la marque. */
.desktop-facts article strong {
  display: inline-grid; place-items: center; flex: none;
  width: 46px; height: 46px;
  border-radius: var(--r-sm);
  background: var(--c-brand-50); color: var(--c-brand-600);
  border: 1px solid var(--c-brand-100);
}
/* Section licence : même surface photo sombre que deployment-section, 4 étapes. */
.licence-steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
  margin: 38px 0 26px;
}
.licence-steps article {
  padding: 26px;
  border: 1px solid var(--c-brand-600);
  border-radius: var(--r-lg);
  background: var(--c-brand-900);
}
.licence-steps h3 { margin: 16px 0 10px; color: #fff; font-size: 1.08rem; }
.licence-steps p { margin: 0; color: #d8eaf7; font-size: var(--fs-sm); line-height: 1.65; }
.licence-note { max-width: 64ch; margin: 0; color: #cae1f1; font-size: var(--fs-sm); }
/* Section IT : cartes claires sur fond de page. */
.desktop-it-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-5);
  margin-top: var(--sp-7);
}
.desktop-it-grid article {
  padding: var(--sp-6);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card-raised); box-shadow: var(--sh-xs);
}
.desktop-it-grid h3 { margin: 0 0 var(--sp-3); font-size: 1.08rem; color: var(--text); }
.desktop-it-grid p { margin: 0; color: var(--text-muted); line-height: 1.65; font-size: var(--fs-sm); }
/* Tableau comparatif web / desktop. */
.desktop-compare .table-wrap { max-width: 940px; margin: var(--sp-7) auto 0; }
.compare-table {
  width: 100%; border-collapse: collapse;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden; font-size: var(--fs-sm);
}
.compare-table th, .compare-table td {
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
  text-align: left; vertical-align: top;
}
.compare-table thead th {
  background: var(--bg-sunken); color: var(--text);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
}
.compare-table thead th:last-child { color: var(--c-brand-600); }
.compare-table tbody th { color: var(--text-muted); font-weight: 700; font-size: var(--fs-xs); }
.compare-table tbody td { color: var(--text); }
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { border-bottom: 0; }
.final-cta-lead { margin: 14px 0 0; max-width: 52ch; color: #d3e7f5; line-height: 1.65; }
@media (max-width: 1080px) {
  .licence-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .desktop-it-grid { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .licence-steps { grid-template-columns: 1fr; }
  .licence-steps article { min-height: 0; }
  .desktop-window { max-width: 100%; }
}

/* ============================================================
   HERO OVERFLOW FIX — decisive, last-in-file.
   Several earlier redesign passes left duplicate, conflicting
   .market-hero-inner grid-template-columns declarations (some with
   a minmax(420-500px, …) second track) at overlapping breakpoints.
   At narrow widths the cascade could resolve to a track wider than
   the viewport, pushing hero content off-screen. This is the single
   rule that is guaranteed to win and pins both hero columns to a
   simple, safe single-column stack below 820px.
   ============================================================ */
@media (max-width: 820px) {
  .market-hero-inner {
    grid-template-columns: minmax(0, 1fr) !important;
  }
  .market-hero-copy, .market-hero-media {
    width: 100% !important; min-width: 0 !important; max-width: 100% !important;
  }
}

/* ============================================================
   ADMIN CONSOLE — chantier « Administration ».
   Reuses the connected-space header/nav/dropdown components as-is
   (.app-header, .app-header-nav, .app-nav-link, .account-menu,
   .lang-switcher) so admin and user space share one visual identity.
   Everything below is new and specific to admin pages only.
   ============================================================ */

/* Static context badge next to the brand — never a button, never a
   hover/focus affordance, since it triggers no action. */
.admin-mode-badge {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: 30px; padding: 0 var(--sp-3);
  border: 1px solid var(--c-brand-200); border-radius: var(--r-full);
  background: var(--c-brand-50); color: var(--c-brand-700);
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  white-space: nowrap; user-select: none;
}
:root[data-theme="dark"] .admin-mode-badge {
  background: color-mix(in srgb, var(--c-brand-700) 35%, transparent);
  border-color: color-mix(in srgb, var(--c-brand-400) 40%, transparent);
  color: var(--c-brand-600);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .admin-mode-badge {
    background: color-mix(in srgb, var(--c-brand-700) 35%, transparent);
    border-color: color-mix(in srgb, var(--c-brand-400) 40%, transparent);
    color: var(--c-brand-600);
  }
}

/* Right-side action slot on the shared compact header (.module-header-compact
   already provides icon + title + kicker; admin pages add one action here). */
.module-header-actions { flex: none; margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); }
@media (max-width: 760px) { .module-header-actions { flex-basis: 100%; margin-left: 0; } }

/* ---------------- KPI stat cards (Vue d'ensemble, Statistiques) ---------------- */
.stat-card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-6); }
/* Vérification navigateur (chantier admin Partie 2) : au même titre que
   .kpi-card-grid ci-dessus, minmax(168px, 1fr) seul force une largeur de
   168px même quand le conteneur est plus étroit (375px de large moins le
   padding de page) -- la grille dépassait alors réellement le viewport de
   quelques pixels (mesuré via getBoundingClientRect, pas supposé). Même
   correctif que .kpi-card-grid : 2 colonnes sans minimum forcé en dessous
   du seuil. */
@media (max-width: 760px) {
  .stat-card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.stat-card {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 11px var(--sp-4); border: 1px solid var(--border); border-left: 3px solid var(--c-brand-300);
  border-radius: var(--r-md); background: var(--card); box-shadow: var(--sh-xs);
  text-decoration: none; color: inherit;
}
a.stat-card:hover, a.stat-card:focus-visible { border-color: var(--c-brand-400); box-shadow: var(--sh-sm); }
a.stat-card:hover { outline: 0; }
a.stat-card:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.stat-card-icon { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: var(--r-md); background: var(--c-brand-50); color: var(--c-brand-600); }
.stat-card-copy { min-width: 0; }
/* Le libellé porte l'identification de la carte, la valeur ne doit pas
   l'écraser : même logique de proportion que .dashboard-stat (espace
   utilisateur) — valeur modérée, libellé en petites capitales lisibles. */
.stat-card-value { font-family: var(--font-mono); font-size: 1.2rem; font-weight: 700; line-height: 1.15; color: var(--text); }
.stat-card-value.is-text { font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 700; line-height: 1.3; }
/* overflow-wrap:anywhere -- un libellé long sans espace ("Administrateurs")
   ne peut pas se répartir sur 2 lignes sans point de coupure autorisé DANS
   le mot ; mesuré en débordement réel de 8px à 375px de large (2 colonnes,
   voir le correctif @media sur .stat-card-grid ci-dessus) avant ce
   correctif. */
.stat-card-label { margin-top: 2px; font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: var(--text-muted); overflow-wrap: anywhere; }
.stat-card-sub { display: block; margin-top: 1px; font-size: var(--fs-2xs); color: var(--text-faint); }
.stat-card.is-warning { border-left-color: var(--c-warn); }
.stat-card.is-warning .stat-card-icon { background: var(--c-warn-bg); color: var(--c-warn); }
.stat-card.is-danger { border-left-color: var(--c-danger); }
.stat-card.is-danger .stat-card-icon { background: var(--c-danger-bg); color: var(--c-danger); }
.stat-card.is-ok { border-left-color: var(--c-ok); }
.stat-card.is-ok .stat-card-icon { background: var(--c-ok-bg); color: var(--c-ok); }
.stat-card.is-muted { border-left-color: var(--border-strong); opacity: .72; }

/* Filtres de statut sous forme de puces (badges filtrants) : pour un
   ensemble d'états qui sert RÉELLEMENT à filtrer une liste (le clic change
   la vue), jamais le langage visuel d'une carte KPI -- confondre les deux
   était exactement le défaut signalé sur Support (des .stat-card cliquables
   sans aucun état actif visible, redondantes avec un <select> de statut
   séparé dans la barre de filtres juste en dessous). Label + compteur +
   état actif + focus + survol, rien de plus. */
.status-filter-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.status-filter-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; border: 1px solid var(--border); border-radius: var(--r-full);
  background: var(--card); color: var(--text); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 650;
}
.status-filter-chip:hover { border-color: var(--c-brand-300); background: var(--bg-sunken); outline: 0; }
.status-filter-chip:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.status-filter-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--bg-sunken); color: var(--text-muted); font-size: var(--fs-2xs); font-weight: 700;
}
.status-filter-chip.is-active { border-color: var(--c-brand-500); background: var(--c-brand-50); color: var(--c-brand-700); }
.status-filter-chip.is-active .status-filter-count { background: var(--c-brand-500); color: #fff; }
.status-filter-chip.has-alert:not(.is-active) { border-color: var(--c-warn); }
.status-filter-chip.has-alert:not(.is-active) .status-filter-count { background: var(--c-warn-bg); color: var(--c-warn); }

/* Zone "Gestion" de la Vue d'ensemble : des raccourcis compacts, pas des
   cartes statistiques — pas de valeur chiffrée, juste icône + destination. */
.admin-quicklinks { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.admin-quicklink {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: var(--text); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 650;
}
.admin-quicklink:hover, .admin-quicklink:focus-visible { border-color: var(--c-brand-400); background: var(--bg-sunken); }
.admin-quicklink:hover { outline: 0; }
.admin-quicklink:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.admin-quicklink svg { flex: none; color: var(--c-brand-600); }

/* ---------------- Section headings reused across admin pages ---------------- */
.admin-block { margin-bottom: var(--sp-8); }
.admin-block-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-3); }
.admin-block-heading h2 { margin: 0 0 3px; font-size: var(--fs-h3); }
.admin-block-heading p { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); max-width: 62ch; }
.admin-block-link { flex: none; font-size: var(--fs-xs); font-weight: 700; color: var(--c-brand-600); text-decoration: none; white-space: nowrap; }
.admin-block-link:hover { text-decoration: underline; }

/* ---------------- Toolbar: search + filters + primary action ---------------- */
.admin-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-2); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card-raised);
}
.admin-toolbar-search { position: relative; flex: 1 1 220px; min-width: 180px; max-width: 320px; }
.admin-toolbar-search > span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-faint); display: grid; }
/* box-sizing:border-box + height (pas min-height) : le padding ne doit
   jamais pousser ce champ plus haut que les select/boutons voisins — bug
   réel mesuré (36px vs 32px) avant ce correctif, cause du désalignement
   vertical de la ligne de filtres. */
/* font-size:var(--fs-xs), pas --fs-sm : même taille que les select voisins
   (.admin-toolbar-filters select) -- un écart mesuré ici (14.72px contre
   13.44px) était la dernière incohérence entre ce champ et les autres
   contrôles de la barre, une fois bordure/rayon/fond alignés ci-dessus. */
.admin-toolbar-search input { width: 100%; height: var(--a-control-h, 32px); box-sizing: border-box; padding: 0 10px 0 36px; font-size: var(--fs-xs); }
/* Grille, pas flex+nowrap : l'ancienne version (flex-wrap:nowrap, enfants
   flex:none) empêchait tout retour à la ligne interne -- le groupe entier
   devait soit tenir en une ligne, soit déborder EN BLOC (aucun de ses
   select ne pouvant rétrécir ni passer à la ligne), cause réelle du
   débordement mesuré sur Statistiques et de la collision Rôle/Filtrer sur
   Utilisateurs. Avec auto-fit/minmax, chaque contrôle occupe au moins
   140px ; ceux qui ne tiennent plus sur la ligne courante passent
   proprement à la ligne suivante PAR GROUPE DE COLONNES, jamais un select
   isolé qui capturerait toute la largeur (même intention que l'ancien
   commentaire, obtenue autrement). min-width:0 est nécessaire pour que ce
   conteneur puisse lui-même rétrécir en tant qu'enfant flex de
   .admin-toolbar (sinon un enfant flex garde sa largeur de contenu
   intrinsèque et force le parent à déborder). */
.admin-toolbar-filters {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  align-items: center; gap: var(--sp-2); flex: 1 1 320px; min-width: 0;
}
.admin-toolbar-filters select, .admin-toolbar-filters input:is([type="date"],[type="datetime-local"]), .admin-toolbar-filters input[type="text"] {
  width: 100%; height: var(--a-control-h, 32px); box-sizing: border-box; font-size: var(--fs-xs); padding: 0 10px;
}
/* Dans les barres DataGrid ordinaires, un filtre unique ne doit pas
   s'étendre sur tout l'espace restant (ex. « Tous les statuts » sur
   Messages). Les contrôles ont une largeur de travail stable et reviennent
   à la ligne proprement quand ils sont nombreux. La barre Statistiques
   --uniform conserve sa grille dédiée plus bas. */
/* `:not(.dg-toolbar)` : les barres des DataGrid ont leur propre grille plus
   bas. Ce qui reste ici ne sert plus qu'aux barres montées à la main
   (Statistiques non-uniforme, Rôles), qui n'ont pas de groupe de filtres
   assez fourni pour souffrir du défaut décrit là-bas. */
.admin-toolbar:not(.admin-toolbar--uniform):not(.dg-toolbar) .admin-toolbar-filters {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:flex-start;
}
.admin-toolbar:not(.admin-toolbar--uniform):not(.dg-toolbar) .admin-toolbar-filters > select,
.admin-toolbar:not(.admin-toolbar--uniform):not(.dg-toolbar) .admin-toolbar-filters > input,
/* `.combo` : un menu converti en menu cherchable est enveloppé, il n'est
   donc plus l'enfant DIRECT de la barre — sans cette ligne, il perdrait la
   largeur de travail que ses voisins gardent (voir static/combo.js). */
.admin-toolbar:not(.admin-toolbar--uniform):not(.dg-toolbar) .admin-toolbar-filters > .combo,
.admin-toolbar:not(.admin-toolbar--uniform):not(.dg-toolbar) .admin-toolbar-filters > .ms-filter {
  flex:0 1 220px;
  width:220px;
  max-width:100%;
}

/* ---------------- Barre de filtres des DataGrid : une seule grille --------
   Douze tableaux partagent cette barre. Elle empilait quatre familles de
   largeurs sur une même ligne — recherche 320 px, filtres 220 px, Filtrer
   93 px, Réinitialiser 62 px — dans deux conteneurs flex imbriqués. Le
   groupe de filtres revenait donc à la ligne TOUT SEUL, à l'intérieur de
   lui-même, pendant que la recherche et les boutons restaient centrés
   verticalement sur sa hauteur totale. Résultat mesuré à 1440 px sur les
   douze barres : la recherche désalignée de la première rangée de filtres
   sur 8 d'entre elles, jusqu'à 3 rangées visuelles et 130 px de hauteur
   (Activité, Journal), et aucune colonne commune d'une rangée à l'autre.

   Une seule grille règle les trois d'un coup. `display: contents` sur le
   groupe de filtres dissout le conteneur intermédiaire : chaque contrôle
   devient une cellule de la MÊME grille que la recherche et les actions,
   donc une largeur unique, des colonnes qui tombent en face et un retour à
   la ligne qui aligne les rangées entre elles. Même mécanisme que la barre
   Statistiques (.admin-toolbar--uniform, plus haut) — pattern déjà validé
   ici, généralisé aux tableaux plutôt que réinventé. */
.admin-toolbar.dg-toolbar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  align-items: center;
  gap: var(--sp-2);
}
/* La recherche vaut deux cellules : c'est le seul champ à saisie libre de la
   barre, et le seul dont le contenu n'est pas borné par une liste. À une
   seule cellule elle n'affichait plus qu'une poignée de caractères. */
.dg-toolbar > .admin-toolbar-search { grid-column: span 2; max-width: none; min-width: 0; }
.dg-toolbar > .admin-toolbar-filters { display: contents; }
.dg-toolbar .admin-toolbar-filters > select,
.dg-toolbar .admin-toolbar-filters > input,
.dg-toolbar .admin-toolbar-filters > .combo,
.dg-toolbar .admin-toolbar-filters > .ms-filter { width: 100%; min-width: 0; max-width: none; }
/* Les deux boutons partagent UNE cellule : ils forment une seule action de
   bout de barre, pas deux champs de plus. À l'intérieur ils se partagent la
   largeur également, ce qui leur donne un bord droit commun avec la colonne
   au-dessus. */
/* `-2 / -1` : dernière colonne de la grille, quelle que soit la rangée où la
   paire atterrit. En placement automatique elle suivait simplement le
   dernier filtre — donc à GAUCHE de la rangée suivante dès qu'elle passait à
   la ligne (mesuré sur Mon activité et Notifications), alors qu'une action
   de barre se cherche à droite. Vérifié sur les douze barres : paire au bord
   droit partout, aucun chevauchement, aucune rangée ajoutée. */
.dg-toolbar .admin-toolbar-actions-pair { display: flex; gap: var(--sp-2); min-width: 0; grid-column: -2 / -1; }
.dg-toolbar .admin-toolbar-actions-pair > .btn {
  flex: 1 1 0; min-width: 0; margin-left: 0;
  justify-content: center; text-align: center;
}
@media (max-width: 640px) {
  .admin-toolbar.dg-toolbar { grid-template-columns: 1fr; }
  .dg-toolbar > .admin-toolbar-search { grid-column: span 1; }
}
/* Bouton Appliquer/Réinitialiser : éléments séparés de .admin-toolbar-filters
   (voir _datagrid.html), poussés au bord droit RÉEL de la barre — pas
   seulement au bord du groupe recherche+filtres — par margin-left:auto sur
   la ligne flex externe (.admin-toolbar). Même hauteur que les champs
   voisins. Jamais de position:absolute pour ce placement -- uniquement le
   flux flex normal, donc jamais de chevauchement possible avec un champ. */
/* Appliquer (Filtrer) vient en premier dans le DOM et porte le
   margin-left:auto (pousse la PAIRE appliquer+reset au bord droit) ;
   Réinitialiser n'en a plus besoin, il suit juste Appliquer
   dans le flux -- éviter que les deux se poussent chacun individuellement,
   ce qui les séparerait au lieu de les garder groupés. Même hauteur/boîte
   que les champs de filtre voisins ; secondaire visuellement (btn-ghost,
   déjà moins dominant que btn plein utilisé par Appliquer). */
.admin-toolbar-reset { margin-left: 0; flex: none; height: var(--a-control-h, 32px); box-sizing: border-box; white-space: nowrap; display: inline-flex; align-items: center; }
/* Filtre de période : les deux dates restent ensemble et portent leur nom
   à l'écran, pas seulement dans un aria-label. Deux cellules de grille pour
   qu'elles ne se retrouvent jamais séparées par un retour à la ligne. */
.filter-daterange { display: flex; align-items: center; gap: 6px; min-width: 0; }
.dg-toolbar .filter-daterange, .admin-toolbar-filters > .filter-daterange { grid-column: span 2; }
.filter-daterange-label {
  flex: none; font-size: var(--fs-2xs); font-weight: 700; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: .04em; white-space: nowrap;
}
.filter-daterange-sep { flex: none; color: var(--text-faint); }
.filter-daterange input:is([type="date"],[type="datetime-local"]) { min-width: 0; }

.admin-toolbar-apply { margin-left: auto; flex: none; height: var(--a-control-h, 32px); box-sizing: border-box; white-space: nowrap; }
@media (max-width: 640px) {
  .admin-toolbar-filters { grid-template-columns: 1fr; }
  .admin-toolbar:not(.admin-toolbar--uniform) .admin-toolbar-filters > select,
  .admin-toolbar:not(.admin-toolbar--uniform) .admin-toolbar-filters > input,
  .admin-toolbar:not(.admin-toolbar--uniform) .admin-toolbar-filters > .combo,
  .admin-toolbar:not(.admin-toolbar--uniform) .admin-toolbar-filters > .ms-filter { flex-basis:100%; width:100%; }
  .admin-toolbar-reset, .admin-toolbar-apply { margin-left: 0; flex: 1 1 0; justify-content: center; }
}
.admin-toolbar-actions { margin-left: auto; display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.admin-toolbar-count { font-size: var(--fs-2xs); color: var(--text-faint); white-space: nowrap; }

/* Barre Statistiques : les sept filtres et les deux actions utilisent une
   cellule de grille strictement identique. Avant, les filtres occupaient un
   groupe extensible tandis que les boutons gardaient leur largeur de texte,
   d'où les tailles différentes visibles sur une même ligne. */
.admin-toolbar--uniform {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  align-items: center;
  gap: var(--sp-2);
}
.admin-toolbar--uniform > .admin-toolbar-filters,
.admin-toolbar--uniform > .admin-toolbar-actions { display: contents; }
.admin-toolbar--uniform select,
.admin-toolbar--uniform input:is([type="date"],[type="datetime-local"]),
.admin-toolbar--uniform input[type="text"],
.admin-toolbar--uniform .btn {
  width: 100%;
  min-width: 0;
  height: var(--a-control-h, 32px);
  margin: 0;
  box-sizing: border-box;
  justify-content: center;
}
@media (max-width: 640px) {
  .admin-toolbar--uniform { grid-template-columns: 1fr; }
}

/* ---------------- Datatable ---------------- */
.datatable-wrap { border: 1px solid var(--border); border-radius: var(--r-md); overflow: visible; background: var(--card); }
/* En dessous de 1000 px, un tableau large sort de son cadre : mesure sur
   /admin/statistiques a 390 px, la table debordait de 461 px et s'echappait
   de la carte. On le fait defiler DANS son cadre.
   Pourquoi seulement en etroit : creer un conteneur de defilement rend
   l'en-tete « position: sticky » solidaire du cadre au lieu de la fenetre,
   et le cadre n'ayant pas de hauteur limitee, l'en-tete cesserait de coller.
   En large il n'y a pas de debordement — on garde donc l'en-tete collant. */
@media (max-width: 1000px) {
  .datatable-wrap { overflow-x: auto; }
}
table.datatable { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
table.datatable th, table.datatable td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
/* `--dg-head-top` est publiée par datagrid.js quand une barre de filtres
   collante précède ce tableau (voir empilerBarresCollantes). Sans barre
   collante, la variable est absente et l'en-tête colle à 0 comme avant. */
table.datatable thead th { position: sticky; top: var(--dg-head-top, 0); z-index: 1; background: var(--bg-sunken); color: var(--text-muted); font-weight: 700; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .03em; }
table.datatable tbody tr:hover { background: var(--bg-sunken); }
table.datatable tbody tr[hidden] { display: none; }
.cell-muted { color: var(--text-muted); }
.cell-mono { font-family: var(--font-mono); font-size: var(--fs-xs); }
.cell-primary-link { color: var(--c-brand-600); font-weight: 700; text-decoration: none; }
.cell-primary-link:hover { text-decoration: underline; }

/* Distribution des colonnes : des classes de type plutôt que des largeurs
   à la cellule. table-layout reste "auto" (par défaut) — ces règles ne font
   qu'orienter l'algorithme automatique, elles ne le remplacent pas, pour que
   le mode .responsive-cards (mobile) garde la main sans conflit. */
table.datatable th.col-check, table.datatable td.col-check { width: 36px; }
table.datatable th.col-icon, table.datatable td.col-icon { width: 40px; }
table.datatable th.col-date, table.datatable td.col-date { width: 128px; white-space: nowrap; }
table.datatable th.col-status, table.datatable td.col-status { width: 132px; }
table.datatable th.col-role, table.datatable td.col-role { width: 110px; }
table.datatable th.col-count, table.datatable td.col-count { width: 92px; }
table.datatable th.col-actions, table.datatable td.col-actions { width: 150px; white-space: nowrap; }
table.datatable th.col-name, table.datatable td.col-name { min-width: 180px; max-width: 260px; }
table.datatable th.col-company, table.datatable td.col-company { min-width: 160px; max-width: 220px; }
table.datatable th.col-email, table.datatable td.col-email { min-width: 200px; max-width: 260px; }
table.datatable th.col-module, table.datatable td.col-module { min-width: 200px; max-width: 280px; }
table.datatable th.col-operation, table.datatable td.col-operation { min-width: 160px; max-width: 240px; }
table.datatable th.col-subject, table.datatable td.col-subject { min-width: 160px; max-width: 240px; }
table.datatable th.col-message, table.datatable td.col-message { min-width: 220px; max-width: 320px; }
table.datatable th.col-description, table.datatable td.col-description { min-width: 200px; max-width: 320px; }
/* Débordement contrôlé : texte long -> points de suspension + info-bulle
   native (title, ajoutée automatiquement par admin.js quand le texte est
   réellement tronqué — voir initAutoTooltips). col-name/col-company restent
   volontairement exclus : plusieurs tableaux y empilent nom + société sur
   deux lignes via <br>, incompatible avec un troncage une-ligne propre. */
table.datatable td.col-email, table.datatable td.col-module, table.datatable td.col-operation,
table.datatable td.col-subject, table.datatable td.col-message, table.datatable td.col-description {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 720px) {
  table.datatable.responsive-cards td.col-email, table.datatable.responsive-cards td.col-module,
  table.datatable.responsive-cards td.col-operation, table.datatable.responsive-cards td.col-subject,
  table.datatable.responsive-cards td.col-message, table.datatable.responsive-cards td.col-description {
    max-width: none; white-space: normal; overflow: visible; text-overflow: clip;
  }
}

/* Row actions: one visible primary action + a secondary kebab menu,
   reusing the existing .dropdown mechanics (Escape/focusout already
   wired once in admin_base.html for every .dropdown on the page). */
.row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.row-action-menu { position: relative; }
.row-action-trigger {
  display: grid; place-items: center; width: 30px; height: 30px;
  border: 1px solid transparent; border-radius: var(--r-sm); background: transparent; color: var(--text-muted); cursor: pointer;
}
.row-action-trigger:hover, .row-action-trigger:focus-visible { border-color: var(--border); background: var(--card); color: var(--text); }
.row-action-trigger:hover { outline: 0; }
.row-action-trigger:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.row-action-panel { right: 0; left: auto; top: calc(100% + 4px); min-width: 190px; grid-template-columns: 1fr; }
.row-action-panel .dropdown-item.is-danger { color: var(--c-danger); }
.row-action-panel form { display:block; width:100%; margin:0; }
.row-action-panel .dropdown-item { position:relative; z-index:1; align-items:center; width:100%; min-height:40px; box-sizing:border-box; padding:10px 12px; cursor:pointer; }
.row-action-panel a.dropdown-item { display:flex; }
.row-action-panel button.dropdown-item { display:flex; }
.row-action-panel .dropdown-item:hover,.row-action-panel .dropdown-item:focus-visible { color:var(--text); background:var(--bg-sunken); outline:none; }
.row-action-panel .dropdown-item.is-danger:hover,.row-action-panel .dropdown-item.is-danger:focus-visible { color:var(--c-danger); background:color-mix(in srgb,var(--c-danger) 9%,var(--surface)); }

/* ---------------- Filtre à sélection multiple (barre d'outils) --------
   Même famille visuelle que les autres champs de filtre : hauteur/rayon/
   bordure identiques (--a-control-h, comme .admin-toolbar-apply/reset et
   les <select> voisins) -- ce n'est PAS un bouton d'action, c'est un champ
   de filtre de plus dans la grille .admin-toolbar-filters. */
.ms-filter { position: relative; min-width: 0; }
.ms-filter-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  width: 100%; height: var(--a-control-h, 32px); box-sizing: border-box;
  padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: var(--text); font: inherit; font-size: var(--fs-xs);
  cursor: pointer;
}
.ms-filter-trigger:hover { border-color: var(--c-brand-300); }
.ms-filter-trigger:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: 2px; }
.ms-filter-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; text-align: left; }
.ms-filter-caret { flex: none; color: var(--text-faint); font-size: 10px; }
/* Panneau : positionné en fixed par openFixedPanel() (datagrid.js) --
   jamais hors viewport quelle que soit la position du champ dans la
   grille de filtres. Liste scrollable indépendamment (recherche + actions
   toujours visibles) plutôt qu'un panneau qui grandirait sans limite avec
   des centaines d'options. */
.ms-filter-panel { min-width: 240px; width: max-content; max-width: min(320px, calc(100vw - 2 * var(--sp-4))); grid-template-columns: 1fr; gap: var(--sp-2); padding: var(--sp-2); }
.ms-filter-search input { width: 100%; }
.ms-filter-actions { display: flex; gap: var(--sp-3); padding: 0 2px; }
.ms-filter-list { overflow-y: auto; max-height: 220px; display: flex; flex-direction: column; gap: 1px; }
.ms-filter-option { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--r-sm); font-size: var(--fs-sm); cursor: pointer; }
.ms-filter-option:hover { background: var(--bg-sunken); }
.ms-filter-option input[type="checkbox"] { width: auto; flex: none; }
.ms-filter-empty { margin: 0; padding: var(--sp-2); color: var(--text-faint); font-size: var(--fs-xs); }
@media (max-width: 480px) {
  .ms-filter-panel { max-width: calc(100vw - 2 * var(--sp-3)); }
}

/* ---------------- Pagination ---------------- */
.datatable-pagination {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border); background: var(--card-raised);
  font-size: var(--fs-xs); color: var(--text-muted); flex-wrap: wrap;
}
.datatable-pagination-pages {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 5px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .10);
}
.datatable-pagination button {
  min-height: 30px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--card); color: var(--text); font: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer;
}
.datatable-pagination button:hover:not(:disabled) { border-color: var(--c-brand-300); color: var(--c-brand-600); }
.datatable-pagination button:disabled { opacity: .4; cursor: not-allowed; }
.datatable-pagination button.is-current { background: var(--c-brand-600); border-color: var(--c-brand-600); color: #fff; }
/* Pagination serveur (liens réels ?page=N, ex. Journal) — même rendu que la
   pagination client-side (boutons) ci-dessus pour rester visuellement
   identique, mais ce sont de vraies navigations, pas un ré-affichage JS. */
.datatable-pagination a {
  min-height: 30px; min-width: 30px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--card); color: var(--text); font-size: var(--fs-xs); font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; text-decoration: none;
}
.datatable-pagination a:hover { border-color: var(--c-brand-300); color: var(--c-brand-600); }
.datatable-pagination a.is-current { background: var(--c-brand-600); border-color: var(--c-brand-600); color: #fff; }

/* Pagination serveur des grilles admin. Le composant est une soeur de
   .datatable-pagination (et non son enfant) : il doit donc porter ses
   propres styles de liens. */
.datatable-pagination-pages a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  font-weight: 750;
  line-height: 1;
  text-decoration: none;
  transition: color .16s ease, background-color .16s ease, border-color .16s ease, transform .16s ease;
}
.datatable-pagination-pages a:first-child,
.datatable-pagination-pages a:last-child {
  border-color: var(--border);
  background: var(--card-raised);
  color: var(--text);
  font-size: 18px;
}
.datatable-pagination-pages a:hover:not([aria-disabled="true"]) {
  border-color: var(--c-brand-300);
  background: color-mix(in srgb, var(--c-brand-600) 10%, transparent);
  color: var(--c-brand-500);
  transform: translateY(-1px);
}
.datatable-pagination-pages a.is-current {
  border-color: var(--c-brand-500);
  background: var(--c-brand-600);
  color: #fff;
  box-shadow: 0 4px 10px color-mix(in srgb, var(--c-brand-600) 30%, transparent);
}
.datatable-pagination-pages a[aria-disabled="true"] {
  opacity: .32;
  cursor: not-allowed;
  box-shadow: none;
}

/* ---------------- Barre inférieure : résultats + taille de page ---------------- */
/* Grille à 3 colonnes (pas flex + space-between) : la pagination doit rester
   VISUELLEMENT centrée sous le tableau quelle que soit la largeur du texte
   de résultats à gauche ou du sélecteur de taille de page à droite --
   space-between ne centre le milieu que si les deux voisins ont la même
   largeur, ce qui n'est jamais garanti ici (bug réel mesuré : pagination à
   16px du bord droit, 1107px du bord gauche, avant ce correctif). Colonne
   centrale vide (pas de pagination, une seule page) : les deux autres
   colonnes 1fr gardent quand même leurs bords gauche/droit corrects. */
.dg-bottombar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border); background: var(--card-raised);
  font-size: var(--fs-xs); color: var(--text-muted);
}
/* grid-column explicite sur les 3 -- .datatable-pagination-pages (colonne 2)
   n'existe carrément pas dans le DOM tant qu'il n'y a qu'une page (bloc
   {% if state.total_pages > 1 %} dans _datagrid.html, pas juste un div
   vide) : sans grid-column fixe, le placement automatique de la grille
   comblait les pistes dans l'ORDRE DOM, donc avec seulement 2 enfants
   (résultats + taille de page), le sélecteur de taille de page atterrissait
   en colonne 2 (centre) au lieu de la colonne 3 (droite) -- bug réel mesuré
   sur toute page à une seule page (la quasi-totalité des tableaux admin en
   usage courant, pas un cas rare). */
.dg-results-count { grid-column: 1; white-space: nowrap; justify-self: start; }
.dg-results-count-standalone { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-xs); color: var(--text-faint); }
.datatable-pagination-pages { grid-column: 2; justify-self: center; }
.dg-bottombar-right { grid-column: 3; display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; justify-self: end; }
@media (max-width: 640px) {
  .dg-bottombar { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: var(--sp-2); }
  .dg-results-count, .datatable-pagination-pages, .dg-bottombar-right { justify-self: center; }
  .datatable-pagination-pages { gap: 3px; padding: 4px; }
  .datatable-pagination-pages a { min-width: 31px; height: 31px; padding: 0 7px; }
}
.dg-pagesize { position: relative; }
.dg-pagesize > button { white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
.dg-pagesize-panel { right: 0; left: auto; min-width: 90px; grid-template-columns: 1fr; padding: 4px; }
.dg-pagesize-panel .dropdown-item { text-align: center; justify-content: center; }
.dg-pagesize-panel .dropdown-item.is-active { background: var(--bg-sunken); font-weight: 700; }

.dg-colmenu-item-mandatory { opacity: .6; cursor: not-allowed; }
.dg-colmenu-item-mandatory input { cursor: not-allowed; }

/* Colonne Actions : jamais démesurée, que ce soit par défaut ou après un
   redimensionnement manuel (borne haute appliquée aussi côté JS). */
table.datatable th.col-actions, table.datatable td.col-actions { max-width: 220px; }

/* ---------------- Card fallback for narrow screens ---------------- */
@media (max-width: 720px) {
  table.datatable.responsive-cards, table.datatable.responsive-cards thead,
  table.datatable.responsive-cards tbody, table.datatable.responsive-cards tr { display: block; width: 100%; }
  table.datatable.responsive-cards thead { position: absolute; left: -9999px; top: -9999px; }
  table.datatable.responsive-cards tbody tr {
    border-bottom: 8px solid var(--bg-sunken); padding: var(--sp-2) 0;
  }
  table.datatable.responsive-cards tbody tr:last-child { border-bottom: 0; }
  table.datatable.responsive-cards td {
    display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3);
    padding: 7px var(--sp-4); border-bottom: 0; text-align: right;
  }
  table.datatable.responsive-cards td::before {
    content: attr(data-label); flex: none; text-align: left; padding-top: 1px;
    color: var(--text-faint); font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  }
  table.datatable.responsive-cards td.row-actions-cell { justify-content: flex-end; }
  table.datatable.responsive-cards td.row-actions-cell::before { display: none; }
}

/* ---------------- Tabs (Sociétés / Codes acteurs) ---------------- */
.admin-tabs { display: flex; gap: var(--sp-1); margin-bottom: var(--sp-4); border-bottom: 1px solid var(--border); }
.admin-tab {
  padding: var(--sp-2) var(--sp-4); border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: transparent; color: var(--text-muted); font: inherit; font-size: var(--fs-sm); font-weight: 700; cursor: pointer;
}
.admin-tab:hover { color: var(--text); }
.admin-tab.is-active { color: var(--c-brand-600); border-bottom-color: var(--c-brand-600); }
.admin-tab-panel[hidden] { display: none; }

/* ---------------- Expandable row detail (Activité) ---------------- */
.detail-row summary { cursor: pointer; list-style: none; }
.detail-row summary::-webkit-details-marker { display: none; }
.detail-row summary .cell-primary-link::after { content: "›"; display: inline-block; margin-left: 5px; transition: transform .15s var(--ease); }
.detail-row[open] summary .cell-primary-link::after { transform: rotate(90deg); }
.detail-row-body { padding: var(--sp-3) var(--sp-4); background: var(--bg-sunken); border-radius: var(--r-sm); margin: 6px 0 10px; font-size: var(--fs-xs); }
.detail-row-body dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px var(--sp-4); margin: 0; }
.detail-row-body dt { color: var(--text-faint); }
.detail-row-body dd { margin: 0; color: var(--text); }

/* ---------------- Inline reason form (reject with note) ---------------- */
.inline-reason-form { display: none; margin-top: var(--sp-3); padding: var(--sp-3); border: 1px dashed var(--border-strong); border-radius: var(--r-md); background: var(--bg-sunken); }
.inline-reason-form.is-open { display: block; }
.inline-reason-form textarea { width: 100%; min-height: 64px; resize: vertical; font: inherit; font-size: var(--fs-sm); }
.inline-reason-form .btn-row { margin-top: var(--sp-2); }

/* ---------------- Ranking bars (Statistiques, tableau de bord) ---------------- */
.rank-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.rank-row { display: grid; grid-template-columns: minmax(90px, 40%) 1fr auto; align-items: center; gap: var(--sp-3); }
.rank-label { font-size: var(--fs-xs); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-track { height: 8px; border-radius: var(--r-full); background: var(--bg-sunken); overflow: hidden; }
.rank-fill { height: 100%; border-radius: var(--r-full); background: var(--c-brand-500); }
.rank-value { flex: none; font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-muted); min-width: 2ch; text-align: right; }
.rank-empty { color: var(--text-faint); font-size: var(--fs-xs); padding: var(--sp-2) 0; }

/* ---------------- Daily evolution chart (hand-rolled SVG, no client JS) ---------------- */
.trend-chart { display: block; width: 100%; height: 96px; overflow: visible; }
.trend-chart rect { fill: var(--c-brand-300); }
.trend-chart rect.is-today { fill: var(--c-brand-600); }
.trend-chart rect.is-success { fill: var(--success); }
.trend-chart rect.is-failure { fill: var(--danger); }
.trend-chart-caption { display: flex; justify-content: space-between; margin-top: var(--sp-1); font-size: var(--fs-2xs); color: var(--text-faint); }
.trend-chart-legend { display: flex; gap: var(--sp-4); margin-top: var(--sp-2); font-size: var(--fs-2xs); color: var(--text-muted); }
.trend-chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.trend-chart-legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.trend-chart-legend i.is-success { background: var(--success); }

/* Regroupements "Niveau" de la page Statistiques (Résumé / Tendances /
   Performance) : plus voyants que les h3 de section qu'ils chapeautent
   (accent de marque + bordure haute), pour qu'un balayage rapide de la
   page distingue immédiatement les 3 zones sans lire chaque titre. Le
   premier groupe (juste après la toolbar) n'a pas besoin de la bordure
   de séparation -- rien au-dessus dont le séparer. */
.genstats-level-heading {
  margin: var(--sp-6) 0 var(--sp-3); padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; color: var(--c-brand-600);
}
.genstats-level-heading:first-of-type { margin-top: 0; padding-top: 0; border-top: none; }
.trend-chart-legend i.is-failure { background: var(--danger); }

/* ---------------- Data-availability notes (honnêteté sur les données) ---------------- */
.data-unavailable-note {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm); background: var(--c-warn-bg); color: var(--c-warn); font-size: var(--fs-2xs); line-height: 1.5;
}
.data-unavailable-note svg { flex: none; margin-top: 1px; }

/* ---------------- Error state (distinct from the existing empty state) ---------------- */
.app-error-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-7); text-align: center; color: var(--c-danger);
  border: 1px solid var(--c-danger-line); border-radius: var(--r-lg); background: var(--c-danger-bg);
}
.app-error-state strong { font-size: var(--fs-sm); }
.app-error-state span { max-width: 52ch; font-size: var(--fs-xs); color: color-mix(in srgb, var(--c-danger) 82%, var(--text)); }

/* ---------------- Fiche société / fiche utilisateur ---------------- */
/* auto-fit crée AUTANT de colonnes que la largeur le permet : sur un grand
   écran, un panneau seul (ex. « Coordonnées » d'un message) se retrouvait
   comprimé à ~260 px avec un vide immense à sa droite, ce qui écrasait son
   contenu. Deux colonnes au maximum : les panneaux respirent, et les lignes
   de texte restent d'une longueur lisible. */
.detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--sp-5); }
@media (min-width: 1000px) {
  .detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.detail-panel { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); padding: var(--sp-5); }
.detail-panel-wide { grid-column: 1 / -1; }
.detail-panel h2 { margin: 0 0 var(--sp-3); font-size: var(--fs-h3); }
/* Bloc « Réponse apportée » de la fiche message. Les champs sont enveloppés
   un par un : posés à plat dans le conteneur flex, les <label> seraient
   devenus des éléments flexibles À CÔTÉ de leur champ au lieu d'être
   au-dessus. */
.reply-recorded { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.reply-note {
  margin: var(--sp-3) 0 0; padding: var(--sp-3);
  border-left: 3px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-sunken); font-size: var(--fs-sm); line-height: 1.55;
  /* La précision porte le plus souvent la réponse collée telle quelle : sans
     pre-wrap, HTML fond ses paragraphes en un pavé continu et le lecteur ne
     retrouve plus où commence chaque point. `anywhere` complète pour les
     longues URL de suivi, qui débordaient sinon du panneau. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.reply-hint { margin: var(--sp-4) 0 var(--sp-3); font-size: var(--fs-xs); line-height: 1.55; }
.reply-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); }
.reply-field { display: flex; flex-direction: column; min-width: 0; }
.reply-field label { margin-bottom: 4px; color: var(--text-muted); font-size: var(--fs-xs); font-weight: 650; }
.reply-field-channel { flex: 0 0 auto; }
.reply-field-channel select { width: auto; min-width: 168px; }
/* Le champ libre occupe désormais SA PROPRE rangée, sous le canal. Partager
   la ligne avec le <select> convenait à un champ d'une ligne ; une zone de
   huit lignes posée à côté aurait étiré la rangée entière à sa hauteur et
   laissé le canal flotter au milieu du vide. Le bouton passe derrière elle,
   ce qui est aussi le bon ordre de lecture : canal, texte, enregistrer. */
.reply-field-note { flex: 1 1 100%; }
.reply-field-note textarea {
  min-height: 168px; resize: vertical; line-height: 1.55;
  /* font: inherit — sans quoi le navigateur impose sa police à chasse fixe,
     et la réponse relue ne ressemble plus au courriel qu'on a envoyé. */
  font: inherit; font-size: var(--fs-sm);
}
/* fit-content(45%) et non max-content : max-content laissait le libellé le
   plus long (« Langue du site lors de l'envoi ») s'emparer de presque toute
   la carte, ne laissant à la valeur qu'une colonne de quelques dizaines de
   pixels — une adresse e-mail s'y cassait alors CARACTÈRE PAR CARACTÈRE.
   fit-content plafonne la colonne des libellés sans l'élargir inutilement
   quand ils sont courts. */
.detail-kv { display: grid; grid-template-columns: fit-content(45%) minmax(0, 1fr); gap: 6px var(--sp-4); font-size: var(--fs-sm); margin: 0; }
.detail-kv dt { color: var(--text-muted); }
/* minmax(0, 1fr) ci-dessus permet à la colonne de rétrécir (les grilles
   ignorent sinon leur propre 1fr face à un mot insécable comme une longue
   adresse e-mail — min-width:auto par défaut sur un item de grille) ; ici
   overflow-wrap coupe le mot au besoin plutôt que de laisser le panneau
   déborder de son conteneur (bug réel constaté sur la fiche d'un message
   avec une adresse e-mail longue). */
.detail-kv dd { margin: 0; color: var(--text); overflow-wrap: anywhere; word-break: break-word; min-width: 0; }
.detail-kv dd a { overflow-wrap: anywhere; word-break: break-word; }
.module-pill-row { display: flex; flex-wrap: wrap; gap: 6px; }
.module-pill { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: var(--r-full); background: var(--c-brand-50); color: var(--c-brand-700); font-size: var(--fs-2xs); font-weight: 700; }
:root[data-theme="dark"] .module-pill { color: var(--c-brand-600); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .module-pill { color: var(--c-brand-600); } }

@media (max-width: 900px) {
  .admin-toolbar-actions { margin-left: 0; width: 100%; }
}

/* ---------------- Admin navigation: compact sidebar on desktop ----------------
   7 real destinations plus the shared header furniture (logo, badge, theme,
   language, account) genuinely do not fit as readable horizontal tabs at
   common desktop widths (measured: ~430px available for 7 links at 1280px).
   The brief explicitly allows a sidebar as the alternative when the item
   count doesn't suit a horizontal bar — used here instead of shrinking labels
   into illegibility or leaving a horizontally-scrolling top bar as the norm. */
/* ---------------- Mode compact Administration ----------------
   Variables de densité communes à tous les tableaux/contrôles admin —
   consolidation SaaS : réduction d'environ 20% des paddings/hauteurs par
   rapport aux valeurs d'origine (cellule 10/14px -> 8/11px, contrôle
   38-43px -> 32px, bloc 46px -> 36px), sans zoom ni transform:scale —
   uniquement de vraies règles CSS, pour que le texte reste net et
   sélectionnable et que la mise en page reste correcte à 200% de zoom
   navigateur. Un seul jeu de variables, un seul endroit à ajuster. */
.admin-shell {
  --a-cell-py: 8px;   --a-cell-px: 11px;   /* cellules de tableau */
  --a-control-h: 32px;                     /* champs, selects, boutons compacts */
  --a-btn-py: 5px;    --a-btn-px: 12px;    /* boutons standard */
  --a-block-gap: var(--sp-6);              /* était --sp-8 */
  --a-panel-pad: var(--sp-4);              /* était --sp-5 */
}
.admin-shell table.datatable th, .admin-shell table.datatable td { padding: var(--a-cell-py) var(--a-cell-px); }
.admin-shell .admin-toolbar-filters select, .admin-shell .admin-toolbar-filters input:is([type="date"],[type="datetime-local"]),
.admin-shell .admin-toolbar-search input { min-height: var(--a-control-h); }
.admin-shell .stat-card { padding: 8px var(--sp-3); }
.admin-shell .detail-panel { padding: var(--a-panel-pad); }
.admin-shell .admin-block { margin-bottom: var(--a-block-gap); }
/* .btn (pas seulement .btn-sm) : les boutons d'action des pages de détail
   (ex. Répondre par e-mail/Prendre en charge/Archiver/Supprimer sur
   admin_message_detail.html) utilisent la classe .btn nue, sans .btn-sm --
   ils échappaient donc à la densité admin ci-dessus et ressortaient
   nettement plus grands (padding 11px/22px, 0.92rem) que le reste de
   l'interface admin (déjà réduite à --a-btn-py/px, 32px de contrôle). Même
   variables, même endroit à ajuster : un .btn dans .admin-shell se comporte
   donc désormais comme un .btn-sm partout, y compris hors DataGrid. */
.admin-shell .btn { padding: var(--a-btn-py) var(--a-btn-px); min-height: var(--a-control-h); font-size: 0.8rem; box-sizing: border-box; }
.admin-shell .row-action-trigger { width: 26px; height: 26px; }
.admin-shell .admin-tab { padding: 6px var(--sp-3); }
.admin-shell .admin-toolbar { padding: var(--sp-2) var(--sp-3); }
.admin-shell-body { display: flex; align-items: flex-start; }
.admin-sidebar {
  flex: none; width: 208px; position: sticky; top: 57px;
  height: calc(100vh - 57px); overflow-y: auto;
  padding: var(--sp-4) var(--sp-3); border-right: 1px solid var(--border); background: var(--card);
  display: flex; flex-direction: column; gap: 2px;
}
.admin-sidebar-link {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px var(--sp-3); border-radius: var(--r-sm);
  color: var(--text-muted); text-decoration: none; font-size: var(--fs-sm); font-weight: 650;
  white-space: nowrap;
}
.admin-sidebar-link svg { flex: none; opacity: .85; }
.admin-sidebar-link:hover, .admin-sidebar-link:focus-visible { background: var(--bg-sunken); color: var(--text); }
.admin-sidebar-link:hover { outline: 0; }
.admin-sidebar-link:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: -2px; }
.admin-sidebar-link.is-active { background: var(--c-brand-50); color: var(--c-brand-700); }
:root[data-theme="dark"] .admin-sidebar-link.is-active { color: var(--c-brand-600); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .admin-sidebar-link.is-active { color: var(--c-brand-600); } }
.admin-sidebar-link.is-active svg { opacity: 1; }
.admin-sidebar-badge { margin-left: auto; }
/* flex-grow:0 (pas flex:1) : avec flex-grow actif, l'algorithme flex
   distribue tout l'espace libre à la croissance de cet unique enfant, ce
   qui remplissait le conteneur AVANT que width/max-width/margin:auto
   (.admin-shell .page) puissent centrer quoi que ce soit -- mesuré :
   margin-left/right calculés à 0px malgré margin:0 auto déclaré. flex-basis
   auto laisse la largeur/marge explicites du .page ci-dessus décider. */
.admin-shell-body > .page { flex: 0 1 auto; min-width: 0; }

/* En-tête admin : même logique de hamburger que public/connecté (voir
   _responsive_nav.html), mais piloté par le MÊME point de rupture que
   .admin-sidebar plutôt que par mesure de débordement -- il n'y a pas de
   nav en ligne dans l'en-tête admin à mesurer, la navigation vit dans la
   barre latérale permanente. Au-delà de 900px : barre latérale seule. En
   dessous : la barre latérale se cache, ce hamburger la remplace. */
#adminHeaderMenu { display: none; }
@media (max-width: 900px) {
  .admin-shell-body { flex-direction: column; }
  .admin-sidebar {
    position: static; width: 100%; height: auto;
    flex-direction: row; overflow-x: auto; overflow-y: visible;
    border-right: 0; border-bottom: 1px solid var(--border);
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent 100%);
            mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent 100%);
  }
  .admin-sidebar-link { flex: none; }
}
@media (max-width: 900px) {
  .admin-sidebar { display: none; }
  /* Le conteneur EXTERIEUR aussi : mesure a 41 px de haut pour un bouton de
     36, tout l'ecart au-dessus -- la signature exacte du defaut decrit plus
     bas. Corriger le seul conteneur interieur laissait 3 px de decalage. */
  #adminHeaderMenuRoot { display: inline-flex; align-items: center; }
  /* inline-flex et non block : un conteneur bloc traite son enfant inline-flex
     comme une boîte inline ordinaire et lui réserve l'espace d'ascendant de sa
     line-height AU-DESSUS. Mesuré le 2026-08-15 : centre du burger 4 px plus
     bas que celui du bouton de thème dans la même rangée. C'est le défaut déjà
     diagnostiqué et corrigé pour .responsive-nav-dropdown ; il subsistait ici. */
  #adminHeaderMenu { display: inline-flex; align-items: center; }
  #adminHeaderMenu .responsive-nav-toggle { display: inline-flex; }
}

/* ============================================================
   PAGES JURIDIQUES ET D'INFORMATION (mentions, confidentialite,
   cookies, conditions beta, securite).

   Ces pages sont lues, pas parcourues : la mesure de ligne est
   volontairement plus etroite que la grille marketing, et la
   hierarchie repose sur l'espacement plutot que sur des cadres.
   Aucun style nouveau n'est invente -- uniquement les jetons du
   Design System.
   ============================================================ */
.legal-page {
  max-width: 78ch;
  margin: 0 auto;
  padding: var(--sp-8) var(--gutter) var(--sp-10);
}

.legal-header h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--sp-2);
}
.legal-revision {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  margin: 0 0 var(--sp-5);
}

/* Bandeau de brouillon : doit etre impossible a confondre avec le
   corps du document, sinon un lecteur presse publierait une page
   contenant encore des champs non renseignes. */
.legal-draft-banner {
  background: var(--c-warn-bg);
  border: 1px solid var(--c-warn-line);
  border-left: 3px solid var(--c-warn);
  border-radius: var(--r-sm);
  padding: var(--sp-4);
  margin-bottom: var(--sp-6);
}
.legal-draft-banner strong { color: var(--c-warn); display: block; margin-bottom: var(--sp-2); }
.legal-draft-banner p { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); }
.legal-draft-banner p:last-child { margin-bottom: 0; }

.legal-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding-bottom: var(--sp-5);
  margin-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.legal-nav a {
  font-size: var(--fs-sm);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  text-decoration: none;
}
.legal-nav a:hover { border-color: var(--border-strong); color: var(--text); }
.legal-nav a[aria-current="page"] {
  background: var(--c-brand-50);
  border-color: var(--c-brand-300);
  color: var(--c-brand-700);
  font-weight: 600;
}

.legal-body { font-size: var(--fs-body); line-height: 1.7; }
.legal-body h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  margin: var(--sp-7) 0 var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border);
}
.legal-body h2:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
.legal-body p, .legal-body li { color: var(--text); }
.legal-body ul, .legal-body ol { padding-left: var(--sp-5); }
.legal-body li { margin-bottom: var(--sp-2); }
.legal-intro { font-size: var(--fs-lead); color: var(--text-muted); margin-bottom: var(--sp-6); }
.legal-note { font-size: var(--fs-sm); color: var(--text-muted); font-style: italic; }
.legal-steps li { margin-bottom: var(--sp-1); }

/* Valeur manquante : signalee, jamais inventee. Le contraste est
   deliberement eleve -- on doit la reperer en survolant la page. */
.legal-placeholder {
  display: inline-block;
  background: var(--c-warn-bg);
  border: 1px dashed var(--c-warn);
  border-radius: var(--r-xs);
  color: var(--c-warn);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  padding: 1px var(--sp-2);
}

.legal-warning {
  background: var(--c-danger-bg);
  border: 1px solid var(--c-danger);
  border-left: 3px solid var(--c-danger);
  border-radius: var(--r-sm);
  padding: var(--sp-4);
  margin: var(--sp-4) 0;
}
.legal-warning strong { display: block; color: var(--c-danger); margin-bottom: var(--sp-2); }
.legal-warning p { margin: 0; }

/* Les tableaux juridiques debordent facilement en mobile : le
   conteneur defile, jamais la page. */
.legal-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  margin: var(--sp-4) 0;
  display: block;
  overflow-x: auto;
}
.legal-table th, .legal-table td {
  border: 1px solid var(--border);
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  vertical-align: top;
}
.legal-table th { background: var(--bg-sunken); font-weight: 600; }

@media (max-width: 640px) {
  .legal-page { padding-top: var(--sp-6); }
  .legal-nav a { flex: 1 1 auto; text-align: center; }
}

/* Avertissement sur les donnees, affiche au-dessus de la zone d'import
   (Mandat beta section 16) et dans la documentation. Ambre et non rouge :
   c'est une regle d'usage a lire, pas une erreur a corriger -- le rouge
   serait use ici alors qu'il signale ailleurs un echec reel. */
.data-warning-inline {
  background: var(--c-warn-bg);
  border: 1px solid var(--c-warn-line);
  border-left: 3px solid var(--c-warn);
  border-radius: var(--r-sm);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4);
}
.data-warning-inline strong {
  display: block;
  color: var(--c-warn);
  font-size: var(--fs-sm);
  margin-bottom: var(--sp-1);
}
.data-warning-inline p {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm);
  line-height: 1.55;
}
.data-warning-inline a {
  font-size: var(--fs-xs);
  color: var(--c-warn);
  text-decoration: underline;
}

/* Statut beta annonce en tete du hero (Mandat beta section 13). Discret
   mais lisible : c'est une information de statut, pas un argument de
   vente -- lui donner le poids d'un bouton tromperait sur son role. */
/* Le badge est en inline-block et le kicker qui le suit en inline-flex :
   deux paragraphes successifs se retrouvaient donc COLLES sur la même
   ligne, « VERSION BÊTA GRATUITE » et « PLATEFORME DE PRÉPARATION… » se
   marchant dessus. width: fit-content garde la pastille ajustée à son
   texte tout en la rendant blocante, ce que ne ferait pas display: block
   seul (la pastille prendrait alors toute la largeur). */
.beta-badge {
  display: block;
  width: fit-content;
  background: var(--c-brand-50);
  border: 1px solid var(--c-brand-300);
  border-radius: var(--r-full);
  color: var(--c-brand-700);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: var(--sp-1) var(--sp-3);
  margin: 0 0 var(--sp-3);
}
/* Les conditions de la beta ne sont pas la suite de la promesse. Sans
   marge ni marque, ce paragraphe se lisait comme les deux dernieres lignes
   de l'accroche : six lignes de gris d'affilee, deux propos differents.
   Meme filet a gauche que les autres precisions de la page -- c'est une
   reserve qui qualifie ce qui precede, pas un argument de plus. */
.market-hero-beta {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.55;
  max-width: 52ch;
  margin: var(--sp-5) 0;
  padding-left: var(--sp-4);
  border-left: 2px solid var(--border-strong);
}

/* Formulaire de retour beta (Mandat beta section 26). Les echelles 1-5
   sont des groupes radio natifs : accessibles au clavier sans JS, et le
   label entier est cliquable. */
.feedback-card { max-width: 720px; margin: 0 auto; }
.feedback-ratings { border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--sp-4); }
.feedback-ratings legend { font-weight: 600; padding: 0 var(--sp-2); }
.feedback-rating-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.feedback-rating-row:last-child { border-bottom: 0; }
.feedback-rating-label { font-size: var(--fs-sm); }
.feedback-rating-scale { display: inline-flex; gap: var(--sp-1); }
.feedback-rating-option { position: relative; cursor: pointer; }
.feedback-rating-option input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.feedback-rating-option span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.feedback-rating-option input:checked + span {
  background: var(--c-brand-600);
  border-color: var(--c-brand-600);
  color: #fff;
  font-weight: 600;
}
.feedback-rating-option input:focus-visible + span {
  outline: 2px solid var(--c-brand-500);
  outline-offset: 2px;
}
.dg-inline-actions { display: inline-flex; gap: var(--sp-1); flex-wrap: wrap; }

/* Accueil restructure en 9 sections (Mandat beta section 11). Chaque bloc
   reutilise les jetons de la charte -- aucun style isole. Ce qui a change
   depuis : reutiliser un composant existant ne suffisait pas, parce que le
   composant reutilise partout etait la grille de cartes. Chaque section
   prend desormais la forme de son contenu ; les jetons, eux, restent les
   memes. */

/* S3 Pourquoi Excel : une colonne a cote du schema, sans coche. Le tiret
   suffit a marquer l'enumeration -- une coche affirme « verifie, compris »,
   ce qui n'a pas de sens devant six phrases qui expliquent un choix
   d'outil. Elle garde ce sens ailleurs : notes du heros, liste des
   controles. */
.home-excel-points {
  list-style: none;
  margin: var(--sp-6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-4);
  text-align: left;
}
.home-excel-points li {
  position: relative;
  padding-left: var(--sp-5);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--text-muted);
}
.home-excel-points li::before {
  content: "";
  position: absolute;
  left: 0; top: .72em;
  width: 10px; height: 1px;
  background: var(--c-brand-400);
}

/* S4 : la piste de 7 etapes. Les deux reglages precedents (flex-wrap et
   flex sur les cartes) ne s'appliquaient a rien : .workflow-track est une
   GRILLE de sept colonnes -- 1fr auto 1fr auto 1fr auto 1fr, quatre cartes
   et trois fleches. Vu a l'ecran le 2026-08-11 : les treize enfants s'y
   rangeaient a la file, si bien que le second rang commencait par une
   fleche pointant vers la droite sans rien avant elle, et que les cartes 05
   a 07 tombaient dans les colonnes « auto », desalignees de celles du
   premier rang.
   La grille est conservee -- c'est elle qui aligne. Seule la quatrieme
   fleche, celle qui tombait au retour a la ligne, est retiree : le passage
   de 04 a 05 est dit par le retour a la ligne, pas par un signe. Les trois
   dernieres etapes retombent alors d'aplomb sous les trois premieres. */
.workflow-track-7 > .workflow-arrow:nth-of-type(4) { display: none; }
.workflow-track-7 article { min-width: 168px; }

/* LES TROIS DERNIÈRES ÉTAPES SONT CENTRÉES sous les quatre premières.
   Laissées au placement automatique, elles se rangeaient à partir de la
   gauche : le rang du bas s'arrêtait aux trois quarts et laissait un vide
   d'une carte à droite, comme s'il en manquait une.
   Centrer trois éléments sous quatre demande un décalage d'UNE DEMI-CARTE,
   qu'aucune grille de quatre colonnes ne peut exprimer. La piste compte donc
   quinze colonnes égales : une carte en occupe trois, une flèche une seule.
   Le rang du bas commence deux colonnes plus loin — 2 à gauche, 2 à droite,
   exactement au milieu. Tout est placé explicitement : en automatique, un
   seul élément masqué décalerait tout le reste. */
.workflow-track-7 { grid-template-columns: repeat(15, minmax(0, 1fr)); }
.workflow-track-7 > article:nth-of-type(1) { grid-area: 1 / 1 / auto / span 3; }
.workflow-track-7 > article:nth-of-type(2) { grid-area: 1 / 5 / auto / span 3; }
.workflow-track-7 > article:nth-of-type(3) { grid-area: 1 / 9 / auto / span 3; }
.workflow-track-7 > article:nth-of-type(4) { grid-area: 1 / 13 / auto / span 3; }
.workflow-track-7 > .workflow-arrow:nth-of-type(1) { grid-area: 1 / 4; }
.workflow-track-7 > .workflow-arrow:nth-of-type(2) { grid-area: 1 / 8; }
.workflow-track-7 > .workflow-arrow:nth-of-type(3) { grid-area: 1 / 12; }
.workflow-track-7 > article:nth-of-type(5) { grid-area: 2 / 3 / auto / span 3; }
.workflow-track-7 > article:nth-of-type(6) { grid-area: 2 / 7 / auto / span 3; }
.workflow-track-7 > article:nth-of-type(7) { grid-area: 2 / 11 / auto / span 3; }
.workflow-track-7 > .workflow-arrow:nth-of-type(5) { grid-area: 2 / 6; }
.workflow-track-7 > .workflow-arrow:nth-of-type(6) { grid-area: 2 / 10; }

/* Sous 1080 px la piste retombe à deux colonnes puis à une (voir plus haut) :
   le placement explicite doit alors s'effacer, sinon il continuerait de
   réclamer quinze colonnes qui n'existent plus. */
@media (max-width: 1080px) {
  .workflow-track-7 { grid-template-columns: repeat(2, 1fr); }
  .workflow-track-7 > article, .workflow-track-7 > .workflow-arrow { grid-area: auto; }
}
@media (max-width: 720px) {
  .workflow-track-7 { grid-template-columns: 1fr; }
  .workflow-track-7 .workflow-arrow { display: none; }
}

/* S6 Benefices : les deux axes du titre de la section, pas une troisieme
   liste a coches. « Moins de ressaisie » a gauche, « plus de controle » a
   droite -- les sept gains se rangent sous l'un ou sous l'autre. L'intitule
   de colonne emprunte le vocabulaire des kickers (mono, filet sous le
   texte) : la meme annotation d'instrument, a l'echelle d'une colonne. */
.benefit-axes {
  max-width: 58rem;
  margin: var(--sp-8) auto 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-8);
  text-align: left;
}
.benefit-axis h3 {
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--c-brand-600);
}
/* sp-5 entre les gains : la plupart tiennent sur deux lignes, et un ecart
   de 16 px ne se distinguait plus assez de l'interligne interne. */
.benefit-axis ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-5); }
.benefit-axis li { font-size: var(--fs-body); line-height: 1.55; color: var(--text); }
/* 860 et non 720 : à 768 de large, deux colonnes tombent à 322 px, soit une
   quarantaine de caractères par ligne pour des phrases entières. La colonne
   unique arrive donc avant la tablette, pas après. */
@media (max-width: 860px) {
  .benefit-axes { grid-template-columns: 1fr; gap: var(--sp-6); }
}

/* Les quatre etapes de la page de recuperation. La classe existait dans le
   gabarit depuis l'origine mais N'AVAIT AUCUN STYLE : intitule et corps
   etant tous deux en ligne, ils se collaient -- « 1. Demandez vos
   donneesGenerez une demande avec le module Pull Request ». Vu le
   2026-08-11 en photographiant la page pour la procedure client, jamais
   avant : c'est une page qui demande d'etre connecte. */
.recuperation-etapes { margin: var(--sp-4) 0 0; padding-left: 1.4em; display: grid; gap: var(--sp-4); }
.recuperation-etapes li { color: var(--text-muted); line-height: 1.6; }
.recuperation-etapes strong { display: block; color: var(--text); font-size: var(--fs-body); }
.recuperation-etapes span { display: block; margin-top: var(--sp-1); font-size: var(--fs-sm); }

/* La bibliotheque de regles. La repartition par source EST l'argument : la
   source la plus fournie n'est pas le schema XSD -- que n'importe quel
   outil sait lire -- mais le comportement observe du registre, qui ne
   s'obtient qu'en soumettant pour de vrai. Des barres proportionnelles le
   montrent d'un coup d'oeil ; quatre cartes l'auraient noye.
   Le nombre en mono a la meme fonction que les numeros de la liste
   « probleme » : une annotation d'instrument, pas une decoration. */
.rule-sources {
  list-style: none;
  max-width: 54rem;
  margin: var(--sp-8) auto 0;
  padding: 0;
  display: grid;
  gap: var(--sp-5);
}
.rule-sources li {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
}
.rule-source-count {
  font-family: var(--font-mono);
  font-size: 1.45rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--c-brand-600);
  font-variant-numeric: tabular-nums;
}
.rule-source-body strong {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text);
}
/* La barre : un filet epais, pas un caisson. Elle porte une proportion, on
   ne lui demande pas d'etre lisible au pixel -- le nombre est a cote. */
.rule-source-bar {
  display: block;
  height: 3px;
  margin: var(--sp-2) 0;
  background: var(--border);
  border-radius: 2px;
  overflow: hidden;
}
.rule-source-bar i {
  display: block;
  height: 100%;
  width: var(--part, 0%);
  background: var(--c-brand-500);
  border-radius: inherit;
}
.rule-source-body small {
  display: block;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.rule-origin {
  max-width: 54rem;
  margin: var(--sp-7) auto 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-body);
  line-height: 1.65;
}
@media (max-width: 620px) {
  .rule-sources li { grid-template-columns: 2.6rem minmax(0, 1fr); gap: var(--sp-3); }
  .rule-source-count { font-size: 1.15rem; }
}

/* /controles -- l'inventaire nomme. Cinquante lignes et plus : la page doit
   se PARCOURIR, pas se lire. D'ou le sommaire des familles en tete, et des
   lignes basses au rythme regulier plutot que des cartes. L'identifiant est
   en mono et tabulaire : c'est ce que le lecteur cherche du regard quand il
   revient d'un rapport d'anomalies qui cite « R-033 ». */
.controls-lang-notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  max-width: var(--page-max);
  margin: var(--sp-6) auto calc(var(--sp-6) * -1);
  padding: var(--sp-4) var(--gutter);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.controls-lang-notice svg { flex: 0 0 auto; margin-top: 2px; }
.controls-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  max-width: 54rem;
  margin: 0 auto var(--sp-9);
}
.controls-summary a {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  text-decoration: none;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.controls-summary a:hover { border-color: var(--c-brand-300); color: var(--primary-ink); }
.controls-summary span {
  color: var(--c-brand-600);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.controls-summary strong { font-size: var(--fs-sm); font-weight: 600; }
.controls-family { max-width: 54rem; margin: 0 auto var(--sp-10); scroll-margin-top: 90px; }
.controls-family-heading { margin-bottom: var(--sp-6); }
.controls-family-heading h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text);
}
.controls-family-count {
  margin: var(--sp-1) 0 0;
  color: var(--c-brand-600);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
}
.controls-family-heading p:last-child {
  margin: var(--sp-2) 0 0;
  max-width: 62ch;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.65;
}
.controls-list { list-style: none; margin: 0; padding: 0; }
.controls-list li {
  display: grid;
  grid-template-columns: 5rem minmax(0, 1fr);
  gap: var(--sp-4);
  padding: var(--sp-4) 0;
  border-top: 1px solid var(--border);
}
.controls-list li:last-child { border-bottom: 1px solid var(--border); }
/* Cinquante et une lignes se lisent en balayant : sans reponse au survol,
   l'oeil perd sa ligne entre l'identifiant a gauche et la portee a droite.
   Le fond tres leger suit le curseur et RELIE les trois colonnes d'une meme
   regle -- c'est une aide a la lecture autant qu'un signe de vie. La marge
   negative/padding compense pour que la surbrillance deborde legerement du
   texte, sinon elle parait collee. */
.controls-list li {
  margin-inline: calc(var(--sp-3) * -1);
  padding-inline: var(--sp-3);
  border-radius: 8px;
  transition: background-color .16s var(--ease);
}
/* Teinte de MARQUE diluee, pas une couleur de surface : `--surface-alt`
   vaut #0a1116 en theme sombre (plus sombre que la carte) et #f6f8fa en
   clair -- la meme declaration aurait donc eclairci d'un cote et assombri
   de l'autre. `color-mix` part de la couleur de marque et n'en garde que
   8 % : le resultat va toujours dans le meme sens, quel que soit le theme.
   La declaration rgba au-dessus sert de repli aux navigateurs sans
   color-mix ; ils obtiennent la meme teinte, figee. */
.controls-list li:hover {
  background: rgba(75, 156, 224, .09);
  background: color-mix(in srgb, var(--c-brand-500) 9%, transparent);
}
.controls-id {
  color: var(--c-brand-600);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.controls-body strong { display: block; color: var(--text); font-weight: 600; line-height: 1.45; }
.controls-body small {
  display: block;
  margin-top: var(--sp-1);
  color: var(--text-muted);
  font-size: var(--fs-xs);
  line-height: 1.55;
}
@media (max-width: 620px) {
  .controls-list li { grid-template-columns: 4.2rem minmax(0, 1fr); gap: var(--sp-3); }
}

/* Filtre de l'inventaire des controles. N'existe qu'avec JavaScript : la
   regle de base le masque, la classe .js (posee par le script d'amorce de
   public_base) le revele. Sans script, la liste complete reste servie. */
.controls-filter { display: none; }
.js .controls-filter {
  display: grid;
  gap: var(--sp-2);
  max-width: 54rem;
  margin: 0 auto var(--sp-7);
}
.controls-filter label { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.controls-filter input {
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
  color: var(--text);
  font-size: var(--fs-body);
}
.controls-filter input:focus-visible {
  outline: 2px solid var(--c-brand-500);
  outline-offset: 2px;
}
.controls-filter-count { min-height: 1.2em; margin: 0; color: var(--text-muted); font-size: var(--fs-xs); }
.controls-filter-none {
  max-width: 54rem;
  margin: 0 auto var(--sp-7);
  padding: var(--sp-4) var(--sp-5);
  border: 1px dashed var(--border);
  border-radius: 12px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Pages d'erreur (404/403/500). Le code en tres grand et en mono : c'est
   l'information n1, et le mono l'affiche comme une donnee technique plutot
   que comme un slogan. Le reste est sobre -- la page dit ce qui s'est
   passe, offre trois sorties, et s'arrete la. */
.error-page { max-width: 46rem; margin: 0 auto; padding-block: clamp(70px, 10vw, 130px); text-align: center; }
.error-code {
  margin: 0;
  color: var(--c-brand-300);
  font-family: var(--font-mono);
  font-size: clamp(3.4rem, 9vw, 6rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: .04em;
}
.error-page h1 { margin: var(--sp-4) 0 0; font-size: clamp(1.5rem, 3vw, 2.1rem); color: var(--text); }
.error-body { max-width: 52ch; margin: var(--sp-4) auto 0; color: var(--text-muted); line-height: 1.65; }
.error-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-3); margin-top: var(--sp-7); }

/* /tarifs. Cinq paliers qui ne different QUE par le volume : les cartes
   doivent donc se comparer d'un balayage vertical, pas se lire une par une.
   D'ou l'ordre fixe -- nom, montant, volume, a qui c'est destine -- et
   l'absence de liste de fonctionnalites dans les cartes : les inclusions
   sont communes et vivent sous la grille. Repetees, elles pousseraient le
   lecteur a chercher ce qui manque en bas de gamme. Il ne manque rien. */
/* En-tete court, partage par /tarifs et /controles.
   Le hero des guides fait 560 px de haut avec un titre a 5.25 rem : il
   ouvre un article qu'on vient LIRE. Une grille tarifaire et un inventaire,
   on vient les PARCOURIR -- le meme traitement y repoussait le contenu sous
   la ligne de flottaison et laissait une demi-page vide a droite, faute de
   visuel a y mettre. Une seule colonne, un titre au calibre d'un titre de
   section, et le contenu commence tout de suite. */
.page-hero-compact { overflow: hidden; }
.page-hero-inner {
  position: relative;
  z-index: 1;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: clamp(44px, 4.4vw, 70px) var(--gutter);
}
.guide-hero.page-hero-compact h1 {
  max-width: 24ch;
  margin: 0;
  color: #fff;
  font-size: clamp(2rem, 3.2vw, 3.05rem);
  line-height: 1.08;
  letter-spacing: -.035em;
}
.guide-hero.page-hero-compact .page-hero-inner > div > p:not(.market-kicker) {
  max-width: 68ch;
  margin: var(--sp-5) 0 0;
  color: rgba(255, 255, 255, .74);
  font-size: var(--fs-lead);
  line-height: 1.65;
}
/* L'orbe restante est repositionnee : calee sur un hero de 560 px, elle
   sortait par le haut d'un hero deux fois moins haut et n'etait plus qu'un
   arc coupe dans l'angle. */
.guide-hero.page-hero-compact .guide-hero-orb-one { width: 340px; height: 340px; right: -110px; top: -120px; }
.pricing-beta-notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  max-width: 72ch;
  margin: var(--sp-8) auto calc(var(--sp-8) * -1);
  padding: var(--sp-4) var(--gutter);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.pricing-beta-notice svg { flex: 0 0 auto; margin-top: 2px; }
/* Cinq colonnes annoncees, pas un `auto-fit`. Avec auto-fit, cinq cartes se
   posaient en 4 + 1 sur une page de 1280 px : le palier le plus large se
   retrouvait seul sur une deuxieme ligne, ce qui le fait lire comme une
   offre a part au lieu du haut de la meme grille. Le nombre de colonnes est
   donc decide par palier de largeur, jamais deduit. */
.pricing-section, .controls-section { padding-top: var(--sp-8); }
.pricing-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  /* Six lignes par carte, portees par la grille parente pour que les
     cartes s'y alignent (voir .pricing-card, subgrid). La cinquieme
     (description) prend le reste : c'est elle qui absorbe la difference
     de longueur des textes, pour que le lien reste en bas partout. */
  grid-auto-rows: auto auto auto auto 1fr auto;
  gap: var(--sp-4);
  margin: 0 0 var(--sp-10);
  padding: 0;
}
@media (max-width: 1180px) { .pricing-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 820px) { .pricing-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .pricing-grid { grid-template-columns: minmax(0, 1fr); }
  /* En colonne unique, chaque carte est seule sur sa rangee : il n'y a plus
     rien a aligner, et le badge vide ne reserverait sa ligne que pour y
     laisser un blanc. */
  .pricing-card:not(.is-featured) .pricing-badge { display: none; }
}
/* Les cinq cartes partagent leurs LIGNES, pas seulement leurs colonnes.
   En flex, chaque carte calculait sa hauteur de ligne dans son coin : le
   badge de la carte mise en avant decalait son prix de 28 px, et une
   description sur deux lignes decalait tout ce qui suivait. Dans une grille
   de comparaison, c'est exactement ce qui empeche de comparer -- l'oeil
   balaie horizontalement et ne trouve pas les prix sur la meme ligne.
   `subgrid` fait porter les six lignes (badge, nom, montant, volume,
   description, lien) par la grille PARENTE : toutes les cartes d'une meme
   rangee s'alignent, quelle que soit la longueur des textes.
   Repli : sans subgrid, le navigateur ignore la valeur et retombe sur des
   lignes automatiques -- la carte reste lisible, seul l'alignement fin est
   perdu. Aucun contenu ne disparait. */
.pricing-card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 6;
  align-content: start;
  gap: var(--sp-2);
  padding: var(--sp-6);
  border: 1px solid var(--border);
  border-radius: 20px;
  background: var(--card);
  /* L'etat au survol : la carte se souleve legerement et sa bordure prend
     la couleur de marque. Sans cela, cinq cartes cliquables ne repondaient
     a rien sous le curseur -- seule la fleche du lien bougeait, ce qui se
     remarque a peine. La transition est bornee aux proprietes concernees
     (jamais `all`, qui animerait aussi la mise en page). */
  /* `translate` et non `transform` : l'animation d'apparition
     (`[data-reveal-group] > *`, plus haut dans ce fichier) pilote deja
     `transform` et l'ecrasait -- le survol ne produisait donc AUCUN
     mouvement, constate le 2026-08-16. `translate` est une propriete
     independante : elle se COMPOSE avec le transform de l'apparition au
     lieu de le remplacer, et les deux effets cohabitent. */
}
/* La regle d'apparition `.js [data-reveal-group] > *` porte deux classes
   (specificite 0,2,0) et gagnait sur `.pricing-card` (0,1,0) : la
   transition de la carte etait remplacee par celle de l'apparition, et le
   survol changeait donc bordure et ombre D'UN COUP, sans adoucissement.
   Ce selecteur remonte a 0,2,0 et vient plus loin dans le fichier : il
   reprend la main. Il REDECLARE opacity/transform pour ne pas priver les
   cartes de leur apparition, et ajoute les trois proprietes du survol.
   Effet de bord assume : l'echelonnement (transition-delay par nth-child)
   ne s'applique plus a ces cartes -- elles apparaissent ensemble. Sur une
   grille de comparaison c'est meme preferable : echelonner cinq prix fait
   attendre le dernier sans rien apporter. */
.pricing-grid > .pricing-card {
  transition: opacity .5s var(--ease), transform .5s var(--ease),
              translate .22s var(--ease), border-color .22s var(--ease),
              box-shadow .22s var(--ease);
  transition-delay: 0s;
}
/* `:focus-within` a cote de `:hover` : un utilisateur au clavier qui atteint
   le lien de la carte voit le meme etat que celui a la souris. */
.pricing-card:hover,
.pricing-card:focus-within {
  translate: 0 -4px;
  border-color: var(--c-brand-500);
  box-shadow: 0 22px 44px -28px rgba(8, 31, 48, .55);
}
/* Le mouvement est un agrement, pas une information : qui demande moins
   d'animations garde le changement de bordure et perd le soulevement. */
@media (prefers-reduced-motion: reduce) {
  .pricing-grid > .pricing-card { transition: border-color .22s var(--ease); }
  .pricing-card:hover, .pricing-card:focus-within { translate: none; }
}
/* Le badge occupe sa ligne sur TOUTES les cartes ; il n'est visible que sur
   celle mise en avant. C'est ce qui garde les six lignes identiques. */
.pricing-card:not(.is-featured) .pricing-badge { visibility: hidden; }
/* Le palier mis en avant se distingue par sa BORDURE, pas par un fond
   colore : cinq cartes dont une pleine casse la lecture en colonne, et le
   prospect compare alors des couleurs au lieu de comparer des volumes. */
.pricing-card.is-featured { border-color: var(--c-brand-400); box-shadow: 0 18px 40px -30px rgba(8, 31, 48, .55); }
.pricing-card.is-featured:hover, .pricing-card.is-featured:focus-within { border-color: var(--c-brand-600); }
.pricing-badge {
  margin: 0;
  color: var(--c-brand-600);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .06em;
}
.pricing-card h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text);
}
.pricing-amount { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0 0; }
.pricing-amount strong {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.6vw, 2.1rem);
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.pricing-amount small { color: var(--text-muted); font-size: var(--fs-xs); }
.pricing-volume {
  margin: 0;
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
  color: var(--primary-ink);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.pricing-desc { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.6; }
.pricing-card > a {
  padding-top: var(--sp-4);
  color: var(--primary-ink);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-decoration: none;
}
.pricing-card > a span { display: inline-block; transition: transform .2s var(--ease); }
.pricing-card > a:hover span { transform: translateX(5px); }
.pricing-included, .pricing-notes { max-width: 72ch; margin: 0 auto var(--sp-10); }
.pricing-included h2, .pricing-notes h2 { margin: 0 0 var(--sp-5); }
.pricing-included ul, .pricing-notes ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-4); }
.pricing-included li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-3);
  color: var(--text);
  line-height: 1.6;
}
.pricing-included li svg { margin-top: 3px; color: var(--c-brand-500); }
.pricing-included li a { display: block; margin-top: var(--sp-1); color: var(--primary-ink); font-size: var(--fs-sm); font-weight: 700; text-decoration: none; }
.pricing-notes li {
  padding-left: var(--sp-5);
  border-left: 2px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.65;
}
.pricing-aside { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); max-width: 72ch; margin: 0 auto var(--sp-10); }
.pricing-aside article { padding: var(--sp-6); border: 1px solid var(--border); border-radius: 20px; background: var(--surface-alt, var(--card)); }
.pricing-aside h3 { margin: 0 0 var(--sp-3); font-size: var(--fs-h3); color: var(--text); }
.pricing-aside p { margin: 0 0 var(--sp-4); color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.65; }
.pricing-aside a { color: var(--primary-ink); font-size: var(--fs-sm); font-weight: 700; text-decoration: none; }
@media (max-width: 760px) {
  .pricing-aside { grid-template-columns: minmax(0, 1fr); }
}

/* Le chemin inverse. Deux colonnes : l'argument a gauche, la marche a
   suivre a droite. Les etapes n'ont ni boite ni icone -- la piste des sept
   etapes en a deja, et ces quatre-la sont une procedure, pas un parcours
   produit. Un filet vertical les relie, c'est tout. */
.return-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}
.return-copy h2 { margin: var(--sp-2) 0 0; }
.return-lead {
  margin: var(--sp-4) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-lead);
  line-height: 1.7;
  max-width: 46ch;
}
/* La note porte le fait le plus concret de la page : le rejet evite. Filet
   a gauche plutot qu'aplat colore -- c'est une precision d'expert, pas une
   alerte. */
.return-note {
  margin: var(--sp-6) 0 0;
  padding-left: var(--sp-5);
  border-left: 2px solid var(--c-brand-400);
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.7;
  max-width: 48ch;
}
.return-steps {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--sp-5);
  border-left: 1px solid var(--border);
  display: grid;
  gap: var(--sp-6);
}
.return-steps li {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: baseline;
}
.return-step-index {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--c-brand-600);
}
.return-steps strong {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text);
  line-height: 1.35;
}
.return-steps small {
  display: block;
  margin-top: var(--sp-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
@media (max-width: 900px) {
  .return-inner { grid-template-columns: 1fr; gap: var(--sp-8); }
}

/* S7 Public cible : une bande de termes entre deux filets. Huit intitules
   de deux mots n'ont pas besoin d'une boite chacun -- on ne les lit pas,
   on s'y reconnait. Les colonnes egales d'une grille donnaient a
   « Qualite » le meme poids qu'a une carte de contenu. */
.audience-flow {
  list-style: none;
  max-width: 58rem;
  margin: var(--sp-8) auto 0;
  padding: var(--sp-5) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* 46 px entre les termes : a 28 px, « Qualite » et « Specialistes UDI /
     EUDAMED » se lisaient un instant comme un seul intitule. C'est le blanc
     qui separe ici, pas un signe de ponctuation -- une puce ou un point
     median tomberait en debut de ligne au retour a la ligne. */
  gap: var(--sp-3) var(--sp-8);
}
.audience-flow li {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text);
}
/* Sur telephone, presque chaque terme occupe deja sa ligne : les 46 px
   d'ecart horizontal n'y separent plus rien et ne font qu'allonger. */
@media (max-width: 620px) {
  .audience-flow { gap: var(--sp-2) var(--sp-5); }
}

/* Lien d evitement (WCAG 2.4.1) : premier element focusable, visible
   UNIQUEMENT au focus clavier -- il ne change rien a la souris. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  background: var(--c-brand-700);
  color: #fff;
  padding: 10px 18px;
  border-radius: 0 0 8px 0;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus-visible { left: 0; }

/* Palier etroit du header public : le libelle FR du CTA beta (le plus long
   des 4 langues) faisait deborder la rangee d actions de 5 px vers 640 px
   (constate par la matrice visuelle, zoom 200 pourcent simule). Le CTA se
   compacte au lieu de pousser la page. */
@media (max-width: 700px) {
  .marketing-cta { padding-left: 12px; padding-right: 12px; font-size: 0.82rem; }
  .marketing-actions { gap: 6px; }
}

/* Piege classique min-width:auto des elements de grille : un enfant de
   grid ne retrecit jamais sous sa largeur de contenu sans min-width:0,
   donc ses conteneurs a defilement interne (overflow-x:auto) ne s
   enclenchent jamais -- la piste des modules et le panneau XML etiraient
   le DOCUMENT a ~630 px sur un ecran de 375 px (constate en ES/EN par la
   matrice visuelle), ce qui faussait au passage la mesure de repli du
   menu (l en-tete, etire avec le document, ne se voyait plus deborder). */
.module-explorer-nav { min-width: 0; }
.transform-pane { min-width: 0; }
.module-explorer { min-width: 0; }

/* Le pied de page connecte a recu 3 liens de plus (retour beta + pages
   juridiques) : la rangee doit pouvoir passer a la ligne sur mobile au
   lieu d etirer la page (constate a 375 px par la matrice visuelle). */
.app-footer-inner { flex-wrap: wrap; row-gap: 6px; }
.app-footer-links { flex-wrap: wrap; row-gap: 4px; min-width: 0; }

/* Diagnostic des réponses EUDAMED : lecture seule, rattaché au job. */
.eudamed-response-card { margin-top: var(--sp-5); }
.response-upload-form { display: grid; gap: var(--sp-2); max-width: 720px; }
.response-upload-form input[type="file"] { border: 1px solid var(--border); border-radius: var(--r-sm); padding: 12px; background: var(--card); }
.response-upload-form .btn { justify-self: start; }
.response-integrity-warning { color: #9a6700; background: #fff8c5; border-left: 4px solid #d4a72c; padding: 10px 12px; }
.response-summary-grid { display: grid; grid-template-columns: repeat(4, minmax(135px, 1fr)); gap: var(--sp-3); margin: var(--sp-5) 0; }
.response-summary-grid > div { border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--sp-3); background: var(--surface-soft, #f6f9fc); }
.response-summary-grid strong { display: block; color: var(--c-brand-700); font-size: 1.75rem; }
.response-summary-grid span { color: var(--muted); font-size: var(--fs-sm); }
.result-alert-warning { background: #fff8e6; border-color: #f2c46d; }
.response-table-wrap { overflow-x: auto; margin-top: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-sm); }
.response-table { width: 100%; min-width: 980px; border-collapse: collapse; }
.response-table th, .response-table td { padding: 12px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--border); }
.response-table th { color: var(--c-brand-800); background: var(--surface-soft, #eef5fa); font-size: var(--fs-sm); }
.response-table td small { display: block; margin-top: 4px; color: var(--muted); }
.response-table tr.is-success td { background: #ecfdf3; }
.response-table tr.is-correctable td { background: #fff8e6; }
.response-table tr.is-review td { background: #fff1f0; }
.response-downloads { margin-top: var(--sp-4); }
@media (max-width: 720px) { .response-summary-grid { grid-template-columns: 1fr 1fr; } }
.correction-choice-group { display: grid; gap: 6px; min-width: 230px; }
.correction-choice-group label { color: var(--muted); font-size: var(--fs-xs); font-weight: 600; }
.correction-choice-group > span { color: var(--muted); font-size: var(--fs-xs); text-align: center; text-transform: uppercase; }
.xsd-company-table .btn-outline { display:inline-flex; align-items:center; justify-content:center; min-width:104px; min-height:38px; border-color:color-mix(in srgb,var(--primary) 70%,var(--border)); background:var(--primary-tint); color:#58b8f4; font-weight:800; }
.xsd-company-table .btn-outline:hover { border-color:var(--primary); background:var(--primary); color:#fff; }
.xsd-assignment-list { display:grid; gap:16px; padding:20px; }
.xsd-global-assignment { padding:20px; border-bottom:1px solid var(--border); background:linear-gradient(135deg,var(--primary-tint),var(--surface)); }
.xsd-global-heading { display:flex; align-items:center; gap:11px; margin-bottom:14px; }
.xsd-global-heading > span { display:grid; place-items:center; width:40px; height:40px; border-radius:10px; color:#fff; background:var(--primary); }
.xsd-global-heading strong,.xsd-global-heading small { display:block; }
.xsd-global-heading small { margin-top:3px; color:var(--text-muted); }
.xsd-global-card { border-color:color-mix(in srgb,var(--primary) 38%,var(--border)); }
.xsd-assignment-company { border:1px solid var(--border); border-radius:14px; background:var(--surface-alt); overflow:hidden; }
.xsd-assignment-company > header { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:17px 20px; border-bottom:1px solid var(--border); }
.xsd-assignment-company > header > div { display:flex; align-items:center; gap:10px; }
.xsd-assignment-company h3 { margin:0; font-size:18px; }
.xsd-assignment-company > header small { color:var(--text-muted); }
.xsd-audience-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; padding:16px; }
.xsd-audience-card { display:grid; gap:14px; padding:17px; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
.xsd-audience-title { display:flex; align-items:center; gap:10px; }
.xsd-audience-title > span { display:grid; place-items:center; width:34px; height:34px; border-radius:9px; background:var(--primary-tint); color:var(--primary-ink); }
.xsd-audience-title strong,.xsd-audience-title small { display:block; }
.xsd-audience-title small { margin-top:2px; color:var(--text-muted); font-size:11px; }
.xsd-audience-options { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
.xsd-audience-options label { display:flex; align-items:center; gap:9px; padding:10px; border:1px solid var(--border); border-radius:9px; cursor:pointer; }
.xsd-audience-options label:has(input:checked) { border-color:color-mix(in srgb,var(--primary) 65%,var(--border)); background:var(--primary-tint); }
.xsd-audience-options input { width:auto; accent-color:var(--primary); }
.xsd-audience-options b,.xsd-audience-options small { display:block; }
.xsd-audience-options small { margin-top:2px; color:var(--text-muted); font-size:10px; }
.xsd-default-select { display:grid; gap:6px; color:var(--text-muted); font-size:12px; font-weight:700; }
.xsd-default-select select { width:100%; }
.xsd-audience-card > .btn { justify-self:start; }
.template-version-picker,.xsd-profile-field { display:grid; gap:7px; margin:16px 0 12px; padding:14px; border:1px solid color-mix(in srgb,var(--primary) 38%,var(--border)); border-radius:11px; background:var(--primary-tint); }
.xsd-profile-field > label { font-size:12px; font-weight:800; color:var(--text); }
.xsd-profile-field .field-hint { margin:0; }
.doc-version-section { margin:0 0 22px; padding:20px; border:1px solid var(--border); border-radius:14px; background:var(--surface); }
.doc-version-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; margin-top:14px; }
.doc-version-card { display:grid; gap:12px; padding:17px; border:1px solid var(--border); border-left:4px solid #18a679; border-radius:11px; background:var(--surface-alt); }
.doc-version-card.is-playground { border-left-color:#4098df; }
.doc-version-card h3 { margin:10px 0 3px; font-size:20px; }
.doc-version-card p { margin:0; color:var(--text-muted); }
.doc-version-card ul { margin:0; padding-left:19px; color:var(--text-muted); font-size:12px; line-height:1.7; }
.doc-version-changes { margin:0 0 26px; padding:22px; border:1px solid var(--border); border-radius:14px; background:var(--surface); scroll-margin-top:88px; }
.doc-version-warning { display:flex; align-items:flex-start; gap:10px; margin:14px 0; padding:12px 14px; border:1px solid rgba(224,153,44,.35); border-radius:10px; color:#d99a37; background:rgba(224,153,44,.08); }
.doc-version-warning div { display:grid; gap:3px; }
.doc-version-warning span { color:var(--text-muted); font-size:12px; line-height:1.5; }
.doc-change-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
.doc-change-grid article { display:flex; align-items:flex-start; gap:11px; padding:14px; border:1px solid var(--border); border-radius:10px; background:var(--surface-alt); }
.doc-change-number { display:inline-flex; align-items:center; justify-content:center; flex:0 0 30px; height:30px; border-radius:8px; color:var(--accent); background:rgba(51,145,225,.12); font-size:11px; font-weight:800; }
.doc-change-grid h3 { margin:1px 0 5px; color:var(--text); font-size:14px; }
.doc-change-grid p { margin:0; color:var(--text-muted); font-size:11px; line-height:1.55; }
.doc-change-grid code { padding:1px 4px; border-radius:4px; color:var(--text); background:var(--bg-sunken); font-size:10px; }
.doc-change-scope { margin:14px 0 0; color:var(--text-muted); font-size:11px; line-height:1.55; }
@media(max-width:760px){.doc-change-grid{grid-template-columns:1fr}}
@media(max-width:720px){.doc-version-grid{grid-template-columns:1fr}}
@media(max-width:900px){.xsd-audience-grid{grid-template-columns:1fr}.xsd-assignment-company>header{align-items:flex-start;flex-direction:column}}

/* Administration — annuaire utilisateurs */
.admin-users-page { display:grid; gap:20px; }
/* Un enfant de grille vaut « min-width: auto » par defaut : il refuse de
   descendre sous la largeur intrinseque de son contenu, et deborde au lieu de
   se comprimer. Mesure a 390 px : les trois sections sortaient de la page de
   862 px, alors meme que la bandeau a bien son point de rupture a 760 px.
   C'est la premiere cause de defilement horizontal parasite sur mobile. */
.admin-users-page > * { min-width: 0; }
.admin-users-hero { position:relative; overflow:hidden; display:flex; align-items:center; justify-content:space-between; gap:24px; padding:26px 28px; border:1px solid var(--border); border-radius:16px; background:linear-gradient(120deg,var(--primary-tint) 0%,var(--surface) 68%); box-shadow:var(--sh-sm); }
.admin-users-hero::after { content:""; position:absolute; right:-60px; top:-100px; width:280px; height:280px; border:42px solid color-mix(in srgb,var(--primary) 7%,transparent); border-radius:50%; pointer-events:none; }
.admin-users-hero-main { position:relative; z-index:1; display:flex; align-items:flex-start; gap:15px; }
.admin-users-hero-icon { display:grid; place-items:center; flex:0 0 48px; height:48px; border-radius:13px; color:#fff; background:linear-gradient(145deg,var(--primary),#3f9ae5); box-shadow:0 8px 24px rgba(19,116,190,.22); }
.admin-users-hero h1 { margin:3px 0 6px; font-size:30px; letter-spacing:-.02em; }
.admin-users-hero p:not(.section-kicker) { max-width:720px; margin:0; color:var(--text-muted); line-height:1.55; }
.admin-users-create-btn { position:relative; z-index:1; flex:0 0 auto; }
.admin-shell .admin-users-stats.kpi-card-grid { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:8px; margin:0; }
.admin-shell .admin-users-stats.kpi-card-grid .kpi-card { display:grid; grid-template-columns:auto 1fr; align-items:center; justify-content:stretch; gap:9px; min-height:78px; padding:11px 12px; border:1px solid var(--border); border-left:2px solid var(--border); border-radius:9px; background:linear-gradient(145deg,var(--surface),var(--surface-alt)); }
.admin-users-stats article > span { display:grid; place-items:center; width:30px; height:30px; border-radius:8px; color:var(--primary-ink); background:var(--primary-tint); }
.admin-users-stats article.is-active > span { color:#20a478; background:rgba(32,164,120,.11); }
.admin-users-stats article.is-admin > span { color:#8a6be0; background:rgba(138,107,224,.11); }
.admin-users-stats article.is-warning > span { color:#d59028; background:rgba(213,144,40,.12); }
.admin-users-stats article.is-danger > span { color:#d35c54; background:rgba(211,92,84,.11); }
.admin-users-stats strong,.admin-users-stats small,.admin-users-stats em { display:block; }
.admin-users-stats small { margin:0 0 3px; color:var(--text-muted); font-size:8px; font-weight:800; letter-spacing:.03em; text-transform:uppercase; }
.admin-users-stats strong { color:var(--text); font-size:20px; line-height:1; }
.admin-users-stats em { margin-top:4px; color:var(--text-faint); font-size:7px; font-style:normal; line-height:1.3; }
.admin-user-create-panel { overflow:hidden; border:1px solid color-mix(in srgb,var(--primary) 34%,var(--border)); border-radius:15px; background:var(--surface); box-shadow:var(--sh-md); }
.admin-user-create-panel > header { display:flex; align-items:flex-start; justify-content:space-between; gap:20px; padding:20px 22px; border-bottom:1px solid var(--border); background:var(--surface-alt); }
.admin-user-create-panel h2 { margin:3px 0 4px; font-size:21px; }
.admin-user-create-panel header p:not(.section-kicker) { margin:0; color:var(--text-muted); font-size:12px; }
.admin-user-create-form { display:grid; grid-template-columns:1fr 1fr; gap:16px; padding:20px 22px 22px; }
.admin-user-create-form fieldset { min-width:0; margin:0; padding:16px; border:1px solid var(--border); border-radius:11px; }
.admin-user-create-form fieldset.is-wide,.admin-user-create-actions { grid-column:1/-1; }
.admin-user-create-form legend { padding:0 7px; color:var(--text); font-size:12px; font-weight:800; }
.admin-user-create-form legend span { display:inline-grid; place-items:center; width:24px; height:24px; margin-right:5px; border-radius:7px; color:var(--primary-ink); background:var(--primary-tint); font-size:10px; }
.admin-user-form-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.admin-user-form-grid label { display:grid; align-content:start; gap:6px; }
.admin-user-form-grid label.is-wide { grid-column:1/-1; }
.admin-user-form-grid label > span { color:var(--text-muted); font-size:11px; font-weight:700; }
.admin-user-form-grid label small { color:var(--text-faint); font-size:10px; }
.admin-user-admin-toggle { display:flex; align-items:center; gap:9px; margin-top:13px; padding:11px; border:1px solid var(--border); border-radius:9px; cursor:pointer; }
.admin-user-admin-toggle input { width:auto; accent-color:var(--primary); }
.admin-user-admin-toggle > span { display:grid; place-items:center; color:#8a6be0; }
.admin-user-admin-toggle b,.admin-user-admin-toggle small { display:block; }
.admin-user-admin-toggle small { margin-top:2px; color:var(--text-muted); font-size:10px; }
.admin-user-module-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
.admin-user-module-grid label { display:flex; align-items:center; gap:8px; min-width:0; padding:10px; border:1px solid var(--border); border-radius:9px; cursor:pointer; }
.admin-user-module-grid label:has(input:checked) { border-color:color-mix(in srgb,var(--primary) 55%,var(--border)); background:var(--primary-tint); }
.admin-user-module-grid input { width:auto; accent-color:var(--primary); }
.admin-user-module-grid b { min-width:0; overflow:hidden; color:var(--text); font-size:10px; text-overflow:ellipsis; white-space:nowrap; }
.admin-user-create-actions { display:flex; align-items:center; gap:12px; padding-top:2px; }
.admin-user-create-actions span { color:var(--text-muted); font-size:11px; }
.admin-users-directory { overflow:visible; border:1px solid var(--border); border-radius:15px; background:var(--surface); }
.admin-users-directory-head { padding:19px 20px 13px; }
.admin-users-directory-head h2 { margin:3px 0 3px; font-size:21px; }
.admin-users-directory-head p:not(.section-kicker) { margin:0; color:var(--text-muted); font-size:11px; }
.admin-users-directory .dg-toolbar { margin:0 14px 10px; }
.admin-users-directory .dg-wrap { border-width:1px 0 0; border-radius:0 0 15px 15px; }
.admin-user-identity { display:flex; align-items:center; gap:9px; min-width:0; }
.admin-user-identity > span { display:grid; place-items:center; flex:0 0 32px; height:32px; border-radius:50%; color:var(--primary-ink); background:var(--primary-tint); font-size:12px; font-weight:850; }
.admin-user-identity > div { min-width:0; }
.admin-user-identity small { display:block; overflow:hidden; margin-top:2px; color:var(--text-muted); font-size:10px; text-overflow:ellipsis; white-space:nowrap; }
.admin-user-email { color:var(--text-muted); font-size:11px; }
.admin-user-module-summary { color:var(--text-muted); font-size:10px; line-height:1.45; }
.admin-user-status-stack { display:flex; flex-wrap:wrap; gap:4px; }
@media(max-width:1100px){.admin-users-stats{grid-template-columns:repeat(3,1fr)}.admin-user-module-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:760px){.admin-users-hero{align-items:flex-start;flex-direction:column}.admin-users-stats{grid-template-columns:1fr 1fr}.admin-user-create-form{grid-template-columns:1fr}.admin-user-create-form fieldset,.admin-user-create-actions{grid-column:1}.admin-user-module-grid{grid-template-columns:1fr}}

/* Administration — fiche d'un utilisateur */
.admin-user-detail-page { display:grid; gap:18px; }
.admin-user-back { width:max-content; color:var(--text-muted); font-size:11px; font-weight:700; text-decoration:none; }
.admin-user-back:hover { color:var(--primary-ink); }
.admin-user-detail-hero { position:relative; overflow:hidden; display:grid; grid-template-columns:1fr auto; gap:20px; padding:24px 26px 0; border:1px solid var(--border); border-radius:16px; background:linear-gradient(120deg,var(--primary-tint),var(--surface) 65%); box-shadow:var(--sh-sm); }
.admin-user-detail-hero::after { content:""; position:absolute; right:-65px; top:-105px; width:300px; height:300px; border:46px solid color-mix(in srgb,var(--primary) 6%,transparent); border-radius:50%; pointer-events:none; }
.admin-user-detail-identity { position:relative; z-index:1; display:flex; align-items:center; gap:15px; }
.admin-user-detail-avatar { display:grid; place-items:center; flex:0 0 64px; height:64px; border-radius:17px; color:#fff; background:linear-gradient(145deg,var(--primary),#439ee8); box-shadow:0 9px 25px rgba(20,117,190,.23); font-size:24px; font-weight:850; }
.admin-user-detail-identity h1 { margin:3px 0 4px; font-size:30px; letter-spacing:-.02em; }
.admin-user-detail-identity p:not(.section-kicker) { margin:0; color:var(--text-muted); }
.admin-user-detail-status { position:relative; z-index:1; display:flex; align-items:flex-start; justify-content:flex-end; flex-wrap:wrap; gap:6px; }
.admin-user-detail-summary { position:relative; z-index:1; grid-column:1/-1; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); margin:2px -26px 0; padding:0 26px; border-top:1px solid var(--border); background:color-mix(in srgb,var(--surface) 82%,transparent); }
.admin-user-detail-summary > div { padding:15px 16px; border-right:1px solid var(--border); }
.admin-user-detail-summary > div:first-child { padding-left:0; }
.admin-user-detail-summary > div:last-child { border-right:0; }
.admin-user-detail-summary dt { color:var(--text-faint); font-size:9px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; }
.admin-user-detail-summary dd { overflow:hidden; margin:5px 0 0; color:var(--text); font-size:12px; font-weight:750; text-overflow:ellipsis; white-space:nowrap; }
.admin-user-detail-page > .cell-muted { margin:-6px 0 0 !important; padding:10px 13px; border:1px solid var(--border); border-radius:9px; background:var(--surface-alt); }
.admin-user-detail-grid { gap:15px; }
.admin-user-detail-grid .detail-panel { padding:20px; border:1px solid var(--border); border-radius:13px; background:var(--surface); box-shadow:none; }
.admin-user-detail-grid .detail-panel > h2 { display:flex; align-items:center; gap:8px; margin:-2px 0 16px; padding-bottom:12px; border-bottom:1px solid var(--border); font-size:16px; }
.admin-user-detail-grid .detail-panel > h2::before { content:""; width:8px; height:8px; border-radius:50%; background:var(--primary); box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 11%,transparent); }
.admin-access-expiry { display:grid; grid-template-columns:40px minmax(0,1fr) auto; align-items:center; gap:12px; padding:13px 14px; border:1px solid color-mix(in srgb,var(--primary) 24%,var(--border)); border-radius:10px; background:var(--primary-tint); }
.admin-access-expiry > span:first-child,.auth-method-icon,.admin-empty-state > span { display:grid; place-items:center; color:var(--primary-ink); background:var(--primary-tint); }
.admin-access-expiry > span:first-child { width:40px; height:40px; border-radius:10px; }
.admin-access-expiry small,.admin-access-expiry strong { display:block; }
.admin-access-expiry small { margin-bottom:3px; color:var(--text-muted); font-size:10px; font-weight:750; text-transform:uppercase; letter-spacing:.04em; }
.admin-access-expiry strong { font-size:17px; }
.admin-access-actions { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:13px; }
.admin-access-actions .access-inline-form { display:block; margin:0; padding:12px; border:1px solid var(--border); border-radius:10px; background:var(--surface-alt); }
.admin-access-actions .access-inline-form label { display:grid; gap:7px; color:var(--text-muted); font-size:11px; font-weight:750; }
.admin-access-actions .access-inline-form label > span { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:8px; }
.admin-access-actions .access-inline-form input { width:100%; min-width:0; box-sizing:border-box; }
.admin-user-detail-grid .auth-method-row { display:grid; grid-template-columns:38px minmax(0,1fr) auto; gap:11px; margin-top:8px; padding:11px 12px; border:1px solid var(--border); border-radius:10px; background:var(--surface-alt); }
.admin-user-detail-grid .auth-method-row:first-of-type { padding-top:11px; border-top:1px solid var(--border); }
.auth-method-icon { width:38px; height:38px; border-radius:9px; }
.admin-user-detail-grid .auth-method-row div small { overflow:hidden; max-width:360px; color:var(--text-muted); font-size:10px; text-overflow:ellipsis; white-space:nowrap; }
.admin-panel-footer-action { display:flex; justify-content:flex-end; margin-top:14px; padding-top:13px; border-top:1px solid var(--border); }
.admin-empty-state { display:grid; grid-template-columns:42px minmax(0,1fr); align-items:center; gap:12px; min-height:72px; padding:12px 14px; border:1px dashed var(--border); border-radius:10px; background:var(--surface-alt); }
.admin-empty-state > span { width:42px; height:42px; border-radius:10px; }
.admin-empty-state p { margin:0; color:var(--text-muted); line-height:1.45; }
.admin-user-detail-grid .module-checkbox-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:9px; }
.admin-user-detail-grid .module-checkbox { padding:9px; border:1px solid var(--border); border-radius:8px; background:var(--surface-alt); }
.admin-user-detail-grid .module-checkbox:has(input:checked) { border-color:color-mix(in srgb,var(--primary) 55%,var(--border)); background:var(--primary-tint); }
.admin-profile-layout { display:grid !important; width:100%; max-width:none !important; grid-template-columns:minmax(320px,.65fr) minmax(620px,1.35fr); gap:18px 24px !important; align-items:start; }
.admin-profile-fields { display:grid; gap:14px; min-width:0; }
.admin-profile-fields > label,.admin-profile-fields input { width:100%; box-sizing:border-box; }
.admin-profile-modules { min-width:0; }
.admin-profile-admin-flag { grid-column:1; align-self:center; margin:0; padding:12px 14px; border:1px solid var(--border); border-radius:9px; background:var(--surface-alt); }
.admin-profile-submit { grid-column:2; display:flex; justify-content:flex-end; align-self:center; }
.admin-profile-submit .btn { min-width:130px; }
.admin-user-detail-grid .danger-panel { border-color:rgba(211,92,84,.35); background:linear-gradient(145deg,var(--surface),rgba(211,92,84,.035)); }
.admin-user-detail-grid .danger-panel > h2::before { background:#d35c54; box-shadow:0 0 0 4px rgba(211,92,84,.11); }
@media(max-width:1100px){.admin-shell .admin-users-stats.kpi-card-grid{grid-template-columns:repeat(3,1fr)}.admin-user-detail-summary{grid-template-columns:repeat(2,1fr)}.admin-user-detail-summary>div:nth-child(2){border-right:0}.admin-profile-layout{grid-template-columns:1fr}.admin-user-detail-grid .module-checkbox-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.admin-profile-admin-flag,.admin-profile-submit{grid-column:1}.admin-profile-submit{justify-content:flex-start}}
@media(max-width:760px){.admin-shell .admin-users-stats.kpi-card-grid{grid-template-columns:1fr}.admin-user-detail-hero{grid-template-columns:1fr}.admin-user-detail-status{justify-content:flex-start}.admin-user-detail-summary{grid-template-columns:1fr}.admin-user-detail-summary>div{border-right:0;border-bottom:1px solid var(--border);padding-left:0}.admin-user-detail-summary>div:last-child{border-bottom:0}.admin-user-detail-grid .module-checkbox-grid{grid-template-columns:1fr}.admin-profile-layout{display:grid !important;grid-template-columns:minmax(0,1fr)}.admin-access-actions{grid-template-columns:1fr}}

/* KPI universels : même langage visuel que la synthèse Utilisateurs,
   réduit de 30 %. Cette classe est émise par le composant partagé
   _kpi_card.html et couvre donc tableau de bord, sociétés, activité,
   statistiques, messages, support, bêta et journal, dans les deux thèmes. */
.kpi-card-grid { gap:8px; }
/* Statistiques : largeur sur mesure, jamais étirée pour remplir une piste.
   Chaque carte prend la place nécessaire à son contenu, dans des bornes
   lisibles, puis les suivantes continuent naturellement vers la droite. */
.kpi-card-grid--stats {
  display:flex;
  flex-wrap:wrap;
  align-items:stretch;
  justify-content:flex-start;
}
.kpi-card-grid.kpi-card-grid--stats .kpi-card.kpi-card--standard {
  flex:0 1 max-content;
  width:max-content;
  min-width:150px;
  max-width:240px;
  min-height:64px;
  padding:7px 10px;
}
@media(max-width:760px){
  .kpi-card-grid.kpi-card-grid--stats .kpi-card.kpi-card--standard {
    flex:1 1 calc(50% - 4px);
    width:auto;
    min-width:0;
    max-width:none;
  }
}
.kpi-card-grid .kpi-card.kpi-card--standard,
.kpi-card-grid--wide .kpi-card.kpi-card--standard,
.dashboard-topbar-stats .kpi-card.kpi-card--standard {
  display:grid;
  grid-template-columns:30px minmax(0,1fr);
  align-items:center;
  justify-content:stretch;
  gap:9px;
  min-height:78px;
  padding:10px 12px;
  border:1px solid var(--border);
  border-left-width:2px;
  border-radius:9px;
  background:linear-gradient(145deg,var(--surface),var(--surface-alt));
}
.kpi-card--standard .kpi-card-icon {
  display:grid;
  place-items:center;
  width:30px;
  height:30px;
  border-radius:8px;
  color:var(--primary-ink);
  background:var(--primary-tint);
}
.kpi-card--standard .kpi-card-copy { display:flex; flex-direction:column; justify-content:center; gap:1px; min-width:0; }
.kpi-card--standard .kpi-card-label { order:1; margin:0; color:var(--text-muted); font-size:8px; font-weight:800; line-height:1.2; letter-spacing:.03em; text-transform:uppercase; }
.kpi-card--standard .kpi-card-value { order:2; color:var(--c-brand-500); font-size:20px; font-weight:800; line-height:1.05; }
.kpi-card--standard .kpi-card-value--md,
.kpi-card--standard .kpi-card-value--sm,
.kpi-card--standard .kpi-card-value--na { font-size:14px; }
.kpi-card--standard .kpi-card-sub { order:3; margin-top:2px; color:var(--text-faint); font-size:7px; line-height:1.3; }
.kpi-card--standard.is-ok .kpi-card-icon { color:var(--c-ok); background:var(--c-ok-bg); }
.kpi-card--standard.is-warning .kpi-card-icon { color:var(--c-warn); background:var(--c-warn-bg); }
.kpi-card--standard.is-danger .kpi-card-icon { color:var(--c-danger); background:var(--c-danger-bg); }
@media(max-width:760px){
  .kpi-card-grid .kpi-card.kpi-card--standard,
  .kpi-card-grid--wide .kpi-card.kpi-card--standard,
  .dashboard-topbar-stats .kpi-card.kpi-card--standard { min-height:70px; }
}

/* Dimensionnement sur mesure généralisé à TOUTES les synthèses KPI de
   l'application. Les cartes suivent leur contenu au lieu de s'étirer pour
   remplir une colonne de grille ; elles progressent vers la droite puis
   passent à la ligne quand l'espace manque. Aucune taille de texte n'est
   modifiée par cette règle. */
.kpi-card-grid {
  display:flex;
  flex-wrap:wrap;
  align-items:stretch;
  justify-content:flex-start;
}
.kpi-card-grid .kpi-card.kpi-card--standard {
  flex:0 1 max-content;
  width:max-content;
  min-width:150px;
  max-width:240px;
  min-height:64px;
  padding:7px 10px;
}
/* La synthèse Utilisateurs est l'unique ancien bloc qui construit encore
   ses cartes directement au lieu d'utiliser la macro --standard. Elle doit
   donc recevoir explicitement le même dimensionnement sur mesure. */
.admin-shell .admin-users-stats.kpi-card-grid {
  display:flex;
  flex-wrap:wrap;
  align-items:stretch;
  justify-content:flex-start;
}
.admin-shell .admin-users-stats.kpi-card-grid .kpi-card {
  flex:0 1 max-content;
  width:max-content;
  min-width:180px;
  max-width:260px;
  min-height:64px;
  padding:7px 10px;
}
@media(max-width:760px){
  .kpi-card-grid .kpi-card.kpi-card--standard {
    flex:1 1 calc(50% - 4px);
    width:auto;
    min-width:0;
    max-width:none;
  }
  .admin-shell .admin-users-stats.kpi-card-grid .kpi-card {
    flex:1 1 calc(50% - 4px);
    width:auto;
    min-width:0;
    max-width:none;
  }
}

/* Fiche de demande d'acces : parcours de traitement admin. */
.admin-request-detail-page { display:grid; gap:18px; }
.admin-request-detail-hero {
  position:relative; overflow:hidden; display:grid; grid-template-columns:auto 1fr auto; gap:16px;
  padding:23px 25px 0; border:1px solid var(--border); border-radius:16px;
  background:linear-gradient(120deg,var(--primary-tint),var(--surface) 68%);
  box-shadow:var(--sh-sm);
}
.admin-request-detail-hero::after { content:""; position:absolute; right:-80px; top:-135px; width:350px; height:350px; border:52px solid color-mix(in srgb,var(--primary) 6%,transparent); border-radius:50%; pointer-events:none; }
.admin-request-detail-hero .module-header-icon { position:relative; z-index:1; display:grid; place-items:center; width:54px; height:54px; margin:0; border-radius:14px; color:#fff; background:linear-gradient(145deg,var(--primary),#439ee8); box-shadow:0 9px 24px rgba(20,117,190,.22); }
.admin-request-detail-hero .module-header-copy,.admin-request-detail-hero .module-header-actions { position:relative; z-index:1; }
.admin-request-detail-hero .module-header-copy h1 { margin:2px 0 4px; font-size:28px; letter-spacing:-.02em; }
.admin-request-detail-hero .module-header-copy > p:last-child { margin:0; color:var(--text-muted); }
.admin-request-detail-summary { position:relative; z-index:1; grid-column:1/-1; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); margin:3px -25px 0; padding:0 25px; border-top:1px solid var(--border); background:color-mix(in srgb,var(--surface) 84%,transparent); }
.admin-request-detail-summary > div { min-width:0; padding:14px 16px; border-right:1px solid var(--border); }
.admin-request-detail-summary > div:first-child { padding-left:0; }
.admin-request-detail-summary > div:last-child { border-right:0; }
.admin-request-detail-summary dt { color:var(--text-faint); font-size:9px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; }
.admin-request-detail-summary dd { overflow:hidden; margin:5px 0 0; color:var(--text); font-size:12px; font-weight:750; text-overflow:ellipsis; white-space:nowrap; }
.admin-request-detail-page > .data-unavailable-note { margin:0 !important; border-radius:10px; }
.admin-request-overview-grid { gap:14px; }
.admin-request-card { padding:20px !important; border-radius:13px; box-shadow:none; }
.admin-request-card > h2,.admin-request-decision-card > h2 { display:flex; align-items:center; gap:8px; padding-bottom:11px; border-bottom:1px solid var(--border); font-size:15px; }
.admin-request-card > h2::before,.admin-request-decision-card > h2::before { content:""; width:8px; height:8px; border-radius:50%; background:var(--primary); box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 11%,transparent); }
.admin-request-access-block { margin:0 !important; padding:22px; border:1px solid var(--border); border-radius:15px; background:var(--surface-alt); }
.admin-request-access-block > .admin-block-heading { margin-bottom:14px; }
.admin-request-access-form { display:grid; gap:16px; padding:20px !important; border-radius:13px; background:var(--surface); }
.admin-request-access-form .grant-basics { margin:0; padding:14px; border:1px solid var(--border); border-radius:10px; background:var(--surface-alt); }
.admin-request-access-form .module-selector { margin:0; padding:16px; border:1px solid var(--border); border-radius:10px; background:var(--surface-alt); }
.admin-request-access-form .module-selector legend { padding:0 7px; color:var(--text); font-size:12px; font-weight:800; }
.admin-request-access-form .module-selector-grid label { border:1px solid var(--border); border-radius:8px; background:var(--surface); }
.admin-request-access-form .module-selector-grid label:has(input:checked) { border-color:color-mix(in srgb,var(--primary) 55%,var(--border)); background:var(--primary-tint); }
.admin-request-primary-actions { justify-content:flex-end; flex-wrap:wrap; margin-top:0; padding-top:15px; border-top:1px solid var(--border); }
.admin-request-primary-actions .btn:not(.btn-outline) { min-width:150px; }
.admin-request-decision-grid { grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin-top:16px; }
.admin-request-decision-card { min-width:0; padding:17px !important; border-radius:11px; }
.admin-request-decision-card textarea { width:100%; box-sizing:border-box; }
.admin-request-reject-card { border-color:color-mix(in srgb,var(--c-danger) 32%,var(--border)); }
.admin-request-reject-card > h2::before { background:var(--c-danger); box-shadow:0 0 0 4px color-mix(in srgb,var(--c-danger) 11%,transparent); }
.admin-request-danger-zone { margin:0 !important; padding:18px 20px; border:1px solid color-mix(in srgb,var(--c-danger) 35%,var(--border)); border-radius:13px; background:linear-gradient(145deg,var(--surface),color-mix(in srgb,var(--c-danger) 4%,var(--surface))); }
.admin-request-danger-zone .row-actions { justify-content:flex-start; }
@media(max-width:1050px){.admin-request-detail-summary{grid-template-columns:repeat(2,1fr)}.admin-request-detail-summary>div:nth-child(2){border-right:0}.admin-request-decision-grid{grid-template-columns:1fr 1fr}.admin-request-reject-card{grid-column:1/-1}}
@media(max-width:720px){.admin-request-detail-hero{grid-template-columns:auto 1fr;padding-left:18px;padding-right:18px}.admin-request-detail-hero .module-header-actions{grid-column:1/-1;justify-content:flex-start}.admin-request-detail-summary{grid-template-columns:1fr;margin-left:-18px;margin-right:-18px;padding:0 18px}.admin-request-detail-summary>div{padding-left:0;border-right:0;border-bottom:1px solid var(--border)}.admin-request-detail-summary>div:last-child{border-bottom:0}.admin-request-access-block{padding:14px}.admin-request-decision-grid{grid-template-columns:1fr}.admin-request-reject-card{grid-column:auto}}
/* Administration des versions XSD */
.xsd-admin-page{display:grid;gap:24px;padding:8px 0 48px;max-width:1480px;margin:0 auto}.xsd-admin-hero{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:20px;align-items:center;padding:28px 30px;border:1px solid var(--border);border-radius:18px;background:linear-gradient(125deg,var(--surface) 0%,var(--primary-tint) 100%);box-shadow:0 14px 38px rgba(4,37,61,.08)}.xsd-admin-hero-icon{display:grid;place-items:center;width:58px;height:58px;border-radius:16px;color:#fff;background:linear-gradient(145deg,#0877c9,#19a8a5);box-shadow:0 10px 24px rgba(8,119,201,.22)}.xsd-admin-hero-copy h1{margin:2px 0 7px;font-size:clamp(25px,2.3vw,38px);letter-spacing:-.035em}.xsd-admin-hero-copy>p:last-child{margin:0;max-width:760px;color:var(--text-muted)}.xsd-admin-hero-summary{display:flex;gap:10px}.xsd-admin-hero-summary span{display:grid;min-width:122px;padding:14px 16px;border:1px solid var(--border);border-radius:13px;background:var(--surface);color:var(--text-muted);font-size:12px}.xsd-admin-hero-summary strong{color:var(--text);font-size:25px;line-height:1}.xsd-profile-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.xsd-profile-card{position:relative;overflow:hidden;border:1px solid var(--border);border-radius:18px;background:var(--surface);box-shadow:0 12px 32px rgba(4,37,61,.07)}.xsd-profile-card:before{content:"";position:absolute;inset:0 auto 0 0;width:5px;background:#18a679}.xsd-profile-card.is-playground:before{background:#4098df}.xsd-profile-head{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;padding:25px 26px 20px}.xsd-profile-head h2{margin:2px 0 6px;font-size:25px}.xsd-profile-head p{margin:0;color:var(--text-muted)}.xsd-profile-environment{text-transform:uppercase;letter-spacing:.12em!important;color:#168c76!important;font-size:11px!important;font-weight:800}.is-playground .xsd-profile-environment{color:#4098df!important}.xsd-status-pill{white-space:nowrap;padding:7px 11px;border-radius:999px;background:rgba(24,166,121,.12);color:#21b987;font-size:11px;font-weight:800}.is-playground .xsd-status-pill{background:rgba(64,152,223,.15);color:#62b6f3}.xsd-profile-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;border-block:1px solid var(--border);background:var(--border)}.xsd-profile-metrics>div{display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center;padding:18px;background:var(--surface)}.xsd-profile-metrics span{grid-row:1/3;color:var(--primary-ink)}.xsd-profile-metrics strong{font-size:19px}.xsd-profile-metrics small{color:var(--text-muted);font-size:11px;line-height:1.25}.xsd-profile-foot{padding:18px 26px}.xsd-profile-foot p{margin:7px 0 0;color:var(--text-muted);font-size:12px}.xsd-role-label{display:inline-flex;align-items:center;gap:7px;font-weight:750;font-size:12px}.xsd-company-panel{overflow:hidden;border:1px solid var(--border);border-radius:18px;background:var(--surface);box-shadow:0 12px 32px rgba(4,37,61,.06)}.xsd-company-panel-head{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;padding:24px 26px;border-bottom:1px solid var(--border)}.xsd-company-panel-head h2{margin:3px 0 5px;font-size:23px}.xsd-company-panel-head p{margin:0;color:var(--text-muted)}.xsd-company-count{padding:7px 11px;border-radius:999px;background:var(--surface-alt);color:var(--text-muted);font-size:12px;font-weight:750}.xsd-table-wrap{overflow:auto}.xsd-company-table{width:100%;border-collapse:collapse}.xsd-company-table th{padding:13px 18px;background:var(--surface-alt);color:var(--text-muted);font-size:11px;text-align:left;text-transform:uppercase;letter-spacing:.05em}.xsd-company-table td{padding:16px 18px;border-top:1px solid var(--border);vertical-align:middle}.xsd-company-table tbody tr:hover{background:color-mix(in srgb,var(--primary) 4%,transparent)}.xsd-company-table td:first-child small{display:block;margin-top:4px;color:var(--text-muted)}.xsd-version-pills{display:flex;flex-wrap:wrap;gap:6px}.xsd-mini-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 9px;border-radius:8px;font-weight:800;font-size:12px;background:rgba(24,166,121,.11);color:#20ae82}.xsd-mini-pill.playground{background:rgba(64,152,223,.13);color:#54a9e9}.xsd-mini-pill small{font-weight:600;opacity:.8}.xsd-default-version{display:inline-flex;align-items:center;gap:5px;color:#21b987;font-weight:800}.xsd-company-status{display:inline-flex;padding:5px 9px;border-radius:999px;background:var(--surface-alt);font-size:11px;font-weight:750}.xsd-company-status.active{background:rgba(24,166,121,.11);color:#21b987}.xsd-company-status.archived,.xsd-company-status.inactive{background:rgba(226,75,67,.12);color:#f07d76}.xsd-empty{text-align:center;color:var(--text-muted)}@media(max-width:1050px){.xsd-admin-hero{grid-template-columns:auto 1fr}.xsd-admin-hero-summary{grid-column:1/-1}.xsd-profile-grid{grid-template-columns:1fr}}@media(max-width:680px){.xsd-admin-hero{grid-template-columns:1fr;padding:22px}.xsd-admin-hero-summary{flex-wrap:wrap}.xsd-profile-head{flex-direction:column}.xsd-profile-metrics{grid-template-columns:repeat(2,1fr)}.xsd-company-panel-head{align-items:flex-start;flex-direction:column}}

/* RBAC — matrice compacte et lisible des permissions */
.rbac-create-panel{scroll-margin-top:92px;margin:0 0 var(--sp-5);overflow:hidden;border:1px solid var(--border);border-radius:18px;background:var(--surface);box-shadow:0 16px 42px rgba(2,18,32,.18)}
.rbac-create-heading{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:20px 22px;border-bottom:1px solid var(--border);background:linear-gradient(115deg,var(--primary-tint),var(--surface))}
.rbac-create-heading h2{margin:3px 0 0;font-size:22px}.rbac-create-heading .eyebrow{color:var(--primary-ink);font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase}
.rbac-role-form{display:grid;gap:18px;padding:22px}.rbac-identity-grid{display:grid;grid-template-columns:minmax(220px,.8fr) minmax(320px,1.7fr);gap:14px}.rbac-identity-grid label{display:block;margin:0 0 7px;font-weight:750}.rbac-identity-grid input{width:100%}
.rbac-permission-panel{padding:0 16px 16px;border:1px solid var(--border);border-radius:16px;background:color-mix(in srgb,var(--surface-alt) 52%,var(--surface));}
.rbac-permission-heading{display:flex;align-items:center;justify-content:space-between;gap:20px;margin:0 -16px 16px;padding:17px 19px;border-bottom:1px solid var(--border);background:var(--surface)}.rbac-permission-heading h3{margin:0 0 3px;font-size:18px}.rbac-permission-heading p{margin:0;max-width:720px;color:var(--text-muted);font-size:12px}.rbac-selection-count{display:inline-flex;align-items:baseline;gap:4px;min-width:82px;justify-content:center;padding:9px 13px;border:1px solid color-mix(in srgb,var(--primary) 26%,var(--border));border-radius:999px;background:var(--primary-tint);color:var(--primary-ink);font-size:12px;font-weight:800}.rbac-selection-count strong{font-size:18px}
.rbac-domain-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;background:transparent}.rbac-domain-card{position:relative;min-width:0;min-height:118px;padding:14px;border:1px solid var(--border);border-radius:13px;background:var(--surface);box-shadow:0 5px 15px rgba(2,18,32,.07);transition:border-color .15s ease,transform .15s ease,box-shadow .15s ease}.rbac-domain-card:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--primary) 32%,var(--border));box-shadow:0 8px 20px rgba(2,18,32,.11)}.rbac-domain-card.has-selection{border-color:color-mix(in srgb,var(--primary) 48%,var(--border));background:linear-gradient(145deg,var(--surface),var(--primary-tint))}.rbac-domain-card.has-selection:before{content:"";position:absolute;inset:0 auto 0 0;width:3px;border-radius:13px 0 0 13px;background:var(--primary)}.rbac-domain-card header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:13px;padding-bottom:10px;border-bottom:1px solid var(--border)}.rbac-domain-card header>div{display:flex;align-items:center;min-width:0;gap:9px}.rbac-domain-card header strong{overflow:hidden;text-overflow:ellipsis;color:var(--text);font-size:11px;letter-spacing:.045em;text-transform:uppercase}.rbac-domain-index{display:grid;place-items:center;flex:0 0 29px;width:29px;height:29px;border-radius:8px;background:var(--primary-tint);color:var(--primary-ink);font-size:10px;font-weight:850}.rbac-domain-toggle{flex:0 0 auto;padding:5px 8px;border:1px solid var(--border);border-radius:7px;background:var(--surface-alt);color:var(--primary-ink);font:inherit;font-size:10px;font-weight:750;cursor:pointer}.rbac-domain-toggle:hover{border-color:var(--primary);background:var(--primary-tint)}
.rbac-permission-list{display:flex;flex-wrap:wrap;gap:7px}.rbac-permission-choice{position:relative;display:inline-flex;align-items:center;gap:7px;min-height:34px;margin:0;padding:6px 10px;border:1px solid var(--border);border-radius:9px;background:var(--surface-alt);font-size:12px;font-weight:650;cursor:pointer;user-select:none}.rbac-permission-choice:hover{border-color:color-mix(in srgb,var(--primary) 60%,var(--border));background:var(--primary-tint)}.rbac-permission-choice input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}.rbac-permission-check{position:relative;flex:0 0 16px;width:16px;height:16px;border:1.5px solid color-mix(in srgb,var(--text-muted) 70%,var(--border));border-radius:4px;background:var(--surface)}.rbac-permission-choice input:checked+.rbac-permission-check{border-color:var(--primary);background:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 13%,transparent)}.rbac-permission-choice input:checked+.rbac-permission-check:after{content:"";position:absolute;left:4px;top:4px;width:6px;height:3px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:rotate(-45deg)}.rbac-permission-choice:has(input:checked){border-color:color-mix(in srgb,var(--primary) 58%,var(--border));background:var(--primary-tint);color:var(--text)}.rbac-permission-choice:has(input:focus-visible){outline:2px solid var(--primary);outline-offset:2px}
.rbac-form-actions{position:sticky;bottom:0;z-index:3;display:flex;justify-content:flex-end;gap:10px;padding:13px 0 0;border-top:1px solid var(--border);background:linear-gradient(180deg,transparent,var(--surface) 28%)}
@media(max-width:1150px){.rbac-domain-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:720px){.rbac-role-form{padding:16px}.rbac-identity-grid,.rbac-domain-grid{grid-template-columns:1fr}.rbac-permission-heading{align-items:flex-start}.rbac-permission-heading p{display:none}}

/* RBAC — état sélectionné explicite, visible dans tous les navigateurs et thèmes. */
.rbac-permission-choice.is-selected{border-color:#0877c9!important;background:rgba(8,119,201,.13)!important;box-shadow:inset 0 0 0 1px rgba(8,119,201,.16)}
.rbac-permission-choice.is-selected .rbac-permission-check{border-color:#0877c9!important;background:#0877c9!important;box-shadow:0 0 0 3px rgba(8,119,201,.18)!important}
.rbac-permission-choice.is-selected .rbac-permission-check::after{content:"";position:absolute;left:3px;top:3px;width:7px;height:4px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:rotate(-45deg)}
.rbac-permission-choice.is-selected>span:last-child{color:#0669b2;font-weight:800}
[data-theme="dark"] .rbac-permission-choice.is-selected>span:last-child{color:#73c4ff}

/* ============================================================
   SITE PUBLIC — REFONTE ÉDITORIALE 2026-08
   L'interface de travail reste inchangée. Cette couche donne à la vitrine
   plus de respiration, un contexte humain et un véritable centre de guides.
   ============================================================ */

/* Héros plus généreux : le visuel réglementaire existant devient une vraie
   scène et la fiche d'analyse reste la preuve produit au premier plan. */
.public-site .market-hero {
  min-height: min(820px, calc(100svh - 108px));
  background:
    radial-gradient(circle at 12% 18%, rgba(92,154,198,.13), transparent 28%),
    linear-gradient(145deg, var(--card) 0%, var(--c-brand-50) 70%, var(--card) 100%);
}
.public-site .market-hero-inner {
  min-height: inherit;
  align-items: center;
  padding-block: clamp(4rem, 8vw, 7.5rem);
}
.public-site .market-hero h1 { font-size: clamp(2.8rem, 5.2vw, 5.6rem); line-height: .98; max-width: 12ch; }
.public-site .market-hero-lead { max-width: 65ch; font-size: clamp(1rem, 1.35vw, 1.18rem); }
.public-site .market-hero-media {
  min-height: clamp(460px, 50vw, 650px);
  border-color: rgba(255,255,255,.55);
  box-shadow: 0 36px 85px -40px rgba(8,31,48,.6);
}
.public-site .market-hero-media::before {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background: linear-gradient(180deg, rgba(5,28,47,.03), rgba(5,28,47,.5));
}
.public-site .market-hero-media > * { position:relative; z-index:1; }

/* Film éditorial : deux plans asymétriques, une seule grande image mobile. */
.home-visual-story {
  display:grid; grid-template-columns:minmax(0,1.16fr) minmax(360px,.84fr);
  min-height:680px; overflow:hidden; background:var(--c-brand-950); color:#fff;
}
.home-visual-media { position:relative; min-height:680px; overflow:hidden; }
.home-visual-media video { width:100%; height:100%; object-fit:cover; display:block; }
.home-video-vignette { position:absolute; inset:0; background:linear-gradient(90deg, transparent 48%, rgba(8,31,48,.82) 100%),linear-gradient(0deg,rgba(8,31,48,.5),transparent 55%); }
.home-video-control {
  position:absolute; top:28px; left:28px; display:inline-flex; align-items:center; gap:10px;
  min-height:42px; padding:0 15px; border:1px solid rgba(255,255,255,.42); border-radius:999px;
  background:rgba(8,31,48,.52); color:#fff; backdrop-filter:blur(10px); cursor:pointer;
  font:700 var(--fs-xs)/1 var(--font-body);
}
.home-video-control-icon { width:9px; height:12px; border-left:3px solid currentColor; border-right:3px solid currentColor; }
.home-video-control[aria-pressed="false"] .home-video-control-icon { width:0; height:0; border:0; border-top:7px solid transparent; border-bottom:7px solid transparent; border-left:11px solid currentColor; }
/* Un bouton de commande DOIT repondre au curseur : celui-ci ne changeait
   ni au survol ni a l'appui -- seul le focus clavier etait signale. Sur un
   controle superpose a une video, l'absence de reponse laisse douter qu'il
   soit cliquable. */
.home-video-control { transition: background-color .18s var(--ease), border-color .18s var(--ease), transform .1s var(--ease); }
.home-video-control:hover { background:rgba(8,31,48,.74); border-color:rgba(255,255,255,.72); }
.home-video-control:active { transform: scale(.97); }
@media (prefers-reduced-motion: reduce) {
  .home-video-control { transition: background-color .18s var(--ease), border-color .18s var(--ease); }
  .home-video-control:active { transform: none; }
}
.home-video-control:focus-visible { outline:3px solid #fff; outline-offset:3px; }
.home-video-caption { position:absolute; left:clamp(24px,5vw,72px); right:32px; bottom:34px; display:grid; gap:6px; max-width:38rem; }
.home-video-caption strong { color:#fff; font-size:clamp(1.05rem,1.8vw,1.45rem); line-height:1.3; }
.home-video-caption small { color:rgba(255,255,255,.68); font:500 10px/1.4 var(--font-mono); letter-spacing:.05em; text-transform:uppercase; }
.home-video-control,.home-video-caption { z-index:5; }
.home-device-hud { position:absolute; inset:0; z-index:3; pointer-events:none; }
.home-device-reticle { position:absolute; top:30%; right:10%; width:clamp(150px,20vw,260px); aspect-ratio:1.35; animation:home-device-float 5.4s ease-in-out infinite; }
.home-device-reticle::before,.home-device-reticle::after { content:""; position:absolute; border-radius:50%; }
.home-device-reticle::before { inset:22%; border:1px solid rgba(119,199,255,.7); box-shadow:0 0 35px rgba(67,171,235,.22),inset 0 0 24px rgba(67,171,235,.14); animation:home-device-scan 3.6s ease-in-out infinite; }
.home-device-reticle::after { width:8px; height:8px; left:50%; top:50%; margin:-4px; background:#57d9b4; box-shadow:0 0 0 9px rgba(87,217,180,.12),0 0 24px #57d9b4; animation:home-device-ping 2.4s ease-out infinite; }
.home-device-reticle span { position:absolute; width:30px; height:30px; border-color:rgba(255,255,255,.82); }
.home-device-reticle span:nth-child(1){top:0;left:0;border-top:2px solid;border-left:2px solid}
.home-device-reticle span:nth-child(2){top:0;right:0;border-top:2px solid;border-right:2px solid}
.home-device-reticle span:nth-child(3){bottom:0;right:0;border-bottom:2px solid;border-right:2px solid}
.home-device-reticle span:nth-child(4){bottom:0;left:0;border-bottom:2px solid;border-left:2px solid}
.home-device-id,.home-device-status { position:absolute; right:clamp(22px,3vw,46px); border:1px solid rgba(119,199,255,.34); background:rgba(3,19,31,.66); box-shadow:0 18px 40px -22px rgba(0,0,0,.9); backdrop-filter:blur(14px); }
.home-device-id { top:92px; display:grid; gap:5px; min-width:205px; padding:14px 16px; border-radius:14px 4px 14px 4px; }
.home-device-id small { color:#57d9b4; font:650 8px/1 var(--font-mono); letter-spacing:.14em; text-transform:uppercase; }
.home-device-id strong { color:#fff; font-size:.77rem; }
.home-device-id code { color:var(--c-brand-200); font:600 11px/1.2 var(--font-mono); }
.home-device-status { bottom:132px; display:flex; align-items:center; gap:9px; padding:10px 12px; border-radius:999px; color:#fff; font:600 8px/1 var(--font-mono); letter-spacing:.08em; }
.home-device-status i { width:7px; height:7px; border-radius:50%; background:#57d9b4; box-shadow:0 0 0 5px rgba(87,217,180,.12); animation:home-device-ping 2.4s ease-out infinite; }
.home-device-status b { color:#57d9b4; font-size:inherit; }
@keyframes home-device-float { 50%{transform:translate3d(0,-8px,0)} }
@keyframes home-device-scan { 50%{transform:scale(1.16);opacity:.45} }
@keyframes home-device-ping { 50%{box-shadow:0 0 0 12px transparent,0 0 28px #57d9b4} }
.home-visual-copy { display:flex; flex-direction:column; justify-content:center; padding:clamp(48px,7vw,100px); }
.home-visual-copy h2 { margin:0; max-width:13ch; color:#fff; font-size:clamp(2.2rem,4vw,4.15rem); line-height:1.04; letter-spacing:-.045em; }
.home-visual-copy > p:not(.market-kicker) { max-width:52ch; margin:24px 0 0; color:rgba(255,255,255,.7); line-height:1.7; }
.home-visual-steps { list-style:none; display:grid; gap:0; margin:38px 0 0; padding:0; }
.home-visual-steps li { display:grid; grid-template-columns:48px 1fr; gap:16px; align-items:center; padding:17px 0; border-top:1px solid rgba(255,255,255,.16); }
.home-visual-steps li:last-child { border-bottom:1px solid rgba(255,255,255,.16); }
.home-visual-steps span { color:var(--c-brand-300); font:600 11px/1 var(--font-mono); }
.home-visual-steps strong { color:#fff; font-size:var(--fs-sm); }

/* Le problème est désormais incarné par une photographie contextuelle. */
.problem-stage { display:grid; grid-template-columns:minmax(320px,.82fr) minmax(0,1.18fr); gap:clamp(34px,6vw,86px); align-items:center; margin-top:var(--sp-9); }
.problem-photo { position:sticky; top:110px; margin:0; overflow:hidden; border-radius:28px 28px 8px 28px; background:var(--c-brand-900); box-shadow:0 28px 65px -38px rgba(8,31,48,.55); }
.problem-photo::after { content:""; position:absolute; inset:0; pointer-events:none; background:linear-gradient(180deg,transparent 48%,rgba(8,31,48,.86)); }
.problem-photo img { display:block; width:100%; aspect-ratio:4/5; object-fit:cover; object-position:47% center; transition:transform .8s var(--ease); }
.problem-photo:hover img { transform:scale(1.025); }
.problem-photo figcaption { position:absolute; z-index:1; left:28px; right:28px; bottom:26px; color:#fff; font-size:var(--fs-sm); font-weight:650; line-height:1.55; }
.problem-photo-signals { position:absolute; z-index:2; top:22px; right:22px; display:flex; gap:7px; }
.problem-photo-signals span { display:grid; place-items:center; min-height:28px; padding:0 10px; border:1px solid rgba(255,255,255,.34); border-radius:999px; background:rgba(3,19,31,.62); color:#fff; backdrop-filter:blur(10px); font:650 8px/1 var(--font-mono); letter-spacing:.1em; animation:problem-signal-float 4.5s ease-in-out infinite; }
.problem-photo-signals span:nth-child(2){animation-delay:-1.5s}.problem-photo-signals span:nth-child(3){animation-delay:-3s}
@keyframes problem-signal-float { 50%{transform:translateY(-5px);border-color:rgba(87,217,180,.68)} }
.problem-stage .problem-list { max-width:none; width:100%; margin:0; }

/* Cartes éditoriales : un léger décalage vertical casse l'effet de grille
   industrielle tout en conservant un ordre de lecture évident. */
.home-guides { max-width:none; padding-inline:max(var(--gutter),calc((100vw - var(--page-max))/2 + var(--gutter))); background:linear-gradient(180deg,var(--c-brand-50),var(--bg)); }
.home-guide-grid { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:18px; max-width:var(--page-max); margin:var(--sp-9) auto 0; }
.home-guide-card { position:relative; display:flex; flex-direction:column; min-height:285px; padding:28px; overflow:hidden; border:1px solid var(--border); border-radius:20px; background:var(--card); transition:transform .25s var(--ease),border-color .25s var(--ease),box-shadow .25s var(--ease); }
.home-guide-card:nth-child(1),.home-guide-card:nth-child(2) { grid-column:span 3; }
.home-guide-card:nth-child(n+3) { grid-column:span 2; }
.home-guide-card:nth-child(2),.home-guide-card:nth-child(4) { transform:translateY(20px); }
.home-guide-card:hover { transform:translateY(-5px); border-color:var(--c-brand-300); box-shadow:0 24px 50px -36px rgba(8,31,48,.7); }
.home-guide-card:nth-child(2):hover,.home-guide-card:nth-child(4):hover { transform:translateY(15px); }
.home-guide-card::after { content:"?"; position:absolute; right:-8px; bottom:-46px; color:var(--c-brand-50); font:700 180px/.8 var(--font-display); pointer-events:none; }
.home-guide-index,.guide-card-number { position:relative; z-index:1; color:var(--c-brand-500); font:600 11px/1 var(--font-mono); letter-spacing:.08em; }
.home-guide-card h3 { position:relative; z-index:1; max-width:23ch; margin:38px 0 12px; color:var(--text); font-size:clamp(1.15rem,1.7vw,1.55rem); line-height:1.2; }
.home-guide-card p { position:relative; z-index:1; margin:0 0 24px; color:var(--text-muted); font-size:var(--fs-sm); line-height:1.65; }
.home-guide-card a { position:relative; z-index:1; margin-top:auto; color:var(--primary-ink); font-size:var(--fs-sm); font-weight:800; text-decoration:none; }
.home-guide-card a span { display:inline-block; transition:transform .2s var(--ease); }
.home-guide-card a:hover span { transform:translateX(5px); }
.home-guides-all { display:flex; justify-content:center; margin:58px auto 0; }

/* Centre des guides. */
.guide-hero,.guide-article-hero { position:relative; overflow:hidden; color:#fff; background:linear-gradient(135deg,var(--c-brand-950),var(--c-brand-700)); }
.guide-hero-inner { position:relative; z-index:1; display:grid; grid-template-columns:1.08fr .92fr; gap:clamp(36px,7vw,110px); align-items:center; max-width:var(--page-max); min-height:560px; margin:0 auto; padding:clamp(70px,9vw,125px) var(--gutter); }
.guide-hero h1,.guide-article-hero h1 { margin:0; color:#fff; font-size:clamp(2.7rem,5.3vw,5.25rem); line-height:1.02; letter-spacing:-.05em; }
.guide-hero-inner > div:first-child > p:not(.market-kicker) { max-width:65ch; margin:24px 0 0; color:rgba(255,255,255,.72); font-size:var(--fs-lead); line-height:1.7; }
.guide-meta-row { display:flex; flex-wrap:wrap; gap:14px 24px; margin-top:30px; }
.guide-meta-row span,.guide-meta-row time { display:inline-flex; align-items:flex-start; gap:8px; max-width:52ch; color:rgba(255,255,255,.7); font-size:var(--fs-xs); line-height:1.5; }
.guide-meta-row svg { flex:0 0 auto; margin-top:2px; }
.guide-hero-orb { position:absolute; border:1px solid rgba(255,255,255,.12); border-radius:50%; animation:guide-float 9s ease-in-out infinite; }
.guide-hero-orb-one { width:460px; height:460px; right:-120px; top:-180px; }
.guide-hero-orb-two { width:280px; height:280px; left:35%; bottom:-210px; animation-delay:-4s; }
@keyframes guide-float { 50% { transform:translate3d(0,18px,0) scale(1.03); } }
.guide-hero-visual { position:relative; min-height:360px; }
.guide-hero-card { position:absolute; inset:58px 34px 30px 24px; display:grid; grid-template-columns:1fr auto 1fr auto 1fr; align-items:center; gap:10px; padding:52px 30px 30px; border:1px solid rgba(255,255,255,.28); border-radius:26px; background:rgba(255,255,255,.09); backdrop-filter:blur(18px); box-shadow:0 30px 70px -36px rgba(0,0,0,.7); }
.guide-hero-card small { position:absolute; top:24px; left:30px; color:var(--c-brand-200); font:600 10px/1 var(--font-mono); letter-spacing:.12em; }
.guide-hero-card strong { text-align:center; color:#fff; font-size:clamp(.9rem,1.3vw,1.1rem); }
.guide-hero-card i { color:var(--c-brand-300); font-style:normal; }
.guide-signal { position:absolute; z-index:2; display:grid; place-items:center; width:72px; height:72px; border-radius:50%; background:#fff; color:var(--c-brand-800); box-shadow:0 18px 40px -20px #000; font:600 12px/1 var(--font-mono); animation:signal-bob 5s ease-in-out infinite; }
.guide-signal-a { left:0; top:15px; }.guide-signal-b { right:0; top:92px; animation-delay:-1.7s; }.guide-signal-c { right:62px; bottom:0; animation-delay:-3.4s; }
@keyframes signal-bob { 50% { transform:translateY(-12px); } }
.guide-index { padding-block:clamp(70px,8vw,120px); }
.guide-card-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; margin-top:var(--sp-9); }
.guide-card { display:grid; grid-template-columns:46px 1fr; gap:12px 18px; min-height:250px; padding:30px; border:1px solid var(--border); border-radius:20px; background:var(--card); }
.guide-card:nth-child(5) { grid-column:1/-1; }
.guide-card h3 { margin:0 0 12px; color:var(--text); font-size:clamp(1.25rem,2vw,1.75rem); line-height:1.2; }
.guide-card p { margin:0; color:var(--text-muted); line-height:1.65; }
.guide-card a { grid-column:2; align-self:end; color:var(--primary-ink); font-size:var(--fs-sm); font-weight:800; text-decoration:none; }
.guide-card a span { display:inline-block; transition:transform .2s var(--ease); }.guide-card a:hover span{transform:translateX(5px)}
.guide-boundary { display:grid; grid-template-columns:auto 1fr; gap:26px; align-items:start; margin-block:var(--sp-10); padding:clamp(28px,4vw,48px); border:1px solid var(--c-brand-200); border-radius:24px; background:var(--c-brand-50); }
.guide-boundary-mark { display:grid; place-items:center; width:58px; height:58px; border-radius:18px; background:var(--c-brand-700); color:#fff; }
.guide-boundary h2 { margin:0 0 12px; }.guide-boundary p:last-child{max-width:78ch;margin:0;color:var(--text-muted);line-height:1.7}
.guide-final-cta p:not(.market-kicker) { max-width:62ch; margin:16px 0 0; color:rgba(255,255,255,.7); line-height:1.65; }

/* Article : une réponse de référence, sans longue page commerciale. */
.guide-article-hero-inner { max-width:var(--page-max); margin:0 auto; padding:clamp(68px,9vw,120px) var(--gutter); }
.guide-article-hero h1 { max-width:17ch; margin-top:24px; }
.guide-back { display:inline-flex; align-items:center; gap:8px; margin-bottom:42px; color:rgba(255,255,255,.72); font-size:var(--fs-sm); font-weight:700; text-decoration:none; }.guide-back:hover{color:#fff}
.guide-article-answer { max-width:65ch; margin:28px 0 0; color:rgba(255,255,255,.82); font-size:clamp(1.05rem,1.7vw,1.35rem); line-height:1.65; }
.guide-article-layout { display:grid; grid-template-columns:minmax(260px,.72fr) minmax(0,1.28fr); gap:clamp(36px,7vw,100px); align-items:start; }
.guide-article-summary { position:sticky; top:110px; display:grid; gap:16px; padding:28px; border-left:4px solid var(--c-teal-500); background:var(--c-brand-50); }
.guide-article-summary > span { color:var(--c-teal-700); }.guide-article-summary p{margin:0;color:var(--text-faint);font:600 10px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase}.guide-article-summary strong{font-size:var(--fs-body);line-height:1.65}
.guide-article-body h2 { margin:0; font-size:clamp(2rem,3vw,3rem); }
.guide-article-body ol { list-style:none; display:grid; gap:0; margin:34px 0; padding:0; border-bottom:1px solid var(--border); }
.guide-article-body li { display:grid; grid-template-columns:50px 1fr; gap:18px; padding:24px 0; border-top:1px solid var(--border); }
.guide-article-body li span { color:var(--c-brand-500); font:600 11px/1.7 var(--font-mono); }.guide-article-body li p{margin:0;color:var(--text);font-size:1.02rem;line-height:1.7}
.guide-source-link { display:flex; align-items:center; gap:14px; padding:18px 20px; border:1px solid var(--border); border-radius:14px; color:var(--primary-ink); text-decoration:none; transition:border-color .2s var(--ease),transform .2s var(--ease); }.guide-source-link:hover{transform:translateY(-2px);border-color:var(--c-brand-400)}
.guide-source-link span{display:grid;gap:3px}.guide-source-link small{color:var(--text-faint);font:600 9px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase}.guide-source-link strong{font-size:var(--fs-sm)}

@media(max-width:1050px){
  .public-site .market-hero h1{font-size:clamp(2.7rem,7vw,4.8rem)}
  .home-visual-story,.guide-hero-inner{grid-template-columns:1fr}
  .home-visual-media{min-height:560px}.home-visual-copy{padding-block:70px}.home-visual-copy h2{max-width:17ch}
  .problem-stage{grid-template-columns:1fr}.problem-photo{position:relative;top:auto;max-width:760px;margin-inline:auto}.problem-photo img{aspect-ratio:16/9}
  .home-guide-card:nth-child(n){grid-column:span 3}.home-guide-card:nth-child(2),.home-guide-card:nth-child(4){transform:none}.home-guide-card:nth-child(2):hover,.home-guide-card:nth-child(4):hover{transform:translateY(-5px)}
  .guide-hero-visual{max-width:620px;width:100%;margin-inline:auto}.guide-hero-inner{padding-bottom:80px}
}
@media(max-width:760px){
  .public-site .market-hero{min-height:auto}.public-site .market-hero-inner{padding-block:52px}.public-site .market-hero h1{font-size:clamp(2.5rem,12vw,4rem)}
  .home-visual-story{min-height:0}.home-visual-media{min-height:440px}.home-video-vignette{background:linear-gradient(0deg,rgba(8,31,48,.72),transparent 70%)}.home-visual-copy{padding:52px var(--gutter)}
  .problem-photo img{aspect-ratio:4/3}.problem-photo figcaption{left:20px;right:20px;bottom:20px}
  .home-guide-grid,.guide-card-grid{grid-template-columns:1fr}.home-guide-card:nth-child(n),.guide-card:nth-child(5){grid-column:auto}.home-guide-card{min-height:250px}
  .guide-hero-inner{min-height:0}.guide-hero h1,.guide-article-hero h1{font-size:clamp(2.4rem,11vw,4rem)}.guide-hero-visual{min-height:300px}.guide-hero-card{inset:45px 8px 25px}.guide-signal{width:58px;height:58px}
  .guide-article-layout{grid-template-columns:1fr}.guide-article-summary{position:relative;top:auto}.guide-boundary{grid-template-columns:1fr}.guide-final-cta{margin-inline:var(--gutter)}
}
@media(max-width:480px){.home-visual-media{min-height:380px}.home-video-control{top:18px;left:18px}.home-video-caption{left:20px;right:20px;bottom:20px}.guide-hero-visual{min-height:260px}.guide-hero-card{padding-inline:15px}.guide-card{grid-template-columns:1fr}.guide-card a{grid-column:1}}
@media(prefers-reduced-motion:reduce){.guide-hero-orb,.guide-signal{animation:none}.problem-photo img{transition:none}}

:root[data-theme="dark"] .home-guides,:root[data-theme="dark"] .guide-boundary,:root[data-theme="dark"] .guide-article-summary{background:rgba(255,255,255,.028)}
@media(prefers-color-scheme:dark){:root:not([data-theme="light"]) .home-guides,:root:not([data-theme="light"]) .guide-boundary,:root:not([data-theme="light"]) .guide-article-summary{background:rgba(255,255,255,.028)}}

/* ============================================================
   SITE PUBLIC — PASSE INTERACTIVE 2026-08
   Le premier écran démontre maintenant le produit, au lieu d'afficher une
   capture figée. Le mouvement reste discret, contrôlable et désactivable.
   ============================================================ */
.public-scroll-progress {
  position:fixed; z-index:80; inset:0 0 auto; height:3px; pointer-events:none;
  background:transparent;
}
.public-scroll-progress span {
  display:block; width:100%; height:100%; transform:scaleX(0); transform-origin:left center;
  background:linear-gradient(90deg,var(--c-brand-400),var(--c-teal-500));
  box-shadow:0 0 14px color-mix(in srgb,var(--c-brand-400) 65%,transparent);
  will-change:transform;
}
.marketing-header {
  transition:min-height .25s var(--ease),background-color .25s var(--ease),box-shadow .25s var(--ease),border-color .25s var(--ease);
}
.marketing-header.is-scrolled {
  border-color:color-mix(in srgb,var(--c-brand-300) 35%,var(--border));
  background:color-mix(in srgb,var(--card) 88%,transparent);
  box-shadow:0 16px 34px -28px rgba(8,31,48,.8);
  backdrop-filter:blur(18px) saturate(135%);
}
.marketing-header.is-scrolled .marketing-header-inner { min-height:66px; }

.public-site .market-hero {
  isolation:isolate;
  background:
    radial-gradient(circle at 8% 9%,rgba(47,121,171,.16),transparent 24%),
    radial-gradient(circle at 85% 22%,rgba(26,145,141,.08),transparent 25%),
    linear-gradient(145deg,var(--card) 0%,var(--c-brand-50) 72%,var(--card) 100%);
}
.public-site .market-hero::after {
  content:""; position:absolute; z-index:-1; width:38vw; height:38vw; min-width:360px; min-height:360px;
  right:-17vw; bottom:-24vw; border:1px solid color-mix(in srgb,var(--c-brand-300) 28%,transparent);
  border-radius:50%; box-shadow:0 0 0 70px color-mix(in srgb,var(--c-brand-200) 7%,transparent),0 0 0 140px color-mix(in srgb,var(--c-brand-200) 4%,transparent);
}
.public-site .market-hero-media {
  position:relative; display:block; min-height:650px; padding:0; overflow:hidden;
  border:1px solid rgba(255,255,255,.14); border-radius:30px;
  background:
    radial-gradient(circle at 82% 12%,rgba(47,121,171,.29),transparent 31%),
    linear-gradient(155deg,#071a28 0%,#0a2a41 58%,#0b2234 100%);
  box-shadow:0 42px 90px -46px rgba(8,31,48,.9),inset 0 1px 0 rgba(255,255,255,.08);
  color:#fff;
}
.public-site .market-hero-media::before {
  background:
    linear-gradient(rgba(255,255,255,.028) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,.028) 1px,transparent 1px);
  background-size:34px 34px;
  mask-image:linear-gradient(180deg,#000,transparent 84%);
}
.hero-tour-topbar {
  position:relative; z-index:2; display:flex; align-items:center; justify-content:space-between; gap:18px;
  min-height:60px; padding:0 26px; border-bottom:1px solid rgba(255,255,255,.1);
  background:rgba(4,19,30,.42); backdrop-filter:blur(12px);
}
.hero-tour-topbar > span { display:inline-flex; align-items:center; gap:10px; color:#fff; font-size:12px; font-weight:800; }
.hero-tour-topbar small { color:rgba(255,255,255,.55); font:600 9px/1 var(--font-mono); letter-spacing:.09em; text-transform:uppercase; }
.hero-tour-tabs {
  position:relative; z-index:2; display:grid; grid-template-columns:repeat(3,1fr); gap:0;
  margin:20px 24px 0; padding:5px; border:1px solid rgba(255,255,255,.1); border-radius:14px;
  background:rgba(3,17,27,.42);
}
.hero-tour-tabs button {
  position:relative; display:flex; align-items:center; justify-content:center; gap:9px; min-height:44px;
  padding:8px 10px; border:0; border-radius:9px; background:transparent; color:rgba(255,255,255,.58);
  font:750 11px/1.2 var(--font-body); cursor:pointer; transition:background-color .22s var(--ease),color .22s var(--ease),transform .22s var(--ease);
}
.hero-tour-tabs button span { color:var(--c-brand-300); font:600 9px/1 var(--font-mono); }
.hero-tour-tabs button:hover { color:#fff; }
.hero-tour-tabs button.is-active { background:rgba(255,255,255,.1); color:#fff; box-shadow:0 10px 28px -20px #000; }
.hero-tour-tabs button:focus-visible { outline:2px solid var(--c-brand-200); outline-offset:2px; }
.hero-tour-panels { position:relative; z-index:2; min-height:468px; padding:22px 24px 16px; }
.hero-tour-panel {
  min-height:430px; padding:22px; border:1px solid rgba(255,255,255,.13); border-radius:20px;
  background:rgba(255,255,255,.065); box-shadow:0 30px 64px -44px #000; backdrop-filter:blur(16px);
}
.hero-tour-panel + .hero-tour-panel { margin-top:14px; }
.hero-tour-panel.is-active { animation:hero-panel-in .48s var(--ease) both; }
.is-interactive .hero-tour-panel + .hero-tour-panel { margin-top:0; }
@keyframes hero-panel-in { from{opacity:0;transform:translateY(10px) scale(.992)} to{opacity:1;transform:none} }
.hero-panel-heading { display:flex; align-items:center; justify-content:space-between; gap:20px; padding-bottom:18px; border-bottom:1px solid rgba(255,255,255,.1); }
.hero-panel-heading > div { display:flex; align-items:center; min-width:0; gap:11px; }
.hero-panel-heading strong { overflow:hidden; color:#fff; font-size:12px; text-overflow:ellipsis; white-space:nowrap; }
.hero-panel-heading small { flex:0 0 auto; color:var(--c-brand-200); font:650 9px/1.3 var(--font-mono); text-transform:uppercase; }
.hero-file-icon { display:grid; place-items:center; flex:0 0 42px; height:34px; border-radius:8px; background:#16784a; color:#fff; font:800 9px/1 var(--font-mono); }
.hero-file-icon.xml { background:var(--c-brand-600); }
.hero-sheet { margin-top:23px; overflow:hidden; border:1px solid rgba(255,255,255,.11); border-radius:12px; background:rgba(2,15,24,.48); }
.hero-sheet-row { display:grid; grid-template-columns:1.7fr .62fr 1fr .8fr; min-width:0; border-top:1px solid rgba(255,255,255,.08); }
.hero-sheet-row:first-child { border-top:0; }
.hero-sheet-row span { min-width:0; padding:14px 12px; overflow:hidden; border-left:1px solid rgba(255,255,255,.07); color:rgba(255,255,255,.7); font:500 9px/1.35 var(--font-mono); text-overflow:ellipsis; white-space:nowrap; }
.hero-sheet-row span:first-child { border-left:0; }
.hero-sheet-head { background:rgba(47,121,171,.18); }
.hero-sheet-head span { color:var(--c-brand-200); font-weight:750; letter-spacing:.045em; text-transform:uppercase; }
.hero-sheet-row .hero-sheet-ok,.hero-sheet-row .hero-sheet-check { display:flex; align-items:center; gap:6px; font-family:var(--font-body); font-weight:800; }
.hero-sheet-ok::before,.hero-sheet-check::before { content:""; width:6px; height:6px; border-radius:50%; background:var(--c-ok); box-shadow:0 0 0 4px color-mix(in srgb,var(--c-ok) 15%,transparent); }
.hero-sheet-check::before { background:var(--c-warning); box-shadow:0 0 0 4px color-mix(in srgb,var(--c-warning) 15%,transparent); }
.hero-panel-footnote { margin:18px 0 0; color:rgba(255,255,255,.52); font-size:11px; line-height:1.55; }

.public-site .hero-tour-panel .market-analysis-card { height:100%; padding:0; border:0; background:transparent; box-shadow:none; }
.public-site .hero-tour-panel .analysis-card-header { padding-bottom:16px; border-bottom:1px solid rgba(255,255,255,.1); }
/* LE PANNEAU D'APERÇU EST SOMBRE DANS LES DEUX THÈMES — c'est une vitrine
   posée sur la page d'accueil, pas une surface de l'application. Mais ses
   composants tiraient leurs couleurs des jetons de la PAGE, qui basculent,
   eux. En thème clair, la pastille « Terminée » et les trois pastilles
   d'icône devenaient des disques presque blancs incrustés dans une carte
   sombre (constaté le 2026-08-23), et le bloc de score passait d'un dégradé
   à un aplat.
   Les jetons sémantiques sont donc redéfinis ICI, aux valeurs du thème
   sombre : tout ce que le panneau contient suit — y compris ce qu'on y
   ajoutera plus tard — sans qu'aucune règle n'ait à nommer un composant. */
.public-site .market-hero-media {
  --c-ok:        #5cc79f;
  --c-ok-bg:     #10322a;
  --c-warn:      #e0ac5a;
  --c-warn-bg:   #382b16;
  --c-danger:    #f08a85;
  --c-danger-bg: #3a1e1d;
  --c-brand-700: #17456d;
  /* Ces deux-là prennent la valeur du thème CLAIR, et ce n'est pas une
     erreur : sur un fond sombre, c'est elle qui se lit. Les en-têtes du
     tableau d'exemple sortaient en bleu foncé sur bleu foncé en thème
     sombre, et la pastille « XML » en bleu clair sous du texte blanc. */
  --c-brand-200: #c2dbec;
  --c-brand-600: #1d6295;
}
/* Le dégradé du bloc de score n'est pas exprimable en jeton : il est posé
   ici pour ne plus dépendre du thème de la page. */
.public-site .hero-tour-panel .analysis-score { background: linear-gradient(135deg, #17456d, #0e2c47); }
.public-site .hero-tour-panel .analysis-card-header strong,.public-site .hero-tour-panel .analysis-file strong,.public-site .hero-tour-panel .analysis-score strong,.public-site .hero-tour-panel .analysis-findings strong { color:#fff; }
.public-site .hero-tour-panel .analysis-example-tag,.public-site .hero-tour-panel .analysis-file small,.public-site .hero-tour-panel .analysis-score span,.public-site .hero-tour-panel .analysis-findings small { color:rgba(255,255,255,.56); }
/* La pastille « Terminée » N'EST PAS une légende secondaire. Rangée avec les
   sous-titres de la carte, elle recevait leur blanc à 56 % — posé sur son
   propre fond vert sombre, ce gris pâle sur vert sombre ne se lisait plus.
   Elle garde donc le vert de l'état, qui est ce qu'elle annonce. */
.public-site .hero-tour-panel .analysis-card-header small { color: var(--c-ok); }
.public-site .hero-tour-panel .analysis-file,.public-site .hero-tour-panel .analysis-findings > div { border-color:rgba(255,255,255,.1); background:rgba(3,17,27,.38); }
.public-site .hero-tour-panel .score-ring { border-color:rgba(255,255,255,.13); }
.public-site .hero-tour-panel .score-ring::before { border-top-color:var(--c-brand-300); }

.hero-xml-heading small { display:inline-flex; align-items:center; gap:7px; color:#8ae0b7; }
.hero-xml-code { margin:22px 0 0; padding:22px; overflow:auto; border:1px solid rgba(255,255,255,.09); border-radius:12px; background:#03121d; color:rgba(255,255,255,.58); font:500 11px/1.9 var(--font-mono); tab-size:2; }
.hero-xml-code span { color:#69b7ec; }.hero-xml-code b { color:#f2c879; font-weight:600; }.hero-xml-code em{color:#78ddb0;font-style:normal}
.hero-xml-proof { display:flex; align-items:center; gap:13px; margin-top:18px; padding:14px 16px; border:1px solid rgba(74,198,137,.2); border-radius:12px; background:rgba(74,198,137,.08); }
.hero-xml-proof > span { display:grid; place-items:center; flex:0 0 27px; height:27px; border-radius:50%; background:#2a9d68; color:#fff; font-size:12px; }
.hero-xml-proof div { display:grid; gap:3px; }.hero-xml-proof strong{color:#fff;font-size:11px}.hero-xml-proof small{color:rgba(255,255,255,.55);font-size:9px;line-height:1.4}
.hero-tour-progress { position:relative; z-index:2; display:grid; grid-template-columns:repeat(3,1fr); gap:7px; padding:0 24px 22px; }
.hero-tour-progress span { position:relative; height:2px; overflow:hidden; border-radius:99px; background:rgba(255,255,255,.12); }
.hero-tour-progress span::after { content:""; position:absolute; inset:0; background:linear-gradient(90deg,var(--c-brand-300),var(--c-teal-400)); transform:scaleX(0); transform-origin:left; }
.hero-tour-progress span.is-active::after { animation:hero-tour-timer 3s linear both; }
.hero-tour-progress span.is-active::after { transform:scaleX(1); }
.is-paused .hero-tour-progress span.is-active::after { animation-play-state:paused; }
@keyframes hero-tour-timer { from{transform:scaleX(0)} to{transform:scaleX(1)} }

@media(max-width:1050px){
  .public-site .market-hero-media{min-height:620px}.hero-tour-panels{min-height:440px}.hero-tour-panel{min-height:405px}
}
@media(max-width:760px){
  .marketing-header.is-scrolled .marketing-header-inner{min-height:0}
  .public-site .market-hero-media{min-height:0;border-radius:22px}
  .hero-tour-topbar{padding-inline:18px}.hero-tour-topbar small{display:none}
  .hero-tour-tabs{margin:14px 14px 0}.hero-tour-tabs button{gap:5px;font-size:10px}.hero-tour-tabs button span{display:none}
  .hero-tour-panels{min-height:0;padding:14px}.hero-tour-panel{min-height:390px;padding:16px}
  .hero-sheet-row{grid-template-columns:1.4fr .55fr .8fr}.hero-sheet-row span:nth-child(3){display:none}.hero-sheet-row span:nth-child(4){border-left:1px solid rgba(255,255,255,.07)}
  .hero-sheet-row span{padding:13px 8px;font-size:8px}.hero-panel-heading{gap:10px}.hero-panel-heading small{font-size:8px}
  .hero-tour-progress{padding:0 14px 16px}.hero-xml-code{padding:16px;font-size:9px}
}
@media(max-width:420px){
  .hero-tour-panel{min-height:405px}.hero-sheet-row{grid-template-columns:1.45fr .55fr .8fr}.hero-sheet-row span{font-size:7px}.hero-panel-footnote{font-size:10px}
  .public-site .hero-tour-panel .analysis-findings{grid-template-columns:1fr}.public-site .hero-tour-panel .analysis-findings>div{min-height:0}
}
@media(prefers-reduced-motion:reduce){
  .marketing-header,.hero-tour-tabs button{transition:none}.hero-tour-panel.is-active,.hero-tour-progress span.is-active::after{animation:none}.public-scroll-progress span{will-change:auto}
}

/* ============================================================
   SITE PUBLIC — RYTHME COMPACT + LANGAGE DE MOUVEMENT HOMOGÈNE
   Un même signal bleu/vert circule dans toute la page : filets, tableaux,
   parcours, médias et actions. Aucun mouvement n'est indispensable au sens.
   ============================================================ */
:root { --public-motion:6.4s; --public-section-y:clamp(46px,6.5svh,76px); }

/* Menu : une rangée stable, puis un panneau réellement mobile. */
.responsive-nav-mobile-only { display:none; }
.is-nav-collapsed .marketing-actions { margin-left:auto; }
.responsive-nav-primary { margin-top:6px; background:linear-gradient(145deg,var(--c-brand-500),var(--c-brand-700)); color:#fff; }
.responsive-nav-primary:hover { background:linear-gradient(145deg,var(--c-brand-600),var(--c-brand-800)); color:#fff; }
@media(max-width:720px){
  .marketing-header-inner{min-height:64px;flex-wrap:nowrap;gap:8px;padding:8px 12px}
  .marketing-brand .brand-logo{height:auto;width:118px}
  .marketing-actions{flex:0 0 auto;flex-basis:auto;gap:5px;margin-left:auto}
  .marketing-actions>.marketing-login,.marketing-actions>.marketing-cta{display:none}
  .marketing-actions .public-utils{gap:5px}
  .responsive-nav-mobile-only{display:flex}
  .responsive-nav-panel{border-radius:16px;box-shadow:0 24px 60px -28px rgba(8,31,48,.72)}
}
@media(max-width:360px){
  .marketing-brand .brand-logo{width:102px}.marketing-header-inner{gap:5px}.marketing-actions .util-btn,.marketing-actions .lang-switcher-trigger-icon{width:30px;height:30px}
}

/* Moins de blanc : les sections gardent leur respiration sans produire de
   grands écrans presque vides entre le titre et le contenu. */
.public-site .market-section { padding-top:var(--public-section-y); padding-bottom:var(--public-section-y); }
.public-site .market-section-heading p:last-child { margin-top:12px; line-height:1.58; }
.public-site .market-capabilities { gap:12px; margin-top:-34px; }
.public-site .market-capabilities article { padding:20px; }
.public-site .home-visual-story,.public-site .home-visual-media { min-height:570px; }
.public-site .home-visual-copy { padding:clamp(42px,5.5vw,76px); }
.public-site .home-visual-copy>p:not(.market-kicker) { margin-top:17px; }
.public-site .home-visual-steps { margin-top:26px; }
.public-site .home-visual-steps li { padding:13px 0; }
.public-site .problem-stage { margin-top:clamp(30px,4vw,48px); gap:clamp(28px,4vw,60px); }
.public-site .problem-list li { padding:18px 0; }
.public-site .pillar-exchanges { margin-top:clamp(28px,4vw,46px); }
.public-site .pillar-exchange { padding:20px 0; }
.public-site .market-demo { padding:clamp(54px,6vw,86px) var(--gutter); }
.public-site .market-demo-inner { gap:clamp(28px,4vw,56px); }
.public-site .deployment-section { padding-top:clamp(54px,6vw,84px); padding-bottom:clamp(54px,6vw,84px); }
.public-site .desktop-secondary { padding-top:clamp(44px,5vw,66px); padding-bottom:clamp(44px,5vw,66px); }
.public-site .workflow-track,.public-site .benefit-axes,.public-site .rule-sources,.public-site .audience-flow,.public-site .home-guide-grid { margin-top:clamp(28px,4vw,46px); }
.public-site .transform-illustration { margin-top:clamp(34px,4.5vw,58px); }
.public-site .validation-showcase,.public-site .validation-copy { padding-top:clamp(56px,6vw,86px); padding-bottom:clamp(56px,6vw,86px); }
.public-site .validation-copy ul { margin-top:28px; gap:12px; }
.public-site .module-explorer { margin-top:clamp(30px,4vw,50px); }
.public-site .trust-group-label { margin-top:34px; }
.public-site .trust-rows>div { padding:17px 0; }
.public-site .home-guides-all { margin-top:40px; }
.public-site .home-guide-card { min-height:245px; }
.public-site .market-approach .approach-image { min-height:520px; padding:clamp(48px,6vw,76px); }
.public-site .market-approach .approach-copy { padding-top:clamp(56px,6vw,84px); padding-bottom:clamp(56px,6vw,84px); }
.public-site .approach-points { margin-top:28px; }
.public-site .market-final-cta { margin:clamp(54px,6vw,82px) auto; padding:clamp(40px,5vw,62px); }
@media(max-width:760px){
  :root{--public-section-y:clamp(42px,11vw,60px)}
  .public-site .market-capabilities{margin-top:20px}.public-site .home-visual-story,.public-site .home-visual-media{min-height:0}.public-site .home-visual-media{height:390px}
  .public-site .problem-stage{margin-top:28px}.public-site .market-validation{gap:0}.public-site .market-approach .approach-image{min-height:390px}
}

/* Signature animée commune. */
.public-site .market-kicker>span { position:relative; overflow:hidden; }
.public-site .market-kicker>span::after {
  content:""; position:absolute; inset:0; transform:translateX(-130%);
  background:linear-gradient(90deg,transparent,var(--c-teal-400),transparent);
  animation:public-signal var(--public-motion) ease-in-out infinite;
}
@keyframes public-signal { 0%,18%{transform:translateX(-130%)} 58%,100%{transform:translateX(130%)} }

.public-site .blueprint-layer { animation:public-grid-drift 18s linear infinite; }
@keyframes public-grid-drift { to{background-position:48px 32px} }

.public-site .market-primary,.public-site .market-ghost,.public-site .marketing-cta {
  position:relative; overflow:hidden; isolation:isolate;
}
.public-site .market-primary::before,.public-site .market-ghost::before,.public-site .marketing-cta::before {
  content:""; position:absolute; z-index:-1; top:-80%; bottom:-80%; left:-45%; width:28%;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.34),transparent); transform:skewX(-18deg);
  animation:public-button-sheen 7.2s ease-in-out infinite;
}
@keyframes public-button-sheen { 0%,58%{left:-45%} 82%,100%{left:125%} }
.public-site .market-primary span,.public-site .desktop-secondary-link span,.public-site .home-guide-card a span { animation:public-arrow 2.4s ease-in-out infinite; }
@keyframes public-arrow { 0%,100%{transform:translateX(0)} 50%{transform:translateX(4px)} }

/* Cartes : même filet lumineux, même élévation, quelle que soit la section. */
.public-site .market-capabilities article,.public-site .workflow-track article,.public-site .project-phases article {
  position:relative; overflow:hidden; transition:transform .28s var(--ease),border-color .28s var(--ease),box-shadow .28s var(--ease);
}
.public-site .market-capabilities article::after,.public-site .workflow-track article::after,.public-site .project-phases article::after {
  content:""; position:absolute; left:-42%; top:0; width:38%; height:2px;
  background:linear-gradient(90deg,transparent,var(--c-brand-400),var(--c-teal-400),transparent);
  animation:public-card-scan 8s linear infinite;
}
.public-site .market-capabilities article:nth-child(2n)::after,.public-site .workflow-track article:nth-of-type(2n)::after,.public-site .project-phases article:nth-child(2n)::after{animation-delay:-3.4s}
@keyframes public-card-scan { 0%{left:-42%} 55%,100%{left:115%} }
.public-site .market-capabilities article:hover,.public-site .workflow-track article:hover,.public-site .project-phases article:hover { transform:translateY(-5px); border-color:var(--c-brand-300); box-shadow:0 22px 45px -32px rgba(8,31,48,.75); }
.public-site .market-capabilities strong { animation:public-number-breathe 4.8s ease-in-out infinite; }
@keyframes public-number-breathe { 0%,100%{text-shadow:0 0 0 transparent} 50%{text-shadow:0 0 18px color-mix(in srgb,var(--c-brand-400) 42%,transparent)} }

/* Visuels : mouvements lents identiques, jamais de secousse. */
.public-site .home-visual-media video,.public-site .problem-photo img { animation:public-media-breathe 16s ease-in-out infinite alternate; }
@keyframes public-media-breathe { from{transform:scale(1.015)} to{transform:scale(1.07)} }
.public-site .home-visual-media:hover video,.public-site .problem-photo:hover img { animation-play-state:paused; }
.public-site .market-approach .approach-image {
  overflow:hidden;
  background:
    linear-gradient(120deg,rgba(5,28,47,.54),rgba(5,28,47,.84)),
    url("images/hero-regulatory-workflow.jpg") center / cover no-repeat;
  animation:public-photo-pan 18s ease-in-out infinite alternate;
}
@keyframes public-photo-pan { from{background-position:46% center} to{background-position:58% center} }
.public-site .approach-tag { animation:public-tag-float 5.8s ease-in-out infinite; }
.public-site .approach-tag:nth-child(2){animation-delay:-1.1s}.public-site .approach-tag:nth-child(3){animation-delay:-2.2s}.public-site .approach-tag:nth-child(4){animation-delay:-3.3s}.public-site .approach-tag:nth-child(5){animation-delay:-4.4s}
@keyframes public-tag-float { 0%,100%{transform:translateY(0) rotate(var(--r,0deg))} 50%{transform:translateY(-8px) rotate(var(--r,0deg))} }

/* Flux, tableaux et listes : le même signal se propage dans les données. */
.public-site .workflow-arrow { animation:public-flow-arrow 2.2s ease-in-out infinite; }
@keyframes public-flow-arrow { 0%,100%{opacity:.35;transform:translateX(-2px)} 50%{opacity:1;transform:translateX(3px)} }
.public-site .market-demo-diagram { animation:public-diagram-breathe 5.5s ease-in-out infinite; }
@keyframes public-diagram-breathe { 0%,100%{filter:drop-shadow(0 0 0 transparent)} 50%{filter:drop-shadow(0 8px 14px color-mix(in srgb,var(--c-brand-400) 24%,transparent))} }
.public-site .transform-arrow { animation:public-transform-pulse 2.8s ease-in-out infinite; }
@keyframes public-transform-pulse { 0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--c-brand-400) 0%,transparent)} 50%{box-shadow:0 0 0 9px color-mix(in srgb,var(--c-brand-400) 12%,transparent)} }
.public-site .validation-window { position:relative; }
.public-site .validation-window::after { content:""; position:absolute; left:0; right:0; top:-8%; height:48px; pointer-events:none; background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--c-brand-300) 12%,transparent),transparent); animation:public-table-scan 6.8s linear infinite; }
@keyframes public-table-scan { to{top:108%} }
.public-site .rule-source-bar i { position:relative; overflow:hidden; background:linear-gradient(90deg,var(--c-brand-600),var(--c-teal-500)); }
.public-site .rule-source-bar i::after { content:""; position:absolute; inset:0; transform:translateX(-110%); background:linear-gradient(90deg,transparent,rgba(255,255,255,.72),transparent); animation:public-bar-sheen 4.8s ease-in-out infinite; }
@keyframes public-bar-sheen { 55%,100%{transform:translateX(110%)} }
.public-site .desktop-mini-line { overflow:hidden; background:linear-gradient(90deg,var(--border) 20%,var(--c-brand-300) 48%,var(--border) 76%); background-size:240% 100%; animation:public-line-load 3.2s linear infinite; }
@keyframes public-line-load { to{background-position:-240% 0} }

/* Les longues listes ne restent plus inertes : une réponse légère au survol
   et un repère animé suffisent, sans transformer chaque phrase en carte. */
.public-site .problem-list li,.public-site .pillar-exchange,.public-site .return-steps li,.public-site .trust-rows>div,.public-site .rule-sources li,.public-site .approach-points>div,.public-site .platform-feature-list li {
  transition:transform .25s var(--ease),background-color .25s var(--ease),border-color .25s var(--ease);
}
.public-site .problem-list li:hover,.public-site .pillar-exchange:hover,.public-site .return-steps li:hover,.public-site .trust-rows>div:hover,.public-site .rule-sources li:hover { transform:translateX(5px); }
.public-site .problem-list li:hover,.public-site .pillar-exchange:hover,.public-site .trust-rows>div:hover { border-color:var(--c-brand-300); }
.public-site .return-steps { position:relative; }
.public-site .return-steps::before { content:""; position:absolute; left:-4px; top:0; width:7px; height:7px; border-radius:50%; background:var(--c-teal-500); box-shadow:0 0 0 5px color-mix(in srgb,var(--c-teal-500) 12%,transparent); animation:public-return-signal 7s ease-in-out infinite; }
@keyframes public-return-signal { 0%,100%{top:0} 50%{top:calc(100% - 7px)} }
.public-site .audience-flow li { position:relative; padding-left:14px; }
.public-site .audience-flow li::before { content:""; position:absolute; left:0; top:.58em; width:5px; height:5px; border-radius:50%; background:var(--c-brand-400); animation:live-pulse 2.6s ease-in-out infinite; }
.public-site .audience-flow li:nth-child(2n)::before{animation-delay:-.8s}.public-site .audience-flow li:nth-child(3n)::before{animation-delay:-1.6s}
.public-site .home-guide-card::before { content:""; position:absolute; z-index:2; left:-45%; top:0; width:42%; height:2px; background:linear-gradient(90deg,transparent,var(--c-brand-400),var(--c-teal-400),transparent); animation:public-card-scan 8.4s linear infinite; }
.public-site .home-guide-card:nth-child(2n)::before{animation-delay:-3.2s}
.public-site .module-nav-item.is-active .module-nav-icon,.public-site .module-op-status { animation:public-module-pulse 3.2s ease-in-out infinite; }
@keyframes public-module-pulse { 0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--c-brand-400) 0%,transparent)} 50%{box-shadow:0 0 0 7px color-mix(in srgb,var(--c-brand-400) 11%,transparent)} }

/* Le mouvement d'entrée est plus court et identique dans toutes les bandes. */
.js .public-site [data-reveal] { transform:translateY(12px); transition-duration:.58s; }
.js .public-site [data-reveal-group]>* { transform:translateY(10px); transition-duration:.46s; }

@media(prefers-reduced-motion:reduce){
  .public-site .market-kicker>span::after,.public-site .blueprint-layer,.public-site .market-primary::before,.public-site .market-ghost::before,.public-site .marketing-cta::before,
  .public-site .market-primary span,.public-site .desktop-secondary-link span,.public-site .home-guide-card a span,.public-site .market-capabilities article::after,.public-site .workflow-track article::after,.public-site .project-phases article::after,
  .public-site .market-capabilities strong,.public-site .home-visual-media video,.public-site .problem-photo img,.public-site .market-approach .approach-image,.public-site .approach-tag,.public-site .workflow-arrow,
  .public-site .market-demo-diagram,.public-site .transform-arrow,.public-site .validation-window::after,.public-site .rule-source-bar i::after,.public-site .desktop-mini-line,.public-site .return-steps::before,
  .public-site .audience-flow li::before,.public-site .home-guide-card::before,.public-site .module-nav-item.is-active .module-nav-icon,.public-site .module-op-status { animation:none!important; }
  .public-site .home-visual-media video,.public-site .problem-photo img { transform:none; }
  .public-site .market-approach .approach-image { background-position:center; }
}

/* Correction mesurée sur la capture 1920×1080 : le héros héritait d'une
   hauteur minimale de 820px ET de 4 à 7,5rem de padding. Le contenu était
   donc centré au milieu d'un grand vide sous la bannière bêta. Ici la
   hauteur redevient celle du contenu réel, avec un départ proche du haut. */
@media(min-width:821px){
  .marketing-header-inner { max-width:1680px; gap:18px; }
  .marketing-links { gap:16px; }
  .marketing-actions { gap:10px; }
  .public-site .market-hero { min-height:0; }
  .public-site .market-hero-inner {
    min-height:0; align-items:start;
    padding-top:clamp(22px,2vw,34px);
    padding-bottom:clamp(32px,2.8vw,44px);
  }
  .public-site .market-hero h1 {
    max-width:15ch;
    font-size:clamp(3rem,3.9vw,4.5rem);
    line-height:.98;
  }
  .public-site .market-hero-lead { margin-top:16px; max-width:58ch; }
  .public-site .market-hero-actions { margin-top:24px; }
  .public-site .market-hero-note { margin-top:18px; }
  .public-site .market-hero-media { min-height:0; }
  .public-site .hero-tour-panels { min-height:0; padding-top:18px; padding-bottom:12px; }
  .public-site .hero-tour-panel { min-height:350px; }
  .public-site .market-capabilities { max-width:1320px; margin-top:-34px; }
}
@media(min-width:821px) and (max-width:1180px){
  .public-site .market-hero-inner{padding-top:32px}.public-site .market-hero h1{font-size:clamp(3rem,6vw,4.4rem)}
  .public-site .hero-tour-panel{min-height:355px}
}
@media(min-width:1440px){
  #appHeaderResponsive:not(.is-nav-collapsed){
    position:relative;
    display:grid;
    grid-template-columns:max-content minmax(0,1fr) max-content;
    align-items:center;
    column-gap:18px;
  }
  #appHeaderResponsive:not(.is-nav-collapsed) .responsive-nav-dropdown{position:absolute}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-links{justify-self:center;margin-left:0}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-actions{justify-self:end}
}
@media(min-width:1440px) and (max-width:1599.98px){
  #appHeaderResponsive:not(.is-nav-collapsed){padding-inline:14px;column-gap:12px}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-brand .brand-logo{width:128px;height:auto}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-links{gap:10px}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-links a{font-size:.8rem}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-actions{gap:6px}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-actions .util-btn,
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-actions .lang-switcher-trigger-icon{width:38px;height:38px}
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-login,
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-cta{padding-inline:14px;font-size:.8rem}
}

/* Stabilisation finale du héros : les trois états ont exactement la même
   hauteur. Le changement d'étape ne déplace plus le reste de la page. */
@media(min-width:821px){
  .public-site .market-hero-inner{
    max-width:1480px;
    grid-template-columns:minmax(0,1.15fr) minmax(520px,.85fr);
    gap:clamp(38px,4vw,62px);
  }
  .public-site .market-hero h1{max-width:none;font-size:clamp(3rem,3.45vw,4.1rem);line-height:1.02}
  .public-site .hero-tour-panels{height:390px;min-height:390px}
  .public-site .hero-tour-panel{height:360px;min-height:360px;overflow:hidden}
  .public-site .hero-tour-panel .market-analysis-card{gap:8px}
  .public-site .hero-tour-panel .analysis-card-header{padding:0 4px 9px}
  .public-site .hero-tour-panel .analysis-example-tag{margin:0;padding:0 4px}
  .public-site .hero-tour-panel .analysis-file{gap:9px;padding:9px}
  .public-site .hero-tour-panel .analysis-file>span{width:36px;height:36px}
  .public-site .hero-tour-panel .analysis-score{padding:9px 12px}
  .public-site .hero-tour-panel .analysis-score strong{font-size:1.8rem}
  .public-site .hero-tour-panel .score-ring{width:50px;height:50px}
  .public-site .hero-tour-panel .score-ring span{width:38px;height:38px}
  .public-site .hero-tour-panel .analysis-findings{grid-template-columns:repeat(3,minmax(0,1fr))!important;gap:7px}
  .public-site .hero-tour-panel .analysis-findings>div{grid-column:auto!important;min-height:72px!important;padding:8px;gap:5px}
  .public-site .hero-tour-panel .analysis-findings i{width:22px;height:22px}
  .public-site .hero-tour-panel .analysis-findings strong{font-size:.68rem;line-height:1.2}
  .public-site .hero-tour-panel .analysis-findings small{display:none}
}
@media(min-width:821px) and (max-width:1180px){
  .public-site .market-hero-inner{grid-template-columns:minmax(0,1fr) minmax(430px,.9fr)}
}

/* Séparation nette entre les indicateurs et la séquence vidéo. */
.public-site .market-capabilities{margin-bottom:36px}
@media(max-width:760px){.public-site .market-capabilities{margin-bottom:24px}}

/* Triptyque métier : davantage d'images et de mouvement, sans hauteur
   variable. Les trois plans restent parfaitement stables pendant l'animation. */
.public-site .home-media-sequence {
  position:relative; overflow:hidden; isolation:isolate;
  padding:clamp(28px,3.5vw,52px) max(var(--gutter),calc((100vw - 1480px)/2 + var(--gutter))) clamp(48px,6vw,82px);
  color:#fff; background:
    radial-gradient(circle at 12% 20%,rgba(49,151,224,.2),transparent 28%),
    radial-gradient(circle at 92% 88%,rgba(36,180,161,.13),transparent 30%),
    var(--c-brand-950);
}
.public-site .home-media-sequence::before {
  content:""; position:absolute; inset:0; z-index:-1; opacity:.32; pointer-events:none;
  background-image:linear-gradient(rgba(122,176,219,.12) 1px,transparent 1px),linear-gradient(90deg,rgba(122,176,219,.12) 1px,transparent 1px);
  background-size:48px 48px; animation:home-media-grid-drift 22s linear infinite;
}
@keyframes home-media-grid-drift { to{background-position:48px 48px} }
.public-site .home-signal-rail {
  position:relative; z-index:2; height:42px; margin:0 auto 18px; overflow:hidden;
  border:1px solid rgba(255,255,255,.14); border-radius:999px;
  background:rgba(3,19,31,.56); backdrop-filter:blur(12px);
}
.public-site .home-signal-rail::before,.public-site .home-signal-rail::after {
  content:""; position:absolute; z-index:2; top:0; bottom:0; width:70px; pointer-events:none;
}
.public-site .home-signal-rail::before { left:0; background:linear-gradient(90deg,var(--c-brand-950),transparent); }
.public-site .home-signal-rail::after { right:0; background:linear-gradient(270deg,var(--c-brand-950),transparent); }
.public-site .home-signal-track {
  display:flex; align-items:center; gap:0; width:max-content; height:100%;
  color:rgba(255,255,255,.72); font:600 10px/1 var(--font-mono); letter-spacing:.12em; text-transform:uppercase;
  animation:home-signal-scroll 24s linear infinite;
}
.public-site .home-signal-set { display:inline-flex; flex:0 0 auto; align-items:center; gap:22px; padding-right:22px; }
.public-site .home-signal-track i { width:5px; height:5px; flex:0 0 auto; border-radius:50%; background:var(--c-teal-400); box-shadow:0 0 0 5px rgba(56,202,178,.09); }
@keyframes home-signal-scroll { to{transform:translateX(-50%)} }
.public-site .home-media-grid {
  position:relative; z-index:1; display:grid; grid-template-columns:repeat(12,minmax(0,1fr));
  gap:16px; height:clamp(390px,31vw,500px); max-width:1480px; margin:0 auto;
}
.public-site .home-media-card {
  position:relative; min-width:0; height:100%; margin:0; overflow:hidden;
  border:1px solid rgba(255,255,255,.18); border-radius:28px 8px 28px 8px;
  background:#0a2940; box-shadow:0 28px 70px -40px rgba(0,0,0,.95);
  transition:transform .35s var(--ease),border-color .35s var(--ease),box-shadow .35s var(--ease);
}
.public-site .home-media-card-source { grid-column:span 5; }
.public-site .home-media-card-control { grid-column:span 3; border-radius:8px 28px 8px 28px; }
.public-site .home-media-card-output { grid-column:span 4; }
.public-site .home-media-card:hover { transform:translateY(-7px); border-color:rgba(119,199,255,.58); box-shadow:0 35px 80px -38px rgba(0,0,0,.95); }
.public-site .home-media-card img,.public-site .home-media-card video {
  display:block; width:100%; height:100%; object-fit:cover; transform:scale(1.05);
  animation:home-media-pan 15s ease-in-out infinite alternate; will-change:transform;
}
.public-site .home-media-card-control img,.public-site .home-media-card-control video { object-position:55% center; animation-name:home-media-pan-alt; animation-delay:-4s; }
.public-site .home-media-card-output img { object-position:63% center; animation-delay:-8s; }
@keyframes home-media-pan { from{transform:scale(1.04) translate3d(-1.4%,0,0)} to{transform:scale(1.11) translate3d(1.4%,-1%,0)} }
@keyframes home-media-pan-alt { from{transform:scale(1.06) translate3d(0,1%,0)} to{transform:scale(1.13) translate3d(-1.5%,-1%,0)} }
.public-site .home-media-card:hover img,.public-site .home-media-card:hover video { animation-play-state:paused; }
.public-site .home-media-product-screen {
  position:absolute; inset:0; display:grid; align-content:start; gap:13px; overflow:hidden;
  padding:66px 18px 118px; color:#fff;
  background:radial-gradient(circle at 76% 12%,rgba(67,171,235,.28),transparent 34%),linear-gradient(145deg,#123d5a,#071e30 72%);
}
.public-site .home-media-product-screen::after { content:""; position:absolute; z-index:3; left:10px; right:10px; top:64px; height:1px; pointer-events:none; background:linear-gradient(90deg,transparent,rgba(87,217,180,.84),transparent); box-shadow:0 0 14px rgba(87,217,180,.55); animation:home-product-scan 4.6s ease-in-out infinite; }
@keyframes home-product-scan { 0%,12%{transform:translateY(0);opacity:0} 20%{opacity:1} 76%{opacity:.65} 88%,100%{transform:translateY(235px);opacity:0} }
.public-site .home-media-product-head { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:0 4px 12px; border-bottom:1px solid rgba(255,255,255,.13); }
.public-site .home-media-product-head span { color:var(--c-brand-200); font:600 9px/1 var(--font-mono); letter-spacing:.11em; text-transform:uppercase; }
.public-site .home-media-product-head strong { color:#fff; font:700 1.45rem/1 var(--font-display); }
.public-site .home-media-product-rows { display:grid; gap:7px; }
.public-site .home-media-product-rows div { display:grid; grid-template-columns:1fr auto; align-items:center; gap:8px; min-height:34px; padding:0 10px; border:1px solid rgba(255,255,255,.1); border-radius:9px; background:rgba(3,18,29,.42); }
.public-site .home-media-product-rows div { animation:home-control-row 5.2s ease-in-out infinite; }
.public-site .home-media-product-rows div:nth-child(2){animation-delay:-3.9s}.public-site .home-media-product-rows div:nth-child(3){animation-delay:-2.6s}.public-site .home-media-product-rows div:nth-child(4){animation-delay:-1.3s}
@keyframes home-control-row { 0%,70%,100%{border-color:rgba(255,255,255,.1);background:rgba(3,18,29,.42)} 82%{border-color:rgba(87,217,180,.38);background:rgba(26,104,126,.32)} }
.public-site .home-media-product-rows span { overflow:hidden; color:rgba(255,255,255,.76); font:600 8px/1 var(--font-mono); text-overflow:ellipsis; white-space:nowrap; }
.public-site .home-media-product-rows i { width:8px; height:8px; border-radius:50%; animation:home-product-status 2.8s ease-in-out infinite; }
.public-site .home-media-product-rows .is-ok { background:#47d5ad; box-shadow:0 0 0 5px rgba(71,213,173,.1); }
.public-site .home-media-product-rows .is-alert { background:#ffb94f; box-shadow:0 0 0 5px rgba(255,185,79,.1); animation-delay:-1.4s; }
@keyframes home-product-status { 50%{box-shadow:0 0 0 8px transparent} }
.public-site .home-media-product-foot { display:flex; align-items:center; gap:9px; padding:10px 12px; border-radius:10px; background:rgba(214,76,70,.14); color:#fff; }
.public-site .home-media-product-foot b { display:grid; place-items:center; width:24px; height:24px; border-radius:50%; background:#8c302c; font-size:.72rem; }
.public-site .home-media-product-foot span { font-size:.68rem; font-weight:750; }
.public-site .home-media-card-control::before { background:linear-gradient(180deg,rgba(2,17,28,.02) 50%,rgba(2,17,28,.94) 100%); }
.public-site .home-media-card::before {
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg,rgba(2,17,28,.05) 30%,rgba(2,17,28,.92) 100%),linear-gradient(115deg,rgba(27,123,191,.18),transparent 48%);
}
.public-site .home-media-card::after {
  content:""; position:absolute; z-index:2; top:-35%; bottom:-35%; left:-42%; width:20%; pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.18),transparent); transform:skewX(-18deg);
  animation:home-media-sweep 8.5s ease-in-out infinite;
}
.public-site .home-media-card:nth-child(2)::after { animation-delay:-2.8s; }
.public-site .home-media-card:nth-child(3)::after { animation-delay:-5.6s; }
@keyframes home-media-sweep { 0%,58%{left:-42%} 82%,100%{left:130%} }
.public-site .home-media-tags { position:absolute; z-index:7; top:20px; left:20px; display:flex; gap:8px; }
.public-site .home-media-tags span {
  display:inline-flex; align-items:center; min-height:28px; padding:0 10px;
  border:1px solid rgba(255,255,255,.26); border-radius:999px; background:rgba(3,19,31,.58);
  color:#fff; backdrop-filter:blur(10px); font:600 9px/1 var(--font-mono); letter-spacing:.1em;
}
.public-site .home-media-tags span:first-child { color:var(--c-brand-200); }
.public-site .home-media-tags span:first-child { animation:home-step-number 4.2s ease-in-out infinite; }
.public-site .home-media-card:nth-child(2) .home-media-tags span:first-child{animation-delay:-2.8s}.public-site .home-media-card:nth-child(3) .home-media-tags span:first-child{animation-delay:-1.4s}
@keyframes home-step-number { 50%{color:#fff;border-color:rgba(87,217,180,.75);box-shadow:0 0 18px rgba(87,217,180,.24)} }
.public-site .home-source-record,.public-site .home-output-code { position:absolute; z-index:3; top:74px; border:1px solid rgba(119,199,255,.28); background:rgba(3,19,31,.67); box-shadow:0 18px 38px -24px rgba(0,0,0,.95); backdrop-filter:blur(13px); }
.public-site .home-source-record { right:24px; display:grid; gap:8px; min-width:210px; padding:15px 16px; border-radius:15px 4px 15px 4px; animation:home-data-card 5.6s ease-in-out infinite; }
.public-site .home-source-record small { color:#57d9b4; font:650 8px/1 var(--font-mono); letter-spacing:.14em; }
.public-site .home-source-record span { display:flex; justify-content:space-between; gap:14px; color:rgba(255,255,255,.72); font:600 9px/1 var(--font-mono); }
.public-site .home-source-record b { color:#fff; font-weight:700; }
.public-site .home-output-code { left:24px; right:24px; display:grid; gap:8px; padding:17px 18px; border-radius:4px 15px 4px 15px; color:rgba(255,255,255,.66); font:600 8px/1.25 var(--font-mono); animation:home-data-card 5.6s ease-in-out infinite reverse; }
.public-site .home-output-code span { overflow:hidden; white-space:nowrap; }
.public-site .home-output-code span:first-child,.public-site .home-output-code span:last-child { color:var(--c-brand-200); }
.public-site .home-output-code b { color:#57d9b4; font-weight:650; }
@keyframes home-data-card { 50%{transform:translateY(-6px);border-color:rgba(87,217,180,.54);box-shadow:0 22px 42px -22px rgba(0,0,0,.95),0 0 26px rgba(67,171,235,.1)} }
.public-site .home-media-card figcaption { position:absolute; z-index:3; left:24px; right:24px; bottom:24px; display:grid; gap:7px; }
.public-site .home-media-card figcaption small { color:var(--c-teal-300); font:600 10px/1 var(--font-mono); letter-spacing:.12em; text-transform:uppercase; }
.public-site .home-media-card figcaption strong { max-width:18ch; color:#fff; font-size:clamp(1.2rem,1.8vw,1.75rem); line-height:1.08; letter-spacing:-.025em; }
.public-site .home-media-card figcaption em { color:rgba(255,255,255,.54); font:500 9px/1.35 var(--font-body); font-style:normal; }

@media(max-width:1050px){
  .public-site .home-media-grid{grid-template-columns:repeat(2,minmax(0,1fr));height:auto}
  .public-site .home-media-card-source,.public-site .home-media-card-control{grid-column:span 1;height:430px}
  .public-site .home-media-card-output{grid-column:1/-1;height:330px}
}
@media(max-width:640px){
  .public-site .home-media-sequence{padding-top:24px;padding-bottom:50px}
  .public-site .home-signal-rail{height:38px;margin-bottom:12px}
  .public-site .home-media-grid{grid-template-columns:1fr;gap:12px}
  .public-site .home-media-card-source,.public-site .home-media-card-control,.public-site .home-media-card-output{grid-column:1;height:330px;border-radius:22px 7px 22px 7px}
  .public-site .home-media-card figcaption{left:20px;right:20px;bottom:20px}
  .public-site .home-source-record{top:70px;right:18px;min-width:190px}.public-site .home-output-code{top:70px;left:18px;right:18px}
}
@media(prefers-reduced-motion:reduce){
  .home-device-reticle,.home-device-reticle::before,.home-device-reticle::after,.home-device-status i,.problem-photo-signals span,
  .public-site .home-media-sequence::before,.public-site .home-signal-track,.public-site .home-media-card img,.public-site .home-media-card video,.public-site .home-media-card::after,.public-site .home-media-product-screen::after,.public-site .home-media-product-rows div,.public-site .home-media-product-rows i,.public-site .home-media-tags span:first-child,.public-site .home-source-record,.public-site .home-output-code{animation:none!important}
  .public-site .home-media-card img,.public-site .home-media-card video{transform:none}
}

@media(max-width:760px){
  .home-device-reticle{right:7%;top:29%;width:170px}.home-device-id{top:74px;right:18px;min-width:180px}.home-device-status{display:none}
  .problem-photo-signals{top:16px;right:16px}
}
@media(max-width:480px){
  .home-device-id{display:none}.home-device-reticle{top:24%;right:8%;width:150px}
  .problem-photo-signals span{padding-inline:8px}
}

/* ============================================================
   IMMERSION VISUELLE — une seule grammaire : circuit, signal, lumière.
   Les couches ci-dessous ne modifient ni le flux ni la hauteur des sections.
   ============================================================ */

/* Halo interactif : suit le pointeur sans déplacer les composants. */
.public-site .hero-circuit-map { display:none!important; }
.public-site .hero-tour-topbar>small,.public-site .analysis-example-tag,.public-site .home-device-id>small,.public-site .home-media-card figcaption em { display:none!important; }
.public-site .market-hero::before {
  content:""; display:block; position:absolute; z-index:0; inset:0; pointer-events:none; opacity:.9;
  background:radial-gradient(440px circle at var(--pointer-x,72%) var(--pointer-y,28%),rgba(67,171,235,.18),transparent 68%);
  transition:opacity .3s ease;
}
.public-site .market-hero-media::after {
  content:""; display:block; position:absolute; z-index:1; inset:0; pointer-events:none;
  background:radial-gradient(300px circle at var(--pointer-x,72%) var(--pointer-y,26%),rgba(87,217,180,.15),transparent 70%);
  mix-blend-mode:screen;
}
.public-site .home-visual-copy {
  background:radial-gradient(420px circle at var(--pointer-x,28%) var(--pointer-y,42%),rgba(67,171,235,.15),transparent 72%);
}

/* Les chiffres restent sur le fond du héros. La bande bleue commence seulement
   après ce bloc, avec la séquence vidéo suivante. */
.public-site .market-capabilities::before {
  content:""; position:absolute; z-index:0; left:0; right:0; top:0; bottom:0; width:auto; height:auto; pointer-events:none;
  background:var(--bg); box-shadow:0 0 0 100vmax var(--bg); clip-path:inset(0 -100vmax); opacity:1;
}
.public-site .market-capabilities { isolation:isolate; }
.public-site .market-capabilities article { z-index:1; backdrop-filter:blur(10px); }

/* Une vague de contrôle traverse les sept étapes, dans l'ordre. */
.public-site .workflow-track article { isolation:isolate; }
.public-site .workflow-track article::before {
  content:""; position:absolute; z-index:-1; inset:0; pointer-events:none; opacity:0;
  background:radial-gradient(circle at 18% 12%,color-mix(in srgb,var(--c-brand-300) 24%,transparent),transparent 56%);
  animation:workflow-stage-wave 9.8s ease-in-out infinite;
}
.public-site .workflow-track article:nth-of-type(2)::before{animation-delay:-8.4s}.public-site .workflow-track article:nth-of-type(3)::before{animation-delay:-7s}.public-site .workflow-track article:nth-of-type(4)::before{animation-delay:-5.6s}.public-site .workflow-track article:nth-of-type(5)::before{animation-delay:-4.2s}.public-site .workflow-track article:nth-of-type(6)::before{animation-delay:-2.8s}.public-site .workflow-track article:nth-of-type(7)::before{animation-delay:-1.4s}
@keyframes workflow-stage-wave { 0%,76%,100%{opacity:0} 86%{opacity:1} }

/* Les échanges et les guides ne restent jamais figés entre deux survols. */
.public-site .pillar-exchange { position:relative; overflow:hidden; }
.public-site .pillar-exchange::after {
  content:""; position:absolute; left:-30%; bottom:0; width:26%; height:1px; pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--c-brand-400),var(--c-teal-400),transparent);
  animation:pillar-signal 8.6s linear infinite;
}
.public-site .pillar-exchange:nth-child(2)::after{animation-delay:-2.15s}.public-site .pillar-exchange:nth-child(3)::after{animation-delay:-4.3s}.public-site .pillar-exchange:nth-child(4)::after{animation-delay:-6.45s}
@keyframes pillar-signal { 56%,100%{left:108%} }
.public-site .home-guide-card::after { animation:guide-mark-float 7s ease-in-out infinite; transform-origin:center; }
.public-site .home-guide-card:nth-child(2n)::after{animation-delay:-2.3s}.public-site .home-guide-card:nth-child(3n)::after{animation-delay:-4.6s}
@keyframes guide-mark-float { 0%,100%{transform:translateY(0) rotate(-2deg);opacity:.72} 50%{transform:translateY(-10px) rotate(3deg);opacity:1} }

/* Aurores lentes dans les grandes scènes sombres. */
.public-site .market-validation::after,.public-site .deployment-section::after {
  content:""; position:absolute; z-index:-1; width:54vw; height:54vw; min-width:520px; min-height:520px; border-radius:50%; pointer-events:none;
  background:radial-gradient(circle,rgba(67,171,235,.16),rgba(87,217,180,.06) 38%,transparent 68%);
  filter:blur(6px); animation:scene-aurora 12s ease-in-out infinite alternate;
}
.public-site .market-validation::after { left:calc(var(--pointer-x,22%) - 30vw); top:calc(var(--pointer-y,48%) - 27vw); }
.public-site .deployment-section::after { right:calc(100% - var(--pointer-x,78%) - 28vw); top:calc(var(--pointer-y,52%) - 27vw); animation-direction:alternate-reverse; }
@keyframes scene-aurora { from{transform:translate3d(-4%,-3%,0) scale(.9);opacity:.62} to{transform:translate3d(7%,5%,0) scale(1.12);opacity:1} }

/* Appel final : profondeur, orbites et faisceau de sortie. */
.public-site .market-final-cta { position:relative; isolation:isolate; overflow:hidden; border:1px solid rgba(119,199,255,.26); box-shadow:0 34px 74px -42px rgba(8,31,48,.88); }
.public-site .market-final-cta > * { position:relative; z-index:2; }
.public-site .market-final-cta::before {
  content:""; position:absolute; z-index:0; inset:0; pointer-events:none;
  background:
    radial-gradient(360px circle at var(--pointer-x,72%) var(--pointer-y,44%),rgba(87,217,180,.22),transparent 72%),
    linear-gradient(112deg,transparent 38%,rgba(119,199,255,.07) 50%,transparent 62%);
}
.public-site .market-final-cta::after {
  content:""; position:absolute; z-index:1; right:-90px; top:50%; width:300px; height:300px; margin-top:-150px; border:1px solid rgba(119,199,255,.28); border-radius:50%; pointer-events:none;
  box-shadow:0 0 0 38px rgba(119,199,255,.055),0 0 0 76px rgba(87,217,180,.035);
  animation:final-orbit 9s ease-in-out infinite;
}
@keyframes final-orbit { 50%{transform:rotate(180deg) scale(1.08);border-color:rgba(87,217,180,.52)} }

@media(max-width:760px){
  .public-site .market-final-cta::after{right:-170px;opacity:.55}
  .public-site .home-visual-copy{background:radial-gradient(300px circle at 50% 14%,rgba(67,171,235,.13),transparent 75%)}
}
@media(prefers-reduced-motion:reduce){
  .public-site .workflow-track article::before,.public-site .pillar-exchange::after,.public-site .home-guide-card::after,.public-site .market-validation::after,.public-site .deployment-section::after,.public-site .market-final-cta::after{animation:none!important}
}

/* Adaptation à la hauteur réelle de l'écran. Sur grand écran, le premier
   ensemble se termine au bord inférieur du viewport ; sur écran bas ou
   mobile, le contenu reprend sa hauteur naturelle et reste intégralement
   scrollable. */
@media(min-width:1181px){
  .public-site .home-opening-scene{
    display:flex; flex-direction:column;
    min-height:clamp(760px,calc(100svh - 116px),940px);
    background:var(--bg);
  }
  .public-site .home-opening-scene .market-hero{display:flex;flex:1 1 auto}
  .public-site .home-opening-scene .market-hero-inner{flex:1 1 auto;width:100%;align-items:center}
  .public-site .home-opening-scene .market-hero-copy{align-self:center}
  .public-site .home-opening-scene .market-capabilities{flex:0 0 auto}
}

/* Les bandes principales utilisent la hauteur de fenêtre pour leur rythme :
   un écran large mais peu haut ne reçoit plus les espacements d'un écran 4K. */
.public-site .market-demo{padding-top:clamp(48px,7svh,82px);padding-bottom:clamp(48px,7svh,82px)}
.public-site .deployment-section{padding-top:clamp(50px,7.2svh,82px);padding-bottom:clamp(50px,7.2svh,82px)}
.public-site .desktop-secondary{padding-top:clamp(42px,6svh,64px);padding-bottom:clamp(42px,6svh,64px)}
.public-site .validation-showcase,.public-site .validation-copy{padding-top:clamp(48px,7svh,82px);padding-bottom:clamp(48px,7svh,82px)}
.public-site .market-approach .approach-copy{padding-top:clamp(48px,7svh,80px);padding-bottom:clamp(48px,7svh,80px)}
.public-site .market-final-cta{margin-top:clamp(48px,6.5svh,74px);margin-bottom:clamp(48px,6.5svh,74px)}

@media(min-width:821px) and (max-height:820px){
  :root{--public-section-y:44px}
  .public-site .home-opening-scene{min-height:0}
  .public-site .market-hero-inner{padding-top:18px;padding-bottom:28px}
  .public-site .hero-tour-panels{height:350px;min-height:350px}
  .public-site .hero-tour-panel{height:320px;min-height:320px}
}
@media(max-width:820px){
  .public-site .home-opening-scene{min-height:0}
}

/* ============================================================
   FINITION PRODUIT — davantage de mouvement, zéro élément ajouté.
   ============================================================ */

/* Accent vivant du titre, limité à la promesse bleue. */
.public-site .market-hero h1 em{
  color:transparent;
  background:linear-gradient(100deg,var(--c-brand-600) 5%,var(--c-brand-400) 44%,var(--c-teal-700) 62%,var(--c-brand-600) 92%);
  background-size:220% 100%;
  background-clip:text;
  -webkit-background-clip:text;
  animation:hero-title-flow 7s ease-in-out infinite;
}
:root[data-theme="dark"] .public-site .market-hero h1 em{
  background-image:linear-gradient(100deg,#69b7ec 5%,#d7efff 44%,#57d9b4 62%,#69b7ec 92%);
}
@media(prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .public-site .market-hero h1 em{
    background-image:linear-gradient(100deg,#69b7ec 5%,#d7efff 44%,#57d9b4 62%,#69b7ec 92%);
  }
}
@keyframes hero-title-flow{0%,100%{background-position:0 50%}50%{background-position:100% 50%}}

/* Le fond actif glisse réellement d'un onglet à l'autre. */
.public-site [data-hero-tour]{--hero-tab-index:0}
.public-site .hero-tour-tabs{isolation:isolate;overflow:hidden}
.public-site .hero-tour-tabs::before{
  content:"";
  position:absolute;
  z-index:0;
  left:5px;
  top:5px;
  bottom:5px;
  width:calc((100% - 10px)/3);
  border-radius:9px;
  background:linear-gradient(135deg,rgba(255,255,255,.12),rgba(67,171,235,.16));
  box-shadow:0 10px 28px -20px #000,inset 0 1px 0 rgba(255,255,255,.08);
  transform:translateX(calc(var(--hero-tab-index) * 100%));
  transition:transform .46s var(--ease),background-color .3s var(--ease);
}
.public-site .hero-tour-tabs button{z-index:1}
.public-site .hero-tour-tabs button.is-active{background:transparent;box-shadow:none}

/* Les données sont contrôlées ligne par ligne, sans déplacement. */
.public-site .hero-sheet-row:not(.hero-sheet-head){animation:hero-row-check 5.6s ease-in-out infinite}
.public-site .hero-sheet-row:nth-child(2){animation-delay:-4.2s}
.public-site .hero-sheet-row:nth-child(3){animation-delay:-2.8s}
.public-site .hero-sheet-row:nth-child(4){animation-delay:-1.4s}
@keyframes hero-row-check{
  0%,68%,100%{background-color:transparent;box-shadow:inset 3px 0 0 transparent}
  80%{background-color:rgba(87,217,180,.07);box-shadow:inset 3px 0 0 rgba(87,217,180,.72)}
}

/* Lecture du XML et résultats d'analyse : même cadence que les onglets. */
.public-site .hero-xml-code{position:relative;overflow:hidden}
.public-site .hero-xml-code::before{
  content:"";
  position:absolute;
  z-index:0;
  left:0;
  right:0;
  top:-36px;
  height:36px;
  pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(67,171,235,.13),rgba(87,217,180,.08),transparent);
  animation:hero-code-scan 5.6s linear infinite;
}
.public-site .hero-xml-code code{position:relative;z-index:1}
.public-site .hero-xml-code code::after{
  content:"";
  display:inline-block;
  width:1px;
  height:1.05em;
  margin-left:5px;
  background:var(--c-teal-300);
  vertical-align:-.12em;
  animation:hero-code-cursor 1s steps(1,end) infinite;
}
@keyframes hero-code-scan{to{transform:translateY(330px)}}
@keyframes hero-code-cursor{50%{opacity:0}}
.public-site .hero-tour-panel .analysis-findings>div{animation:hero-finding-wave 5.6s ease-in-out infinite}
.public-site .hero-tour-panel .analysis-findings>div:nth-child(2){animation-delay:-3.7s}
.public-site .hero-tour-panel .analysis-findings>div:nth-child(3){animation-delay:-1.85s}
@keyframes hero-finding-wave{
  0%,70%,100%{border-color:rgba(255,255,255,.1);transform:translateY(0)}
  82%{border-color:rgba(87,217,180,.38);transform:translateY(-2px)}
}

/* Profondeur locale sur les chiffres, sans élargir leur boîte. */
.public-site .market-capabilities article::before{
  content:"";
  position:absolute;
  z-index:0;
  inset:0;
  pointer-events:none;
  opacity:.36;
  background:radial-gradient(circle at 12% 8%,color-mix(in srgb,var(--c-brand-400) 18%,transparent),transparent 52%);
  transform:scale(.92);
  transition:opacity .3s var(--ease),transform .3s var(--ease);
}
.public-site .market-capabilities article>*{position:relative;z-index:1}
.public-site .market-capabilities article:hover::before{opacity:1;transform:scale(1)}

@media(prefers-reduced-motion:reduce){
  .public-site .market-hero h1 em,
  .public-site .hero-sheet-row:not(.hero-sheet-head),
  .public-site .hero-xml-code::before,
  .public-site .hero-xml-code code::after,
  .public-site .hero-tour-panel .analysis-findings>div{animation:none!important}
  .public-site .hero-tour-tabs::before{transition:none!important}
}

/* Le héros et ses quatre chiffres forment une seule scène continue. */
.public-site .home-opening-scene{
  position:relative;
  isolation:isolate;
  overflow:hidden;
  background:
    radial-gradient(circle at 8% 9%,rgba(47,121,171,.16),transparent 24%),
    radial-gradient(circle at 85% 22%,rgba(26,145,141,.08),transparent 25%),
    linear-gradient(145deg,var(--card) 0%,var(--c-brand-50) 72%,var(--card) 100%);
}
.public-site .home-opening-scene>.blueprint-layer{z-index:0}
.public-site .home-opening-scene>.market-hero{
  z-index:1;
  background:transparent!important;
  border-bottom:0;
}
.public-site .home-opening-scene>.market-capabilities{z-index:2}
.public-site .market-capabilities::before{
  background:transparent;
  box-shadow:none;
  clip-path:none;
}

/* ============================================================
   PASSE DE DENSITÉ — la page reste riche, mais chaque écran avance.
   ============================================================ */
@media(min-width:1000px){
  /* Le format intrinsèque de la vidéo ne dicte plus la hauteur de la scène. */
  .public-site .home-visual-story{
    height:clamp(620px,76svh,720px);
    min-height:620px;
  }
  .public-site .home-visual-media{
    height:100%;
    min-height:0;
  }
  .public-site .home-visual-media video{
    position:absolute;
    inset:0;
  }
  .public-site .home-visual-copy{
    height:100%;
    padding:clamp(38px,4.5vw,64px);
  }
  .public-site .home-visual-copy h2{
    max-width:15ch;
    font-size:clamp(2.35rem,3.25vw,3.55rem);
  }
  .public-site .home-visual-copy>p:not(.market-kicker){margin-top:18px;line-height:1.58}
  .public-site .home-visual-steps{margin-top:24px}
  .public-site .home-visual-steps li{padding:13px 0}

  /* Huit contrôles lisibles en quatre lignes, face à leur tableau. */
  .public-site .market-validation{min-height:0;align-items:stretch}
  .public-site .validation-showcase,
  .public-site .validation-copy{padding:clamp(44px,5.5vw,70px) clamp(34px,4vw,64px)}
  .public-site .validation-showcase{place-items:start center}
  .public-site .validation-window-wrap{margin-top:42px}
  .public-site .validation-copy ul{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:18px 22px;
    margin-top:24px;
  }
  .public-site .validation-copy li{gap:10px;align-items:flex-start}
  .public-site .validation-copy li small{line-height:1.42}
  .public-site .control-rules{margin-top:5px}

  /* Les preuves de confiance deviennent deux colonnes scannables. */
  .public-site .trust-group-label,
  .public-site .trust-rows{max-width:70rem}
  .public-site .trust-group-label{margin-top:26px}
  .public-site .trust-rows{
    display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));
    column-gap:36px;
    margin-top:10px;
  }
  .public-site .trust-rows>div{
    grid-template-columns:1fr;
    align-content:start;
    gap:6px;
    padding:14px 0;
  }
  .public-site .trust-rows dt{font-size:1.05rem}
  .public-site .trust-rows dd{font-size:var(--fs-sm);line-height:1.5}

  /* Les cinq phases tiennent sur une ligne : le détail reste dans le texte. */
  .public-site .approach-method .project-phases{
    max-width:none;
    grid-template-columns:repeat(5,minmax(0,1fr));
    gap:12px;
    margin-top:34px;
  }
  .public-site .project-phases::before{display:none}
  .public-site .project-phases article{
    min-height:230px;
    padding:62px 18px 20px;
  }
  .public-site .project-phases article>span{
    top:18px;
    left:18px;
    width:34px;
    height:34px;
    border:0;
  }
  .public-site .project-phases h3{font-size:1rem;line-height:1.3}
  .public-site .project-phases p{font-size:.78rem;line-height:1.48}

  /* Guides : moins de vide dans les cartes, même hiérarchie éditoriale. */
  .public-site .home-guide-grid{margin-top:32px;gap:14px}
  .public-site .home-guide-card{
    min-height:215px;
    padding:22px;
  }
  .public-site .home-guide-card:nth-child(2),
  .public-site .home-guide-card:nth-child(4),
  .public-site .home-guide-card:nth-child(2):hover,
  .public-site .home-guide-card:nth-child(4):hover{transform:none}
  .public-site .home-guide-card h3{margin:24px 0 9px;font-size:clamp(1.05rem,1.35vw,1.3rem)}
  .public-site .home-guide-card p{margin-bottom:16px;line-height:1.5}
  .public-site .home-guides-all{margin-top:28px}

  /* L’explorateur garde toutes ses opérations, avec une navigation plus dense. */
  .public-site .coverage-strip{margin-top:28px;padding:24px}
  .public-site .module-explorer{margin-top:34px;grid-template-columns:300px minmax(0,1fr)}
  .public-site .module-explorer-nav{gap:3px;padding:9px}
  .public-site .module-nav-item{grid-template-columns:30px 1fr;column-gap:10px;padding:9px 10px}
  .public-site .module-nav-icon{width:30px;height:30px}
  .public-site .module-explorer-panels{padding:24px;min-height:340px}
  .public-site .module-panel-head{padding-bottom:16px}
  .public-site .module-ops-kicker{margin:18px 0 10px}
  .public-site .module-ops-list{gap:7px}
  .public-site .module-ops-list li{padding:10px 12px}

  /* La carte centrale garde une zone distincte pour le résultat et le titre. */
  .public-site .home-media-product-screen{gap:8px;padding:66px 18px 108px}
  .public-site .home-media-product-head{padding-bottom:8px}
  .public-site .home-media-product-rows{gap:5px}
  .public-site .home-media-product-rows div{min-height:28px}
  .public-site .home-media-product-foot{padding:7px 10px}
  .public-site .home-media-product-foot b{width:20px;height:20px}
}

@media(min-width:1000px) and (max-height:820px){
  .public-site .home-visual-story{height:600px;min-height:600px}
  .public-site .home-visual-copy{padding-block:36px}
  .public-site .home-visual-copy h2{font-size:2.7rem}
  .public-site .validation-showcase,
  .public-site .validation-copy{padding-top:42px;padding-bottom:42px}
}

/* ============================================================
   CONNECTED PRODUCT SYSTEM — USER + ADMIN
   The public product language now continues inside the application:
   navy blueprint canvas, instrument-like panels, blue/teal signals,
   compact spacing and one responsive geometry for every workspace.
   Everything is scoped to the authenticated shells so public pages keep
   their own composition.
   ============================================================ */
body.user-shell {
  --app-canvas: color-mix(in srgb, var(--bg) 95%, #071a29);
  --app-panel: color-mix(in srgb, var(--card) 93%, #0a2b42);
  --app-panel-strong: color-mix(in srgb, var(--card-raised) 88%, #0c3853);
  --app-panel-soft: color-mix(in srgb, var(--bg-sunken) 83%, #0b3048);
  --app-line: color-mix(in srgb, var(--c-brand-400) 27%, var(--border));
  --app-line-bright: color-mix(in srgb, var(--c-brand-400) 58%, var(--border));
  --app-glow: color-mix(in srgb, var(--c-brand-500) 17%, transparent);
  --app-teal-glow: color-mix(in srgb, var(--c-teal-400) 14%, transparent);
  min-width: 0;
  overflow-x: clip;
  background-color: var(--app-canvas);
  background-image:
    linear-gradient(color-mix(in srgb, var(--c-brand-400) 5%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--c-brand-400) 5%, transparent) 1px, transparent 1px),
    radial-gradient(850px 460px at 94% 8%, var(--app-glow), transparent 72%),
    radial-gradient(700px 420px at 4% 55%, var(--app-teal-glow), transparent 74%);
  background-size: 52px 52px, 52px 52px, auto, auto;
  background-attachment: fixed;
}

:root[data-theme="light"] body.user-shell {
  --app-canvas: #f4f8fb;
  --app-panel: rgba(255,255,255,.93);
  --app-panel-strong: #edf5fa;
  --app-panel-soft: #f7fafc;
  --app-line: color-mix(in srgb, var(--c-brand-400) 24%, var(--border));
  --app-line-bright: color-mix(in srgb, var(--c-brand-500) 48%, var(--border));
}

/* Header: same calm, technical masthead in user and admin spaces. */
body.user-shell .app-header {
  min-width: 0;
  background: color-mix(in srgb, #101f2b 94%, transparent);
  border-bottom-color: rgba(120, 190, 231, .23);
  box-shadow: 0 12px 34px rgba(0, 12, 22, .22);
  -webkit-backdrop-filter: blur(18px) saturate(125%);
          backdrop-filter: blur(18px) saturate(125%);
}
body.user-shell .app-header::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: -1px;
  left: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 2%, rgba(82,170,225,.58) 28%, rgba(87,185,145,.72) 50%, rgba(82,170,225,.58) 72%, transparent 98%);
  opacity: .55;
  pointer-events: none;
}
body.user-shell .app-header-inner {
  width: min(100%, 1540px);
  max-width: 1540px;
  min-height: 64px;
  padding-inline: clamp(16px, 2.4vw, 36px);
  gap: clamp(14px, 2vw, 30px);
}
body.user-shell .app-header-brand .brand-logo { transform: translateZ(0); }
body.user-shell .app-header-nav { justify-content: center; gap: 3px; }
body.user-shell .app-nav-link {
  height: 64px;
  padding-inline: clamp(9px, 1vw, 15px);
  color: #9eb6c8;
  font-size: .79rem;
  letter-spacing: -.01em;
}
body.user-shell .app-nav-link:hover,
body.user-shell .app-nav-link:focus-visible {
  color: #fff;
  background: rgba(66, 147, 202, .09);
}
body.user-shell .app-nav-link.is-active { color: #77bff0; }
body.user-shell .app-nav-link::after {
  bottom: 7px;
  height: 2px;
  border-radius: 999px;
  background: linear-gradient(90deg, #4a9fe0, #57b991);
  box-shadow: 0 0 12px rgba(83, 183, 194, .38);
}
body.user-shell .app-header-actions :is(.util-btn,.lang-switcher-trigger,.account-trigger),
body.user-shell .admin-mode-badge {
  border-color: rgba(121, 168, 199, .22);
  background: rgba(5, 24, 38, .42);
  color: #aac1d1;
}
body.user-shell .app-header-actions :is(.util-btn,.lang-switcher-trigger,.account-trigger):hover,
body.user-shell .app-header-actions :is(.util-btn,.lang-switcher-trigger,.account-trigger):focus-visible {
  border-color: rgba(106, 188, 234, .55);
  background: rgba(36, 104, 149, .2);
  color: #fff;
}
body.user-shell .account-avatar {
  background: linear-gradient(145deg, #287fba, #2e9c81);
  box-shadow: 0 0 0 3px rgba(79,166,218,.12);
}
body.user-shell .dropdown-panel,
body.user-shell .responsive-nav-panel {
  border-color: var(--app-line-bright);
  background: color-mix(in srgb, var(--app-panel) 97%, transparent);
  box-shadow: 0 24px 60px rgba(0, 12, 22, .34);
  -webkit-backdrop-filter: blur(20px);
          backdrop-filter: blur(20px);
}

/* Shared content frame: no wide dead gutters and no horizontal page bug. */
body.user-shell > .breadcrumb,
body.user-shell > .page {
  width: min(calc(100% - 40px), 1320px);
  max-width: 1320px;
}
body.user-shell > .page {
  padding: 26px 0 58px;
}
body.admin-shell .admin-shell-body {
  display: grid;
  grid-template-columns: 224px minmax(0, 1fr);
  min-width: 0;
  width: 100%;
  /* Pied de page collant. <body> est bien une colonne flex en min-height:
     100vh, mais l'enfant qui doit absorber la hauteur restante n'est pas le
     même selon le gabarit : hors administration c'est .page (flex: 1, plus
     haut dans ce fichier), en administration c'est CE conteneur — .page y
     est descendu d'un cran, dans la grille, et se voit d'ailleurs remettre
     `flex: none` juste en dessous. Personne ne grandissait donc, et le pied
     s'arrêtait où le contenu s'arrêtait : mesuré à 1280x970, 254 px de vide
     sous le pied sur Maintenance, 196 px sur Rôles, 74 px sur Messages —
     d'autant plus visible que la barre latérale, elle, est `fixed` et
     descend jusqu'en bas de la fenêtre.
     `1 0 auto` et non `1 1 auto` : la croissance est la seule chose voulue
     ici. Autoriser le retrait laisserait une page dense se faire comprimer
     pour tenir dans la fenêtre au lieu de la faire défiler. */
  flex: 1 0 auto;
}
/* La barre latérale étant en `position: fixed` (voir plus bas, « fixed et non
   sticky »), elle n'est plus un élément de grille. Sans ce placement
   explicite, le placement automatique remettrait .page dans la PREMIÈRE
   colonne, sous la barre — mesuré : contenu commençant à x=24 au lieu de 248,
   entièrement recouvert. La piste réservée reste large de 224 px parce
   qu'elle est déclarée en dur ci-dessus, pas déduite de son contenu. */
body.admin-shell .admin-shell-body > .page { grid-column: 2; }
body.admin-shell .admin-shell-body > .page {
  flex: none;
  width: min(calc(100% - 48px), 1450px);
  max-width: 1450px;
  min-width: 0;
  justify-self: center;
  margin-inline: auto;
  padding: 26px 0 58px;
}
body.user-shell .breadcrumb {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 12px 0 0;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .02em;
}
body.user-shell .breadcrumb .sep { color: var(--c-teal-400); }
body.user-shell .back-link {
  margin-bottom: 12px;
  color: var(--c-brand-600);
  font-weight: 750;
}

/* Page headings become concise instrument panels, not large empty heroes. */
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border: 1px solid var(--app-line);
  border-radius: 18px;
  background:
    linear-gradient(115deg, var(--app-panel) 0%, color-mix(in srgb, var(--app-panel-strong) 82%, transparent) 100%);
  box-shadow: 0 18px 48px rgba(1, 18, 31, .13), inset 0 1px rgba(255,255,255,.025);
}
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero)::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: -150px;
  right: -80px;
  width: 390px;
  height: 390px;
  border: 1px solid color-mix(in srgb, var(--c-brand-400) 24%, transparent);
  border-radius: 50%;
  box-shadow:
    0 0 0 42px color-mix(in srgb, var(--c-brand-500) 4%, transparent),
    0 0 0 92px color-mix(in srgb, var(--c-teal-400) 3%, transparent);
  pointer-events: none;
}
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero)::after {
  content: "";
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  width: 32px;
  height: 22px;
  border-top: 2px solid var(--c-teal-400);
  border-left: 2px solid var(--c-brand-400);
  border-radius: 17px 0 0;
  opacity: .88;
  pointer-events: none;
}
body.user-shell .dashboard-topbar {
  min-height: 124px;
  margin: 0 0 18px;
  padding: 22px 24px;
}
body.user-shell .dashboard-topbar-copy { position: relative; z-index: 3; }
body.user-shell .dashboard-topbar-copy h1,
body.user-shell .module-header-copy h1,
body.user-shell .app-page-hero h1 {
  font-family: var(--font-display);
  letter-spacing: -.035em;
}
body.user-shell .dashboard-topbar-copy h1 { font-size: clamp(1.45rem, 2vw, 2rem); }
body.user-shell .dashboard-topbar-copy p { max-width: 58ch; line-height: 1.55; }
body.user-shell .module-header-compact {
  align-items: center;
  min-height: 108px;
  margin: 0 0 22px;
  padding: 20px 22px;
  border-bottom: 1px solid var(--app-line);
}
body.user-shell .module-header-copy,
body.user-shell .module-header-actions,
body.user-shell .process-mini-flow,
body.user-shell .version-badge { position: relative; z-index: 3; }
body.user-shell .module-header-copy h1 { font-size: clamp(1.2rem, 1.8vw, 1.7rem); }
body.user-shell .module-header-copy p {
  margin-top: 5px;
  color: var(--c-brand-600);
  font-family: var(--font-mono);
  font-size: .66rem;
  letter-spacing: .075em;
}
body.user-shell .module-header-icon,
body.user-shell :is(.admin-users-hero-icon,.xsd-admin-hero-icon) {
  position: relative;
  z-index: 3;
  border: 1px solid color-mix(in srgb, var(--c-brand-400) 35%, transparent);
  background: linear-gradient(145deg, color-mix(in srgb, var(--c-brand-500) 26%, var(--app-panel)), color-mix(in srgb, var(--c-teal-400) 13%, var(--app-panel)));
  color: var(--c-brand-600);
  box-shadow: inset 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(3, 37, 59, .16);
  animation: app-signal-breathe 4s ease-in-out infinite;
}
body.user-shell .app-page-hero {
  margin: 0 0 24px;
  padding: clamp(22px, 3vw, 34px);
}
body.user-shell .app-page-hero::after { right: auto; bottom: auto; }

/* Functional panels and cards use the same material throughout the app. */
body.user-shell :is(.card,.tile,.operation-card,.detail-panel,.account-panel,.resource-card,.admin-block,.admin-users-directory,.admin-user-create-panel,.xsd-profile-card,.xsd-company-panel,.rbac-create-panel,.support-thread-card,.feedback-card,.validation-issues-card) {
  border-color: var(--app-line);
  background: linear-gradient(145deg, var(--app-panel), color-mix(in srgb, var(--app-panel-strong) 60%, var(--app-panel)));
  box-shadow: 0 14px 38px rgba(1, 17, 29, .10), inset 0 1px rgba(255,255,255,.025);
}
body.user-shell :is(.card,.detail-panel,.account-panel,.resource-card,.admin-users-directory,.admin-user-create-panel,.xsd-profile-card,.xsd-company-panel,.rbac-create-panel,.validation-issues-card) {
  border-radius: 16px;
}
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,.kpi-card) {
  position: relative;
  overflow: hidden;
  transition: transform .2s var(--ease), border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,.kpi-card)::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: linear-gradient(180deg, var(--c-brand-400), var(--c-teal-400));
  opacity: .62;
  transition: width .2s var(--ease), opacity .2s var(--ease);
}
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,a.kpi-card):hover,
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,a.kpi-card):focus-within {
  transform: translateY(-3px);
  border-color: var(--app-line-bright);
  background: linear-gradient(145deg, color-mix(in srgb, var(--app-panel) 92%, var(--c-brand-500)), var(--app-panel-strong));
  box-shadow: 0 20px 45px rgba(0, 18, 31, .18), 0 0 0 1px color-mix(in srgb, var(--c-brand-400) 10%, transparent);
}
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,.kpi-card):hover::before,
body.user-shell :is(.tile,.operation-card,.resource-card,.admin-quicklink,.kpi-card):focus-within::before {
  width: 4px;
  opacity: 1;
}
body.user-shell .grid { gap: 14px; }
body.user-shell .tile {
  min-height: 92px;
  padding: 16px 17px;
  border-radius: 14px;
}
body.user-shell .tile-icon,
body.user-shell .operation-card .op-icon,
body.user-shell :is(.card-heading-icon,.resource-card-icon) {
  border: 1px solid color-mix(in srgb, var(--c-brand-400) 24%, transparent);
  background: color-mix(in srgb, var(--c-brand-500) 13%, var(--app-panel-soft));
  color: var(--c-brand-600);
  box-shadow: inset 0 1px rgba(255,255,255,.04);
}
body.user-shell .tile-copy h3,
body.user-shell .operation-card .op-title { font-family: var(--font-display); letter-spacing: -.015em; }
body.user-shell .tile-op-count,
body.user-shell .section-kicker,
body.user-shell .app-page-kicker { font-family: var(--font-mono); }
body.user-shell .operation-grid { gap: 12px; }
body.user-shell .operation-card {
  min-height: 76px;
  padding: 14px 16px;
  border-radius: 13px;
}

/* KPI cards: compact dashboards with a visible signal, not flat counters. */
body.user-shell .kpi-card-grid { gap: 9px; }
body.user-shell .kpi-card-grid .kpi-card,
body.admin-shell .admin-users-stats.kpi-card-grid .kpi-card {
  min-height: 72px;
  border-color: var(--app-line);
  border-left: 2px solid var(--c-brand-400);
  border-radius: 12px;
  background: linear-gradient(145deg, var(--app-panel), var(--app-panel-strong));
  box-shadow: 0 10px 28px rgba(0, 17, 29, .10), inset 0 1px rgba(255,255,255,.025);
}
body.user-shell .kpi-card--standard .kpi-card-icon,
body.admin-shell .admin-users-stats .kpi-card > span {
  border: 1px solid color-mix(in srgb, var(--c-brand-400) 25%, transparent);
  background: color-mix(in srgb, var(--c-brand-500) 13%, var(--app-panel-soft));
}
body.user-shell .kpi-card--standard .kpi-card-value,
body.admin-shell .admin-users-stats .kpi-card strong {
  font-family: var(--font-display);
  color: var(--c-brand-600);
}
body.user-shell .kpi-card--standard .kpi-card-label,
body.admin-shell .admin-users-stats .kpi-card small {
  font-family: var(--font-mono);
  letter-spacing: .05em;
}
body.user-shell .dashboard-topbar-stats { position: relative; z-index: 3; }
body.user-shell .dashboard-topbar-stats .kpi-card { background: color-mix(in srgb, var(--app-panel) 90%, transparent); }
body.admin-shell .page > .module-header-compact + .admin-block .kpi-card-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
}
body.admin-shell .page > .module-header-compact + .admin-block .kpi-card {
  width: auto;
  min-width: 0;
  max-width: none;
}

/* Version, status and workflow elements form one coherent signal language. */
body.user-shell .version-badge,
body.user-shell .admin-xsd-indicator {
  border-color: var(--app-line);
  background: linear-gradient(90deg, color-mix(in srgb, var(--c-teal-400) 8%, var(--app-panel)), var(--app-panel));
  box-shadow: 0 9px 24px rgba(0, 20, 32, .08);
}
body.user-shell .version-badge-full { border-radius: 12px; }
body.user-shell .process-mini-flow span {
  border-color: var(--app-line);
  background: color-mix(in srgb, var(--app-panel) 92%, transparent);
}
body.user-shell .process-mini-flow .is-current {
  border-color: var(--app-line-bright);
  background: color-mix(in srgb, var(--c-brand-500) 10%, var(--app-panel));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-500) 7%, transparent);
}
body.user-shell :is(.badge,.status-pill,.section-count,.module-pill,.status-filter-chip) {
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
  box-shadow: inset 0 1px rgba(255,255,255,.035);
}
body.user-shell .validation-summary {
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: transparent;
}
body.user-shell .validation-summary > div {
  min-height: 84px;
  padding: 16px;
  border: 1px solid var(--app-line);
  border-radius: 12px;
  background: linear-gradient(145deg, var(--app-panel), var(--app-panel-strong));
  box-shadow: 0 10px 26px rgba(0, 17, 29, .09), inset 2px 0 color-mix(in srgb, var(--c-brand-400) 70%, transparent);
}
body.user-shell .validation-summary-success > div {
  background: linear-gradient(145deg, var(--app-panel), color-mix(in srgb, var(--c-ok) 6%, var(--app-panel-strong)));
  box-shadow: 0 10px 26px rgba(0, 17, 29, .09), inset 2px 0 color-mix(in srgb, var(--c-ok) 75%, transparent);
}
body.user-shell .validation-summary-error > div {
  background: linear-gradient(145deg, var(--app-panel), color-mix(in srgb, var(--c-danger) 6%, var(--app-panel-strong)));
  box-shadow: 0 10px 26px rgba(0, 17, 29, .09), inset 2px 0 color-mix(in srgb, var(--c-danger) 75%, transparent);
}
body.user-shell .result-list li {
  border: 1px solid var(--app-line);
  background: color-mix(in srgb, var(--app-panel-soft) 84%, transparent);
}

/* Forms and upload surfaces. */
body.user-shell :is(input[type=text],input[type=email],input[type=password],input[type=tel],input[type=number],input[type=search],input:is([type=date],[type=datetime-local]),textarea,select) {
  border-color: var(--app-line);
  /* `background-color` et NON la forme condensée `background` : celle-ci
     réinitialise aussi `background-image`, et emportait donc le chevron que
     `select:not([multiple])` dessine. Résultat, dans tout l'espace
     utilisateur, un menu déroulant gardait ses 34 px de retrait à droite
     pour une flèche qui n'était plus peinte : rien ne distinguait plus un
     menu d'un champ de saisie. */
  background-color: color-mix(in srgb, var(--app-panel-soft) 90%, transparent);
  color: var(--text);
  box-shadow: inset 0 1px rgba(255,255,255,.025);
}
body.user-shell :is(input[type=text],input[type=email],input[type=password],input[type=tel],input[type=number],input[type=search],input:is([type=date],[type=datetime-local]),textarea,select):focus {
  border-color: var(--c-brand-500);
  outline: 0;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-brand-500) 16%, transparent), inset 0 1px rgba(255,255,255,.03);
}
body.user-shell .dashboard-search input {
  min-height: 48px;
  border-color: var(--app-line);
  background: linear-gradient(90deg, var(--app-panel), color-mix(in srgb, var(--app-panel-strong) 60%, var(--app-panel)));
}
body.user-shell .file-drop-zone {
  border-color: var(--app-line-bright);
  background:
    linear-gradient(135deg, color-mix(in srgb, var(--c-brand-500) 7%, var(--app-panel-soft)), color-mix(in srgb, var(--c-teal-400) 6%, var(--app-panel-soft)));
}
body.user-shell .file-drop-zone:hover,
body.user-shell .file-drop-zone:focus-within {
  border-color: var(--c-brand-500);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--c-brand-500) 10%, transparent);
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger) {
  border-color: transparent;
  background: linear-gradient(135deg, #2f86c3, #1e6f9f 66%, #24866f);
  color: #fff;
  box-shadow: 0 10px 24px rgba(20, 101, 153, .22);
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger):hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 14px 30px rgba(20, 101, 153, .3);
}
body.user-shell .btn-outline,
body.user-shell .btn-ghost,
body.user-shell .info-trigger {
  border-color: var(--app-line-bright);
  background: color-mix(in srgb, var(--app-panel) 90%, transparent);
}

/* Data tables: contained, readable and consistent on all admin screens. */
body.user-shell .datatable-wrap,
body.user-shell .dg-wrap {
  max-width: 100%;
  border-color: var(--app-line);
  border-radius: 15px;
  background: var(--app-panel);
  box-shadow: 0 14px 38px rgba(0, 17, 29, .10);
}
body.user-shell .dg-table-scroll { max-width: 100%; }
body.user-shell :is(.dg-topbar,.admin-toolbar,.datatable-pagination,.dg-bottombar) {
  border-color: var(--app-line);
  background: linear-gradient(90deg, var(--app-panel-strong), var(--app-panel));
}
body.user-shell table.datatable thead th,
body.user-shell .dg-table thead th,
body.user-shell .validation-table thead th {
  border-color: var(--app-line);
  background: color-mix(in srgb, var(--app-panel-strong) 88%, #082237);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: .67rem;
  letter-spacing: .055em;
}
body.user-shell table.datatable td,
body.user-shell .dg-table td,
body.user-shell .validation-table td { border-color: var(--app-line); }
body.user-shell table.datatable tbody tr,
body.user-shell .dg-table tbody tr { transition: background .16s ease, box-shadow .16s ease; }
body.user-shell table.datatable tbody tr:hover,
body.user-shell .dg-table tbody tr:hover {
  background: linear-gradient(90deg, color-mix(in srgb, var(--c-brand-500) 9%, transparent), color-mix(in srgb, var(--c-teal-400) 4%, transparent));
  box-shadow: inset 3px 0 var(--c-brand-500);
}
body.user-shell .cell-primary-link { color: var(--c-brand-600); font-weight: 750; }

/* Admin navigation and page blocks. */
body.admin-shell .admin-shell-body {
  background: linear-gradient(90deg, rgba(5,20,31,.12), transparent 260px);
}
/* `fixed` et non `sticky`. Un élément collant est confiné à sa ZONE DE
   GRILLE : il ne peut pas descendre plus bas que le bas de
   .admin-shell-body. Or le pied de page vit HORS de cette grille — 177 px
   mesurés (105 px de pied + 72 px de marge haute). Arrivé en bas de page, la
   barre était donc tirée vers le haut de ces 177 px exactement : les quatre
   premières entrées de navigation sortaient de l'écran et un trou de même
   hauteur s'ouvrait sous elle. Signalé le 2026-08-15, reproduit à 1440x970 :
   haut de barre à -112 px au lieu de 65.
   Fixer la barre la sort du flux ; le placement explicite de .page ci-dessous
   est donc indissociable de ce changement. */
body.admin-shell .admin-sidebar {
  position: fixed;
  top: 65px;
  left: 0;
  width: 224px;
  height: calc(100vh - 65px);
  padding: 18px 12px 24px;
  border-right-color: rgba(95, 164, 207, .2);
  background:
    linear-gradient(180deg, color-mix(in srgb, #132836 96%, transparent), color-mix(in srgb, #0d202d 96%, transparent));
  box-shadow: 14px 0 40px rgba(0, 15, 26, .12);
}
body.admin-shell .admin-sidebar-link {
  position: relative;
  min-height: 40px;
  padding: 9px 11px;
  border: 1px solid transparent;
  color: #91aabb;
  font-size: .8rem;
  transition: color .16s ease, border-color .16s ease, background .16s ease, transform .16s ease;
}
body.admin-shell .admin-sidebar-link:hover,
body.admin-shell .admin-sidebar-link:focus-visible {
  border-color: rgba(102, 172, 216, .2);
  background: rgba(53, 128, 178, .11);
  color: #dcebf4;
  transform: translateX(2px);
}
body.admin-shell .admin-sidebar-link.is-active {
  border-color: rgba(85, 174, 224, .25);
  background: linear-gradient(90deg, rgba(48, 133, 191, .23), rgba(57, 147, 141, .08));
  color: #78c1ef;
  box-shadow: inset 3px 0 #4aa3df, 0 8px 22px rgba(0, 19, 32, .13);
}
body.admin-shell .admin-sidebar-link.is-active::after {
  content: "";
  position: absolute;
  right: 9px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-teal-400);
  box-shadow: 0 0 0 4px rgba(87,185,145,.1);
}
body.admin-shell .admin-mode-badge {
  color: #82c5ed;
  box-shadow: inset 0 0 0 1px rgba(78,165,218,.06);
}
body.user-shell .admin-block {
  margin-bottom: 22px;
  padding: 18px;
  border: 1px solid var(--app-line);
  border-radius: 16px;
}
body.user-shell .admin-block-heading {
  align-items: center;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--app-line);
}
body.user-shell .admin-block-heading h2 { font-family: var(--font-display); letter-spacing: -.02em; }
body.user-shell .admin-quicklinks { gap: 10px; }
body.user-shell .admin-quicklink {
  min-height: 62px;
  border-color: var(--app-line);
  border-radius: 12px;
  background: linear-gradient(145deg, var(--app-panel), var(--app-panel-strong));
}

/* Specialized admin heroes are brought onto the same geometry. */
body.admin-shell :is(.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) {
  margin: 0 0 18px;
  padding: 22px 24px;
}
body.admin-shell .admin-users-hero-main,
body.admin-shell .admin-user-detail-hero > *,
body.admin-shell .xsd-admin-hero > *,
body.admin-shell .email-preview-hero > * { position: relative; z-index: 3; }
body.admin-shell :is(.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) h1 {
  font-family: var(--font-display);
  letter-spacing: -.035em;
}
body.admin-shell .admin-users-directory,
body.admin-shell .xsd-company-panel { overflow: visible; }

/* Rich but restrained motion: signals and panel appearance only. */
@keyframes app-signal-breathe {
  0%, 100% { box-shadow: inset 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(3,37,59,.13), 0 0 0 0 color-mix(in srgb, var(--c-brand-500) 0%, transparent); }
  50% { box-shadow: inset 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(3,37,59,.17), 0 0 0 6px color-mix(in srgb, var(--c-brand-500) 6%, transparent); }
}
/* N'ANIMER QUE L'OPACITÉ ICI. Ne jamais y remettre `filter` ni `transform`.
   Cette animation est posée sur six conteneurs de page (.operation-grid,
   .generation-layout, .dashboard-modules-zone, .admin-block,
   .admin-users-directory, .account-settings-grid). Un `filter` non nul crée un
   bloc conteneur pour les descendants en `position: fixed` : les panneaux
   d'aide, qui sont des modaux plein écran imbriqués dans ces conteneurs,
   cessaient d'être plein écran.
   Le piège est que `to { filter: none }` ne suffit pas à s'en sortir : à
   l'interpolation, `none` devient le filtre identité, et avec
   `animation-fill-mode: both` la valeur retenue à la fin est `saturate(1)` --
   visuellement identique à `none`, mais TOUJOURS un filtre, donc toujours un
   bloc conteneur. Le défaut était donc permanent, pas limité à la demi-seconde
   d'entrée. Constaté le 2026-08-14 : le panneau « À propos de l'opération »
   s'affichait confiné à une cellule de la grille, texte tronqué.
   Même raisonnement pour `transform`, dont `none` se calcule en matrice
   identité. La grille aggrave encore le symptôme : quand le bloc conteneur est
   une grille, la spécification confine l'élément à sa zone de grille.

   ATTENTION — l'opacité n'est pas innocente pour autant (mesuré le
   2026-08-19). Elle ne crée PAS de bloc conteneur (le correctif ci-dessus
   tient : les panneaux sont bien redevenus plein écran), mais elle crée un
   CONTEXTE D'EMPILEMENT, et `animation-fill-mode: both` laisse l'animation
   en état de remplissage indéfiniment après sa fin : ce contexte ne
   disparaît donc jamais. Tout descendant en position:fixed y est enfermé et
   son z-index cesse de se comparer à celui de l'en-tête collant. Le panneau
   d'aide s'ouvrait ainsi SOUS la barre du haut, titre et bouton Fermer
   cachés. C'est pourquoi ces panneaux sont désormais déplacés sous <body> à
   l'ouverture (voir base.html) plutôt que de dépendre de ce que fait le
   conteneur qui les héberge. Toute future animation posée ici hérite du même
   piège : ne comptez pas sur le z-index d'un descendant. */
@keyframes app-panel-arrive {
  from { opacity: 0; }
  to { opacity: 1; }
}
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) {
  animation: app-panel-arrive .38s var(--ease) both;
}
body.user-shell :is(.dashboard-modules-zone,.generation-layout,.operation-grid,.admin-block,.admin-users-directory,.account-settings-grid) {
  animation: app-panel-arrive .48s .06s var(--ease) both;
}

/* Responsive contract shared by user and admin pages. */
@media (max-width: 1180px) {
  body.user-shell .app-header-inner { gap: 13px; padding-inline: 16px; }
  body.user-shell .app-nav-link { padding-inline: 9px; }
  body.user-shell > .page,
  body.user-shell > .breadcrumb { width: min(calc(100% - 32px), 1320px); }
  body.admin-shell .admin-shell-body { grid-template-columns: 208px minmax(0, 1fr); }
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 32px); }
  body.admin-shell .admin-sidebar { width: 208px; }
}
@media (min-width: 901px) and (max-width: 1120px) {
  body.admin-shell .page > .module-header-compact + .admin-block .kpi-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  body.user-shell .app-header-inner { min-height: 58px; }
  body.user-shell .app-nav-link { height: 58px; }
  body.admin-shell .admin-shell-body { display: block; }
  body.admin-shell .admin-shell-body > .page { width: min(calc(100% - 32px), 1320px); }
  /* `position: static` autant que `display: none` : la règle de base
     .admin-sidebar remet bien `static` sous 900 px, mais elle perd contre
     `body.admin-shell .admin-sidebar` par simple spécificité. Sans ce
     rappel, une future levée du display:none ressusciterait une barre fixe
     de 224 px par-dessus le contenu mobile — c'est le hamburger qui prend
     le relais ici, pas la barre. */
  body.admin-shell .admin-sidebar { display: none; position: static; }
  body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) { border-radius: 15px; }
}
@media (max-width: 760px) {
  body.user-shell > .page,
  body.user-shell > .breadcrumb,
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 24px); }
  body.user-shell > .page,
  body.admin-shell .admin-shell-body > .page { padding-top: 18px; padding-bottom: 40px; }
  body.user-shell .dashboard-topbar { align-items: stretch; padding: 18px; }
  body.user-shell .dashboard-topbar-stats .kpi-card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.user-shell .module-header-compact { min-height: 0; padding: 18px; }
  body.user-shell .module-header-compact .module-header-icon { width: 40px; height: 40px; }
  body.user-shell .module-header-compact .module-header-copy { flex-basis: calc(100% - 60px); }
  body.user-shell .module-header-compact :is(.version-badge,.process-mini-flow,.validation-score-pill,.module-header-actions,.info-trigger) { flex-basis: 100%; }
  body.user-shell .generation-layout { gap: 14px; }
  body.user-shell .generation-layout > .card { padding: 20px; }
  body.user-shell .operation-grid,
  body.user-shell .grid { grid-template-columns: 1fr; }
  body.user-shell .admin-block { padding: 14px; border-radius: 13px; }
  body.user-shell .admin-block-heading { align-items: flex-start; flex-direction: column; }
  body.user-shell table.datatable.responsive-cards tbody tr {
    margin: 10px;
    border: 1px solid var(--app-line);
    border-radius: 12px;
    background: var(--app-panel);
  }
}
@media (max-width: 520px) {
  body.user-shell .app-header-inner { padding-inline: 11px; gap: 8px; }
  body.user-shell > .page,
  body.user-shell > .breadcrumb,
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 20px); }
  body.user-shell .dashboard-topbar-stats .kpi-card-grid { grid-template-columns: 1fr; }
  body.user-shell .dashboard-topbar-stats .kpi-card { width: 100%; max-width: none; }
  body.user-shell .process-mini-flow { width: 100%; overflow-x: auto; padding-bottom: 3px; }
  body.user-shell .process-mini-flow span { flex: none; }
}
@media (prefers-reduced-motion: reduce) {
  body.user-shell .module-header-icon,
  body.user-shell :is(.admin-users-hero-icon,.xsd-admin-hero-icon),
  body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero),
  body.user-shell :is(.dashboard-modules-zone,.generation-layout,.operation-grid,.admin-block,.admin-users-directory,.account-settings-grid) {
    animation: none;
  }
}

/* ============================================================
   CONNECTED PRODUCT SYSTEM — ADVANCED INTERACTION PASS
   One discreet motion language for navigation, work surfaces and data.
   Motion never carries information and is disabled when requested.
   ============================================================ */
.app-scroll-progress {
  position: fixed;
  z-index: 90;
  inset: 0 0 auto;
  height: 2px;
  overflow: hidden;
  pointer-events: none;
}
.app-scroll-progress span {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: linear-gradient(90deg, #4a9fe0, #57b991 58%, #78c8ee);
  box-shadow: 0 0 14px rgba(87,185,145,.55);
  will-change: transform;
}
body.user-shell .app-header {
  transition: background-color .24s var(--ease), border-color .24s var(--ease), box-shadow .24s var(--ease);
}
body.user-shell .app-header.is-scrolled {
  border-bottom-color: rgba(95,183,226,.38);
  background: color-mix(in srgb, #0c1d29 97%, transparent);
  box-shadow: 0 18px 40px rgba(0,11,20,.32);
}

/* Progressive entrance: the page is fully visible without JavaScript. */
.app-shell-enhanced body.user-shell .app-reveal {
  opacity: 0;
  transform: translateY(10px) scale(.996);
  transition:
    opacity .42s var(--ease),
    transform .42s var(--ease),
    border-color .2s var(--ease),
    box-shadow .2s var(--ease);
  transition-delay: calc(var(--app-reveal-order, 0) * 26ms);
}
.app-shell-enhanced body.user-shell .app-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Pointer light follows the work surface on precise pointing devices. */
body.user-shell .app-pointer-glow { isolation: isolate; }
body.user-shell .app-pointer-glow::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  border-radius: inherit;
  opacity: 0;
  background: radial-gradient(320px circle at var(--app-pointer-x, 50%) var(--app-pointer-y, 50%), color-mix(in srgb, var(--c-brand-400) 15%, transparent), transparent 67%);
  transition: opacity .24s ease;
  pointer-events: none;
}
body.user-shell .app-pointer-glow.has-pointer::after { opacity: 1; }
body.user-shell .app-pointer-glow > :not(.tile-link):not(.operation-card-link) { position: relative; z-index: 1; }
body.user-shell .tile.app-pointer-glow > .tile-link,
body.user-shell .operation-card.app-pointer-glow > .operation-card-link {
  position: absolute;
  inset: 0;
  z-index: 3;
}
body.user-shell .tile.app-pointer-glow > :is(.tile-info-btn,.op-info-btn),
body.user-shell .operation-card.app-pointer-glow > :is(.tile-info-btn,.op-info-btn) { z-index: 4; }

/* Même besoin, pour la barre de filtres : ses panneaux (filtres à sélection
   multiple) s'ouvrent PAR-DESSUS le tableau, c'est leur raison d'être. Sans
   cette ligne, la barre et le cadre du tableau héritent tous deux du
   z-index:1 uniforme posé plus haut, et le cadre — plus bas dans le DOM —
   l'emporte. Mesuré le 2026-08-19 sur /admin/utilisateurs en 1915x960 :
   seuls les ~30 premiers pixels du panneau restaient visibles, le reste
   passait SOUS les lignes (les points du panneau répondaient th.col-email,
   span.admin-user-email, span.badge).

   Le panneau est pourtant en position:fixed avec un z-index élevé : ça ne
   suffit pas, car ce z-index ne le compare qu'à ses voisins DANS la barre.
   C'est la BARRE qui doit passer devant, jamais le panneau tout seul.

   Les deux :not() reprennent ceux de la règle qu'on ajuste : ils lui font
   dépasser sa spécificité (0,5,1 contre 0,4,1) au lieu de dépendre de
   l'ordre des lignes, qu'un futur déplacement de bloc suffirait à inverser. */
body.user-shell .app-pointer-glow > :is(.admin-toolbar, .dg-toolbar):not(.tile-link):not(.operation-card-link) { z-index: 2; }

/* Volume de l'abonnement, sur le tableau de bord. Même caisse que la zone
   des modules juste en dessous : c'est une information de la même famille
   (« où j'en suis »), elle ne doit pas se présenter comme une alerte. La
   barre ne dépasse jamais son cadre — au-delà du plafond elle est pleine et
   c'est le texte qui dit le dépassement. */
body.user-shell .dashboard-volume {
  margin-top: 14px;
  padding: 16px 20px;
  border: 1px solid var(--app-line);
  border-radius: 17px;
  background: var(--bg-surface, var(--bg-sunken));
}
body.user-shell .dashboard-volume-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 8px 16px;
}
body.user-shell .dashboard-volume-head h2 {
  margin: 0; font-size: var(--fs-md); display: flex; align-items: center; gap: 8px;
}
body.user-shell .dashboard-volume-figure { margin: 0; font-size: var(--fs-sm); }
body.user-shell .dashboard-volume-figure strong { font-size: var(--fs-md); }
body.user-shell .dashboard-volume-bar {
  margin-top: 12px; height: 6px; border-radius: 999px;
  background: var(--bg-sunken); overflow: hidden;
}
body.user-shell .dashboard-volume-bar > span {
  display: block; height: 100%; border-radius: 999px;
  background: var(--c-brand-600, var(--primary-ink));
}
body.user-shell .dashboard-volume-bar > span.is-warning { background: var(--c-warn); }
body.user-shell .dashboard-volume-help {
  margin: 10px 0 0; color: var(--text-faint); font-size: var(--fs-xs); max-width: 78ch;
}

/* The module finder becomes a contained operating surface. */
body.user-shell .dashboard-modules-zone {
  position: relative;
  overflow: hidden;
  margin-top: 14px;
  padding: 20px;
  border: 1px solid var(--app-line);
  border-radius: 17px;
  background:
    linear-gradient(145deg, color-mix(in srgb, var(--app-panel) 88%, transparent), color-mix(in srgb, var(--app-panel-strong) 68%, transparent));
  box-shadow: 0 16px 44px rgba(0,17,29,.1), inset 0 1px rgba(255,255,255,.025);
  transition: border-color .2s ease, box-shadow .2s ease;
}
body.user-shell .dashboard-modules-zone::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400), transparent 84%);
  opacity: .8;
}
body.user-shell .dashboard-modules-zone::after {
  content: "MODULE / OPERATION / XML";
  position: absolute;
  top: 18px;
  right: 20px;
  color: var(--text-faint);
  font: 600 .56rem/1 var(--font-mono);
  letter-spacing: .08em;
  opacity: .58;
  pointer-events: none;
}
body.user-shell .dashboard-modules-zone.has-search-focus {
  border-color: var(--app-line-bright);
  box-shadow: 0 18px 50px rgba(0,17,29,.13), 0 0 0 3px color-mix(in srgb, var(--c-brand-400) 6%, transparent);
}
body.user-shell .dashboard-modules-zone .grid { margin-top: 16px; }
body.user-shell .dashboard-modules-zone > h2 { padding-right: 180px; }

/* Generation and validation read as a connected process. */
body.user-shell .generation-layout { position: relative; }
body.user-shell .generation-layout > .card {
  position: relative;
  overflow: hidden;
}
body.user-shell .generation-layout > .card::before {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400), transparent 82%);
}
body.user-shell .generation-layout > .upload-card::before {
  background: linear-gradient(90deg, var(--c-teal-400), var(--c-brand-400), transparent 82%);
}
body.user-shell .instruction-list { position: relative; }
body.user-shell .instruction-list::before {
  content: "";
  position: absolute;
  top: 16px;
  bottom: 16px;
  left: 15px;
  width: 1px;
  background: linear-gradient(var(--c-brand-400), color-mix(in srgb, var(--c-teal-400) 55%, transparent));
  opacity: .42;
}
body.user-shell .instruction-list > div { position: relative; }
body.user-shell .instruction-list > div > b {
  z-index: 2;
  box-shadow: 0 0 0 5px var(--app-panel), 0 0 16px color-mix(in srgb, var(--c-brand-400) 18%, transparent);
}
body.user-shell .validation-summary > div strong {
  text-shadow: 0 0 20px color-mix(in srgb, currentColor 16%, transparent);
}

/* Dashboards and data views gain a stronger instrument rhythm. */
/* Barre de filtres RETENUE au défilement, dans les DEUX espaces et à
   n'importe quelle profondeur.
   Deux limites la privaient de cet effet là où elle était pourtant la plus
   utile. Le sélecteur exigeait un ENFANT DIRECT de .page : les barres logées
   dans un panneau d'onglet (Sociétés, Clients), dans l'annuaire (Utilisateurs)
   ou dans un bloc (Statistiques) défilaient hors de vue, soit quatre des
   quinze tableaux. Et la règle ne visait que body.admin-shell : « Mon
   activité » et « Notifications », côté utilisateur, n'en profitaient pas
   davantage.
   Le passage au sélecteur DESCENDANT est sans risque d'empilement : une
   barre collante ne colle que dans son bloc conteneur. Deux barres d'une
   même page (Statistiques) ne peuvent donc pas se superposer — la première
   se décolle en même temps que son bloc sort de l'écran, la seconde prend
   le relais. Aucun de ces conteneurs ne pose overflow, seule cause
   silencieuse d'échec de position:sticky (vérifié : .admin-users-directory
   déclare même overflow:visible explicitement). */
/* Les deux :not() ne décorent pas : ils portent ce sélecteur de (0,3,1) à
   (0,5,1), ce qui lui fait dépasser `.app-pointer-glow > :not(...)`
   (0,4,1 — `position: relative`) et `.app-pointer-glow > :is(.admin-toolbar,
   .dg-toolbar):not(...)` (0,5,1 — `z-index: 2`). Même parade que celle déjà
   employée plus haut dans ce fichier, et pour la même raison : dépendre de
   l'ordre des lignes se casse au premier déplacement de bloc.

   SANS CELA, LA RÈGLE NE SE CONTENTAIT PAS D'ÊTRE INERTE, ELLE NUISAIT.
   `position: sticky` était écrasé en `relative`, mais `top` — que rien
   d'autre ne pose — continuait de s'appliquer : sur un élément relatif, top
   ne colle pas, il DÉCALE. La barre était donc poussée 77 px vers le bas,
   par-dessus l'en-tête du tableau. Constaté en production le 2026-08-24 sur
   /admin/utilisateurs. */
body.user-shell .page .admin-toolbar:not(.tile-link):not(.operation-card-link) {
  position: sticky;
  z-index: 18;
  top: var(--sticky-toolbar-top, 76px);
  border-color: var(--app-line);
  background: color-mix(in srgb, var(--app-panel) 88%, transparent);
  box-shadow: 0 14px 32px -26px rgba(0,13,23,.85);
  -webkit-backdrop-filter: blur(16px) saturate(125%);
          backdrop-filter: blur(16px) saturate(125%);
}
body.user-shell .admin-block {
  position: relative;
  overflow: visible;
}
body.user-shell .admin-block::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 18px;
  width: 52px;
  height: 2px;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400));
  opacity: .72;
}
body.user-shell .trend-chart {
  border: 1px solid var(--app-line);
  border-radius: 11px;
  background:
    linear-gradient(color-mix(in srgb, var(--c-brand-400) 6%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--c-brand-400) 6%, transparent) 1px, transparent 1px),
    color-mix(in srgb, var(--app-panel-soft) 84%, transparent);
  background-size: 24px 24px;
}
body.user-shell .trend-chart rect {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: app-chart-rise .6s var(--ease) both;
  animation-delay: calc(var(--app-chart-index, 0) * 14ms);
}
body.user-shell .rank-fill {
  transform-origin: left center;
  animation: app-rank-fill .7s .12s var(--ease) both;
}
@keyframes app-chart-rise { from { transform: scaleY(.08); opacity: .22; } to { transform: scaleY(1); opacity: 1; } }
@keyframes app-rank-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Dense tables signal horizontal overflow without adding page scroll. */
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap) { position: relative; }
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap).is-horizontally-scrollable {
  box-shadow: inset -22px 0 18px -22px color-mix(in srgb, var(--c-brand-400) 75%, transparent);
}
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap).is-horizontally-scrollable.is-at-scroll-end { box-shadow: none; }
body.user-shell table.datatable tbody tr:focus-within,
body.user-shell .dg-table tbody tr:focus-within {
  background: color-mix(in srgb, var(--c-brand-500) 9%, transparent);
  box-shadow: inset 3px 0 var(--c-teal-400);
}

/* Barre d'onglets — UN seul dessin, pas deux empilés.
   Elle en portait deux à la fois : le dessin de base est une barre SOULIGNÉE
   (trait sous le conteneur, bordure basse de 2 px sur l'onglet actif,
   margin-bottom:-1px pour poser les onglets sur ce trait), et cette section
   ajoutait par-dessus un CADRE arrondi plus une barre lumineuse en ::after.
   Deux indicateurs d'onglet actif se disputaient donc la même place, et le
   margin négatif tirait les onglets hors du cadre qui venait de les
   entourer — d'où l'aspect bâclé de « Liste / Pipeline », signalé le
   2026-08-24.

   Un seul parti est retenu : le contrôle segmenté. Le cadre reste, les
   mécaniques de soulignement sont neutralisées, et l'onglet actif se
   distingue par un FOND plein — lisible sans dépendre d'un filet de 2 px. */
body.user-shell .admin-tabs {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  margin-bottom: var(--sp-4);
  border: 1px solid var(--app-line);
  border-bottom: 1px solid var(--app-line);   /* et non le trait de la base */
  border-radius: 12px;
  background: color-mix(in srgb, var(--app-panel) 88%, transparent);
  box-shadow: inset 0 1px rgba(255,255,255,.025);
}
body.user-shell .admin-tab {
  position: relative;
  margin-bottom: 0;                 /* le -1px sortait l'onglet du cadre */
  border-bottom: 0;                 /* l'indicateur n'est plus un filet */
  padding: 7px 16px;
  border-radius: 9px;
  white-space: nowrap;
  transition: background-color .15s var(--ease), color .15s var(--ease);
}
body.user-shell .admin-tab:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--c-brand-400) 8%, transparent);
}
body.user-shell .admin-tab.is-active {
  color: #fff;
  background: linear-gradient(135deg, var(--c-brand-600), var(--c-teal-600, var(--c-brand-500)));
  box-shadow: 0 1px 6px -2px color-mix(in srgb, var(--c-brand-600) 60%, transparent);
}
/* Plus d'::after : le fond plein EST l'indicateur. En garder un second
   revenait à dire deux fois la même chose, moins bien. */
body.user-shell .admin-tab.is-active::after { content: none; }

body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger) {
  border-color: transparent;
  background: linear-gradient(135deg, #2f86c3, #1e6f9f 66%, #24866f);
  color: #fff;
  box-shadow: 0 10px 24px rgba(20, 101, 153, .22);
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger):hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 14px 30px rgba(20, 101, 153, .3);
}
body.user-shell .btn-outline,
body.user-shell .btn-ghost,
body.user-shell .info-trigger {
  border-color: var(--app-line-bright);
  background: color-mix(in srgb, var(--app-panel) 90%, transparent);
}

/* Data tables: contained, readable and consistent on all admin screens. */
body.user-shell .datatable-wrap,
body.user-shell .dg-wrap {
  max-width: 100%;
  border-color: var(--app-line);
  border-radius: 15px;
  background: var(--app-panel);
  box-shadow: 0 14px 38px rgba(0, 17, 29, .10);
}
body.user-shell .dg-table-scroll { max-width: 100%; }
body.user-shell :is(.dg-topbar,.admin-toolbar,.datatable-pagination,.dg-bottombar) {
  border-color: var(--app-line);
  background: linear-gradient(90deg, var(--app-panel-strong), var(--app-panel));
}
body.user-shell table.datatable thead th,
body.user-shell .dg-table thead th,
body.user-shell .validation-table thead th {
  border-color: var(--app-line);
  background: color-mix(in srgb, var(--app-panel-strong) 88%, #082237);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: .67rem;
  letter-spacing: .055em;
}
body.user-shell table.datatable td,
body.user-shell .dg-table td,
body.user-shell .validation-table td { border-color: var(--app-line); }
body.user-shell table.datatable tbody tr,
body.user-shell .dg-table tbody tr { transition: background .16s ease, box-shadow .16s ease; }
body.user-shell table.datatable tbody tr:hover,
body.user-shell .dg-table tbody tr:hover {
  background: linear-gradient(90deg, color-mix(in srgb, var(--c-brand-500) 9%, transparent), color-mix(in srgb, var(--c-teal-400) 4%, transparent));
  box-shadow: inset 3px 0 var(--c-brand-500);
}
body.user-shell .cell-primary-link { color: var(--c-brand-600); font-weight: 750; }

/* Admin navigation and page blocks. */
body.admin-shell .admin-shell-body {
  background: linear-gradient(90deg, rgba(5,20,31,.12), transparent 260px);
}
/* `fixed` et non `sticky`. Un élément collant est confiné à sa ZONE DE
   GRILLE : il ne peut pas descendre plus bas que le bas de
   .admin-shell-body. Or le pied de page vit HORS de cette grille — 177 px
   mesurés (105 px de pied + 72 px de marge haute). Arrivé en bas de page, la
   barre était donc tirée vers le haut de ces 177 px exactement : les quatre
   premières entrées de navigation sortaient de l'écran et un trou de même
   hauteur s'ouvrait sous elle. Signalé le 2026-08-15, reproduit à 1440x970 :
   haut de barre à -112 px au lieu de 65.
   Fixer la barre la sort du flux ; le placement explicite de .page ci-dessous
   est donc indissociable de ce changement. */
body.admin-shell .admin-sidebar {
  position: fixed;
  top: 65px;
  left: 0;
  width: 224px;
  height: calc(100vh - 65px);
  padding: 18px 12px 24px;
  border-right-color: rgba(95, 164, 207, .2);
  background:
    linear-gradient(180deg, color-mix(in srgb, #132836 96%, transparent), color-mix(in srgb, #0d202d 96%, transparent));
  box-shadow: 14px 0 40px rgba(0, 15, 26, .12);
}
body.admin-shell .admin-sidebar-link {
  position: relative;
  min-height: 40px;
  padding: 9px 11px;
  border: 1px solid transparent;
  color: #91aabb;
  font-size: .8rem;
  transition: color .16s ease, border-color .16s ease, background .16s ease, transform .16s ease;
}
body.admin-shell .admin-sidebar-link:hover,
body.admin-shell .admin-sidebar-link:focus-visible {
  border-color: rgba(102, 172, 216, .2);
  background: rgba(53, 128, 178, .11);
  color: #dcebf4;
  transform: translateX(2px);
}
body.admin-shell .admin-sidebar-link.is-active {
  border-color: rgba(85, 174, 224, .25);
  background: linear-gradient(90deg, rgba(48, 133, 191, .23), rgba(57, 147, 141, .08));
  color: #78c1ef;
  box-shadow: inset 3px 0 #4aa3df, 0 8px 22px rgba(0, 19, 32, .13);
}
body.admin-shell .admin-sidebar-link.is-active::after {
  content: "";
  position: absolute;
  right: 9px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-teal-400);
  box-shadow: 0 0 0 4px rgba(87,185,145,.1);
}
body.admin-shell .admin-mode-badge {
  color: #82c5ed;
  box-shadow: inset 0 0 0 1px rgba(78,165,218,.06);
}
body.user-shell .admin-block {
  margin-bottom: 22px;
  padding: 18px;
  border: 1px solid var(--app-line);
  border-radius: 16px;
}
body.user-shell .admin-block-heading {
  align-items: center;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--app-line);
}
body.user-shell .admin-block-heading h2 { font-family: var(--font-display); letter-spacing: -.02em; }
body.user-shell .admin-quicklinks { gap: 10px; }
body.user-shell .admin-quicklink {
  min-height: 62px;
  border-color: var(--app-line);
  border-radius: 12px;
  background: linear-gradient(145deg, var(--app-panel), var(--app-panel-strong));
}

/* Specialized admin heroes are brought onto the same geometry. */
body.admin-shell :is(.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) {
  margin: 0 0 18px;
  padding: 22px 24px;
}
body.admin-shell .admin-users-hero-main,
body.admin-shell .admin-user-detail-hero > *,
body.admin-shell .xsd-admin-hero > *,
body.admin-shell .email-preview-hero > * { position: relative; z-index: 3; }
body.admin-shell :is(.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) h1 {
  font-family: var(--font-display);
  letter-spacing: -.035em;
}
body.admin-shell .admin-users-directory,
body.admin-shell .xsd-company-panel { overflow: visible; }

/* Rich but restrained motion: signals and panel appearance only. */
@keyframes app-signal-breathe {
  0%, 100% { box-shadow: inset 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(3,37,59,.13), 0 0 0 0 color-mix(in srgb, var(--c-brand-500) 0%, transparent); }
  50% { box-shadow: inset 0 1px rgba(255,255,255,.08), 0 10px 22px rgba(3,37,59,.17), 0 0 0 6px color-mix(in srgb, var(--c-brand-500) 6%, transparent); }
}
/* N'ANIMER QUE L'OPACITÉ ICI. Ne jamais y remettre `filter` ni `transform`.
   Cette animation est posée sur six conteneurs de page (.operation-grid,
   .generation-layout, .dashboard-modules-zone, .admin-block,
   .admin-users-directory, .account-settings-grid). Un `filter` non nul crée un
   bloc conteneur pour les descendants en `position: fixed` : les panneaux
   d'aide, qui sont des modaux plein écran imbriqués dans ces conteneurs,
   cessaient d'être plein écran.
   Le piège est que `to { filter: none }` ne suffit pas à s'en sortir : à
   l'interpolation, `none` devient le filtre identité, et avec
   `animation-fill-mode: both` la valeur retenue à la fin est `saturate(1)` --
   visuellement identique à `none`, mais TOUJOURS un filtre, donc toujours un
   bloc conteneur. Le défaut était donc permanent, pas limité à la demi-seconde
   d'entrée. Constaté le 2026-08-14 : le panneau « À propos de l'opération »
   s'affichait confiné à une cellule de la grille, texte tronqué.
   Même raisonnement pour `transform`, dont `none` se calcule en matrice
   identité. La grille aggrave encore le symptôme : quand le bloc conteneur est
   une grille, la spécification confine l'élément à sa zone de grille.

   ATTENTION — l'opacité n'est pas innocente pour autant (mesuré le
   2026-08-19). Elle ne crée PAS de bloc conteneur (le correctif ci-dessus
   tient : les panneaux sont bien redevenus plein écran), mais elle crée un
   CONTEXTE D'EMPILEMENT, et `animation-fill-mode: both` laisse l'animation
   en état de remplissage indéfiniment après sa fin : ce contexte ne
   disparaît donc jamais. Tout descendant en position:fixed y est enfermé et
   son z-index cesse de se comparer à celui de l'en-tête collant. Le panneau
   d'aide s'ouvrait ainsi SOUS la barre du haut, titre et bouton Fermer
   cachés. C'est pourquoi ces panneaux sont désormais déplacés sous <body> à
   l'ouverture (voir base.html) plutôt que de dépendre de ce que fait le
   conteneur qui les héberge. Toute future animation posée ici hérite du même
   piège : ne comptez pas sur le z-index d'un descendant. */
@keyframes app-panel-arrive {
  from { opacity: 0; }
  to { opacity: 1; }
}
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) {
  animation: app-panel-arrive .38s var(--ease) both;
}
body.user-shell :is(.dashboard-modules-zone,.generation-layout,.operation-grid,.admin-block,.admin-users-directory,.account-settings-grid) {
  animation: app-panel-arrive .48s .06s var(--ease) both;
}

/* Responsive contract shared by user and admin pages. */
@media (max-width: 1180px) {
  body.user-shell .app-header-inner { gap: 13px; padding-inline: 16px; }
  body.user-shell .app-nav-link { padding-inline: 9px; }
  body.user-shell > .page,
  body.user-shell > .breadcrumb { width: min(calc(100% - 32px), 1320px); }
  body.admin-shell .admin-shell-body { grid-template-columns: 208px minmax(0, 1fr); }
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 32px); }
  body.admin-shell .admin-sidebar { width: 208px; }
}
@media (min-width: 901px) and (max-width: 1120px) {
  body.admin-shell .page > .module-header-compact + .admin-block .kpi-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  body.user-shell .app-header-inner { min-height: 58px; }
  body.user-shell .app-nav-link { height: 58px; }
  body.admin-shell .admin-shell-body { display: block; }
  body.admin-shell .admin-shell-body > .page { width: min(calc(100% - 32px), 1320px); }
  /* `position: static` autant que `display: none` : la règle de base
     .admin-sidebar remet bien `static` sous 900 px, mais elle perd contre
     `body.admin-shell .admin-sidebar` par simple spécificité. Sans ce
     rappel, une future levée du display:none ressusciterait une barre fixe
     de 224 px par-dessus le contenu mobile — c'est le hamburger qui prend
     le relais ici, pas la barre. */
  body.admin-shell .admin-sidebar { display: none; position: static; }
  body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero) { border-radius: 15px; }
}
@media (max-width: 760px) {
  body.user-shell > .page,
  body.user-shell > .breadcrumb,
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 24px); }
  body.user-shell > .page,
  body.admin-shell .admin-shell-body > .page { padding-top: 18px; padding-bottom: 40px; }
  body.user-shell .dashboard-topbar { align-items: stretch; padding: 18px; }
  body.user-shell .dashboard-topbar-stats .kpi-card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.user-shell .module-header-compact { min-height: 0; padding: 18px; }
  body.user-shell .module-header-compact .module-header-icon { width: 40px; height: 40px; }
  body.user-shell .module-header-compact .module-header-copy { flex-basis: calc(100% - 60px); }
  body.user-shell .module-header-compact :is(.version-badge,.process-mini-flow,.validation-score-pill,.module-header-actions,.info-trigger) { flex-basis: 100%; }
  body.user-shell .generation-layout { gap: 14px; }
  body.user-shell .generation-layout > .card { padding: 20px; }
  body.user-shell .operation-grid,
  body.user-shell .grid { grid-template-columns: 1fr; }
  body.user-shell .admin-block { padding: 14px; border-radius: 13px; }
  body.user-shell .admin-block-heading { align-items: flex-start; flex-direction: column; }
  body.user-shell table.datatable.responsive-cards tbody tr {
    margin: 10px;
    border: 1px solid var(--app-line);
    border-radius: 12px;
    background: var(--app-panel);
  }
}
@media (max-width: 520px) {
  body.user-shell .app-header-inner { padding-inline: 11px; gap: 8px; }
  body.user-shell > .page,
  body.user-shell > .breadcrumb,
  body.admin-shell .admin-shell-body > .page { width: calc(100% - 20px); }
  body.user-shell .dashboard-topbar-stats .kpi-card-grid { grid-template-columns: 1fr; }
  body.user-shell .dashboard-topbar-stats .kpi-card { width: 100%; max-width: none; }
  body.user-shell .process-mini-flow { width: 100%; overflow-x: auto; padding-bottom: 3px; }
  body.user-shell .process-mini-flow span { flex: none; }
}
@media (prefers-reduced-motion: reduce) {
  body.user-shell .module-header-icon,
  body.user-shell :is(.admin-users-hero-icon,.xsd-admin-hero-icon),
  body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero),
  body.user-shell :is(.dashboard-modules-zone,.generation-layout,.operation-grid,.admin-block,.admin-users-directory,.account-settings-grid) {
    animation: none;
  }
}

/* ============================================================
   CONNECTED PRODUCT SYSTEM — ADVANCED INTERACTION PASS
   One discreet motion language for navigation, work surfaces and data.
   Motion never carries information and is disabled when requested.
   ============================================================ */
.app-scroll-progress {
  position: fixed;
  z-index: 90;
  inset: 0 0 auto;
  height: 2px;
  overflow: hidden;
  pointer-events: none;
}
.app-scroll-progress span {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: linear-gradient(90deg, #4a9fe0, #57b991 58%, #78c8ee);
  box-shadow: 0 0 14px rgba(87,185,145,.55);
  will-change: transform;
}
body.user-shell .app-header {
  transition: background-color .24s var(--ease), border-color .24s var(--ease), box-shadow .24s var(--ease);
}
body.user-shell .app-header.is-scrolled {
  border-bottom-color: rgba(95,183,226,.38);
  background: color-mix(in srgb, #0c1d29 97%, transparent);
  box-shadow: 0 18px 40px rgba(0,11,20,.32);
}

/* Progressive entrance: the page is fully visible without JavaScript. */
.app-shell-enhanced body.user-shell .app-reveal {
  opacity: 0;
  transform: translateY(10px) scale(.996);
  transition:
    opacity .42s var(--ease),
    transform .42s var(--ease),
    border-color .2s var(--ease),
    box-shadow .2s var(--ease);
  transition-delay: calc(var(--app-reveal-order, 0) * 26ms);
}
.app-shell-enhanced body.user-shell .app-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Pointer light follows the work surface on precise pointing devices. */
body.user-shell .app-pointer-glow { isolation: isolate; }
body.user-shell .app-pointer-glow::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  border-radius: inherit;
  opacity: 0;
  background: radial-gradient(320px circle at var(--app-pointer-x, 50%) var(--app-pointer-y, 50%), color-mix(in srgb, var(--c-brand-400) 15%, transparent), transparent 67%);
  transition: opacity .24s ease;
  pointer-events: none;
}
body.user-shell .app-pointer-glow.has-pointer::after { opacity: 1; }
body.user-shell .app-pointer-glow > :not(.tile-link):not(.operation-card-link) { position: relative; z-index: 1; }
body.user-shell .tile.app-pointer-glow > .tile-link,
body.user-shell .operation-card.app-pointer-glow > .operation-card-link {
  position: absolute;
  inset: 0;
  z-index: 3;
}
body.user-shell .tile.app-pointer-glow > :is(.tile-info-btn,.op-info-btn),
body.user-shell .operation-card.app-pointer-glow > :is(.tile-info-btn,.op-info-btn) { z-index: 4; }

/* Même besoin, pour la barre de filtres : ses panneaux (filtres à sélection
   multiple) s'ouvrent PAR-DESSUS le tableau, c'est leur raison d'être. Sans
   cette ligne, la barre et le cadre du tableau héritent tous deux du
   z-index:1 uniforme posé plus haut, et le cadre — plus bas dans le DOM —
   l'emporte. Mesuré le 2026-08-19 sur /admin/utilisateurs en 1915x960 :
   seuls les ~30 premiers pixels du panneau restaient visibles, le reste
   passait SOUS les lignes (les points du panneau répondaient th.col-email,
   span.admin-user-email, span.badge).

   Le panneau est pourtant en position:fixed avec un z-index élevé : ça ne
   suffit pas, car ce z-index ne le compare qu'à ses voisins DANS la barre.
   C'est la BARRE qui doit passer devant, jamais le panneau tout seul.

   Les deux :not() reprennent ceux de la règle qu'on ajuste : ils lui font
   dépasser sa spécificité (0,5,1 contre 0,4,1) au lieu de dépendre de
   l'ordre des lignes, qu'un futur déplacement de bloc suffirait à inverser. */
body.user-shell .app-pointer-glow > :is(.admin-toolbar, .dg-toolbar):not(.tile-link):not(.operation-card-link) { z-index: 2; }

/* Volume de l'abonnement, sur le tableau de bord. Même caisse que la zone
   des modules juste en dessous : c'est une information de la même famille
   (« où j'en suis »), elle ne doit pas se présenter comme une alerte. La
   barre ne dépasse jamais son cadre — au-delà du plafond elle est pleine et
   c'est le texte qui dit le dépassement. */
body.user-shell .dashboard-volume {
  margin-top: 14px;
  padding: 16px 20px;
  border: 1px solid var(--app-line);
  border-radius: 17px;
  background: var(--bg-surface, var(--bg-sunken));
}
body.user-shell .dashboard-volume-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 8px 16px;
}
body.user-shell .dashboard-volume-head h2 {
  margin: 0; font-size: var(--fs-md); display: flex; align-items: center; gap: 8px;
}
body.user-shell .dashboard-volume-figure { margin: 0; font-size: var(--fs-sm); }
body.user-shell .dashboard-volume-figure strong { font-size: var(--fs-md); }
body.user-shell .dashboard-volume-bar {
  margin-top: 12px; height: 6px; border-radius: 999px;
  background: var(--bg-sunken); overflow: hidden;
}
body.user-shell .dashboard-volume-bar > span {
  display: block; height: 100%; border-radius: 999px;
  background: var(--c-brand-600, var(--primary-ink));
}
body.user-shell .dashboard-volume-bar > span.is-warning { background: var(--c-warn); }
body.user-shell .dashboard-volume-help {
  margin: 10px 0 0; color: var(--text-faint); font-size: var(--fs-xs); max-width: 78ch;
}

/* The module finder becomes a contained operating surface. */
body.user-shell .dashboard-modules-zone {
  position: relative;
  overflow: hidden;
  margin-top: 14px;
  padding: 20px;
  border: 1px solid var(--app-line);
  border-radius: 17px;
  background:
    linear-gradient(145deg, color-mix(in srgb, var(--app-panel) 88%, transparent), color-mix(in srgb, var(--app-panel-strong) 68%, transparent));
  box-shadow: 0 16px 44px rgba(0,17,29,.1), inset 0 1px rgba(255,255,255,.025);
  transition: border-color .2s ease, box-shadow .2s ease;
}
body.user-shell .dashboard-modules-zone::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400), transparent 84%);
  opacity: .8;
}
body.user-shell .dashboard-modules-zone::after {
  content: "MODULE / OPERATION / XML";
  position: absolute;
  top: 18px;
  right: 20px;
  color: var(--text-faint);
  font: 600 .56rem/1 var(--font-mono);
  letter-spacing: .08em;
  opacity: .58;
  pointer-events: none;
}
body.user-shell .dashboard-modules-zone.has-search-focus {
  border-color: var(--app-line-bright);
  box-shadow: 0 18px 50px rgba(0,17,29,.13), 0 0 0 3px color-mix(in srgb, var(--c-brand-400) 6%, transparent);
}
body.user-shell .dashboard-modules-zone .grid { margin-top: 16px; }
body.user-shell .dashboard-modules-zone > h2 { padding-right: 180px; }

/* Generation and validation read as a connected process. */
body.user-shell .generation-layout { position: relative; }
body.user-shell .generation-layout > .card {
  position: relative;
  overflow: hidden;
}
body.user-shell .generation-layout > .card::before {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400), transparent 82%);
}
body.user-shell .generation-layout > .upload-card::before {
  background: linear-gradient(90deg, var(--c-teal-400), var(--c-brand-400), transparent 82%);
}
body.user-shell .instruction-list { position: relative; }
body.user-shell .instruction-list::before {
  content: "";
  position: absolute;
  top: 16px;
  bottom: 16px;
  left: 15px;
  width: 1px;
  background: linear-gradient(var(--c-brand-400), color-mix(in srgb, var(--c-teal-400) 55%, transparent));
  opacity: .42;
}
body.user-shell .instruction-list > div { position: relative; }
body.user-shell .instruction-list > div > b {
  z-index: 2;
  box-shadow: 0 0 0 5px var(--app-panel), 0 0 16px color-mix(in srgb, var(--c-brand-400) 18%, transparent);
}
body.user-shell .validation-summary > div strong {
  text-shadow: 0 0 20px color-mix(in srgb, currentColor 16%, transparent);
}

/* Dashboards and data views gain a stronger instrument rhythm. */
/* Barre de filtres RETENUE au défilement, dans les DEUX espaces et à
   n'importe quelle profondeur.
   Deux limites la privaient de cet effet là où elle était pourtant la plus
   utile. Le sélecteur exigeait un ENFANT DIRECT de .page : les barres logées
   dans un panneau d'onglet (Sociétés, Clients), dans l'annuaire (Utilisateurs)
   ou dans un bloc (Statistiques) défilaient hors de vue, soit quatre des
   quinze tableaux. Et la règle ne visait que body.admin-shell : « Mon
   activité » et « Notifications », côté utilisateur, n'en profitaient pas
   davantage.
   Le passage au sélecteur DESCENDANT est sans risque d'empilement : une
   barre collante ne colle que dans son bloc conteneur. Deux barres d'une
   même page (Statistiques) ne peuvent donc pas se superposer — la première
   se décolle en même temps que son bloc sort de l'écran, la seconde prend
   le relais. Aucun de ces conteneurs ne pose overflow, seule cause
   silencieuse d'échec de position:sticky (vérifié : .admin-users-directory
   déclare même overflow:visible explicitement). */
/* Les deux :not() ne décorent pas : ils portent ce sélecteur de (0,3,1) à
   (0,5,1), ce qui lui fait dépasser `.app-pointer-glow > :not(...)`
   (0,4,1 — `position: relative`) et `.app-pointer-glow > :is(.admin-toolbar,
   .dg-toolbar):not(...)` (0,5,1 — `z-index: 2`). Même parade que celle déjà
   employée plus haut dans ce fichier, et pour la même raison : dépendre de
   l'ordre des lignes se casse au premier déplacement de bloc.

   SANS CELA, LA RÈGLE NE SE CONTENTAIT PAS D'ÊTRE INERTE, ELLE NUISAIT.
   `position: sticky` était écrasé en `relative`, mais `top` — que rien
   d'autre ne pose — continuait de s'appliquer : sur un élément relatif, top
   ne colle pas, il DÉCALE. La barre était donc poussée 77 px vers le bas,
   par-dessus l'en-tête du tableau. Constaté en production le 2026-08-24 sur
   /admin/utilisateurs. */
body.user-shell .page .admin-toolbar:not(.tile-link):not(.operation-card-link) {
  position: sticky;
  z-index: 18;
  top: var(--sticky-toolbar-top, 76px);
  border-color: var(--app-line);
  background: color-mix(in srgb, var(--app-panel) 88%, transparent);
  box-shadow: 0 14px 32px -26px rgba(0,13,23,.85);
  -webkit-backdrop-filter: blur(16px) saturate(125%);
          backdrop-filter: blur(16px) saturate(125%);
}
body.user-shell .admin-block {
  position: relative;
  overflow: visible;
}
body.user-shell .admin-block::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 18px;
  width: 52px;
  height: 2px;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400));
  opacity: .72;
}
body.user-shell .trend-chart {
  border: 1px solid var(--app-line);
  border-radius: 11px;
  background:
    linear-gradient(color-mix(in srgb, var(--c-brand-400) 6%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--c-brand-400) 6%, transparent) 1px, transparent 1px),
    color-mix(in srgb, var(--app-panel-soft) 84%, transparent);
  background-size: 24px 24px;
}
body.user-shell .trend-chart rect {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: app-chart-rise .6s var(--ease) both;
  animation-delay: calc(var(--app-chart-index, 0) * 14ms);
}
body.user-shell .rank-fill {
  transform-origin: left center;
  animation: app-rank-fill .7s .12s var(--ease) both;
}
@keyframes app-chart-rise { from { transform: scaleY(.08); opacity: .22; } to { transform: scaleY(1); opacity: 1; } }
@keyframes app-rank-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Dense tables signal horizontal overflow without adding page scroll. */
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap) { position: relative; }
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap).is-horizontally-scrollable {
  box-shadow: inset -22px 0 18px -22px color-mix(in srgb, var(--c-brand-400) 75%, transparent);
}
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap).is-horizontally-scrollable.is-at-scroll-end { box-shadow: none; }
body.user-shell table.datatable tbody tr:focus-within,
body.user-shell .dg-table tbody tr:focus-within {
  background: color-mix(in srgb, var(--c-brand-500) 9%, transparent);
  box-shadow: inset 3px 0 var(--c-teal-400);
}

/* Active tabs, primary actions and empty states share the same motion cue. */
body.user-shell .admin-tabs {
  border: 1px solid var(--app-line);
  border-radius: 12px;
  background: color-mix(in srgb, var(--app-panel) 88%, transparent);
  box-shadow: inset 0 1px rgba(255,255,255,.025);
}
body.user-shell .admin-tab { position: relative; }
body.user-shell .admin-tab.is-active::after {
  content: "";
  position: absolute;
  right: 14px;
  bottom: 3px;
  left: 14px;
  height: 2px;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--c-brand-400), var(--c-teal-400));
  box-shadow: 0 0 9px color-mix(in srgb, var(--c-brand-400) 35%, transparent);
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger) {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger)::after {
  content: "";
  position: absolute;
  z-index: -1;
  top: -50%;
  bottom: -50%;
  left: -38%;
  width: 26%;
  transform: skewX(-20deg) translateX(0);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.22), transparent);
  transition: transform .48s ease;
}
body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger):hover::after { transform: skewX(-20deg) translateX(560%); }
body.user-shell .app-empty-state > svg {
  animation: app-empty-float 3.8s ease-in-out infinite;
  filter: drop-shadow(0 8px 16px color-mix(in srgb, var(--c-brand-500) 22%, transparent));
}
@keyframes app-empty-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
body.admin-shell .admin-sidebar-link.is-active::after { animation: app-nav-signal 2.8s ease-in-out infinite; }
@keyframes app-nav-signal { 0%,100% { opacity: .65; transform: scale(.86); } 50% { opacity: 1; transform: scale(1); } }

@media (max-width: 900px) {
  body.admin-shell .page > .admin-toolbar { top: 70px; }
}
@media (max-width: 760px) {
  body.user-shell .dashboard-modules-zone { padding: 16px; border-radius: 14px; }
  body.user-shell .dashboard-modules-zone::after { display: none; }
  body.user-shell .dashboard-modules-zone > h2 { padding-right: 0; }
  body.admin-shell .page > .admin-toolbar { position: static; }
}
@media (prefers-reduced-motion: reduce) {
  .app-scroll-progress span { will-change: auto; }
  .app-shell-enhanced body.user-shell .app-reveal { opacity: 1; transform: none; transition: none; }
  body.user-shell .app-pointer-glow::after,
  body.user-shell .trend-chart rect,
  body.user-shell .rank-fill,
  body.user-shell .app-empty-state > svg,
  body.admin-shell .admin-sidebar-link.is-active::after { animation: none; transition: none; }
  body.user-shell .btn:not(.btn-outline):not(.btn-ghost):not(.btn-danger)::after { display: none; }
}

/* ============================================================
   CONNECTED PRODUCT SYSTEM — PRECISION PASS
   Faster orientation, denser administration and explicit feedback.
   ============================================================ */

/* Active navigation reads as a selected workspace, not only an underline. */
body.user-shell .app-nav-link.is-active {
  background: linear-gradient(180deg, rgba(58,139,194,.12), rgba(33,101,147,.04));
  text-shadow: 0 0 18px rgba(110,194,239,.24);
}
body.user-shell .app-nav-link .chevron {
  transition: transform .18s ease;
}
body.user-shell #modulesMenuTrigger[aria-expanded="true"] .chevron { transform: rotate(180deg); }
body.user-shell :is(.dropdown.open .dropdown-panel,.modules-panel-detached.is-open) {
  transform-origin: top center;
  animation: app-menu-arrive .18s var(--ease) both;
}
@keyframes app-menu-arrive {
  from { opacity: 0; transform: translateY(-5px) scale(.985); }
  to { opacity: 1; transform: none; }
}

/* A compact search control: clear action, keyboard hint and stronger focus. */
body.user-shell .app-search-enhanced { position: relative; }
body.user-shell .app-search-enhanced > input { padding-right: 50px; }
body.user-shell .app-search-clear {
  position: absolute;
  z-index: 4;
  top: 50%;
  right: 9px;
  display: grid;
  width: 28px;
  height: 28px;
  padding: 0;
  place-items: center;
  transform: translateY(-50%) scale(.82);
  border: 1px solid var(--app-line);
  border-radius: 8px;
  background: color-mix(in srgb, var(--app-panel-strong) 88%, transparent);
  color: var(--text-muted);
  font: 700 1rem/1 sans-serif;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .16s ease, transform .16s ease, color .16s ease, border-color .16s ease;
}
body.user-shell .app-search-enhanced.has-value .app-search-clear {
  transform: translateY(-50%) scale(1);
  opacity: 1;
  pointer-events: auto;
}
body.user-shell .app-search-clear:hover,
body.user-shell .app-search-clear:focus-visible {
  border-color: var(--app-line-bright);
  color: var(--c-brand-600);
}
body.user-shell .app-search-enhanced.has-search-shortcut:not(.has-value):not(:focus-within)::after {
  content: "/";
  position: absolute;
  z-index: 3;
  top: 50%;
  right: 12px;
  display: grid;
  width: 23px;
  height: 23px;
  place-items: center;
  transform: translateY(-50%);
  border: 1px solid var(--app-line);
  border-radius: 6px;
  background: color-mix(in srgb, var(--app-panel-strong) 84%, transparent);
  color: var(--text-faint);
  font: 700 .67rem/1 var(--font-mono);
  box-shadow: inset 0 1px rgba(255,255,255,.03);
  pointer-events: none;
}
body.user-shell :is(.dashboard-search,.admin-toolbar-search,.app-search-box):focus-within > span:first-child {
  color: var(--c-brand-500);
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--c-brand-400) 40%, transparent));
}

/* Admin overview metrics switch from compressed to readable without dead space. */
body.admin-shell .admin-block.app-kpi-band {
  padding: 12px;
  overflow: hidden;
}
body.admin-shell .admin-block.app-kpi-band::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(105deg, transparent 0 68%, color-mix(in srgb, var(--c-brand-400) 5%, transparent));
  pointer-events: none;
}
body.admin-shell .admin-block.app-kpi-band .kpi-card-grid { position: relative; z-index: 1; }
@media (min-width: 901px) and (max-width: 1500px) {
  body.admin-shell .page > .module-header-compact + .admin-block.app-kpi-band .kpi-card-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  body.admin-shell .admin-block.app-kpi-band .kpi-card { min-height: 62px; }
}

/* Better scan rhythm for dense data without increasing the table height. */
body.user-shell table.datatable tbody tr:nth-child(even),
body.user-shell .dg-table tbody tr:nth-child(even),
body.user-shell .validation-table tbody tr:nth-child(even) {
  background-color: color-mix(in srgb, var(--c-brand-400) 2.3%, transparent);
}
/* `top` consomme --dg-head-top comme la règle de base (voir
   `table.datatable thead th`) : cette règle-ci est plus spécifique et la
   recouvrait, si bien que l'en-tête recollait à 0 — sous la barre de
   filtres — alors que la variable était bien calculée et posée. Le
   correctif existait donc à moitié, et rien ne le disait. */
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.operation-structure-table-wrap,.history-table-wrap) thead th {
  position: sticky;
  z-index: 3;
  top: var(--dg-head-top, 0);
  box-shadow: 0 1px var(--app-line), 0 8px 18px -18px rgba(0,10,18,.9);
}

/* DANS UN CADRE RÉELLEMENT DÉFILANT, `--dg-head-top` EST UN PIÈGE.
   La variable vaut la distance depuis le haut de la FENÊTRE (bas de la
   barre de filtres). Mais dès que le cadre porte `overflow-x: auto`, la
   spécification force `overflow-y` à `auto` : le cadre devient le scrollport
   le plus proche, et `top` se compte alors depuis SON haut. L'en-tête
   atterrissait donc 127 à 327 px plus bas, PAR-DESSUS les lignes — sur une
   table courte il se retrouvait même sous la dernière (captures du
   2026-08-24 : /admin/messages et /admin/fichiers).

   Il n'existe pas de troisième voie : un en-tête ancré à la fenêtre ne
   survit pas à un ancêtre défilant. Dans ce cas il se colle au haut du
   cadre. Il redescend avec la page — comme avant le 2026-08-24 — mais il
   est À SA PLACE, et ce cas est devenu rare depuis que la classe se retire
   quand la table tient.

   `.datatable-wrap` n'est PAS dans cette liste : il porte lui aussi la
   classe, mais son overflow reste `visible` — sauf sous 1000 px, traité
   juste après. */
:is(.dg-table-scroll,.xsd-table-wrap,.table-wrap,.response-table-wrap,.operation-structure-table-wrap,.history-table-wrap).is-horizontally-scrollable thead th,
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap,.response-table-wrap,.operation-structure-table-wrap,.history-table-wrap).is-horizontally-scrollable thead th {
  top: 0;
}
@media (max-width: 1000px) {
  /* Sous 1000px, `.datatable-wrap` défile sans condition (voir la règle
     dédiée plus haut) : c'est LUI le scrollport, même quand le cadre
     intérieur n'a pas la classe. */
  .datatable-wrap thead th,
  body.user-shell .datatable-wrap thead th { top: 0; }
}
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap,.datatable-wrap,.response-table-wrap,.operation-structure-table-wrap,.history-table-wrap).is-horizontally-scrollable {
  box-shadow: inset -28px 0 22px -24px color-mix(in srgb, var(--c-brand-400) 78%, transparent);
}
body.user-shell :is(.dg-table-scroll,.xsd-table-wrap,.table-wrap,.datatable-wrap,.response-table-wrap,.operation-structure-table-wrap,.history-table-wrap).is-horizontally-scrollable.is-at-scroll-end {
  box-shadow: none;
}

/* Clear tactile feedback on every primary interactive surface. */
body.user-shell :is(.tile,.operation-card,.admin-quicklink,a.kpi-card,.btn).is-pressed {
  transform: translateY(0) scale(.985) !important;
  transition-duration: .06s !important;
}
body.user-shell :is(.tile,.operation-card):hover :is(.tile-arrow,.operation-card-arrow) {
  transform: translateX(4px);
  color: var(--c-teal-400);
}
body.user-shell :is(.tile-arrow,.operation-card-arrow) {
  transition: transform .18s ease, color .18s ease;
}
body.admin-shell .admin-sidebar-link > svg { transition: transform .18s ease, color .18s ease; }
body.admin-shell .admin-sidebar-link:hover > svg,
body.admin-shell .admin-sidebar-link:focus-visible > svg { transform: translateX(2px) scale(1.06); color: #77bff0; }

/* Submission and navigation feedback are immediate but never block content. */
body.user-shell .btn.is-processing,
body.user-shell button.is-processing {
  cursor: progress;
  opacity: .86;
}
body.user-shell .btn.is-processing::before,
body.user-shell button.is-processing::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: app-processing-spin .7s linear infinite;
}
@keyframes app-processing-spin { to { transform: rotate(360deg); } }
html.app-is-navigating .app-scroll-progress span {
  transform: scaleX(.82) !important;
  transition: transform .18s ease;
}
html.app-is-navigating body.user-shell main.page {
  transform: translateY(2px);
  opacity: .72;
}
body.user-shell main.page { transition: opacity .12s ease, transform .12s ease; }

/* Living technical canvas: movement stays subtle and outside the data. */
body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero)::before {
  animation: app-hero-signal 11s ease-in-out infinite alternate;
}
@keyframes app-hero-signal {
  from { transform: translate3d(0,0,0) scale(.98); opacity: .72; }
  to { transform: translate3d(-16px,10px,0) scale(1.04); opacity: 1; }
}
body.user-shell .app-empty-state {
  background-image: radial-gradient(circle at 50% 0, color-mix(in srgb, var(--c-brand-400) 8%, transparent), transparent 58%);
}

/* Native scrollbars match the application and remain easy to grab. */
body.user-shell,
body.user-shell * {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--c-brand-400) 52%, var(--border)) color-mix(in srgb, var(--app-panel-soft) 85%, transparent);
}
body.user-shell *::-webkit-scrollbar { width: 10px; height: 10px; }
body.user-shell *::-webkit-scrollbar-track { background: color-mix(in srgb, var(--app-panel-soft) 85%, transparent); }
body.user-shell *::-webkit-scrollbar-thumb {
  border: 2px solid color-mix(in srgb, var(--app-panel-soft) 85%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--c-brand-400) 52%, var(--border));
}

@media (max-width: 760px) {
  body.admin-shell .page > .module-header-compact + .admin-block.app-kpi-band .kpi-card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.user-shell .app-search-enhanced.has-search-shortcut:not(.has-value):not(:focus-within)::after { display: none; }
}
@media (max-width: 480px) {
  body.admin-shell .page > .module-header-compact + .admin-block.app-kpi-band .kpi-card-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  body.user-shell :is(.dropdown.open .dropdown-panel,.modules-panel-detached.is-open),
  body.user-shell :is(.dashboard-topbar,.module-header-compact,.app-page-hero,.admin-users-hero,.admin-user-detail-hero,.xsd-admin-hero,.email-preview-hero)::before,
  body.user-shell .btn.is-processing::before,
  body.user-shell button.is-processing::before {
    animation: none;
  }
  html.app-is-navigating body.user-shell main.page { transform: none; opacity: 1; }
}

/* Documentation command center: concise, task-first and consistent with the app. */
body.user-shell .doc-command-center {
  position: relative;
  overflow: hidden;
  margin: 0 0 22px;
  padding: 20px;
  border: 1px solid var(--app-line);
  border-radius: 17px;
  background: linear-gradient(145deg, var(--app-panel), color-mix(in srgb, var(--app-panel-strong) 66%, var(--app-panel)));
  box-shadow: 0 16px 42px rgba(0,17,29,.11), inset 0 1px rgba(255,255,255,.025);
  scroll-margin-top: 84px;
}
body.user-shell .doc-command-center::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--c-teal-400), var(--c-brand-400), transparent 85%);
}
body.user-shell .doc-command-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 16px;
}
body.user-shell .doc-command-heading h2 { margin: 3px 0 4px; font: 750 clamp(1.25rem,2vw,1.65rem)/1.15 var(--font-display); letter-spacing: -.025em; }
body.user-shell .doc-command-heading p:last-child { max-width: 72ch; margin: 0; color: var(--text-muted); font-size: var(--fs-xs); }
body.user-shell .doc-flow-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0,1fr));
  gap: 10px;
}
body.user-shell .doc-flow-card {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 10px;
  min-height: 174px;
  padding: 15px;
  overflow: hidden;
  border: 1px solid var(--app-line);
  border-radius: 13px;
  background: linear-gradient(145deg, color-mix(in srgb,var(--app-panel-soft) 88%,transparent), color-mix(in srgb,var(--app-panel-strong) 78%,transparent));
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
body.user-shell .doc-flow-card:not(:last-child)::after {
  content: "";
  position: absolute;
  z-index: 3;
  top: 29px;
  right: -10px;
  width: 20px;
  height: 1px;
  background: linear-gradient(90deg,var(--c-brand-400),var(--c-teal-400));
}
body.user-shell .doc-flow-card:hover { transform: translateY(-2px); border-color: var(--app-line-bright); box-shadow: 0 14px 30px rgba(0,17,29,.14); }
body.user-shell .doc-flow-number { color: var(--text-faint); font: 750 .62rem/28px var(--font-mono); letter-spacing: .08em; }
body.user-shell .doc-flow-icon {
  justify-self: end;
  display: grid;
  width: 32px;
  height: 32px;
  place-items: center;
  border: 1px solid color-mix(in srgb,var(--c-brand-400) 28%,transparent);
  border-radius: 9px;
  background: color-mix(in srgb,var(--c-brand-500) 11%,var(--app-panel-soft));
  color: var(--c-brand-600);
}
body.user-shell .doc-flow-card h3 { grid-column: 1/-1; margin: 1px 0 0; font: 750 .92rem/1.28 var(--font-display); }
body.user-shell .doc-flow-card p { grid-column: 1/-1; margin: 0; color: var(--text-muted); font-size: .73rem; line-height: 1.5; }
body.user-shell .doc-flow-card a { grid-column: 1/-1; align-self: end; color: var(--c-brand-600); font-size: .7rem; font-weight: 750; text-decoration: none; }
body.user-shell .doc-flow-card a:hover { text-decoration: underline; }
body.user-shell .doc-scope-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: 10px; margin-top: 10px; }
body.user-shell .doc-scope-card { padding: 13px 15px; border: 1px solid var(--app-line); border-radius: 12px; background: color-mix(in srgb,var(--app-panel-soft) 84%,transparent); }
body.user-shell .doc-scope-card strong { font-size: .75rem; }
body.user-shell .doc-scope-card ul { display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 8px 0 0; padding-left: 18px; color: var(--text-muted); font-size: .69rem; }
body.user-shell .doc-scope-card p { margin: 6px 0 0; color: var(--text-muted); font-size: .69rem; line-height: 1.5; }
body.user-shell .doc-scope-card.is-platform { box-shadow: inset 3px 0 color-mix(in srgb,var(--c-teal-400) 72%,transparent); }
body.user-shell .doc-scope-card.is-boundary { box-shadow: inset 3px 0 color-mix(in srgb,var(--c-warn) 72%,transparent); }

body.user-shell .doc-recovery-grid { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 10px; margin-bottom: 12px; }
body.user-shell .doc-recovery-grid article {
  padding: 15px;
  border: 1px solid var(--app-line);
  border-radius: 12px;
  background: linear-gradient(145deg,var(--app-panel),var(--app-panel-strong));
}
body.user-shell .doc-recovery-grid article > span { color: var(--c-teal-400); font: 750 .62rem/1 var(--font-mono); letter-spacing: .08em; }
body.user-shell .doc-recovery-grid h3 { margin: 9px 0 5px; font-size: .85rem; }
body.user-shell .doc-recovery-grid p { margin: 0; color: var(--text-muted); font-size: .72rem; line-height: 1.55; }
body.user-shell .doc-section-action { margin: 14px 0 0; }
body.user-shell .doc-troubleshoot-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 9px; }
body.user-shell .doc-troubleshoot-grid details { overflow: hidden; border: 1px solid var(--app-line); border-radius: 11px; background: color-mix(in srgb,var(--app-panel-soft) 86%,transparent); }
body.user-shell .doc-troubleshoot-grid summary { position: relative; padding: 13px 42px 13px 14px; color: var(--text); font-size: .75rem; font-weight: 750; cursor: pointer; list-style: none; }
body.user-shell .doc-troubleshoot-grid summary::-webkit-details-marker { display: none; }
body.user-shell .doc-troubleshoot-grid summary::after { content: "+"; position: absolute; top: 50%; right: 14px; transform: translateY(-50%); color: var(--c-brand-600); font-size: 1rem; }
body.user-shell .doc-troubleshoot-grid details[open] summary::after { content: "−"; }
body.user-shell .doc-troubleshoot-grid details[open] summary { border-bottom: 1px solid var(--app-line); background: color-mix(in srgb,var(--c-brand-500) 6%,transparent); }
body.user-shell .doc-troubleshoot-grid details p { margin: 0; padding: 12px 14px 14px; color: var(--text-muted); font-size: .72rem; line-height: 1.58; }

@media (max-width: 1050px) {
  body.user-shell .doc-flow-grid { grid-template-columns: repeat(2,minmax(0,1fr)); }
  body.user-shell .doc-flow-card:not(:last-child)::after { display: none; }
}
@media (max-width: 700px) {
  body.user-shell .doc-command-center { padding: 15px; }
  body.user-shell .doc-command-heading { align-items: flex-start; flex-direction: column; }
  body.user-shell .doc-command-heading .btn { width: 100%; justify-content: center; }
  body.user-shell .doc-flow-grid,
  body.user-shell .doc-scope-grid,
  body.user-shell .doc-recovery-grid,
  body.user-shell .doc-troubleshoot-grid { grid-template-columns: 1fr; }
  body.user-shell .doc-flow-card { min-height: 0; }
  body.user-shell .doc-scope-card ul { display: grid; }
}

/* ── État d'attente des formulaires longs (action-progress.js) ─────────────
   La barre est DÉCORATIVE et le dit : elle ne mesure rien, elle indique
   seulement que le travail est en cours. Ce qui informe, c'est le libellé et
   le temps écoulé juste en dessous. Une barre qui prétendrait mesurer une
   progression que le serveur ne communique pas serait un chiffre inventé.
   Sous « réduire les animations », elle ne bouge plus du tout : la classe
   .is-static la remplace par un trait plein, et le texte suffit. */
.action-progress {
  margin-top: 14px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius, 8px);
  background: var(--bg-sunken);
}
.action-progress-track {
  height: 4px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.action-progress-track span {
  display: block;
  height: 100%;
  width: 38%;
  border-radius: inherit;
  background: var(--primary);
  animation: action-progress-slide 1.6s ease-in-out infinite;
}
.action-progress-track.is-static span {
  width: 100%;
  animation: none;
  opacity: .5;
}
@keyframes action-progress-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}
.action-progress-label {
  margin: 10px 0 2px;
  font-weight: 600;
}
.action-progress-meta {
  margin: 0;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums; /* le compteur ne doit pas frémir chaque seconde */
}
.action-progress-hint {
  margin: 8px 0 0;
  color: var(--text-muted);
  font-size: .92em;
}
@media (prefers-reduced-motion: reduce) {
  .action-progress-track span { animation: none; width: 100%; opacity: .5; }
}

/* ── Hauteur minimale des cibles tactiles (WCAG 2.5.8) ─────────────────────
   Mesuré le 2026-08-14 à 375 px de large : ces liens faisaient 15 à 20 px de
   haut, sous le minimum de 24. Un lien de 15 px se vise mal au pouce, et les
   listes de documentation en alignent quatorze d'affilée.

   La correction porte une hauteur minimale plutôt qu'un remplissage : le
   remplissage aurait décalé les alignements existants, `min-height` avec
   `align-items: center` agrandit la zone cliquable sans bouger le texte.

   NE SONT PAS VISÉS, et c'est délibéré : les liens EN LIGNE dans une phrase,
   que la règle 2.5.8 exempte explicitement — les agrandir déformerait
   l'interligne du paragraphe qui les contient pour aucun gain réel. */
.app-footer-links a,
.doc-op-list a,
.doc-flow-card a,
.doc-catalog-download,
.dg-sort,
.breadcrumb a,
/* Malgre son nom, .doc-inline-link n'est pas un lien en ligne dans une phrase :
   le gabarit le pose seul dans son paragraphe (« Ouvrir ce module → »). C'est
   une action autonome, elle merite donc une vraie zone cliquable. */
.doc-inline-link {
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}

/* ── En-tête public sur grand écran ────────────────────────────────────────
   Signalé le 2026-08-15 : sur un écran large, le menu paraît minuscule. Deux
   contraintes s'additionnaient. La barre est plafonnée à 1400 px, ce qui laisse
   plus de 500 px inutilisés à partir de 1920 ; et ses liens gardent la taille
   de compromis (--fs-sm) choisie pour les largeurs où la barre débordait.

   Ce compromis reste juste en dessous de 1600 px — les réglages fins par palier
   déjà en place ne sont pas touchés. Au-delà, la place existe : la barre s'étend
   et les liens reprennent la taille du texte courant.

   Le plafond monte à 1600 et non à la largeur de l'écran : une barre de
   navigation qui court sur 2560 px sépare tellement la marque des actions que
   l'œil ne fait plus le lien entre les deux. */
@media (min-width: 1600px) {
  .marketing-header-inner { max-width: 1600px; }
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-links { gap: var(--sp-5); }
  #appHeaderResponsive:not(.is-nav-collapsed) .marketing-links a { font-size: var(--fs-body); }
}

/* ── Le burger doit faire la taille de ses voisins ─────────────────────────
   Signalé le 2026-08-15, mesuré ensuite sur les trois en-têtes : le bouton de
   menu était figé à 36 px pendant que le bouton de thème et le sélecteur de
   langue changeaient de taille selon la largeur. Résultat, dans la même
   rangée : trop petit de 6 px sur l'en-tête public, trop gros de 4 px sur
   mobile partout.

   Les seuils reprennent EXACTEMENT ceux de .util-btn — 540.02, 540, 360 --
   pour que les deux ne puissent pas diverger à une largeur intermédiaire.

   Le premier est réservé au site public : c'est le seul endroit où .util-btn
   monte à 42 px. Dans l'espace connecté et l'administration, le bouton de
   thème reste à 36 px, taille que le burger a déjà — les aligner globalement
   l'aurait désaccordé là où il était juste. */
@media (min-width: 540.02px) {
  .public-site .responsive-nav-toggle { width: 42px; height: 42px; }
}
@media (max-width: 540px) {
  .responsive-nav-toggle { width: 32px; height: 32px; }
}
@media (max-width: 360px) {
  /* Réservé au public : la règle .util-btn à 30 px l'est aussi (elle est
     scopée .marketing-actions). Dans l'espace connecté, le bouton de thème
     reste à 32 px même à 340 px de large -- mesuré. */
  .public-site .responsive-nav-toggle { width: 30px; height: 30px; }
}

/* ============================================================
   Couleurs du logo — liste unique des surfaces
   ============================================================
   Le logo est un SVG en ligne à trois couleurs : --logo-encre (la grille et
   le chevron, « EUDA »), --logo-accent (la cellule mappée, « MED ») et
   --logo-lets (« Let's »). Les redéfinir suffit à le retourner : il n'y a
   plus deux images à basculer, ni de plaque blanche à glisser dessous.

   Les valeurs par défaut sont posées avec les autres jetons en tête de
   fichier, et suivent le thème. Ne restent ici que les surfaces dont le fond
   est une couleur LITTÉRALE, sombre dans les deux thèmes — celles-là ne
   peuvent pas s'en remettre au thème. Toute surface de ce genre portant un
   logo s'ajoute ICI ; test_logo_svg_en_ligne.py vérifie que la liste reste
   complète.

   Les surfaces dont le fond est un JETON n'ont besoin de rien : elles
   héritent de :root, qui bascule déjà. C'est le cas de .app-footer
   (--bg-sunken), de .auth-brand (--bg) et — contrairement à ce qu'affirmait
   le commentaire d'origine — de .marketing-header, dont le fond est
   var(--card), donc blanc en thème clair. L'y avoir classée en surface
   permanente rendait le logo invisible sur la barre publique en thème clair,
   signalé et mesuré le 2026-08-15.
   ------------------------------------------------------------ */
.marketing-footer-logo,     /* pied marketing,  rgb(8,31,48) littéral  */
body.user-shell .app-header /* connecté+admin,  #101f2b littéral       */
{
  --logo-encre: #f2f7fb;
  --logo-accent: var(--c-brand-400);
  --logo-lets: var(--c-brand-400);
}


/* ==========================================================================
   REPONSE AU SURVOL -- les manques releves par le balayage du 2026-08-16.
   Chaque element ci-dessous est cliquable mais ne le montrait pas. Ordre
   des priorites : ce qu'on clique le plus souvent d'abord.
   ========================================================================== */

/* Le logo de l'en-tete ramene a l'accueil sur TOUTES les pages, et ne
   reagissait sur aucune. C'est le lien le plus universel du site. */
.marketing-brand { transition: opacity .18s var(--ease); }
.marketing-brand:hover { opacity: .78; }

/* Le lien des conditions, dans la bande d'accueil : signale par sa couleur
   au repos, par rien du tout au survol. */
.beta-banner a { transition: text-decoration-color .18s var(--ease); text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 3px; }
.beta-banner a:hover { text-decoration-color: currentColor; }

/* Liens dans les corps de texte editoriaux et juridiques. Ils sont
   souvent la SEULE navigation d'une page juridique -- « politique de
   confidentialite », « formulaire de contact » -- et n'avaient aucun etat.
   Souligne au repos (un lien dans un paragraphe doit se voir sans couleur
   seule), trait epaissi et teinte au survol. */
.legal-body a, .about-story a, .guide-article-body p a {
  color: var(--primary-ink);
  text-underline-offset: 3px;
  transition: color .18s var(--ease);
}
.legal-body a:hover, .about-story a:hover, .guide-article-body p a:hover {
  color: var(--c-brand-500);
  text-decoration-thickness: 2px;
}

/* Les liens des deux encarts de /tarifs (deploiement accompagne, licence
   locale) et le renvoi vers l'inventaire des controles : contrairement aux
   liens DANS une carte de tarif -- ou c'est la carte entiere qui repond --
   ceux-ci n'ont aucun conteneur qui reagit pour eux. Ils etaient les trois
   derniers elements muets du site apres le balayage des quinze pages. */
.pricing-aside a, .pricing-included li a, .pricing-card > a,
.guide-card a, .home-guide-card a {
  transition: color .18s var(--ease);
}
.pricing-aside a:hover, .pricing-included li a:hover, .pricing-card > a:hover,
.guide-card a:hover, .home-guide-card a:hover {
  color: var(--c-brand-500);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Les cartes de l'index des guides : seule la fleche du lien bougeait.
   Meme traitement que les cartes de tarifs, et meme raison technique --
   `translate` plutot que `transform`, que l'animation d'apparition pilote
   deja (voir .pricing-card). */
.guide-card {
  transition: translate .22s var(--ease), border-color .22s var(--ease),
              box-shadow .22s var(--ease);
}
.guide-card:hover, .guide-card:focus-within {
  translate: 0 -4px;
  border-color: var(--c-brand-400);
  box-shadow: 0 22px 44px -28px rgba(8, 31, 48, .5);
}
@media (prefers-reduced-motion: reduce) {
  .marketing-brand { transition: opacity .18s var(--ease); }
  .guide-card { transition: border-color .22s var(--ease); }
  .guide-card:hover, .guide-card:focus-within { translate: none; }
}


/* ==========================================================================
   RYTHME VERTICAL ET RANGEES DE COMMANDES (revue d'ensemble)

   Constat mesure sur 20 ecrans (admin ET utilisateur), a 1280px, en lisant
   l'ecart reel entre chaque bloc de premier niveau de `.page` :

     /admin              22 / 22 / 28 / 22 / 22
     /admin/demandes     22 / 8
     /admin/societes     22 / 8 / 16
     /admin/devis        22 / 22 / 0 / 0      <- trois cadres qui se touchent
     /admin/abonnements  0 / 22               <- l'en-tete colle au precedent
     /admin/support      22 / 8 / 16 / 16 / 16 / 8 / 8
     /admin/roles        22 / 12
     /tableau-de-bord    18 / 30 / 58 / 16

   Dix valeurs differentes (0, 8, 12, 16, 18, 22, 24, 28, 30, 58) sans regle :
   chaque gabarit portait sa propre marge, et `.detail-panel` n'en portait
   AUCUNE -- d'ou les cadres colles bord a bord sur /admin/devis. Aucun
   correctif de gabarit ne pouvait tenir : le onzieme ecran aurait invente sa
   onzieme valeur.

   La regle est donc posee UNE fois, sur le conteneur de page, et vaut pour
   les deux coques (.admin-shell et .user-shell) :

     - ecart normal entre deux zones            : --sp-5 (22px), deja majoritaire
     - une barre d'outils/onglets appartient au tableau qu'elle pilote : --sp-2
     - un titre de section ouvre une zone       : --sp-7 au-dessus

   Les marges basses ad hoc des enfants directs sont neutralisees : sans
   cela elles s'ajoutent a l'ecart pose ici (44px la ou 22 sont voulus).
   ========================================================================== */

body.admin-shell .page > *,
body.user-shell .page > * { margin-bottom: 0; }

body.admin-shell .page > * + *,
body.user-shell .page > * + * { margin-top: var(--sp-5); }

/* Une barre de filtres, une barre d'onglets ou un decompte de resultats ne
   sont pas des zones autonomes : ils annoncent le bloc suivant et doivent
   rester colles a lui, sinon la barre semble filtrer le bloc d'au-dessus. */
body.admin-shell .page > :is(.admin-toolbar, .dg-toolbar, .admin-tabs, .dg-results-count) + *,
body.user-shell .page > :is(.admin-toolbar, .dg-toolbar, .admin-tabs, .dg-results-count) + * {
  margin-top: var(--sp-2);
}

/* Un intitule de section ouvre une nouvelle zone : plus d'air au-dessus que
   d'espace normal entre blocs, moins que les 58px isoles du tableau de bord. */
body.admin-shell .page > * + :is(.section-heading, .module-header-compact),
body.user-shell .page > * + :is(.section-heading, .module-header-compact) {
  margin-top: var(--sp-7);
}

/* --------------------------------------------------------------------------
   Barre d'outils : les commandes suivaient le MILIEU du bloc de filtres.

   `.admin-toolbar` est une ligne flex en `align-items:center`, et
   `.admin-toolbar-filters` grandit en hauteur des qu'il y a plus de filtres
   que de largeur : mesure sur /admin/journal, filtres = 112px (trois
   rangees), tandis que la recherche et les boutons Filtrer/Reinitialiser
   font 32px. Centres, ils se posaient a la hauteur de la DEUXIEME rangee de
   filtres -- flottants, alignes sur rien. Cales en bas, ils reviennent sur
   la derniere rangee de filtres, la ou on termine sa saisie.
   -------------------------------------------------------------------------- */
.admin-toolbar { align-items: flex-end; }
.admin-toolbar:not(.admin-toolbar--uniform) .admin-toolbar-filters { align-items: flex-end; }

/* --------------------------------------------------------------------------
   Rangee « champs + bouton d'action » (.actor-code-form, 8 gabarits admin).

   Deux defauts mesures sur /admin/devis :

   1. le bouton se calait sur le haut de la cellule, donc a la hauteur de
      l'ETIQUETTE et non du champ : 40px trop haut. Cause : trois gabarits
      posent `align-items:start` en style en ligne (legitime quand une
      colonne contient un textarea plus haut) -- mais cela emportait aussi
      la cellule d'action. `align-self` sur la cellule bat `align-items` du
      conteneur : l'action redescend sans toucher aux colonnes de saisie.

   2. bouton 32px contre champ 43px (45px pour un champ fichier) : cotes a
      cote, deux hauteurs differentes. La densite admin (--a-control-h:32px)
      vaut pour les barres d'outils ; dans un panneau, les champs gardent la
      taille de saisie generale. C'est donc le bouton qui s'aligne sur le
      champ voisin, et les champs de la rangee qui s'accordent entre eux.
   -------------------------------------------------------------------------- */
body.admin-shell { --a-field-h: 42px; }

/* 3. le bouton occupait une COLONNE DE CHAMP entiere. Le formulaire etait une
      grille de colonnes egales, et dix-sept gabarits en redefinissaient les
      colonnes en style en ligne -- le plus souvent
      `repeat(auto-fit, minmax(230px,1fr))`. Consequences mesurees sur
      /admin/devis : la cellule d'action recevait 309px pour un bouton de
      130px (d'ou le bouton « tres loin » du dernier champ), une piste
      fantome de 0px s'ajoutait quand le compte de cellules ne tombait pas
      juste (471.5 / 471.5 / 0), et des que le compte depassait le nombre de
      colonnes le bouton partait SEUL sur une deuxieme ligne.

      Une ligne flexible dit ce que la mise en page veut vraiment : les
      champs se partagent la largeur, l'action prend la sienne et reste au
      bout de la rangee. Les `grid-template-columns` en ligne des gabarits
      deviennent alors sans effet -- ils ne sont plus annules au cas par
      cas, ils ne s'appliquent plus du tout. */
.actor-code-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-4);
}
.actor-code-form > * { flex: 1 1 220px; min-width: 0; }
/* Dix-neuf cellules disaient « je prends toute la rangee » par un
   `grid-column:1/-1` en style en ligne -- une consigne de grille, donc
   muette des que la rangee devient flexible. Ces cellules portent
   desormais une classe : la consigne survit au mode de mise en page, et
   ne se perd plus silencieusement au prochain changement. */
.actor-code-form > .actor-form-full { flex: 1 1 100%; }
.actor-code-form > .actor-form-wide { flex: 2 1 320px; }
/* `0 1 auto`, jamais `0 0 auto` : la cellule prend la largeur de son contenu
   MAIS accepte de retrecir. Sans cela, une cellule portant deux boutons
   (/recuperation : « Obtenir le modele Excel » + « Analyser sans
   telecharger », 448px a elles deux) ne pouvait plus jamais retrecir, donc
   ses boutons ne revenaient plus a la ligne : debordement horizontal de
   27px mesure a 900px de large. Meme defaut que celui deja documente plus
   haut pour la grille, retrouve ici a l'identique. */
.actor-code-form > .actor-form-action {
  flex: 0 1 auto;
  max-width: 100%;
  align-self: flex-end;
  padding-bottom: 0;
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
/* Une cellule d'action qui prend AUSSI toute la rangee (Maintenance) : la
   consigne de largeur l'emporte sur le dimensionnement au contenu. */
.actor-code-form > .actor-form-action.actor-form-full { flex: 1 1 100%; }

/* Les replis de la grille (grid-column, grid-template-columns !important)
   n'ont plus prise sur une ligne flex : memes seuils, meme resultat. */
@media (max-width: 1080px) {
  .actor-code-form > * { flex-basis: calc(50% - var(--sp-4)); }
}
@media (max-width: 760px) {
  .actor-code-form > *, .actor-code-form > .actor-form-action { flex-basis: 100%; }
  .actor-code-form > .actor-form-action { flex-grow: 1; }
}

body.admin-shell .actor-code-form :is(input:not([type=checkbox]):not([type=radio]):not([type=file]), select) {
  height: var(--a-field-h);
  box-sizing: border-box;
}
/* Un champ « fichier » garde sa hauteur naturelle. Lui imposer `height` et
   des marges internes nulles collait son bouton « Selectionner » CONTRE LE
   HAUT du cadre : le contenu d'un champ fichier tient sur une seule ligne,
   qui se pose en haut de la boite -- elle ne se centre pas comme le ferait
   la valeur d'un champ texte. Trois pixels de plus que ses voisins, mais
   les bas de rangee restent alignes, ce qui est ce qui se voit. */
body.admin-shell .actor-code-form textarea { min-height: var(--a-field-h); }
body.admin-shell .actor-code-form .actor-form-action .btn { min-height: var(--a-field-h); }

/* --------------------------------------------------------------------------
   Catalogue de l'offre (ajout d'une ligne de devis depuis /tarifs).

   Ces cartes empruntaient `.module-checkbox-grid`, une grille prevue pour des
   CASES A COCHER : deux colonnes fixes, quel que soit l'espace. Sur un
   panneau de 959px, deux cartes de 473px, la ou trois tiennent
   confortablement. Trois defauts s'ajoutaient :

     - `form.stack` plafonne a `max-width:480px` : la carte cessait de suivre
       sa colonne et laissait un vide a droite dans la cellule ;
     - `border-radius: var(--radius-sm)` -- ce jeton n'existe pas dans cette
       feuille (le nom reel est --r-sm) : les cartes avaient donc des angles
       droits au milieu d'une interface arrondie ;
     - le bouton d'ajout suivait la fin du texte, donc a une hauteur
       differente dans chaque carte d'une meme rangee.

   Grille dediee, trois par ligne, cartes de hauteur egale et bouton pose au
   meme niveau dans toutes.
   -------------------------------------------------------------------------- */
.devis-catalogue-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-3);
  align-items: stretch;
}
.devis-catalogue-card.stack {
  max-width: none;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--card-raised);
}
/* Le bouton est pousse en bas : les cartes d'une rangee n'ont pas le meme
   volume de texte, et sans cela il flottait a une hauteur differente dans
   chacune. Les champs, eux, prennent toute la largeur de leur carte. */
.devis-catalogue-card .btn { margin-top: auto; }
.devis-catalogue-card :is(input[type=text], input[type=number]) { width: 100%; }
@media (max-width: 1100px) { .devis-catalogue-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .devis-catalogue-grid { grid-template-columns: minmax(0, 1fr); } }

/* --------------------------------------------------------------------------
   Barre laterale : la pastille « rubrique active » et le compteur de la
   rubrique occupaient le meme coin. La pastille est posee a `right:9px`
   (186->192px dans un lien de 201px) et le badge, pousse par
   `margin-left:auto`, s'arretait a 189px : les deux se chevauchaient des
   qu'une rubrique a compteur etait active -- le cas exact de « Demandes »
   avec une demande en attente. Les deux signaux se gardent, ils cessent de
   se superposer.
   -------------------------------------------------------------------------- */
body.admin-shell .admin-sidebar-link.is-active .admin-sidebar-badge { margin-right: var(--sp-3); }

/* ==========================================================================
   IMPRESSION. Le public de ce site archive : un responsable reglementaire
   imprime l'inventaire des controles ou la grille tarifaire pour son
   dossier qualite. Regle d'ensemble : ne reste sur papier que le CONTENU --
   ni navigation, ni bandeaux, ni appels a l'action, ni decor anime. Les
   fonds sombres passent en noir sur blanc : l'encre est a l'utilisateur.
   ========================================================================== */
@media print {
  .marketing-header, .marketing-footer, .beta-banner,
  .announcement-banner-stack, .public-scroll-progress, .skip-link,
  .market-final-cta, .guide-final-cta, .controls-filter,
  .controls-filter-none, .error-actions, .pricing-card > a,
  .home-video-control, .guide-hero-orb, .hero-return-link {
    display: none !important;
  }
  body { background: #fff !important; color: #000 !important; }
  /* Les heros a fond sombre (guides, tarifs, controles) : texte blanc sur
     degrade a l'ecran, noir sur blanc sur papier. */
  .guide-hero, .guide-article-hero, .page-hero-compact,
  .market-final-cta {
    background: none !important;
    color: #000 !important;
  }
  .guide-hero h1, .guide-article-hero h1, .guide-hero.page-hero-compact h1,
  .guide-hero.page-hero-compact .page-hero-inner > div > p:not(.market-kicker) {
    color: #000 !important;
  }
  /* Une regle de l'inventaire ou une carte de tarif ne se coupe jamais au
     changement de page : la moitie d'un intitule ne sert a rien. */
  .controls-list li, .pricing-card, .guide-card { break-inside: avoid; }
  /* Le lien source des guides imprime SA CIBLE : sur papier, un lien sans
     adresse est une impasse. Uniquement celui-la -- imprimer l'adresse de
     chaque lien de navigation rendrait le texte illisible. */
  .guide-source-link::after {
    content: " (" attr(href) ")";
    font-size: .8em;
    color: #000;
  }
  /* Le compte des familles reste, leur ancre de sommaire disparait. */
  .controls-summary { display: none !important; }
}

/* Trace d'une opération (admin_activity_trace.html) — les étapes dans leur
   ordre chronologique, reliées par un filet vertical continu. Le filet est
   porté par ::before de la liste et non par chaque puce : autrement il se
   coupe entre deux étapes de hauteurs différentes, et la lecture « ceci a
   suivi cela » se perd. */
.trace-steps {
  list-style: none; margin: var(--sp-4) 0 0; padding: 0 0 0 34px;
  position: relative;
}
.trace-steps::before {
  content: ""; position: absolute; left: 13px; top: 10px; bottom: 10px;
  width: 2px; background: var(--app-line, var(--border));
}
.trace-step { position: relative; padding: 0 0 var(--sp-4) 0; }
.trace-step:last-child { padding-bottom: 0; }
.trace-step-mark {
  position: absolute; left: -34px; top: 0;
  width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--app-panel, #fff); color: var(--c-brand-600);
  border: 2px solid var(--app-line, var(--border));
}
.trace-step-body { padding-top: 3px; }
.trace-step-body h3 { margin: 0 0 4px; font-size: 1rem; }
.trace-step-when { margin: 0 0 6px; font-size: var(--fs-xs); color: var(--muted); }
.trace-step-body p { margin: 0 0 6px; }
.trace-files { list-style: none; margin: 8px 0 0; padding: 0; }
.trace-files li {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 6px 0; border-top: 1px solid var(--app-line, var(--border));
  min-width: 0;
}
.trace-files li > span:first-child { flex: 1 1 auto; min-width: 0; }
.rank-sub { display: block; margin-top: 2px; color: var(--text-muted); font-size: var(--fs-2xs); font-weight: 400; }
/* Pastille d'annonce : même rôle visuel que l'étiquette du bandeau public
   figé (.beta-banner-tag), mais alimentée par Maintenance. Flex:none pour
   qu'elle ne se comprime jamais au profit du message. */
.announcement-banner-tag {
  flex: none; align-self: flex-start;
  padding: 2px 10px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 14%, transparent);
  color: inherit; font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
}
.announcement-banner-text > a { color: inherit; font-weight: 600; text-decoration: underline; }
/* Note compacte + ⓘ (voir _field_help.html::note). Une seule ligne, le
   bouton d'aide calé à droite du texte et jamais comprimé. */
.inline-note {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: var(--sp-3) 0; padding: 8px 12px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--r-sm); background: var(--card-raised);
  font-size: var(--fs-xs); color: var(--text);
}
.inline-note.is-warning { border-left-color: var(--c-warn, #c98a2b); }
.inline-note.is-info { border-left-color: var(--c-brand-500); }
.inline-note-icon { flex: none; display: grid; color: var(--text-muted); }
.inline-note-text { flex: 1 1 auto; min-width: 0; }
.inline-note .op-info-btn { flex: none; }

/* Tuile de mesure incomplète (modèles manquants pour une version) : le
   chiffre seul ne se remarque pas, un liseré si. */
.xsd-profile-metrics > div.is-warning { box-shadow: inset 3px 0 var(--c-warn, #c98a2b); }

/* Focus de `.btn` — le bouton le plus employé de l'application n'en avait
   AUCUN, et héritait donc de l'anneau par défaut du navigateur : un cadre
   clair collé au bord, qui rognait le libellé après un clic. Anneau propre
   au clavier (:focus-visible), rien à la souris — cliquer ne doit pas
   laisser de marque, mais tabuler doit rester visible. */
.btn:focus-visible {
  outline: 2px solid var(--c-brand-500);
  outline-offset: 2px;
}
.btn:focus:not(:focus-visible) { outline: none; }
