/* =====================================================================
   Optin — feuille de style unique (vitrine, backoffice, portail, recueil).
   ---------------------------------------------------------------------
   Alignée sur le système de design FinixPhone : mêmes fontes, mêmes jetons,
   même structure de page — bandeaux pleine largeur, contenu dans un
   `.container` de 1200 px. Les deux produits sont édités par FINIX WORLD ;
   ils doivent se ressembler.

   Un ajout propre à Optin : une famille monospace. Ce produit affiche des
   empreintes SHA-256 et des références destinées à être collationnées
   caractère par caractère — cela ne se lit pas comme de la prose.

   Servie localement, jamais depuis un CDN : la politique de sécurité de
   contenu (`default-src 'self'`) l'interdit, et une dépendance externe sur un
   outil de conformité serait un point de défaillance de trop. Les fontes
   suivent la même règle.
   ===================================================================== */

/* ── Fontes ──────────────────────────────────────────────────────────────
   Sous-ensemble latin uniquement : il couvre le français, guillemets et œ
   compris. Les variantes étendues tripleraient le poids sans usage ici. */
@font-face {
    font-family: 'Bricolage Grotesque'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('/assets/fonts/bricolage-grotesque-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Bricolage Grotesque'; font-style: normal; font-weight: 700; font-display: swap;
    src: url('/assets/fonts/bricolage-grotesque-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('/assets/fonts/geist-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('/assets/fonts/geist-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('/assets/fonts/geist-600.woff2') format('woff2');
}
@font-face {
    font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('/assets/fonts/ibm-plex-mono-400.woff2') format('woff2');
}
@font-face {
    font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('/assets/fonts/ibm-plex-mono-500.woff2') format('woff2');
}

/* ── 1. Jetons ─────────────────────────────────────────────────────────── */
:root {
    --font-display: 'Bricolage Grotesque', system-ui, sans-serif;
    --font-body: 'Geist', system-ui, -apple-system, sans-serif;
    --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

    --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
    --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;
    --s-9: 96px; --s-10: 128px;

    --r-sm: 8px; --r: 12px; --r-lg: 20px; --r-xl: 28px; --r-full: 999px;

    --maxw: 1200px;
    --measure: 62ch;
    --ease: cubic-bezier(.2, .7, .2, 1);
    --ease-out: cubic-bezier(.16, 1, .3, 1);
}

/* ── 2. Thèmes ─────────────────────────────────────────────────────────
   Le clair est le défaut ; le sombre s'applique soit par préférence système,
   soit par choix explicite. Les trois états doivent être écrits séparément,
   sinon un visiteur dont le système est en sombre et qui demande le clair ne
   l'obtient pas. */
:root, [data-theme='light'] {
    --bg: #f8f6f3;
    --bg-2: #f0ede7;
    --surface: #ffffff;
    --surface-2: #f4f1eb;
    --text: #16242e;
    --text-muted: #55636d;
    --text-soft: #8a949c;
    --border: #e8e2d9;
    --border-strong: #d8d1c5;

    --accent: #e94e1b;
    --accent-strong: #ce3e0e;
    --accent-soft: rgb(233 78 27 / .10);
    --accent-line: rgb(233 78 27 / .28);

    --petrol: #1e536b;
    --petrol-2: #2c6a85;
    --petrol-soft: rgb(30 83 107 / .10);

    /* États métier, distincts de l'orange de marque : un aplat coloré doit
       toujours vouloir dire quelque chose, et l'accent n'est pas un état. */
    --ok: #1f7d55;
    --ok-soft: #e6f2ec;
    --warn: #8a5600;
    --warn-soft: #faf0dc;
    --danger: #a41d34;
    --danger-soft: #f9e8ea;
    /* Encre posée SUR l'aplat rouge. Elle est un jeton à part parce qu'elle
       s'inverse avec le thème : rouge profond en clair, donc du blanc ; rouge
       pâli en sombre, donc de l'encre foncée. Un `color: #fff` en dur rendait
       le bouton sombre illisible, et son absence laissait `.btn--ghost` imposer
       du NOIR SUR ROUGE. */
    --danger-ink: #fff;

    --shadow-sm: 0 1px 2px rgb(20 34 44 / .05), 0 2px 6px rgb(20 34 44 / .04);
    --shadow-md: 0 10px 28px rgb(20 34 44 / .09), 0 2px 6px rgb(20 34 44 / .05);
    --shadow-lg: 0 34px 74px rgb(14 42 56 / .18), 0 10px 24px rgb(20 34 44 / .08);
    --grain-opacity: .025;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) {
        --bg: #0a1d26;
        --bg-2: #0d2530;
        --surface: #112e3a;
        --surface-2: #163845;
        --text: #eaf1f3;
        --text-muted: #9db2bc;
        --text-soft: #6c8792;
        --border: rgb(255 255 255 / .09);
        --border-strong: rgb(255 255 255 / .17);

        --accent: #ff6a3d;
        --accent-strong: #ff8258;
        --accent-soft: rgb(255 106 61 / .16);
        --accent-line: rgb(255 106 61 / .34);

        --petrol: #52a0b8;
        --petrol-2: #6fb6cc;
        --petrol-soft: rgb(82 160 184 / .16);

        --ok: #35c08a;
        --ok-soft: rgb(53 192 138 / .13);
        --warn: #e0b263;
        --warn-soft: rgb(224 178 99 / .13);
        --danger: #f1948f;
        --danger-soft: rgb(241 148 143 / .13);
        --danger-ink: #2b0d11;

        --shadow-sm: 0 1px 2px rgb(0 0 0 / .3);
        --shadow-md: 0 10px 28px rgb(0 0 0 / .4);
        --shadow-lg: 0 34px 74px rgb(0 0 0 / .55), 0 10px 24px rgb(0 0 0 / .4);
        --grain-opacity: .035;
    }
}

[data-theme='dark'] {
    --bg: #0a1d26;
    --bg-2: #0d2530;
    --surface: #112e3a;
    --surface-2: #163845;
    --text: #eaf1f3;
    --text-muted: #9db2bc;
    --text-soft: #6c8792;
    --border: rgb(255 255 255 / .09);
    --border-strong: rgb(255 255 255 / .17);

    --accent: #ff6a3d;
    --accent-strong: #ff8258;
    --accent-soft: rgb(255 106 61 / .16);
    --accent-line: rgb(255 106 61 / .34);

    --petrol: #52a0b8;
    --petrol-2: #6fb6cc;
    --petrol-soft: rgb(82 160 184 / .16);

    --ok: #35c08a;
    --ok-soft: rgb(53 192 138 / .13);
    --warn: #e0b263;
    --warn-soft: rgb(224 178 99 / .13);
    --danger: #f1948f;
    --danger-soft: rgb(241 148 143 / .13);
    --danger-ink: #2b0d11;

    --shadow-sm: 0 1px 2px rgb(0 0 0 / .3);
    --shadow-md: 0 10px 28px rgb(0 0 0 / .4);
    --shadow-lg: 0 34px 74px rgb(0 0 0 / .55), 0 10px 24px rgb(0 0 0 / .4);
    --grain-opacity: .035;
}

/* Alias hérités : des styles en ligne dans les gabarits référencent encore ces
   noms. Les garder évite de réécrire vingt-neuf vues pour un renommage. */
:root {
    --paper: var(--bg);
    --surface-alt: var(--surface-2);
    --ink: var(--text);
    --ink-soft: var(--text-muted);
    --muted: var(--text-muted);
    --rule: var(--border);
    --rule-strong: var(--border-strong);
    --brand: var(--accent);
    --brand-ink: var(--accent-strong);
    --radius: var(--r-sm);
    --radius-lg: var(--r);
    --shadow: var(--shadow-sm);
    --shadow-lift: var(--shadow-md);
    --accent-ink: #fff;
    --gap: var(--s-4);
}

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
    margin: 0;
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
    font-size: 17px;
    -webkit-font-smoothing: antialiased;
    transition: background .4s var(--ease), color .4s var(--ease);
    position: relative;
}

/* Grain : de la profondeur sans image à charger. */
body::before {
    content: ''; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
    opacity: var(--grain-opacity);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1, h2, h3, h4 {
    font-family: var(--font-display); font-weight: 700;
    line-height: 1.12; letter-spacing: -.022em; margin: 0; color: var(--text);
}
p { margin: 0; text-wrap: pretty; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); }
img, svg { display: block; max-width: 100%; }
strong, b { font-weight: 600; }
::selection { background: var(--accent); color: #fff; }

:where(a, button, input, select, textarea, summary):focus-visible {
    outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px;
}

.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s-5); }

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── 4. Typographie technique ──────────────────────────────────────────
   Tout ce qui est destiné à être recopié, dicté ou vérifié passe en
   monospace : une référence et une empreinte se collationnent, elles ne se
   lisent pas. */
.hash, code, kbd, samp {
    font-family: var(--font-mono); font-size: .82em;
    word-break: break-all; color: var(--text-muted);
}
.ref {
    font-family: var(--font-mono); font-size: .88em; font-weight: 500;
    color: var(--text); white-space: nowrap;
}
a .ref, .ref a { color: inherit; }
/* Valeur en chasse fixe qui reste du texte courant : un numéro de téléphone se
   lit et se compose, il ne se collationne pas comme une empreinte. */
.mono { font-family: var(--font-mono); font-size: .92em; font-variant-numeric: tabular-nums; }

pre {
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--r); padding: var(--s-4) var(--s-5);
    overflow-x: auto; font-family: var(--font-mono); font-size: 13px;
    line-height: 1.7; white-space: pre-wrap; color: var(--text-muted); margin: 0;
}

/* ── 5. Boutons ────────────────────────────────────────────────────────── */
button, .btn {
    display: inline-flex; align-items: center; gap: var(--s-2);
    font-family: var(--font-body); font-weight: 600; font-size: 15px;
    padding: 11px 19px; border-radius: var(--r-full);
    border: 1px solid transparent; background: var(--accent); color: #fff;
    cursor: pointer; transition: all .22s var(--ease); white-space: nowrap;
    box-shadow: 0 6px 18px -6px var(--accent); line-height: 1.4;
}
button:hover, .btn:hover {
    background: var(--accent-strong); color: #fff;
    transform: translateY(-2px); box-shadow: 0 12px 26px -8px var(--accent);
}
button:active, .btn:active { transform: none; }
button:disabled, .btn[aria-disabled='true'] {
    opacity: .45; cursor: not-allowed; transform: none; box-shadow: none;
}

.btn-lg { padding: 15px 28px; font-size: 16px; }

.btn--ghost, .btn-line {
    background: transparent; color: var(--text);
    border-color: var(--border-strong); box-shadow: none;
}
.btn--ghost:hover, .btn-line:hover {
    background: transparent; color: var(--accent);
    border-color: var(--accent); box-shadow: none;
}

.btn--danger {
    background: var(--danger); color: var(--danger-ink); border-color: var(--danger);
    box-shadow: 0 6px 18px -6px var(--danger);
}
.btn--danger:hover {
    background: var(--danger); color: var(--danger-ink);
    filter: brightness(.92); box-shadow: none;
}

/* Variante discrète, pour les gestes proposés sur chaque ligne d'un tableau :
   le rouge y est porté par le trait et le texte, pas par un aplat répété. Elle
   DOIT suivre `.btn--danger` : à spécificité égale, c'est la dernière règle qui
   l'emporte, et c'est ici que se réglait le noir sur rouge — `.btn--ghost`
   posait `color: var(--text)` sans que rien ne repeigne l'encre. */
.btn--ghost.btn--danger {
    background: transparent; color: var(--danger);
    border-color: var(--danger); box-shadow: none;
}
.btn--ghost.btn--danger:hover {
    background: var(--danger); color: var(--danger-ink); border-color: var(--danger);
}

.btn--sm { padding: 6px 13px; font-size: 13.5px; }

/* Bouton réduit à son icône, pour les colonnes d'actions d'un tableau : le
   libellé y répétait le même mot sur chaque ligne et poussait les données hors
   de l'écran. Le sens reste porté par `aria-label` et `title` — jamais par la
   seule forme du dessin.

   TAILLE FIXE, comme `.theme-toggle`, et pour la même raison : un bouton qui se
   dimensionne sur son contenu est un bouton compressible. Dans une cellule
   étroite, les items flex se réduisent — le bouton s'aplatit en fuseau vertical
   et l'icône, comprimée à quelques pixels de large, devient illisible. C'est
   l'escamotage silencieux déjà rencontré sur la barre de navigation : rien
   n'est masqué, tout est écrasé. */
.btn--icon {
    display: grid; place-items: center;
    width: 32px; height: 32px; min-width: 32px; padding: 0;
    border-radius: 9px; line-height: 0; flex: none;
}
.btn--icon svg { width: 16px; height: 16px; flex: none; display: block; }
.btn--icon:hover { transform: none; }
/* Rouge au survol seulement : une colonne de corbeilles rouges attire l'œil
   sur le geste qu'on veut justement faire réfléchir. */
.btn--icon.is-danger:hover { color: var(--danger); border-color: var(--danger); }

/* La cellule d'actions : les icônes en ligne, sans retour à la ligne. Chaque
   enfant — y compris le `<form>` qui enveloppe un bouton — est incompressible,
   sinon la largeur manquante se prend sur eux. */
.cell-actions { display: flex; gap: 6px; align-items: center; white-space: nowrap; }
/* `flex: none` sur les enfants, et RIEN D'AUTRE : poser `display: flex` ici
   écrasait la grille centrée de `.btn--icon` — même spécificité, règle plus
   tardive — et l'icône repartait se coller à gauche de son bouton. Seul le
   `<form>`, qui n'enveloppe qu'un bouton, a besoin d'être remis à plat. */
.cell-actions > * { flex: none; }
.cell-actions form { display: flex; margin: 0; }

/* La colonne d'actions reste VISIBLE quand le tableau défile horizontalement.
   Sans cela, elle sort de l'écran dès qu'une raison sociale est longue — et des
   boutons qu'il faut aller chercher en faisant défiler n'existent pas : on ne
   les cherche pas, on conclut qu'ils ne sont pas là. Le fond est repeint, sinon
   les colonnes glissent en transparence derrière. */
th.col-actions, td.col-actions {
    position: sticky; right: 0; z-index: 1; width: 1%;
    background: var(--surface); box-shadow: -8px 0 8px -8px rgb(0 0 0 / .18);
}
tbody tr:hover td.col-actions { background: var(--surface-2); }

/* ── 6. En-tête ────────────────────────────────────────────────────────── */
.site-header {
    position: sticky; top: 0; z-index: 50;
    background: color-mix(in srgb, var(--bg) 80%, transparent);
    backdrop-filter: saturate(1.4) blur(16px);
    border-bottom: 1px solid var(--border);
}
.header-inner { display: flex; align-items: center; gap: var(--s-5); height: 74px; }

.main-nav { display: flex; gap: var(--s-5); margin-left: var(--s-6); }
.main-nav a {
    font-size: 15px; font-weight: 500; color: var(--text-muted);
    position: relative; padding: 4px 0;
}
.main-nav a:hover { color: var(--text); }
.main-nav a::after {
    content: ''; position: absolute; left: 0; bottom: -2px;
    height: 2px; width: 0; background: var(--accent); transition: width .25s var(--ease);
}
.main-nav a:hover::after, .main-nav a[aria-current='page']::after { width: 100%; }
.main-nav a[aria-current='page'] { color: var(--text); font-weight: 600; }

.header-actions { display: flex; align-items: center; gap: var(--s-3); margin-left: auto; }

.theme-toggle {
    display: grid; place-items: center; width: 38px; height: 38px; padding: 0;
    border-radius: var(--r-full); background: var(--surface-2);
    border: 1px solid var(--border); color: var(--text-muted);
    cursor: pointer; box-shadow: none; transition: all .2s var(--ease);
}
.theme-toggle:hover {
    background: var(--surface-2); color: var(--accent);
    border-color: var(--accent-line); transform: none; box-shadow: none;
}
.theme-toggle svg { width: 17px; height: 17px; }
.icon-moon { display: none; }
[data-theme='dark'] .icon-sun { display: none; }
[data-theme='dark'] .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .icon-sun { display: none; }
    :root:not([data-theme='light']) .icon-moon { display: block; }
}

/* ── 7. Marque ─────────────────────────────────────────────────────────
   « Optin by FINIX WORLD » est un seul verrou : le mot-symbole du produit et
   le logo de l'éditeur se posent côte à côte, sur une ligne, partout et à
   toutes les largeurs. Un seul gabarit les rend — components/brand.blade.php.

   Une taille se règle avec --brand-scale sur .brand, jamais en redéfinissant
   la police ou la hauteur d'image morceau par morceau : c'est ce qui garde
   les proportions du verrou identiques d'un écran à l'autre. */
.brand {
    --brand-scale: 1;
    display: inline-flex; flex-direction: column; align-items: flex-start;
    gap: calc(7px * var(--brand-scale)); color: var(--text);
    /* Rien ne comprime le verrou : dans un en-tête serré, ce sont les
       boutons qui rétrécissent, jamais le logo. */
    flex: none;
}
.brand-lock {
    display: inline-flex; align-items: center; flex-wrap: nowrap;
    gap: calc(var(--s-3) * var(--brand-scale));
}
.brand-word {
    font-family: var(--font-display); font-weight: 700;
    font-size: calc(27px * var(--brand-scale));
    line-height: 1; letter-spacing: -.03em; color: var(--text); white-space: nowrap;
}
.brand-word em { font-style: normal; color: var(--accent); }
.brand-by {
    display: flex; align-items: center; align-self: stretch;
    gap: calc(var(--s-2) * var(--brand-scale));
    padding-left: calc(var(--s-3) * var(--brand-scale));
    border-left: 1px solid var(--border-strong);
}
.brand-by span {
    font-family: var(--font-mono); font-size: calc(10.5px * var(--brand-scale));
    font-weight: 500; letter-spacing: .09em; color: var(--text-soft);
}
.brand-by img { height: calc(26px * var(--brand-scale)); width: auto; }
.brand-sub {
    font-family: var(--font-body); font-weight: 500;
    font-size: calc(11px * var(--brand-scale)); color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .07em;
}

.publisher-mark { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.publisher-mark img { height: 30px; width: auto; }
.publisher-mark span {
    font-family: var(--font-mono); font-size: 10.5px; font-weight: 500;
    text-transform: uppercase; letter-spacing: .12em; color: var(--text-soft);
    padding-left: var(--s-3); border-left: 1px solid var(--border-strong);
}

/* Le lettrage navy du logo disparaît sur fond sombre : on sert un fichier au
   lettrage clair plutôt qu'un filtre, qui délavait l'orange sans sauver le
   texte. Les deux tracés sont posés par components/finix-mark.blade.php. */
.mark-dark { display: none; }
[data-theme='dark'] .mark-light { display: none; }
[data-theme='dark'] .mark-dark { display: inline; }
[data-theme='dark'] .hero-art { opacity: .12; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .mark-light { display: none; }
    :root:not([data-theme='light']) .mark-dark { display: inline; }
    :root:not([data-theme='light']) .hero-art { opacity: .12; }
}

/* ── 8. Hero ───────────────────────────────────────────────────────────── */
.hero { position: relative; overflow: hidden; padding: var(--s-7) 0 var(--s-8); }
.hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hero-grid {
    position: absolute; inset: -1px;
    background-image:
        linear-gradient(var(--border) 1px, transparent 1px),
        linear-gradient(90deg, var(--border) 1px, transparent 1px);
    background-size: 58px 58px;
    mask-image: radial-gradient(ellipse 78% 62% at 72% 18%, #000 0%, transparent 74%);
    opacity: .55;
}
.hero-glow {
    position: absolute; top: -18%; right: -8%; width: 760px; height: 760px;
    background:
        radial-gradient(circle at 60% 40%, var(--accent-soft) 0%, transparent 62%),
        radial-gradient(circle at 30% 70%, var(--petrol-soft) 0%, transparent 60%);
    filter: blur(6px);
}
.hero-inner { position: relative; z-index: 1; }

/* Hero en deux colonnes : le discours à gauche, la photographie à droite. La
   colonne de texte reste la plus large — on vient lire une promesse, l'image
   la met en situation. */
.hero-inner--split {
    display: grid; align-items: center;
    grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
    gap: clamp(var(--s-6), 4vw, var(--s-8));
}
/* La mesure du titre s'élargit un peu par rapport au hero pleine largeur :
   dans une colonne, 18 caractères par ligne empilaient cinq lignes et
   donnaient une colonne de texte en tour. */
.hero-copy { min-width: 0; }

/* La marge basse ménage la place du cachet, qui déborde du cadre. Sans elle,
   il mordrait sur la section suivante. */
.hero-media { position: relative; margin: 0 0 var(--s-6); min-width: 0; }
.hero-media img {
    display: block; width: 100%; height: auto;
    border-radius: var(--r-xl); border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}
.eyebrow {
    display: inline-flex; align-items: center; gap: var(--s-2);
    padding: 6px 13px; background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--r-full);
    font-size: 13px; font-weight: 500; color: var(--text-muted);
    box-shadow: var(--shadow-sm);
}
.eyebrow-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft);
}

.hero h1, .hero-title {
    font-size: clamp(36px, 5.1vw, 60px);
    margin: var(--s-5) 0 var(--s-4);
    max-width: 18ch;
}
.hero h1 em, .hero-title .hl { position: relative; font-style: normal; color: var(--accent); }
.hero h1 em::after, .hero-title .hl::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: .04em;
    height: .1em; background: var(--accent-line); border-radius: 2px;
}
.hero p.claim, .hero-sub { font-size: 19px; color: var(--text-muted); max-width: 54ch; }

/*
 * Le cachet de la vitrine.
 *
 * Objet purement graphique, masqué aux lecteurs d'écran : il emprunte au
 * tampon d'un registre et reprend la promesse du titre sans ajouter
 * d'information. Posé au coin de la photographie, il se lit comme un tampon
 * appliqué sur la scène.
 *
 * Porté à `.hero-media` et jamais laissé nu : le contrat de sous-traitance a
 * lui aussi une `table.seal` — son pavé de référence et d'empreintes — et une
 * règle `.seal` globale la faisait tourner à -9° en disque absolu par-dessus
 * les articles.
 */
.hero-media .seal {
    position: absolute; top: auto; bottom: -2.2rem; left: -2.2rem; right: auto;
    transform: rotate(-9deg);
    width: 10rem; height: 10rem;
    display: grid; place-items: center; text-align: center;
    border: 2px solid var(--accent); border-radius: 50%;
    color: var(--accent);
    font-family: var(--font-mono); font-size: 11px; font-weight: 500;
    letter-spacing: .11em; text-transform: uppercase; line-height: 1.8;
    padding: var(--s-4);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(2px);
}
.hero-media .seal::before {
    content: ''; position: absolute; inset: 5px;
    border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent); border-radius: 50%;
}
.hero-media .seal b { display: block; font-size: 15px; font-weight: 600; letter-spacing: .04em; }
.hero .actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin: var(--s-6) 0 0; }

/* ── 9. Sections ───────────────────────────────────────────────────────── */
.section { padding: var(--s-7) 0 var(--s-9); }
/* Une section visée par une ancre s'arrête sous l'en-tête collant, jamais
   dessous : sans cette marge, le titre atteint disparaît derrière la barre de
   74 px et le lecteur croit avoir manqué la cible. */
.section[id] { scroll-margin-top: calc(74px + var(--s-4)); }
.section-alt { background: var(--bg-2); }
.section h2 { font-size: clamp(26px, 3.5vw, 40px); }
/* Les introductions de section ne sont PAS limitées à une mesure de lecture :
   elles suivent la largeur du `.container`, comme les cartes et les tableaux
   qu'elles annoncent. Un plafond typographique — 62ch à l'origine — les faisait
   s'arrêter à la moitié de la page et se lire comme décrochées du bloc suivant.
   Choix de l'utilisateur, 2026-08-24. */
.section > .container > p, .section-lead {
    color: var(--text-muted); font-size: 18px; margin-top: var(--s-4);
}
.section > .container > p + p { margin-top: var(--s-3); }

.cards {
    display: grid; gap: var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    margin-top: var(--s-7);
}
.cards .card { margin: 0; }
.card__icon { width: 26px; height: 26px; color: var(--accent); margin-bottom: var(--s-4); }
.cards .card h3 { font-size: 20px; margin-bottom: var(--s-2); }
.cards .card p { color: var(--text-muted); font-size: 15.5px; }
.cards .card:hover {
    border-color: var(--accent-line); transform: translateY(-3px); box-shadow: var(--shadow-md);
}

/* Étapes : numéro en pastille dans la marge, comme la value-list de FinixPhone. */
.steps {
    counter-reset: step; list-style: none; margin: var(--s-7) 0 0; padding: 0;
    display: grid; border-top: 1px solid var(--border);
}
/* Grille, et non flex : le contenu d'une étape s'empile — titre, description,
   puis ce que l'étape demande de faire. En flex, chacun de ces éléments devient
   un item de rang et se range à côté du précédent, pastille comprise. */
.steps li {
    counter-increment: step; display: grid;
    grid-template-columns: 30px minmax(0, 1fr); column-gap: var(--s-5);
    padding: var(--s-5) 0; border-bottom: 1px solid var(--border);
}
.steps li > * { grid-column: 2; }
.steps li::before {
    content: counter(step, decimal-leading-zero);
    font-family: var(--font-display); font-weight: 700; font-size: 14px; color: #fff;
    background: var(--accent); width: 30px; height: 30px; border-radius: 9px;
    display: grid; place-items: center; grid-column: 1; grid-row: 1;
}
.steps li strong {
    display: block; font-family: var(--font-display); font-size: 19px;
    font-weight: 700; letter-spacing: -.015em; margin-bottom: 6px;
}
.steps li span { color: var(--text-muted); font-size: 15.5px; }

/* Leviers d'acquisition : une bande de marques, en texte. La phrase reste un
   titre de section, mais bien plus courte à l'œil que les autres h2 — elle
   annonce la bande, elle n'ouvre pas un développement. */
.levers { text-align: center; }
.levers-title {
    font-size: clamp(19px, 2.2vw, 26px); line-height: 1.35;
    max-width: 34ch; margin: 0 auto;
}
/* Bande de marques. Un seul ton pour toutes : les logos d'origine n'ont ni la
   même couleur ni le même contraste, et alignés tels quels ils se disputent
   l'œil au lieu de se lire comme une liste. La teinte suit le texte, donc le
   thème sombre est couvert sans second jeu de fichiers. */
.lever-band {
    list-style: none; margin: var(--s-7) 0 0;
    padding: var(--s-6) 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
    display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
    gap: var(--s-5) clamp(var(--s-5), 5vw, var(--s-8));
}
.lever-band li { display: flex; color: var(--text-muted); transition: color .2s var(--ease); }
.lever-band li:hover { color: var(--text); }
/* Hauteur, et non largeur : c'est elle qui donne le poids visuel d'une marque.
   Les mots-symboles sont posés plus bas que les pictogrammes — à hauteur
   égale, une suite de lettres paraît deux fois plus grosse qu'un signe. */
.lever-mark { display: block; height: 30px; width: auto; max-width: 100%; }
.lever-mark--word { height: 21px; }

/* Contact : la saisie à gauche, ce qui ne se saisit pas à droite — adresse
   directe, identité de l'éditeur, portée du message. Le formulaire garde la
   part la plus large : c'est lui qu'on vient remplir. */
.contact-grid {
    display: grid; gap: var(--s-6); align-items: start; margin-top: var(--s-7);
    grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
}
.contact-form { margin-bottom: 0; }
.contact-aside p { color: var(--text-muted); font-size: 15.5px; margin-bottom: var(--s-4); }
.contact-aside .field__label { margin-bottom: var(--s-1); }
.contact-aside > :last-child { margin-bottom: 0; }

/* Numéro de renseignement. Un numéro coupé en fin de ligne se recompose de
   travers : il ne se scinde jamais, quelle que soit la largeur d'écran. */
.tel-link { white-space: nowrap; }

/* Volume souhaité : « [500] contacts consentis par [semaine] ». La ligne se lit
   comme une phrase, donc les deux commandes gardent leur largeur propre — la
   règle générale les étirerait chacune à 100 %, et la phrase se casserait en
   trois blocs empilés. Sur écran étroit, elle passe à la ligne d'elle-même. */
.volume-line {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--s-2) var(--s-3);
}
/* Les largeurs passent par `flex-basis`, pas par `width` : la règle générale
   des champs s'appuie sur neuf `:not()`, elle bat n'importe quel sélecteur de
   classe et ramènerait chaque commande à 100 % — la phrase se briserait en
   trois blocs empilés. La base flex, elle, n'est déclarée nulle part ailleurs. */
.volume-line input[type=number] { flex: 0 0 9rem; }
.volume-line select { flex: 0 0 10rem; }
.volume-line > span { color: var(--text-muted); font-size: 15.5px; }

/* Piège à robots. Sorti du cadre plutôt que masqué : `display: none` est le
   premier signe qu'un remplisseur automatique apprend à reconnaître. */
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Bande illustrée : une photographie et un propos, à parts égales. Le titre
   vit dans le corps de la bande et non au-dessus, sinon l'image se retrouve
   orpheline sous un intertitre qu'elle n'illustre plus. */
.media-band {
    display: grid; align-items: center; gap: clamp(var(--s-6), 4vw, var(--s-8));
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
.media-band__figure { margin: 0; }
.media-band__figure img {
    display: block; width: 100%; height: auto;
    border-radius: var(--r-lg); border: 1px solid var(--border); box-shadow: var(--shadow-md);
}
.media-band__body > :first-child { margin-top: 0; }
.media-band__body p { color: var(--text-muted); font-size: 18px; margin-top: var(--s-4); }
.media-band--reverse .media-band__figure { order: 2; }

/* Étapes et photographie côte à côte. L'image est centrée sur la hauteur de la
   liste : calée en haut, elle laissait un vide sous elle en face des deux
   dernières étapes, et ce vide se lisait comme un oubli. */
.approach {
    display: grid; align-items: center; gap: clamp(var(--s-6), 4vw, var(--s-8));
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
}
.approach__steps { margin-top: var(--s-6); }
.approach__figure { margin: 0; }
.approach__figure img {
    display: block; width: 100%; height: auto;
    border-radius: var(--r-lg); border: 1px solid var(--border); box-shadow: var(--shadow-md);
}

.compare { margin-top: var(--s-6); }
.compare th { width: 50%; }
.compare--versus tbody td:first-child { color: var(--text-muted); }
.compare--versus tbody td:last-child { font-weight: 500; color: var(--text); }

/* ── 10. Pied de page ──────────────────────────────────────────────────── */
.site-footer {
    border-top: 1px solid var(--border);
    background: var(--bg-2);
    padding: var(--s-8) 0 var(--s-6);
    color: var(--text-muted); font-size: 15px;
}
.site-footer .inner {
    display: flex; justify-content: space-between; gap: var(--s-6); flex-wrap: wrap;
}
.site-footer strong { color: var(--text); }
.site-footer a { color: var(--text-muted); }
.site-footer a:hover { color: var(--accent); }

/* ── 11. Cartes, tableaux, listes ──────────────────────────────────────── */
.card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); padding: var(--s-6); margin-bottom: var(--s-4);
    transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }
.card--flush { padding: 0; overflow: hidden; }
.card--flush > .card-head { padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--border); }
/* Note de bas de carte : se lit après le tableau, et ne doit pas ressembler à
   une ligne de plus. */
.card--flush > .card-foot { padding: var(--s-4) var(--s-6) var(--s-5); border-top: 1px solid var(--border); }
.card--flush > .card-foot > :last-child { margin-bottom: 0; }

.grid { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* Grille de saisie, distincte de `.grid` : le nombre de colonnes y est posé et
   non déduit d'une largeur minimale. Un formulaire d'identité se lit par paires
   — code postal et ville, registre et numéro — et l'ajustement automatique
   casse ces paires dès qu'un écran s'élargit. La marge basse des champs est
   reprise par l'écart de la grille, sinon les deux s'additionnent. */
.form-grid {
    display: grid; gap: var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr));
}
.form-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.form-grid > .field { margin-bottom: 0; }
.form-grid .field--wide { grid-column: 1 / -1; }
.form-grid + button, .form-grid + .field { margin-top: var(--s-5); }

/* Intitulé d'une valeur affichée sans champ derrière. Même apparence qu'un
   `label`, mais sans en être un : un label qui ne commande aucun contrôle
   envoie un lecteur d'écran vers rien. */
.field__label {
    display: block; font-size: 14.5px; font-weight: 600;
    margin-bottom: 6px; color: var(--text);
}

.stat .label {
    font-size: 12.5px; color: var(--text-soft); text-transform: uppercase;
    letter-spacing: .05em; font-weight: 600;
}
.stat .value {
    font-family: var(--font-display); font-size: 40px; font-weight: 700;
    line-height: 1.05; letter-spacing: -.03em; margin: var(--s-2) 0 var(--s-1);
    color: var(--petrol); font-variant-numeric: tabular-nums;
}
/* Une valeur qui n'est pas un nombre — « Sans abonnement », « Sur devis » —
   ne tient pas dans le corps de 40 px : sur un téléphone, elle débordait de sa
   carte. Même rôle, même couleur, taille adaptée au texte. */
.stat .value--text {
    font-size: 24px; letter-spacing: -.01em; line-height: 1.15;
    overflow-wrap: anywhere;
}
.stat .hint { margin-top: 0; }

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

/* Graphique de fréquentation — barres en CSS pur.
   Aucune bibliothèque : une page du back-office ne charge rien depuis
   l'extérieur, et le détail chiffré est de toute façon donné sous le
   graphique pour qui ne peut pas le voir. */
.chart {
    display: flex; align-items: flex-end; gap: 2px;
    overflow-x: auto; padding-top: var(--s-4);
}
.chart-col { flex: 1 0 14px; min-width: 14px; text-align: center; }
.chart-stack {
    position: relative; height: 180px;
    border-bottom: 1px solid var(--border);
}
.chart-bar {
    position: absolute; bottom: 0; left: 0; right: 0;
    background: var(--accent-soft); border-radius: 3px 3px 0 0;
    min-height: 1px;
}
.chart-bar--visitors {
    left: 25%; right: 25%;
    background: var(--accent); border-radius: 2px 2px 0 0;
}
.chart-tick {
    display: block; font-size: 10px; color: var(--text-muted);
    margin-top: var(--s-2); white-space: nowrap;
    writing-mode: vertical-rl; text-orientation: mixed;
}
.chart-col:nth-child(even) .chart-tick { visibility: hidden; }
@media (max-width: 720px) {
    .chart-col:nth-child(4n+1) .chart-tick { visibility: visible; }
    .chart-col:not(:nth-child(4n+1)) .chart-tick { visibility: hidden; }
}

table { width: 100%; border-collapse: collapse; font-size: 15px; }
th, td {
    text-align: left; padding: var(--s-3) var(--s-4);
    border-bottom: 1px solid var(--border); vertical-align: top;
}
th {
    font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text-soft); font-weight: 600; background: var(--surface-2);
    border-bottom-color: var(--border-strong); white-space: nowrap;
}
tbody tr { transition: background .18s var(--ease); }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: none; }

/* Ligne d'explication rattachée à la ligne qui la précède — le motif d'un
   changement d'état sous l'événement qui le date. Sélecteur porté par la
   classe, jamais par `tr` seul : une règle de détail devenue globale
   repeindrait toutes les tables du back-office. */
tbody tr.row-detail > td {
    padding-top: 0; border-top: 0; color: var(--text-muted); font-size: 14px;
}
tbody tr.row-detail > td > div + div { margin-top: var(--s-1); }
tbody tr.row-detail pre {
    margin: var(--s-2) 0 0; white-space: pre-wrap; word-break: break-word; font-size: 12px;
}
tbody tr.row-detail summary { cursor: pointer; }

/* En-tête d'un tableau posé dans une carte `card--flush` : compte de lignes à
   gauche, position dans la pagination à droite. */
.table-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); }
.table-head .hint { margin-top: 0; }

/* Un tableau dans une carte à fond perdu touche les bords : on rétablit la
   marge de la carte sur la première et la dernière colonne, sinon la première
   référence colle au filet. */
.card--flush .table-scroll th:first-child,
.card--flush .table-scroll td:first-child { padding-left: var(--s-6); }
.card--flush .table-scroll th:last-child,
.card--flush .table-scroll td:last-child { padding-right: var(--s-6); }
.card--flush .pagination-wrap { padding: 0 var(--s-6) var(--s-5); }
.card--flush .pagination-wrap .pagination { margin-top: var(--s-4); }

/* ── Tableaux de registre ───────────────────────────────────────────────
   Des listes de consentements : beaucoup de colonnes courtes, dont plusieurs
   se lisent par paires (date de recueil et échéance, campagne et origine).
   La cellule empile alors une valeur principale et sa précision, ce qui
   supprime des colonnes sans rien retirer de l'information. */
.table--registry td { vertical-align: middle; }
.table--registry .cell-main { display: block; line-height: 1.35; }
.table--registry .cell-sub {
    display: block; margin-top: 3px; font-size: 13px;
    color: var(--text-soft); line-height: 1.35;
}
/* Numéros et dates : chasse fixe et pas de retour à la ligne — ils se
   collationnent en colonne, ils ne se lisent pas comme de la prose. */
.num {
    font-family: var(--font-mono); font-size: 13px;
    font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-muted);
}
/* L'e-mail est la seule donnée longue de la ligne : on le laisse se tronquer
   plutôt qu'élargir la colonne au plus long du lot. La fiche l'affiche entier. */
.table--registry .cell-email {
    max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.table--registry .col-when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.table--registry .col-act { width: 1%; white-space: nowrap; text-align: right; }
.table--registry .cell-badges { display: flex; flex-wrap: wrap; gap: var(--s-1); margin-top: var(--s-2); }
.table--registry .ref { white-space: nowrap; }

dl.detail {
    display: grid; grid-template-columns: minmax(170px, 270px) 1fr;
    gap: var(--s-3) var(--s-5); margin: 0;
}
dl.detail dt { font-size: 14px; color: var(--text-soft); font-weight: 500; }
dl.detail dd { margin: 0; }
dl.detail dt, dl.detail dd { padding-bottom: var(--s-3); border-bottom: 1px solid var(--border); }
dl.detail > :nth-last-child(-n+2) { border-bottom: none; padding-bottom: 0; }

.empty { text-align: center; padding: var(--s-8) var(--s-4); color: var(--text-soft); }
.empty::before {
    content: '⌗'; display: block; font-family: var(--font-mono);
    font-size: 32px; color: var(--border-strong); margin-bottom: var(--s-2);
}

/* ── 12. Badges et messages ────────────────────────────────────────────── */
.badge {
    display: inline-block; padding: 3px 11px; border-radius: var(--r-full);
    font-size: 12.5px; font-weight: 600; border: 1px solid transparent;
    white-space: nowrap; line-height: 1.5;
}
.badge--ok { background: var(--ok-soft); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 25%, transparent); }
.badge--warn { background: var(--warn-soft); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 25%, transparent); }
.badge--danger { background: var(--danger-soft); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 25%, transparent); }
.badge--muted { background: var(--surface-2); color: var(--text-muted); border-color: var(--border); }

.notice, .errors, .optin-banner {
    border: 1px solid var(--border); border-left: 3px solid var(--petrol);
    background: var(--surface); padding: var(--s-4) var(--s-5);
    border-radius: 0 var(--r) var(--r) 0; margin-bottom: var(--s-4); font-size: 15.5px;
}
.notice strong, .errors strong, .optin-banner strong { display: block; margin-bottom: var(--s-1); font-weight: 600; }
.notice--warn, .optin-banner { border-left-color: var(--warn); background: var(--warn-soft); }
.notice--danger { border-left-color: var(--danger); background: var(--danger-soft); }
.errors { border-left-color: var(--danger); background: var(--danger-soft); }
.errors ul { margin: var(--s-1) 0 0; padding-left: var(--s-5); }

/* Erreur rattachée à un champ. Le bandeau du haut liste tout ; ici on dit
   lequel, à l'endroit où la correction se fait. */
.error {
    display: block; margin-top: 6px;
    font-size: 14px; font-weight: 500; color: var(--danger);
}

/* ── 13. Formulaires ───────────────────────────────────────────────────── */
form .field { margin-bottom: var(--s-5); }

/* Barre de filtres : une grille de champs courts, puis les actions. L'espacement
   vertical d'un formulaire de saisie y est de trop — cinq champs sur une ligne
   n'ont pas besoin de respirer comme cinq questions posées l'une après l'autre. */
.filters__grid {
    display: grid; gap: var(--s-3) var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.filters__grid .field { margin-bottom: 0; }
.filters__grid label { font-size: 13px; font-weight: 600; color: var(--text-soft); }
.filters__actions {
    display: flex; align-items: center; gap: var(--s-3);
    margin-top: var(--s-4); flex-wrap: wrap;
}

label { display: block; font-size: 14.5px; font-weight: 600; margin-bottom: 6px; color: var(--text); }

/* Tous les champs de saisie, SAUF ceux qui ont leur propre apparence.
   Énumérer les types stylés a déjà laissé passer `url` et `datetime-local` :
   la liste ne suit pas les écrans qu'on ajoute. En excluant plutôt qu'en
   incluant, un type inconnu est habillé par défaut au lieu d'être oublié. */
input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=reset]):not([type=range]):not([type=color]):not([type=image]):not([type=hidden]),
select, textarea {
    width: 100%; padding: 11px 14px;
    border: 1px solid var(--border-strong); border-radius: var(--r);
    font: inherit; font-size: 15.5px; color: var(--text); background: var(--surface);
    transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--text-soft); }
textarea { min-height: 130px; resize: vertical; line-height: 1.6; }
input[type=checkbox], input[type=radio] { width: auto; accent-color: var(--accent); }

.hint { font-size: 14px; color: var(--text-muted); margin-top: 6px; line-height: 1.55; }
/* `.hint` reste en ligne par défaut : il précise souvent une valeur au milieu
   d'une phrase. Quand il porte une ligne à lui, on le dit. */
.hint--block { display: block; }

.input--code {
    font-family: var(--font-mono); font-size: 26px; letter-spacing: .38em;
    text-align: center; text-indent: .38em; padding: var(--s-3);
}

/* Enrôlement du second facteur : le QR est la voie normale, la clé en toutes
   lettres reste offerte pour les gestionnaires de mots de passe qui ne lisent
   pas d'image, et pour un poste sans caméra en face de soi. */
.totp-enrol {
    display: grid; grid-template-columns: 160px minmax(0, 1fr);
    gap: var(--s-4) var(--s-5); align-items: center; margin: var(--s-4) 0 0;
}
.totp-enrol svg {
    width: 160px; height: 160px; display: block; border-radius: var(--r);
    /* Fond blanc explicite : en thème sombre, un QR transparent devient illisible. */
    background: #fff; border: 1px solid var(--border);
}
.totp-key {
    font-family: var(--font-mono); font-size: 14.5px; font-weight: 500;
    letter-spacing: .08em; line-height: 1.7; color: var(--text);
    /* La clé se recopie caractère par caractère : elle doit passer à la ligne
       plutôt que déborder, contrairement aux références de .ref. */
    white-space: normal; word-break: break-all; margin: 0;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--r); padding: var(--s-3) var(--s-4);
}
.totp-enrol .totp-key { margin-top: var(--s-2); }

.recovery-codes {
    list-style: none; padding: var(--s-4) var(--s-5); margin: var(--s-5) 0 var(--s-2);
    display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-2) var(--s-4);
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r);
    font-family: var(--font-mono); font-size: 15px; letter-spacing: .06em; color: var(--text);
}

/* ── 14. Pagination et onglets ─────────────────────────────────────────── */
.pagination {
    display: flex; flex-wrap: wrap; gap: var(--s-1); list-style: none;
    padding: 0; margin: var(--s-5) 0 0; font-size: 14.5px;
}
.pagination li > a, .pagination li > span {
    display: block; min-width: 36px; padding: 5px 10px; text-align: center;
    border: 1px solid var(--border); border-radius: var(--r-sm);
    color: var(--accent); background: var(--surface); font-variant-numeric: tabular-nums;
}
.pagination li > a:hover { border-color: var(--accent); background: var(--accent-soft); }
.pagination li.active > span { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.pagination li.disabled > span { color: var(--text-soft); opacity: .5; }
.pagination p { margin: 0; color: var(--text-muted); }

.tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--border); margin-bottom: var(--s-5); flex-wrap: wrap; }
.tabs a {
    padding: 10px 16px; color: var(--text-muted); font-size: 15px;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs a:hover { color: var(--text); }
.tabs a[aria-current='page'] { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }

/* ── 15. Backoffice ────────────────────────────────────────────────────── */
.layout { display: flex; min-height: 100vh; }

.sidebar {
    width: 262px; flex: 0 0 262px; background: var(--surface);
    border-right: 1px solid var(--border); padding: var(--s-5) 0;
    display: flex; flex-direction: column; gap: var(--s-1);
}
.sidebar .brand {
    padding: 0 var(--s-5) var(--s-5); margin-bottom: var(--s-3);
    border-bottom: 1px solid var(--border);
}
.sidebar .brand { --brand-scale: .88; }
.sidebar nav a {
    display: block; padding: 9px var(--s-5); color: var(--text-muted);
    font-size: 15px; font-weight: 500; border-left: 2px solid transparent;
    transition: background .15s var(--ease), color .15s var(--ease);
}
.sidebar nav a:hover { background: var(--surface-2); color: var(--text); }
.sidebar nav a[aria-current='page'] {
    background: var(--accent-soft); border-left-color: var(--accent);
    color: var(--text); font-weight: 600;
}
.sidebar .section {
    padding: var(--s-5) var(--s-5) 4px; font-size: 11px; text-transform: uppercase;
    letter-spacing: .09em; color: var(--text-soft); font-weight: 600;
}
.sidebar-foot { margin-top: auto; padding: var(--s-5) var(--s-5) 0; border-top: 1px solid var(--border); }
.sidebar-foot .hint { font-size: 12.5px; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
    padding: var(--s-4) var(--s-6);
    background: color-mix(in srgb, var(--bg) 80%, transparent);
    backdrop-filter: saturate(1.4) blur(16px);
    border-bottom: 1px solid var(--border);
    position: sticky; top: 0; z-index: 5;
}
.topbar strong { font-family: var(--font-display); font-weight: 700; font-size: 17px; letter-spacing: -.02em; }
.topbar .who { font-size: 14px; color: var(--text-muted); display: flex; align-items: center; gap: var(--s-4); }

.content { padding: var(--s-6); max-width: 1280px; width: 100%; flex: 1; }
.content > h1 { font-size: 34px; margin: 0 0 var(--s-2); }
.content h2 { font-size: 24px; margin: var(--s-7) 0 var(--s-4); }
.content h3 { font-size: 18px; margin: var(--s-5) 0 var(--s-2); }
.lede { color: var(--text-muted); margin: 0 0 var(--s-6); max-width: var(--measure); font-size: 16.5px; }

.page-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-6);
}
.page-head > div { min-width: 0; }
.page-head h1 { font-size: 34px; margin: 0 0 var(--s-2); }
.page-head .lede { margin-bottom: 0; }
/* Rangée d'états sous le titre : niveau de preuve, statut, origine. Ce sont des
   qualifications, pas une phrase — d'où la rangée plutôt qu'un paragraphe. */
.page-head__badges { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-3); }
/* Plusieurs actions en tête de page : elles s'alignent, et passent à la ligne
   ensemble plutôt qu'une par une quand la fenêtre se resserre. */
.page-head__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* En-tête de section : le titre, ce qu'il compte, et l'action qui s'y rapporte
   sur la même ligne de base. Remplace la suite de `h2` isolés, qui laissaient
   les actions flotter dans un paragraphe au-dessus de la carte. */
.section-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s-3) var(--s-5); flex-wrap: wrap;
}
.content .section-head { margin: var(--s-7) 0 var(--s-4); }
.content .section-head h2 { margin: 0; display: flex; align-items: center; gap: var(--s-3); }
.section-head .hint, .section-head p { margin: 0; }
.section-head__side { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }

/* Numéro d'ordre d'une section qui se lit comme un document — la fiche de
   preuve. Le rang compte : il est cité tel quel dans l'attestation. */
.ord {
    flex: none; width: 30px; height: 30px; border-radius: 9px;
    display: grid; place-items: center;
    font-family: var(--font-display); font-weight: 700; font-size: 14px;
    background: var(--accent-soft); color: var(--accent-strong);
    border: 1px solid var(--accent-line);
}

/* Rangée d'actions détachée d'un formulaire ou d'une carte. */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-top: var(--s-5); }
.actions .hint { margin: 0; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* Bouton posé sous un bloc de texte. La remise à zéro de `p` le collait au
   texte du dessus — paragraphe, tableau ou liste de détails suivis d'un bouton,
   partout dans les deux espaces. Vise le bouton lui-même, ou le premier bouton
   d'un formulaire qui suit le texte : un `<form style="display:inline">`
   ignore toute marge verticale, son bouton non. Rien dans les rangées en ligne
   (flex), où la marge décalerait le bouton de ses voisins. Spécificité nulle :
   toute règle explicite l'emporte. */
:where(:not(.actions, .btn-row, .form-actions, .status-card, .section-head, .table-head, .page-head, .card-head)
    > :is(p, ul, ol, dl, table, pre, .table-wrap)) + :where(.btn, button),
:where(:not(.actions, .btn-row, .form-actions, .status-card, .section-head, .table-head, .page-head, .card-head)
    > :is(p, ul, ol, dl, table, pre, .table-wrap)) + :where(form)
    > :where(.btn, button):not(:where(label, .field, .form-grid, div, fieldset, select, textarea, input:not([type=hidden])) ~ *),
:where(:not(.actions, .btn-row, .form-actions, .status-card, .section-head, .table-head, .page-head, .card-head)
    > :is(p, ul, ol, dl, table, pre, .table-wrap)) + :where(.btn-row) {
    margin-top: var(--s-5);
}

/* Intitulé court au-dessus de sa rangée de boutons (familles de modèles de
   campagne) : il s'y rattache comme un libellé à son champ, pas comme un
   paragraphe à son action. */
.field > .hint + .btn-row { margin-top: var(--s-2); }

/* Champs hors formulaire : `form .field` porte seul la marge basse, et un
   sélecteur posé hors du `<form>` empilait ses rubriques sans le moindre écart.
   Dans un formulaire, cette marge haute se fond avec la marge basse existante
   (marges de blocs adjacents) ; dans une grille, rien ne fusionne, d'où
   l'exclusion. */
:where(:not(.form-grid, .filters__grid, .grid) > .field) + :where(.field, .btn-row) {
    margin-top: var(--s-5);
}

/* Carte encastrée dans une autre : un cran de fond en moins pour qu'elle se
   lise comme un élément de la liste, et non comme une carte de même rang. */
.card--muted { background: var(--surface-2); box-shadow: none; }

/* Case à cocher et son libellé. Le libellé d'une case n'est pas un intitulé de
   champ : il porte la phrase qu'on accepte, pas le nom de la donnée. */
.choice {
    display: flex; align-items: flex-start; gap: var(--s-3);
    font-weight: 400; font-size: 15.5px; margin-bottom: 0; cursor: pointer;
}
.choice input[type=checkbox], .choice input[type=radio] {
    flex: none; width: 18px; height: 18px; margin-top: 3px;
}

/* Contrôle des mentions obligatoires : une liste de verdicts, pas un tableau.
   Il n'y a rien à comparer d'une ligne à l'autre — seulement à voir ce qui
   manque, et pourquoi. */
.checklist { list-style: none; margin: 0; padding: 0; }
.checklist li {
    display: grid; grid-template-columns: 26px minmax(0, 1fr);
    gap: var(--s-1) var(--s-3); align-items: start;
    padding: var(--s-3) 0; border-bottom: 1px solid var(--border);
}
.checklist li:first-child { padding-top: 0; }
.checklist li:last-child { padding-bottom: 0; border-bottom: none; }
.checklist__mark {
    grid-row: span 2; width: 24px; height: 24px; border-radius: 50%;
    display: grid; place-items: center; font-size: 12px; font-weight: 700;
    border: 1px solid transparent;
}
.checklist__mark--ok {
    background: var(--ok-soft); color: var(--ok);
    border-color: color-mix(in srgb, var(--ok) 28%, transparent);
}
.checklist__mark--ko {
    background: var(--danger-soft); color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 28%, transparent);
}
.checklist strong { font-weight: 600; line-height: 1.4; }
.checklist .hint { margin-top: 0; }
/* Sans détail à afficher, l'intitulé occupe les deux rangs. */
.checklist li > strong:last-child { grid-row: span 2; align-self: center; }

/* Zone de saisie courte : un libellé de finalité tient en deux lignes, la
   hauteur par défaut d'un textarea en laisse cinq vides. */
textarea.textarea--sm { min-height: 5.5rem; }

/* ── 16. Écrans centrés : connexion, double facteur ────────────────────── */
.auth { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--s-6) var(--s-5); }
.auth .card { width: 100%; max-width: 460px; padding: var(--s-7); box-shadow: var(--shadow-lg); }
.auth .brand { margin-bottom: var(--s-5); --brand-scale: 1.15; }
.auth .lede { margin-bottom: var(--s-5); font-size: 15.5px; }
.auth button[type=submit] { width: 100%; justify-content: center; }

/* ── 17. Portail de preuve ─────────────────────────────────────────────
   Lu une fois, par une personne qui n'a pas demandé à être là : colonne
   étroite, corps de texte généreux, aucune densité inutile. */
.portal { max-width: 780px; margin: 0 auto; padding: var(--s-7) var(--s-5) var(--s-9); }
.portal-header {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
    border-bottom: 1px solid var(--border); padding-bottom: var(--s-5); margin-bottom: var(--s-7);
}
.portal h1 { font-size: clamp(30px, 5vw, 44px); margin: var(--s-4) 0 var(--s-3); }
.portal h2 { font-size: 24px; margin: var(--s-8) 0 var(--s-4); }
.portal p { font-size: 17px; color: var(--text-muted); max-width: var(--measure); }
.portal-form { max-width: 440px; margin-top: var(--s-6); box-shadow: var(--shadow-md); }
.portal-form input[type=tel] { font-size: 18px; }
.portal-form button[type=submit] { width: 100%; justify-content: center; }

.proof-list { list-style: none; padding: 0; margin: var(--s-6) 0 0; display: grid; gap: var(--s-4); }

.answers { list-style: none; padding: 0; margin: var(--s-4) 0 0; display: grid; }
.answers li {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
    padding: var(--s-3) 0; border-bottom: 1px solid var(--border);
}
.answers li:last-child { border-bottom: none; }
.answers .verdict { font-family: var(--font-mono); font-size: 14px; font-weight: 500; white-space: nowrap; }
.answers .verdict--yes { color: var(--ok); }
.answers .verdict--no { color: var(--text-soft); }

.assurance { list-style: none; padding: 0; margin: var(--s-5) 0 0; display: grid; gap: var(--s-4); }
.assurance li { display: flex; gap: var(--s-3); align-items: flex-start; font-size: 16px; color: var(--text-muted); }
.assurance li::before {
    content: '✓'; flex: none; margin-top: 3px;
    width: 24px; height: 24px; border-radius: 50%;
    display: grid; place-items: center; font-size: 13px; font-weight: 700;
    background: var(--ok-soft); color: var(--ok);
    border: 1px solid color-mix(in srgb, var(--ok) 28%, transparent);
}
.assurance strong { color: var(--text); font-weight: 600; }

.portal-footer {
    margin-top: var(--s-8); padding-top: var(--s-6);
    border-top: 1px solid var(--border); font-size: 14.5px; color: var(--text-muted);
}
.portal-footer strong { color: var(--text); }

/* Le logotype mène à la vitrine : il doit se voir cliquable sans devenir un
   bouton — c'est une sortie, pas l'action de la page. */
.portal-header .brand { transition: opacity .2s var(--ease); }
.portal-header .brand:hover { opacity: .72; color: var(--text); }

/* ── 17 bis. Portail : page d'accueil ──────────────────────────────────
   Les autres écrans du portail restituent une preuve déjà ouverte : une
   colonne suffit. Ici, la page doit d'abord convaincre de saisir un numéro.
   Le formulaire tient donc sa propre colonne, à hauteur du titre, et les
   garanties se lisent à côté de lui plutôt qu'après. */
.portal--home { max-width: 1080px; }

.portal-hero {
    position: relative;
    /* Centrage vertical : selon qu'un bandeau d'indisponibilité coiffe ou non
       le formulaire, c'est tantôt une colonne tantôt l'autre qui est la plus
       haute — alignées en haut, elles laissent alors un vide sous la plus
       courte. */
    display: grid; align-items: center;
    grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
    gap: clamp(var(--s-6), 5vw, var(--s-8));
    padding-bottom: var(--s-8);
}
/* Halo repris du hero de la vitrine : le portail est le même produit vu de
   l'autre côté. Posé derrière et inerte — il ne doit jamais intercepter le
   clic destiné au champ. */
.portal-hero::before {
    content: ''; position: absolute; z-index: 0; pointer-events: none;
    top: -34%; right: -14%; width: 620px; height: 620px; max-width: 120%;
    background:
        radial-gradient(circle at 60% 40%, var(--accent-soft) 0%, transparent 62%),
        radial-gradient(circle at 30% 70%, var(--petrol-soft) 0%, transparent 60%);
    filter: blur(6px);
}
.portal-hero > * { position: relative; z-index: 1; min-width: 0; }
.portal-hero h1 { font-size: clamp(32px, 4.3vw, 50px); max-width: 16ch; }
/* `.portal p` porte déjà taille et couleur : toute règle sur un paragraphe de
   cette page doit la dépasser en spécificité, sinon elle est ignorée. */
.portal-hero p.lede { font-size: 18.5px; max-width: 46ch; }

.portal-facts { list-style: none; margin: var(--s-6) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-2); }
.portal-facts li {
    display: inline-flex; align-items: center; gap: var(--s-2);
    padding: 7px 14px 7px 11px; border-radius: var(--r-full);
    background: var(--surface); border: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    font-size: 13.5px; font-weight: 500; color: var(--text-muted);
}
.portal-facts svg { width: 15px; height: 15px; flex: none; color: var(--accent); }

/* La carte de saisie porte l'action de la page : elle est la seule surface
   surélevée du premier écran. */
.portal--home .portal-form {
    max-width: none; margin: 0; border-radius: var(--r-xl);
    box-shadow: var(--shadow-lg); border-color: var(--border-strong);
}
.portal p.portal-form__title {
    font-family: var(--font-display); font-weight: 700; font-size: 21px;
    letter-spacing: -.02em; margin: 0 0 var(--s-5); color: var(--text);
}
.portal-access > .notice { margin-bottom: var(--s-4); }
.portal p.portal-access__seal {
    display: flex; align-items: flex-start; gap: var(--s-2);
    margin: var(--s-4) 0 0; font-size: 13.5px; color: var(--text-soft);
}
.portal-access__seal svg { width: 15px; height: 15px; flex: none; margin-top: 2px; }

/* Rubriques de bas de page : séparées par un filet plutôt que par du vide,
   pour que la page se lise comme un document et non comme une succession
   d'arguments. */
.portal-section { padding-top: var(--s-7); border-top: 1px solid var(--border); }
.portal--home .portal-section h2 { margin-top: 0; }
.portal-cards {
    display: grid; gap: var(--s-4); margin-top: var(--s-5);
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.portal-cards .card { margin: 0; }
.portal-cards .card h3 { font-size: 19px; margin: 0 0 var(--s-2); }
.portal-cards .card p { font-size: 15.5px; margin: 0; }
.portal-cards .card:hover { border-color: var(--accent-line); box-shadow: var(--shadow-md); }

.portal-panel {
    margin-top: var(--s-5); padding: var(--s-6);
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-lg);
}
.portal-panel .assurance { margin-top: 0; }

/* ── 18. Page de recueil (domaine de collecte) ─────────────────────────
   Le HTML est enregistré et haché à la publication : la présentation ne peut
   agir que depuis l'extérieur, par sélecteurs. Aucune règle ici ne doit
   dépendre d'une classe absente du gabarit publié. */
.optin-page { max-width: 720px; margin: 0 auto; padding: var(--s-7) var(--s-5) var(--s-9); }
.optin-page h1 { font-size: clamp(30px, 5vw, 42px); margin: 0 0 var(--s-6); }
.optin-page h2 {
    font-family: var(--font-body); font-size: 17px; font-weight: 600;
    letter-spacing: 0; margin: 0 0 var(--s-2);
}
.optin-page section { margin-bottom: var(--s-6); }
.optin-page p { margin: 2px 0; color: var(--text-muted); }

.optin-page fieldset {
    border: 1px solid var(--border); border-radius: var(--r-lg);
    background: var(--surface); padding: var(--s-5) var(--s-6) var(--s-6); margin: 0 0 var(--s-6);
}
.optin-page legend { font-family: var(--font-body); font-weight: 600; font-size: 16px; padding: 0 var(--s-2); color: var(--text); }
/* Deux colonnes : les champs vont deux par deux (prénom/nom, téléphone/e-mail,
   code postal/ville). Mise en page purement CSS — le HTML publié est figé et
   hashé, on ne le réorganise pas pour habiller un écran. */
.optin-identity {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4) var(--s-5);
}
.optin-identity legend { grid-column: 1 / -1; }
.optin-identity label { display: block; margin-bottom: 0; }
.optin-identity input { margin-top: 6px; }

/* ── Page de recueil, version courte (12/09/2026) ──────────────────────
   Ajoutée À CÔTÉ des règles ci-dessus, jamais à leur place : une page publiée
   avant cette date garde son HTML figé, et continuerait de s'afficher avec les
   anciennes classes. Rien ici ne doit casser ce rendu-là.

   Ce que la refonte cherche : un seul écran, un seul geste. Le formel est
   replié, ce qui décide est en haut, et la case se coche au pouce. */
.optin-page--compact { max-width: 34rem; padding: var(--s-6) var(--s-5) var(--s-7); }
.optin-page--compact h1 {
    font-size: clamp(26px, 4.6vw, 34px); line-height: 1.15; margin: 0 0 var(--s-3);
}
.optin-hero { margin-bottom: var(--s-6); }
.optin-by {
    font-size: 12.5px; text-transform: uppercase; letter-spacing: .07em;
    font-weight: 600; color: var(--accent); margin: 0 0 var(--s-2);
}
.optin-claim { font-size: 17px; color: var(--text); margin: 0 0 var(--s-3); }
.optin-scope-line { font-size: 14.5px; color: var(--text-muted); margin: 0; }

.optin-page--compact fieldset { padding: var(--s-5); margin-bottom: var(--s-5); }
.optin-page--compact .optin-identity { gap: var(--s-3) var(--s-4); }
.optin-page--compact .optin-identity input { padding: 10px 12px; }

/* Repli des mentions : présent dans la page, donc dans le texte scellé, mais
   il ne mange plus l'écran avant la première case. */
.optin-legal {
    border: 1px solid var(--border); border-radius: var(--r);
    background: var(--surface-2); padding: var(--s-3) var(--s-4); margin-bottom: var(--s-5);
}
.optin-legal > summary {
    cursor: pointer; font-size: 14px; font-weight: 600; color: var(--text-muted);
}
.optin-legal[open] > summary { margin-bottom: var(--s-3); }
.optin-legal .optin-mentions {
    border-top: none; padding-top: 0; text-align: left; font-size: 13.5px;
}
.optin-legal .optin-mentions h2 {
    font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text-soft); margin-top: var(--s-4);
}
.optin-legal .optin-mentions h2:first-child { margin-top: 0; }

.optin-submit {
    width: 100%; padding: 14px 18px; font-size: 16.5px; border-radius: var(--r);
}

@media (max-width: 520px) {
    /* Deux colonnes de champs deviennent illisibles sous 520 px : un champ par
       ligne, et le pouce atteint tout. */
    .optin-page--compact .optin-identity { grid-template-columns: minmax(0, 1fr); }
}

.optin-free-choice { font-size: 13px; font-style: italic; color: var(--text-muted); margin: 0; }
.optin-purpose { border-bottom: 1px solid var(--border); padding: var(--s-4) 0; }
.optin-purpose:first-of-type { padding-top: 0; }
.optin-purpose label {
    display: flex; gap: var(--s-3); align-items: flex-start;
    font-weight: 500; font-size: 16.5px; color: var(--text); cursor: pointer; margin: 0;
}
.optin-purpose input[type=checkbox] { margin-top: 5px; width: 18px; height: 18px; flex: none; }
.optin-purpose-detail { margin: 5px 0 0 30px; font-size: 15px; color: var(--text-muted); }

/* Liste des destinataires, dépliable — mode « une seule case ».

   Repliée par défaut, mais JAMAIS absente : les noms sont dans le document
   servi, donc dans le texte scellé et reproduit en annexe de l'attestation.
   `<details>` seul, sans script : la liste s'ouvre même si le JS est bloqué, et
   à l'impression comme dans un lecteur d'écran elle est simplement là. */
.optin-recipient-list { margin: 8px 0 0 30px; font-size: 14.5px; }
.optin-recipient-list > summary {
    cursor: pointer; color: var(--text-muted); text-decoration: underline;
    text-underline-offset: 3px; padding: 4px 0; width: fit-content;
}
.optin-recipient-list > summary:hover { color: var(--text); }
.optin-recipient-list ul { margin: var(--s-2) 0 0; padding-left: var(--s-4); }
.optin-recipient-list li { margin-bottom: var(--s-2); color: var(--text-muted); line-height: 1.5; }
.optin-recipient-list .optin-recipient-name { display: block; font-weight: 600; color: var(--text); }
.optin-recipient-list .optin-recipient-siren { font-size: 13.5px; }

/* Lien vers la liste servie à part. Discret, mais pas caché : c'est le seul
   chemin de la personne vers l'identité des destinataires. */
.optin-recipient-link { margin: 8px 0 0 30px; font-size: 14.5px; }
.optin-recipient-link a { color: var(--text-muted); text-underline-offset: 3px; }
.optin-recipient-link a:hover { color: var(--text); }

/* Page dédiée aux destinataires. */
.optin-recipient-list-page { margin: var(--s-4) 0 0; padding-left: var(--s-4); }
.optin-recipient-list-page li { margin-bottom: var(--s-4); color: var(--text-muted); line-height: 1.55; }
.optin-recipient-list-page .optin-recipient-name { display: block; font-weight: 600; color: var(--text); }
.optin-recipient-list-page .optin-recipient-activity { display: block; font-size: 14px; }
.optin-recipient-list-page .optin-recipient-siren,
.optin-recipient-list-page .optin-recipient-city { font-size: 13.5px; }
.optin-recipient-list-page .optin-recipient-city::before { content: ' · '; }

.optin-mentions { font-size: 14.5px; color: var(--text-muted); border-top: 1px solid var(--border); padding-top: var(--s-5); text-align: justify; }
.optin-mentions h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--text-soft); }
.optin-mentions p { margin: var(--s-2) 0; color: var(--text-muted); }
/* Action principale de la page : pleine largeur du formulaire, comme sur les
   écrans de connexion et du portail. `white-space` est remis à normal car le
   style commun des boutons interdit le retour à la ligne, ce qui déborderait
   sur un écran étroit. */
.optin-form > button[type=submit] {
    margin-top: var(--s-6); width: 100%; font-size: 18px; padding: 17px 38px;
    justify-content: center; min-height: 56px; white-space: normal;
}

/* ── 19. Mouvement ─────────────────────────────────────────────────────
   Une seule séquence, à l'ouverture de la vitrine. Le backoffice n'en a
   aucune : un outil consulté vingt fois par jour ne doit pas se rejouer. */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

@keyframes stamp {
    from { opacity: 0; transform: rotate(-26deg) scale(1.28); }
    to   { opacity: 1; transform: rotate(-9deg) scale(1); }
}

.hero-inner > * { animation: rise .6s var(--ease-out) backwards; }
.hero-media .seal { animation: stamp .55s cubic-bezier(.3, 1.35, .5, 1) .4s backwards; }
.hero-inner > .eyebrow { animation-delay: .02s; }
.hero-inner > h1 { animation-delay: .09s; }
.hero-inner > p.claim { animation-delay: .17s; }
.hero-inner > .actions { animation-delay: .25s; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important; animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* ── 20. Adaptatif ─────────────────────────────────────────────────────── */
@media (max-width: 1100px) {
    .hero-media .seal { display: none; }
    /* La marge basse ne servait qu'à loger le cachet, qui vient de disparaître. */
    .hero-media { margin-bottom: 0; }
}

/* Une colonne dès que deux ne tiennent plus : sous cette largeur, un texte de
   parcours et une photographie côte à côte deviennent deux bandes étroites. */
@media (max-width: 900px) {
    .hero-inner--split, .media-band, .approach { grid-template-columns: minmax(0, 1fr); }
    .media-band--reverse .media-band__figure { order: 0; }
    .approach__figure { position: static; margin-top: var(--s-5); }
    .hero-media { max-width: 560px; }
    /* Le formulaire passe sous le discours, mais reste borné : étiré sur
       toute la largeur d'une tablette, un champ de numéro n'a plus de forme. */
    .portal-hero { grid-template-columns: minmax(0, 1fr); padding-bottom: var(--s-7); }
    .portal-hero__copy .portal-facts { margin-bottom: var(--s-2); }
    .portal-access { max-width: 480px; }
    .portal-hero::before { display: none; }
}

@media (max-width: 960px) {
    .layout { flex-direction: column; }
    .sidebar {
        width: 100%; flex: none; padding: var(--s-4) 0 var(--s-2);
        border-right: none; border-bottom: 1px solid var(--border);
    }
    .sidebar .brand { padding-bottom: var(--s-4); }
    .sidebar nav { display: flex; flex-wrap: wrap; gap: var(--s-1); padding: 0 var(--s-4); }
    .sidebar nav a { border-left: none; border-bottom: 2px solid transparent; padding: 7px var(--s-3); }
    .sidebar nav a[aria-current='page'] { border-left: none; border-bottom-color: var(--accent); }
    .sidebar .section { padding: var(--s-3) var(--s-4) 2px; width: 100%; }
    .content { padding: var(--s-5); }
    .topbar { padding: var(--s-3) var(--s-5); }
}

@media (max-width: 760px) {
    body { font-size: 16px; }
    .header-inner { height: 64px; gap: var(--s-3); }
    .main-nav { display: none; }
    /* Le verrou rétrécit, il ne se coupe pas : « by FINIX WORLD » ne
       disparaît sur aucune largeur. */
    .brand { --brand-scale: .78; }
    .hero { padding: var(--s-6) 0 var(--s-7); }
    .hero-art { width: 340px; right: -6rem; opacity: .05; }
    .section { padding: var(--s-6) 0 var(--s-7); }
    .steps li { grid-template-columns: minmax(0, 1fr); row-gap: var(--s-3); }
    .steps li > * { grid-column: 1; }
    dl.detail { grid-template-columns: 1fr; gap: 2px; }
    dl.detail dt { border-bottom: none; padding-bottom: 2px; }
    .optin-identity { grid-template-columns: minmax(0, 1fr); }
    .form-grid, .form-grid--3 { grid-template-columns: minmax(0, 1fr); }
    .contact-grid { grid-template-columns: minmax(0, 1fr); }
    .auth { padding: var(--s-4); }
    .auth .card { padding: var(--s-5); }
    .totp-enrol { grid-template-columns: minmax(0, 1fr); justify-items: center; }
    .totp-enrol .totp-key { justify-self: stretch; }
    .site-footer .inner { flex-direction: column; gap: var(--s-5); }
    .recovery-codes { grid-template-columns: 1fr; }
}

/* Sous 560 px, le verrou et les deux entrées (« Connexion », « Ouvrir un
   compte ») ne tiennent plus sur une ligne. L'en-tête passe donc sur deux
   rangs : rien n'est masqué, ni le logo ni une porte d'entrée. */
@media (max-width: 560px) {
    .header-inner {
        height: auto; flex-wrap: wrap;
        padding-top: var(--s-3); padding-bottom: var(--s-3); row-gap: var(--s-3);
    }
    .header-actions { width: 100%; margin-left: 0; }
    .header-actions .btn { flex: 1; justify-content: center; }
}

/* ── 20 ter. Fil d'étapes horizontal ───────────────────────────────────
   Import, inscription : un parcours en trois temps, indicatif. L'état qui fait
   foi reste ailleurs — le statut du lot, celui de la demande.

   NOM PROPRE, ET C'EST LE SUJET DE CE BLOC. Ce composant s'appelait `.steps`,
   comme la liste verticale numérotée de la vitrine (§ Étapes, plus haut). Deux
   composants, un seul nom, et celui-ci passait en dernier : il imposait son
   `display: flex` à la liste de la page « La plateforme », dont les quatre
   étapes se rangeaient côte à côte au lieu de s'empiler. Renommé en `.wizard`
   le 26/08/2026. Ne pas réintroduire `.steps` ici. */
.wizard {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    list-style: none;
    margin: 0 0 var(--s-5);
    padding: 0;
}
.wizard__item {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    color: var(--text-soft);
    font-size: .92rem;
}
.wizard__item + .wizard__item::before {
    content: '';
    width: var(--s-5);
    height: 1px;
    background: var(--border-strong);
    margin-right: var(--s-2);
}
.wizard__number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: var(--r-full);
    border: 1px solid var(--border-strong);
    font-variant-numeric: tabular-nums;
}
.wizard__item.is-current { color: var(--text); font-weight: 600; }
.wizard__item.is-current .wizard__number {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.wizard__item.is-done { color: var(--text-muted); }
.wizard__item.is-done .wizard__number {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--accent-strong);
}

/* ── 20. Dialogue de confirmation ──────────────────────────────────────
   Élément <dialog> natif : le fond inerte et le piège de focus viennent du
   navigateur. On ne style que l'apparence. */
dialog.modal, dialog.confirm {
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--text);
    padding: 0;
    max-width: min(34rem, calc(100vw - 2rem));
    box-shadow: 0 24px 60px rgb(0 0 0 / .22);
}
dialog.modal::backdrop, dialog.confirm::backdrop { background: rgb(0 0 0 / .45); }
/* Un formulaire respire davantage qu'une question fermée. */
dialog.modal { max-width: min(52rem, calc(100vw - 2rem)); width: 100%; }
.modal__box { padding: var(--s-5); }
.modal__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-4);
    margin-bottom: var(--s-4);
}
.modal__title { font-family: var(--font-display); font-size: 1.15rem; }
.modal__actions {
    display: flex;
    gap: var(--s-3);
    justify-content: flex-end;
    flex-wrap: wrap;
    margin-top: var(--s-5);
}
.confirm__box { padding: var(--s-5); }
.confirm__title { display: block; font-family: var(--font-display); font-size: 1.1rem; }
.confirm__message { margin: var(--s-3) 0 var(--s-5); color: var(--text-muted); }
.confirm__actions { display: flex; gap: var(--s-3); justify-content: flex-end; flex-wrap: wrap; }
.confirm__go { background: var(--danger); border-color: var(--danger); color: #fff; }

/* ── 20 bis. Avancement d'import ───────────────────────────────────────
   Deux phases suivies : l'envoi du fichier, dont le navigateur connaît les
   octets, puis le traitement, dont le serveur écrit les compteurs. La barre
   passe en indéterminé quand le total est encore inconnu — un pourcentage
   inventé serait pire qu'une absence de pourcentage. */
.progress { margin-top: var(--s-5); }
.progress__label { margin-bottom: var(--s-2); }
.progress__track {
    height: 10px;
    border-radius: var(--r-full);
    background: var(--surface-2);
    border: 1px solid var(--border);
    overflow: hidden;
}
.progress__bar {
    height: 100%;
    width: 0;
    background: var(--accent);
    border-radius: var(--r-full);
    transition: width .3s var(--ease-out);
}
.progress__bar.is-indeterminate { opacity: .35; }
.progress__bar.is-error { background: var(--danger); }
.progress .hint { margin-top: var(--s-2); }

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

/* ── 20 quater. Contrat de sous-traitance ──────────────────────────────
   Un contrat n'est pas un écran de réglages : il se lit d'un bout à l'autre.
   D'où une largeur de ligne tenue, un sommaire qui suit la lecture, et un
   bloc de signature détaché du corps — l'acte ne doit pas se confondre avec
   le texte qu'il engage. */
.dpa-layout { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: var(--s-6); align-items: start; }

.dpa-toc { position: sticky; top: var(--s-5); }
.dpa-toc .label {
    font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-soft); font-weight: 600; margin-bottom: var(--s-3);
}
.dpa-toc ol { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--border); }
.dpa-toc li { margin: 0; }
.dpa-toc a {
    display: block; padding: 5px var(--s-3); margin-left: -2px;
    border-left: 2px solid transparent; color: var(--text-muted);
    font-size: 13.5px; line-height: 1.45; text-decoration: none;
}
.dpa-toc a:hover { color: var(--text); }
.dpa-toc a[aria-current='true'] {
    color: var(--accent); border-left-color: var(--accent); font-weight: 600;
}
.dpa-toc a .n { color: var(--text-soft); padding-right: 5px; font-variant-numeric: tabular-nums; }
.dpa-toc a[aria-current='true'] .n { color: inherit; }

.dpa-doc { max-width: 74ch; }
.dpa-doc > h2:first-child { margin-top: 0; }
.dpa-doc h2 {
    font-family: var(--font-display); font-size: 1.02rem; line-height: 1.3;
    margin: var(--s-7) 0 var(--s-3); padding-top: var(--s-4);
    border-top: 1px solid var(--border); scroll-margin-top: var(--s-5);
}
.dpa-doc h2 .num { color: var(--accent); }
.dpa-doc p { margin: 0 0 var(--s-3); color: var(--text-muted); }
.dpa-doc p strong, .dpa-doc li strong { color: var(--text); }
.dpa-doc p.et { color: var(--text-soft); font-style: italic; }
.dpa-doc p.hint-note { font-size: 14px; color: var(--text-soft); }
.dpa-doc ul { margin: 0 0 var(--s-3); padding-left: 1.15rem; color: var(--text-muted); }
.dpa-doc li { margin-bottom: var(--s-2); }
.dpa-doc .mono { font-family: var(--font-mono); font-size: .92em; }

/* Le bandeau « projet » vient du corps partagé avec le PDF : il doit se lire
   comme un avertissement ici aussi, pas comme un paragraphe de plus. */
.dpa-doc .callout {
    background: var(--warn-soft); border-left: 3px solid var(--warn);
    padding: var(--s-3) var(--s-4); margin: 0 0 var(--s-5); border-radius: var(--r-sm);
}
.dpa-doc .callout .title { display: block; font-weight: 600; color: var(--warn); margin-bottom: 2px; }

/* Pavé de signature et bloc de scellement, en fin de contrat. Ces deux tables
   viennent du corps partagé avec le PDF, qui porte ses propres styles
   d'impression : sans les reprendre ici, elles se rendaient à l'écran en
   tableaux nus — colonnes collées, cadres absents, valeurs désalignées du
   reste du contrat. */
.dpa-doc table.signblock {
    width: 100%; border-collapse: separate; border-spacing: var(--s-3) 0;
    margin: var(--s-4) calc(-1 * var(--s-3)) var(--s-5);
}
.dpa-doc table.signblock td {
    width: 50%; vertical-align: top; padding: var(--s-4);
    border: 1px solid var(--border-strong); border-radius: var(--r-sm);
    background: var(--surface-2);
}
.dpa-doc table.signblock .who {
    display: block; font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-soft); font-weight: 600; margin-bottom: var(--s-2);
}
.dpa-doc table.signblock strong { color: var(--text); }
.dpa-doc table.signblock .muted { color: var(--text-muted); }
.dpa-doc table.signblock .how {
    margin-top: var(--s-3); font-size: 14px; line-height: 1.5; color: var(--text-muted);
}

.dpa-doc table.seal { width: 100%; border-collapse: collapse; margin-bottom: var(--s-5); }
.dpa-doc table.seal td {
    padding: var(--s-3) 0; border-bottom: 1px solid var(--border); vertical-align: top;
}
.dpa-doc table.seal td.k {
    width: 34%; padding-right: var(--s-4); color: var(--text-soft); font-size: 14px;
}
.dpa-doc table.seal tr:last-child td { border-bottom: none; }
.dpa-doc table.seal .hash {
    font-family: var(--font-mono); font-size: 13px; word-break: break-all; color: var(--text);
}
.dpa-doc .value-note { font-size: 13.5px; color: var(--text-soft); margin-top: var(--s-2); }

/* Signature depuis une fenêtre : le texte défile dans son cadre, pour que les
   informations à compléter et la case d'acceptation restent à portée. */
.dpa-doc--modal {
    max-width: none;
    max-height: 42vh;
    overflow-y: auto;
    margin: var(--s-4) 0 var(--s-5);
    padding: var(--s-4) var(--s-5);
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface-2);
}
.dpa-missing {
    margin: 0 0 var(--s-5);
    padding: var(--s-4) var(--s-5) var(--s-5);
    border: 1px solid var(--accent-line);
    border-radius: var(--r);
    background: var(--accent-soft);
}
.dpa-missing legend { font-weight: 600; padding: 0 var(--s-2); color: var(--text); }
.dpa-missing > .hint { margin: 0 0 var(--s-4); }
form details.field { margin-top: var(--s-4); }
form details.field > summary { cursor: pointer; color: var(--text-muted); margin-bottom: var(--s-3); }
form details.field > summary + label { margin-top: var(--s-2); }
dialog.modal--wide { max-width: min(62rem, calc(100vw - 2rem)); }

@media (max-width: 640px) {
    /* Deux colonnes de signature ne tiennent pas sur un téléphone : elles
       s'empilent plutôt que de comprimer les noms en colonnes d'un mot. */
    .dpa-doc table.signblock,
    .dpa-doc table.signblock tbody,
    .dpa-doc table.signblock tr,
    .dpa-doc table.signblock td { display: block; width: auto; margin-left: 0; margin-right: 0; }
    .dpa-doc table.signblock td + td { margin-top: var(--s-3); }
}

/* Fiche de l'acte : ce qui a été signé, par qui, sous quelle empreinte. */
.dpa-act { border-left: 3px solid var(--ok); }
.dpa-act .ref { font-family: var(--font-mono); font-size: 13px; word-break: break-all; }

/* Bloc de signature. Bordure pleine et fond distinct : on quitte la lecture
   pour un geste qui engage la société. */
.dpa-sign { border: 1px solid var(--accent-line); background: var(--accent-soft); margin-top: var(--s-6); }
.dpa-sign h3 { margin: 0 0 var(--s-2); font-size: 1rem; }
.dpa-sign > .hint { margin-top: 0; }

@media (max-width: 1080px) {
    .dpa-layout { grid-template-columns: minmax(0, 1fr); }
    .dpa-toc { position: static; }
    .dpa-toc ol { display: flex; flex-wrap: wrap; gap: 2px 0; border-left: none; }
    .dpa-toc a { border-left: none; border-bottom: 2px solid transparent; padding: 4px var(--s-3) 4px 0; }
    .dpa-toc a[aria-current='true'] { border-left-color: transparent; border-bottom-color: var(--accent); }
}

/* ── 20 quinquies. Écrans en rubriques (fiche entreprise) ──────────────
   Un formulaire long qu'on remplit une fois, puis qu'on relit pour vérifier
   avant un contrôle. Deux colonnes : à gauche ce que la rubrique engage, à
   droite la saisie. La colonne de gauche n'est pas décorative — elle porte la
   conséquence de ce qui est saisi en face, seule chose qu'on ne devine pas en
   relisant sa propre fiche six mois plus tard. */
.form-section {
    display: grid;
    grid-template-columns: minmax(0, 256px) minmax(0, 1fr);
    gap: var(--s-5) var(--s-6);
    align-items: start;
    margin-bottom: var(--s-7);
}
.form-section__aside { padding-top: var(--s-2); }
.content .form-section__aside h2 {
    font-size: 18px; margin: 0 0 var(--s-2);
}
.form-section__aside p { font-size: 14px; color: var(--text-muted); line-height: 1.55; }
.form-section__aside p + p { margin-top: var(--s-2); }
.form-section__body > .card:last-child { margin-bottom: 0; }

/* Filet de rubrique : sépare deux sections sans réintroduire un titre de
   niveau au milieu du formulaire. */
.form-section + .form-section,
form + .form-section { border-top: 1px solid var(--border); padding-top: var(--s-7); }

/* Section d'un document qui se lit d'un bout à l'autre — la fiche de preuve.
   Pleine largeur, contrairement à `.form-section` : ce qu'on y consulte, ce
   sont des empreintes de 71 caractères, une URL de portail et une chaîne de
   navigateur. Les pousser dans la colonne droite d'une rubrique, elle-même
   redécoupée en intitulé/valeur par `dl.detail`, les rend illisibles au moment
   précis où l'on cherche à les collationner. */
.doc-section { margin-top: var(--s-7); }
.content .doc-section > h2 {
    display: flex; align-items: center; gap: var(--s-3); margin: 0;
}
.doc-section > .lede { margin: var(--s-3) 0 var(--s-4); }
.doc-section > .card:last-child,
.doc-section > .notice:last-child { margin-bottom: 0; }

/* Valeur affichée mais non saisissable. Bordure discontinue plutôt qu'un champ
   désactivé : il n'y a pas de champ derrière, et un input grisé laisserait
   croire qu'un réglage existe quelque part. */
.field__static {
    margin: 0; padding: 10px var(--s-4);
    border: 1px dashed var(--border-strong); border-radius: var(--r);
    background: var(--surface-2);
}
.field__static code { color: var(--text); }

/* Barre d'enregistrement. Collée en bas de la fenêtre tant que le formulaire
   défile : sur une fiche de cette longueur, un bouton posé à la fin se perd,
   et une saisie corrigée en haut de page repart sans être enregistrée. */
.form-actions {
    position: sticky; bottom: var(--s-4); z-index: 4;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s-4); flex-wrap: wrap;
    margin: var(--s-6) 0 var(--s-7);
    padding: var(--s-3) var(--s-4) var(--s-3) var(--s-5);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: saturate(1.4) blur(14px);
    border: 1px solid var(--border-strong); border-radius: var(--r-full);
    box-shadow: var(--shadow-md);
}
.form-actions .hint { margin: 0; }

/* État d'un dispositif : un verdict à gauche, l'accès au document à droite. */
.status-card { display: flex; align-items: center; gap: var(--s-5); flex-wrap: wrap; }
.status-card__text { flex: 1 1 34ch; min-width: 0; }
.status-card__text > :first-child { margin-bottom: var(--s-2); }
.status-card .btn { flex: none; }

/* Dépôt d'une pièce : replié par défaut, car on consulte la liste bien plus
   souvent qu'on ne dépose. Déplié d'office quand il n'y a encore rien. */
.disclosure {
    border: 1px solid var(--border); border-radius: var(--r-lg);
    background: var(--surface); margin-bottom: var(--s-4);
}
.disclosure > summary {
    display: flex; align-items: center; gap: var(--s-3);
    padding: var(--s-4) var(--s-5); cursor: pointer; list-style: none;
    font-weight: 600; font-size: 15.5px; color: var(--text);
    border-radius: var(--r-lg);
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before {
    content: '+'; flex: none;
    width: 24px; height: 24px; border-radius: var(--r-full);
    display: grid; place-items: center;
    font-family: var(--font-mono); font-size: 15px; line-height: 1;
    background: var(--accent-soft); color: var(--accent-strong);
    border: 1px solid var(--accent-line);
}
.disclosure[open] > summary::before { content: '−'; }
.disclosure > summary:hover { color: var(--accent); }
.disclosure__body {
    padding: var(--s-5) var(--s-5) var(--s-5);
    border-top: 1px solid var(--border);
}
.disclosure__body > :last-child { margin-bottom: 0; }

@media (max-width: 1080px) {
    .form-section { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
    .form-section__aside { padding-top: 0; max-width: var(--measure); }
}

/* ── 21. Impression ────────────────────────────────────────────────────
   Une preuve consultée à l'écran finit souvent imprimée et jointe à un
   dossier : la mise en page doit y survivre, bandeau d'avertissement compris. */
/* Colonne étroite : formulaires et pages de lecture suivie, où une pleine
   largeur ferait des lignes trop longues pour être relues. */
.container--narrow { max-width: 780px; }

/* Suggestions du registre des entreprises, sous le champ de raison sociale.
   Positionnée en absolu pour ne pas décaler le formulaire à chaque frappe :
   une liste qui pousse les champs vers le bas fait manquer sa cible au clic. */
.lookup-results {
    position: absolute; z-index: 20; left: 0; right: 0; margin: 4px 0 0;
    padding: 0; list-style: none; max-height: 320px; overflow-y: auto;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-2, 0 8px 24px rgb(0 0 0 / .12));
}
.lookup-results li {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--s-3) var(--s-4); cursor: pointer;
    border-bottom: 1px solid var(--border);
}
.lookup-results li:last-child { border-bottom: 0; }
.lookup-results li:hover,
.lookup-results li:focus { background: var(--surface-soft, var(--bg)); outline: none; }
.lookup-results .hint { margin-top: 0; }

/* Le champ porteur de la liste doit établir le contexte de positionnement. */
.field:has(> .lookup-results) { position: relative; }

/* ---------------------------------------------------------------------------
   Schéma de flux (écran API du client)

   Les couleurs passent par les jetons de thème : un `fill="var(--accent)"` posé
   en ATTRIBUT n'est pas résolu de façon fiable, il faut passer par le CSS.
   --------------------------------------------------------------------------- */
.flow { width: 100%; height: auto; display: block; }
.flow-box { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1.5; }
.flow-box--accent { fill: var(--accent-soft); stroke: var(--accent-line); }
.flow-title { fill: var(--text); font-size: 15px; font-weight: 600; }
.flow-sub { fill: var(--text-muted); font-size: 12.5px; }
.flow-step { fill: var(--text-soft); font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
.flow-line { stroke: var(--border-strong); stroke-width: 1.5; fill: none; }
.flow-line--accent { stroke: var(--accent); stroke-width: 2; fill: none; }
.flow-line--dashed { stroke: var(--text-soft); stroke-width: 1.5; fill: none; stroke-dasharray: 5 4; }
.flow-arrow { fill: var(--border-strong); }
.flow-arrow--accent { fill: var(--accent); }
.flow-arrow--soft { fill: var(--text-soft); }
.flow-label { fill: var(--text); font-size: 12.5px; font-weight: 600; }
.flow-label--soft { fill: var(--text-soft); font-size: 12px; }
.flow-mono { fill: var(--text-muted); font-family: var(--font-mono); font-size: 11.5px; }

/* Le schéma défile plutôt que de comprimer son texte jusqu'à l'illisible. */
.flow-wrap { overflow-x: auto; margin: var(--s-5) 0; }
.flow-wrap > svg { min-width: 620px; }

/* Exemples d'intégration : deux colonnes quand la place le permet. */
.snippets { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.snippets h4 { margin: 0 0 var(--s-2); font-size: 14.5px; }

/* ── Cards tarifaires ──────────────────────────────────────────────────
   Disposition de page de tarifs : trois offres côte à côte, celle du milieu
   mise en avant par une bordure d'accent, une élévation et un ruban. Le motif
   est repris de finixphone.com pour que les deux sites se lisent de la même
   façon — la structure est commune, la palette reste celle d'Optin.

   HAUTEUR ÉGALE, demandée le 26/08/2026. La règle était `align-items: start`,
   qui laissait chaque card à sa hauteur propre : trois blocs de tailles
   différentes, alignés par le haut. C'est `stretch` désormais — le défaut de
   la grille — donc les trois colonnes descendent au même niveau.

   Ce que cela coûte, et pourquoi la contrepartie est traitée juste en dessous :
   étirer une card courte y crée du vide, et sans précaution son bouton flotte
   au milieu de ce vide au lieu de s'aligner sur celui des voisines. C'est le
   `margin-top: auto` du pied (`.pricing__fine`, sinon `.pricing__cta`) qui
   pousse ce bloc en bas et rétablit l'alignement — le seul qui compte à l'œil. */
.pricing {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
    gap: var(--s-5); align-items: stretch; margin-top: var(--s-7);
}
.pricing__card {
    position: relative; display: flex; flex-direction: column;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); padding: var(--s-6);
    transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.pricing__card:hover { border-color: var(--border-strong); }

/* L'offre mise en avant. La bordure porte l'accent, l'élévation reste discrète :
   une ombre marquée sur fond crème vire au sale. */
.pricing__card--featured {
    border-color: var(--accent); border-width: 2px;
    box-shadow: 0 10px 30px -18px rgb(0 0 0 / .35);
    padding-top: var(--s-7);
}
.pricing__badge {
    position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
    /* Encre sombre plutôt que blanc : l'accent est un orange moyen en clair
       (#e94e1b) et plus clair encore en sombre (#ff6a3d). Du blanc dessus
       tombe à 2,6:1 en thème sombre, très en dessous du seuil lisible pour
       un libellé de 11 px en capitales. Valeur fixe et non un token : aucun
       token n'est sombre dans LES DEUX thèmes, et c'est la couleur de fond
       qui commande ici, pas celle du thème. */
    background: var(--accent); color: #231007;
    font-family: var(--font-mono); font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: .1em;
    padding: 5px 12px; border-radius: var(--r-full); white-space: nowrap;
}

.pricing__name {
    font-family: var(--font-display); font-size: 22px; font-weight: 700;
    margin: 0 0 var(--s-2); color: var(--text);
}
.pricing__lede { color: var(--text-muted); font-size: 14.5px; margin: 0 0 var(--s-5); line-height: 1.5; }

/* Le prix : un seul chiffre lisible de loin, sa période en retrait. Mettre la
   période à la même taille ferait hésiter sur ce qui est le montant. */
.pricing__price {
    display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap;
    font-family: var(--font-display); font-size: 38px; font-weight: 700;
    line-height: 1.05; color: var(--text); margin: 0;
}
.pricing__price small {
    font-family: var(--font-body); font-size: 14px; font-weight: 500;
    color: var(--text-muted); letter-spacing: 0;
}
.pricing__price--quote { font-size: 28px; }

/* Prix barré + mention d'offre. Le montant barré reste lisible : il donne son
   sens à la remise, un chiffre trop pâle la rendrait décorative. `line-through`
   plutôt qu'une classe utilitaire pour que la sémantique reste dans le CSS.

   `--ok` porte le mot « GRATUIT » : c'est le même vert que `.pricing__lines
   .is-free`, déjà employé pour ce qui est offert, et il tient le contraste dans
   les deux thèmes — un orange d'accent sur fond crème n'y arrivait pas. */
.pricing__was {
    font-family: var(--font-display); font-size: 24px; font-weight: 600;
    color: var(--text-soft); text-decoration: line-through;
    text-decoration-thickness: 2px; letter-spacing: 0;
}
.pricing__free { color: var(--ok); }
/* La ligne qui dit ce qui se passe APRÈS l'offre. Sans elle, « GRATUIT » seul
   laisserait croire à un pack gratuit — ce qu'aucun de ces packs n'est. */
.pricing__after {
    margin: var(--s-2) 0 0; font-size: 13.5px; line-height: 1.45;
    color: var(--text-muted);
}

/* Lignes de tarif secondaires : ce qui s'ajoute, ou ce qui est offert. */
.pricing__lines {
    list-style: none; margin: var(--s-4) 0 0; padding: var(--s-4) 0 0;
    border-top: 1px solid var(--border);
    display: flex; flex-direction: column; gap: var(--s-2);
}
.pricing__lines li {
    display: flex; justify-content: space-between; gap: var(--s-3);
    font-size: 14px; color: var(--text-muted); line-height: 1.4;
}
.pricing__lines b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pricing__lines .is-free { color: var(--ok); }

.pricing__features {
    list-style: none; margin: var(--s-5) 0 0; padding: 0;
    display: flex; flex-direction: column; gap: var(--s-3);
}
/* SURTOUT PAS `display: grid` ICI, et c'est une erreur que j'ai commise :
   une grille fait de CHAQUE enfant une cellule, y compris les <strong> au
   milieu d'une phrase. « Recueil illimité sur vos fiches » se retrouve alors
   éclaté en « Recueil illimité » / « sur » / « vos » / « fiches », un mot par
   ligne. La grille ne convient qu'à une liste dont chaque entrée a exactement
   le nombre d'enfants annoncé — c'est le cas de `.checklist`, pas d'un texte
   courant.

   Puce en positionnement absolu : le texte reste un flux inline normal, les
   <strong> y restent inline, et le retrait est tenu par le padding. */
.pricing__features li {
    position: relative; padding-left: 18px;
    font-size: 14.5px; line-height: 1.45; color: var(--text-muted);
}
/* Un tiret plutôt qu'une icône : aucun fichier à charger, et la puce ne prétend
   pas valider quoi que ce soit — ces lignes décrivent, elles ne cochent pas. */
.pricing__features li::before {
    content: '–'; position: absolute; left: 0; top: 0;
    color: var(--accent); font-weight: 700;
}
.pricing__features strong { color: var(--text); font-weight: 600; }

/* `margin-top: auto` absorbe tout l'espace libre de la card étirée et pousse
   la note — puis le bouton qui la suit — en bas. L'écart minimal passe donc en
   `padding-top` : une marge fixe serait écrasée par le `auto`. */
.pricing__fine {
    margin: 0; margin-top: auto; padding-top: var(--s-5);
    font-size: 12.5px; line-height: 1.5; color: var(--text-soft);
}
.pricing__cta { margin-top: var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--border); }
/* Une card sans note fine n'a rien pour absorber le vide : c'est alors au pied
   de le faire. Placée APRÈS la règle ci-dessus, dont elle écrase le
   `margin-top` — sa spécificité y suffirait, l'ordre le rend lisible.
   `:has()` évite de marquer ce cas dans le gabarit : la feuille lit la
   structure qu'elle met en forme. */
.pricing__card:not(:has(.pricing__fine)) .pricing__cta { margin-top: auto; }
.pricing__cta .btn { width: 100%; justify-content: center; }

/* Bandeau « Tarifs HT » et rappel de portée, au-dessus des cards. */
.pricing__note {
    font-family: var(--font-mono); font-size: 12px; text-transform: uppercase;
    letter-spacing: .1em; color: var(--text-soft);
}

@media (max-width: 720px) {
    .pricing { gap: var(--s-6); }
    /* Le ruban chevauche la card précédente une fois les colonnes empilées. */
    .pricing__card--featured { margin-top: var(--s-3); }
}

/* ------------------------------------------------------------------------
   Bandeau de la vitrine : cinq entrées de navigation, un sélecteur de thème
   et deux boutons dans une barre de 74 px de haut.

   Le verrou de marque porte déjà `flex: none` — ce sont donc la navigation et
   les boutons qui absorbent le manque de place. Sans `nowrap`, un intitulé
   comme « Demander un devis » se replie sur deux lignes dès que la largeur
   manque et déborde la hauteur fixe : la barre paraît cassée alors que rien
   n'a débordé au sens strict. Constaté le 16/08/2026 en ajoutant « Les packs »,
   qui a fait passer la barre au-delà de son budget de largeur.

   On resserre plutôt que de retirer une entrée : sous 760 px la navigation
   disparaît déjà, et la faire disparaître plus tôt priverait de menu les
   largeurs d'ordinateur portable les plus courantes.
   ------------------------------------------------------------------------ */
/* `flex: none`, et surtout PAS `min-width: 0` : autoriser la navigation à
   passer sous la largeur de son contenu la fait se comprimer jusqu'à
   disparaître derrière les boutons, sans rien signaler. Une barre trop
   étroite doit déborder visiblement, jamais escamoter ses entrées. */
.main-nav { flex: none; }
.main-nav a { white-space: nowrap; }
/* Les boutons gardent leur libellé entier : un « Ouvrir un compte » coupé en
   deux lignes est pire qu'une navigation resserrée. */
.header-actions { flex: none; }
.header-actions .btn { white-space: nowrap; }

@media (max-width: 1260px) {
    .main-nav { gap: var(--s-3); margin-left: var(--s-4); }
    .main-nav a { font-size: 14px; }
    .header-actions { gap: var(--s-2); }
}

/* Aucune entrée n'est masquée par la largeur. La tentation était forte — un
   sélecteur suffit — mais un menu dont les entrées s'effacent selon la taille
   de la fenêtre est un menu auquel on ne peut pas se fier, et le visiteur n'a
   aucun moyen de savoir qu'il manque quelque chose. Le bandeau tient dans son
   budget parce qu'il compte QUATRE entrées, pas parce qu'on en cache une.

   Si une cinquième devient nécessaire, refaire le calcul de largeur avant de
   l'ajouter — et si elle ne rentre pas, la place est dans le corps de la page
   ou en pied, pas derrière une règle de média. */

@media print {
    body { background: #fff; color: #000; font-size: 11pt; }
    body::before { display: none; }
    .sidebar, .topbar, .site-header, .site-footer, .hero-bg, form button, .btn,
    .form-actions { display: none !important; }
    /* Sur papier, la colonne d'explication passe au-dessus de sa rubrique :
       deux colonnes étroites sur une feuille A4 ne se lisent plus. */
    .form-section { display: block; }
    .form-section__aside { margin-bottom: .4rem; }
    .card, .notice, .optin-banner { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
    .content, .portal, .container, .optin-page { max-width: none; padding: 0; }
    a { color: #000; text-decoration: underline; }
}
