/* ============================================================================
   _noyau/design/ui.css — SOCLE COMMUN, identité « Lin & Encre ».
   Trois couches, jamais mélangées :
     ① SEED       les 14 couleurs + 6 entrées typo d'UNE marque. Écrit par
                  generer_marques.py dans marques/<id>.css. Le bloc ci-dessous
                  n'est qu'un DÉFAUT, pour une page qui ne charge aucune marque.
     ② PRIMITIVES rampes dérivées des deux pôles par color-mix. Ne pas écrire ici.
     ③ ALIAS      la seule interface des composants. Un composant qui lit une
                  primitive ou un seed est un bug d'architecture.

   Le thème sombre ÉCHANGE les deux pôles ; il ne réécrit aucune rampe.
   Ordre de chargement : polices.css → ui.css → marques/<id>.css → app.css|site.css
   ---------------------------------------------------------------------------- */

/* ═══ ① SEED (défaut : marque « maison »). Écrasé par marques/<id>.css. ═══════ */
:root {
  --marque-papier: #FFFDF9;   /* JAMAIS #ffffff : le blanc pur est ce qui rend froid */
  --marque-encre:  #211C12;   /* charbon CHAUD — 14,68:1 sur le lin                  */
  --marque-neutre: #8C6A2A;   /* température des neutres = différenciateur de marque */
  --nt-0: 3%; --nt-1: 6%; --nt-2: 10%; --nt-3: 15%;

  --marque-primary: #1E4FA3;  /* encre bleue                                         */
  --marque-accent:  #7C580E;  /* laiton LISIBLE : porte un lien, un chiffre-preuve   */
  --marque-signal:  #C9A227;  /* laiton d'APLAT (CTA) — jamais du texte              */
  --marque-success: #3F6B2E; --marque-warning: #8A6410;
  --marque-danger:  #A8332C; --marque-info:    #4A5C7A;

  /* Encres posées SUR un aplat et contours des aplats : SORTIES CALCULÉES de
     generer_marques.py. Recopiées ici uniquement comme défaut. */
  --marque-on-primary: #FFFDF9; --marque-on-accent: #FFFDF9; --marque-on-signal: #211C12;
  --marque-on-success: #FFFDF9; --marque-on-warning: #FFFDF9;
  --marque-on-danger:  #FFFDF9; --marque-on-info:   #FFFDF9;
  --marque-contour-primary: #1E4FA3; --marque-contour-accent: #7C580E;
  --marque-contour-signal:  #a48522; --marque-contour-success: #3F6B2E;
  --marque-contour-warning: #8A6410; --marque-contour-danger:  #A8332C;
  --marque-contour-info:    #4A5C7A;

  --seed-ff:           "DM Sans", "DM Sans repli", system-ui, -apple-system, sans-serif;
  --seed-ff-display:   "Barlow Condensed", "Barlow Condensed repli", "Arial Narrow", sans-serif;
  --seed-ff-editorial: "Cormorant Garamond", "Cormorant Garamond repli", ui-serif, Georgia, serif;
  --seed-ff-mono:      "DM Mono", "DM Mono repli", ui-monospace, "SF Mono", Menlo, monospace;
  --seed-fs: .875rem;              /* 14px — remonté de 13 : c'est ce qui rend
                                      l'échelle tenable et laisse travailler opsz */
  --seed-radius: 8px;
  --seed-tracking-display: .022em; /* tracking POSITIF des titres condensés       */
  --seed-display-fw: 400;          /* la hiérarchie vient de la LARGEUR, pas du gras */
}

/* ═══ ② PRIMITIVES — tout dérive des deux pôles et de la température. ═════════ */
:root {
  --pole-fond: var(--marque-papier);
  --pole-ink:  var(--marque-encre);
  --soft-amt: 10%;

  --seed-primary: var(--marque-primary);  --seed-accent:  var(--marque-accent);
  --seed-signal:  var(--marque-signal);   --seed-success: var(--marque-success);
  --seed-warning: var(--marque-warning);  --seed-danger:  var(--marque-danger);
  --seed-info:    var(--marque-info);

  /* Bas de rampe = température dans le papier ; haut de rampe = encre diluée
     (teinter le haut donnerait du caramel à la place d'une bordure). */
  --n-0: color-mix(in srgb, var(--marque-neutre) var(--nt-0), var(--pole-fond));
  --n-1: color-mix(in srgb, var(--marque-neutre) var(--nt-1), var(--pole-fond));
  --n-2: color-mix(in srgb, var(--marque-neutre) var(--nt-2), var(--pole-fond));
  --n-3: color-mix(in srgb, var(--marque-neutre) var(--nt-3), var(--pole-fond));
  --n-4: color-mix(in srgb, var(--pole-ink) 22%, var(--n-1));   /* filet de CARTE — 12→22 : 1,01→1,55:1. En dessous l'œil ne voyait pas de bord. */
  --n-5: color-mix(in srgb, var(--pole-ink) 26%, var(--n-1));
  --n-6: color-mix(in srgb, var(--pole-ink) 56%, var(--n-1));   /* bordure de CHAMP — 50→56 : tient 3:1 aussi sur le plan de travail assombri */

  /* ── Le TROISIÈME pôle. --pole-ink et --pole-fond s'échangent au thème sombre ;
     --pole-ombre ne s'échange JAMAIS. C'est la correction de bug la plus lourde :
     --sh-* étaient bâties sur --pole-ink → en sombre chaque carte projetait un
     HALO BLANC. Toutes les ombres, tous les creux, le fond de plan de travail et
     les filets translucides sont désormais tirés d'ici. */
  --pole-ombre: var(--marque-encre);

  --ink:   var(--pole-ink);
  --ink-2: color-mix(in srgb, var(--pole-ink) 82%, var(--n-0));
  --muted: color-mix(in srgb, var(--pole-ink) 72%, var(--n-0)); /* PLANCHER du texte — 68→72 : garde 4,5:1 même SOUS un voile-1 */
  --faint: color-mix(in srgb, var(--pole-ink) 45%, var(--n-0)); /* DÉCORATIF SEUL :
             filets, icônes muettes. Interdit en color: sur un porteur de texte.  */
  --line:        var(--n-4);   /* filet de carte VISIBLE — était n-3 (1,01:1) */
  --line-strong: var(--n-5);
  /* Filets translucides — ils se composent sur n'importe quelle surface et
     s'inversent seuls en sombre. Décoratifs (hors WCAG 1.4.11), pour SÉPARER,
     pas pour porter une forme d'objet. */
  --filet:         color-mix(in srgb, var(--line) 55%, transparent);        /* séparateur de ligne */
  --filet-colonne: color-mix(in srgb, var(--line) 40%, transparent);        /* filet vertical, registre planche de calcul */
  --filet-section: var(--line-strong);                                      /* thead/tfoot ↔ corps */

  --accent:       var(--seed-primary);
  --accent-ink:   color-mix(in srgb, var(--seed-primary) 78%, var(--pole-ink));
  --accent-soft:  color-mix(in srgb, var(--seed-primary) var(--soft-amt), var(--n-0));
  --accent2:      var(--seed-accent);
  --accent2-ink:  color-mix(in srgb, var(--seed-accent) 78%, var(--pole-ink));
  --accent2-soft: color-mix(in srgb, var(--seed-accent) var(--soft-amt), var(--n-0));
  --signal:       var(--seed-signal);
  --signal-ink:   color-mix(in srgb, var(--seed-signal) 82%, var(--pole-ink));
  --signal-soft:  color-mix(in srgb, var(--seed-signal) var(--soft-amt), var(--n-0));
  --ok: var(--seed-success);    --ok-soft:     color-mix(in srgb, var(--seed-success) var(--soft-amt), var(--n-0));
  --warn: var(--seed-warning);  --warn-soft:   color-mix(in srgb, var(--seed-warning) var(--soft-amt), var(--n-0));
  --danger: var(--seed-danger); --danger-soft: color-mix(in srgb, var(--seed-danger) var(--soft-amt), var(--n-0));
  --violet: var(--seed-info);   --violet-soft: color-mix(in srgb, var(--seed-info) var(--soft-amt), var(--n-0));

  --nav:       color-mix(in srgb, var(--seed-primary) 14%, var(--pole-ink));
  /* Survol/actif du rail : on ÉCLAIRCIT vers le papier (clair), jamais vers plus
     de primaire. mix(primary 22%, encre) mesurait 1,04:1 sur --nav = invisible.
     Formule unique : elle se résout sur le --nav du thème courant → 1,34:1 mini. */
  --nav-2:     color-mix(in srgb, var(--marque-papier) 10%, var(--nav));
  --nav-ink:   var(--marque-papier);   /* l'encre du rail suit le PAPIER de la marque */
  --nav-ink-2: color-mix(in srgb, var(--nav-ink) 66%, transparent);

  --bg: var(--n-2);
  /* Halo de laiton dilué : un halo bleuté verdit sur du lin. RÉSERVÉ à la vitrine. */
  --bg-glow: radial-gradient(1100px 420px at 88% -140px,
             color-mix(in srgb, var(--seed-accent) 6%, var(--n-2)), transparent);
  /* Plan de travail d'un CRM : LÉGÈREMENT plus sombre que les cartes (micro-contraste
     ~2 % d'encre), pour que la carte EXISTE. Assombrir le --bg de vitrine ferait
     tomber les liens colorés d'un paragraphe sous 4,5:1 — d'où deux fonds séparés. */
  --fond-appli: color-mix(in srgb, var(--pole-ombre) 6%, var(--n-2));

  /* ── VOILES — le micro-contraste de 21st.dev, en alpha. Ils se posent EN PLUS
     d'une surface (background-image), jamais à la place, et s'inversent seuls en
     sombre (--pole-ink est le papier après échange). Un voile n'est JAMAIS le
     seul marqueur d'un état : il se double d'une graisse et d'un repère. */
  --voile-1: color-mix(in srgb, var(--pole-ink) 5%, transparent);   /* survol */
  --voile-2: color-mix(in srgb, var(--pole-ink) 8%, transparent);   /* actif / sélectionné */
  --voile-3: color-mix(in srgb, var(--pole-ink) 12%, transparent);  /* enfoncé, piste creusée */
  --lueur-1: color-mix(in srgb, var(--pole-fond) 8%, transparent);  /* survol sur surface d'encre */
  --lueur-2: color-mix(in srgb, var(--pole-fond) 14%, transparent); /* actif sur surface d'encre */

  /* ── OMBRES — deux couches, TERME DE CONTACT court + spread NÉGATIF (l'ombre
     reste sous l'objet au lieu de baver un halo de 48 px). Bâties sur --pole-ombre,
     jamais échangé → plus de halo blanc en sombre. Le repos n'est PAS élevé. */
  --anneau: 0 0 0 1px color-mix(in srgb, var(--pole-ombre) 5%, transparent);   /* filet de tranche, au repos */
  --sh-1: 0 1px 1px color-mix(in srgb, var(--pole-ombre) 5%, transparent),
          0 1px 3px -1px color-mix(in srgb, var(--pole-ombre) 8%, transparent);
  --sh-2: 0 1px 2px color-mix(in srgb, var(--pole-ombre) 6%, transparent),
          0 8px 20px -6px color-mix(in srgb, var(--pole-ombre) 16%, transparent);
  --sh-3: 0 2px 6px color-mix(in srgb, var(--pole-ombre) 8%, transparent),
          0 28px 56px -24px color-mix(in srgb, var(--pole-ombre) 42%, transparent);
  --luisant:       inset 0 1px 0 color-mix(in srgb, var(--marque-papier) 55%, transparent);  /* biseau des contrôles clairs */
  --luisant-aplat: inset 0 1px 0 color-mix(in srgb, var(--on-primary) 26%, transparent);     /* biseau des aplats colorés */
  --creux:         inset 0 1px 2px color-mix(in srgb, var(--pole-ombre) 14%, transparent);   /* champ, état pressé, piste de segment */

  /* Trame de 4 px complétée vers le HAUT : le hero et une section ordinaire
     avaient le même air parce que 44 px était le plafond du système. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
  --sp-8:64px; --sp-9:96px; --sp-10:128px;
  /* Rayons dérivés : --r-0 suit la marque (archi 2px, sante 12px) au lieu d'un
     plancher fixe ; --r-4 est le cran de vitrine qui manquait (hero, CTA). */
  --r-0: max(2px, calc(var(--r-2) / 2));
  --r-1:6px; --r-2:var(--seed-radius); --r-3:12px; --r-4: calc(var(--r-3) + 6px); --r-pill:999px;

  /* Géométrie DÉCLARÉE des contrôles : une rangée de filtres alignait 31/37/39 px
     parce que la hauteur émergeait de line-height:1 (donc des métriques de police). */
  --h-ctrl-sm:30px; --h-ctrl:36px; --h-ctrl-lg:44px; --h-cta:56px;
  --h-topbar:56px; --ligne-h:44px; --tete-h:38px; --cell-px:12px;

  /* Trois courbes, trois intentions ; --t reçoit désormais une VRAIE courbe.
     --e-sortie existait depuis le début avec ZÉRO appel. */
  --e-appui:   cubic-bezier(.3, 0, .2, 1);    /* pression : part vite, arrive net */
  --e-sortie:  cubic-bezier(.23, 1, .32, 1);  /* ce qui ENTRE : longue décélération */
  --e-retrait: cubic-bezier(.4, 0, 1, 1);     /* ce qui SORT : accélère et part */
  --t-fast:   .09s var(--e-appui);
  --t:        .14s var(--e-appui);
  --t-lent:   .22s var(--e-sortie);
  --t-panneau:.18s var(--e-sortie);
  --t-retrait:.12s var(--e-retrait);
}

/* ═══ ÉCHELLE TYPOGRAPHIQUE — 11 tailles nommées, en rem, ZÉRO décimale.
   Elle remplace ~40 tailles au juger et ~2130 demi-pixels. En rem, parce qu'un
   utilisateur qui a monté sa police par défaut doit y gagner quelque chose.
   Sous 1rem le pas est de +1px (à cette taille l'œil lit une hauteur d'x, pas
   un ratio) ; au-dessus, ×1,25 aligné sur la trame de 4px.
   INTERDIT : toute taille hors de cette liste. Plancher 11px.
   Nommée --fs-* et non --t-* : --t, --t-fast et --t-lent sont des durées.   */
:root {
  --fs-1:  .6875rem; /* 11px — PLANCHER. Bas de casse, graisse 500. Pas de capitales. */
  --fs-2:  .75rem;   /* 12px — étiquette mono, en-tête de colonne, méta               */
  --fs-3:  .8125rem; /* 13px — texte secondaire, bouton --sm                          */
  --fs-4:  .875rem;  /* 14px — CORPS COMPACT (CRM)                                    */
  --fs-5:  1rem;     /* 16px — CORPS CONFORT (vitrine) / h3 du CRM                    */
  --fs-6:  1.25rem;  /* 20px */   --fs-7:  1.5rem;   /* 24px */
  --fs-8:  2rem;     /* 32px */   --fs-9:  2.5rem;   /* 40px */
  --fs-10: 3rem;     /* 48px */   --fs-11: 3.75rem;  /* 60px */

  /* Tracking — DEUX courbes, parce que DEUX familles.
     CONDENSÉ : positif, décroissant quand la taille monte (c'est la condensation
     qui referme les contre-formes, et elle fait d'autant plus mal en petit).
     GROTESQUE : ~0 au corps, léger négatif en grand — l'axe opsz de DM Sans fait
     déjà le travail optique, on ne le double pas. */
  --ls-cd-1: .040em;                        /* condensé 11-14px */
  --ls-cd-2: var(--seed-tracking-display);  /* condensé 16-24px — entrée de marque */
  --ls-cd-3: .014em;                        /* condensé 32-40px */
  --ls-cd-4: .006em;                        /* condensé 48px+   */
  --ls-tx-1: .012em;   /* DM Sans 11-13px — .004em valait 0,048px à 12px : rien */
  --ls-btn: 0;         /* tracking d'étiquette de bouton — token de MARQUE, négatif seulement sur les moods saas/dark */
  --ls-tx-2: 0;        /* DM Sans 14-20px — chasses natives, ne pas y toucher */
  --ls-tx-3: -.012em;  /* DM Sans 24-32px */
  --ls-tx-4: -.020em;  /* DM Sans 40px+   */
  --ls-caps: .080em;   /* LA seule capitale du système : .eyebrow, ≥12px, mono */

  --lh: 1.5; --lh-confort: 1.6; --lh-dense: 1.32;
  --lh-titre: 1.08;      /* condensé ≥32px      */
  --lh-hero: 1.20;       /* grotesque léger ≥40px */
  --lh-etiquette: 1.30;
  --mesure: 68ch; --mesure-chapo: 46ch;

  /* Graisses. La hiérarchie vient de la FAMILLE, pas du gras. Le 700 n'existe
     que sur le CTA de vitrine. Le 650 d'avant n'a jamais existé à l'écran. */
  --fw-leger:300; --fw-normal:400; --fw-medium:500; --fw-fort:600; --fw-cta:700;
  --fs: var(--seed-fs); --lh-corps: var(--lh);
}

/* ═══ ③ ALIAS — l'interface des composants. ═══════════════════════════════════ */
:root {
  --surface: var(--n-0); --surface-sunken: var(--n-1); --surface-muted: var(--n-2);
  --text: var(--ink); --text-2: var(--ink-2); --text-muted: var(--muted); --text-faint: var(--faint);
  --border: var(--line); --border-strong: var(--line-strong); --border-hover: var(--n-6);
  /* --border-field : WCAG 1.4.11. C'est LE correctif du plus gros défaut du socle —
     --border-strong (1,22:1) était la SEULE bordure des 763 .btn et de tous les
     champs. Il est CÂBLÉ au repos, pas seulement déclaré. */
  --border-field: var(--n-6);
  /* Anneau de focus : primary partout, SIGNAL sur les surfaces d'encre (rail, CTA,
     barre de masse) où primary tombe à 1,48:1. Redéfini par contexte, jamais en dur. */
  --focus: var(--color-primary);

  --ff: var(--seed-ff); --ff-display: var(--seed-ff-display);
  --ff-mono: var(--seed-ff-mono); --ff-editorial: var(--seed-ff-editorial);

  --color-primary: var(--accent); --color-primary-hover: var(--accent-ink); --color-primary-bg: var(--accent-soft);
  --color-accent: var(--accent2); --color-accent-hover: var(--accent2-ink); --color-accent-bg: var(--accent2-soft);
  --color-signal: var(--signal);  --color-signal-hover: var(--signal-ink);  --color-signal-bg: var(--signal-soft);
  --color-success: var(--ok);     --color-success-bg: var(--ok-soft);
  --color-warning: var(--warn);   --color-warning-bg: var(--warn-soft);
  --color-danger: var(--danger);  --color-danger-bg: var(--danger-soft);
  --color-info: var(--violet);    --color-info-bg: var(--violet-soft);

  /* Encres et contours d'aplat — calculés par marque, dans les DEUX thèmes. */
  --on-primary: var(--marque-on-primary); --on-accent: var(--marque-on-accent);
  --on-signal:  var(--marque-on-signal);  --on-success: var(--marque-on-success);
  --on-warning: var(--marque-on-warning); --on-danger: var(--marque-on-danger);
  --on-info:    var(--marque-on-info);
  --contour-primary: var(--marque-contour-primary); --contour-accent: var(--marque-contour-accent);
  --contour-signal:  var(--marque-contour-signal);  --contour-success: var(--marque-contour-success);
  --contour-warning: var(--marque-contour-warning); --contour-danger: var(--marque-contour-danger);
  --contour-info:    var(--marque-contour-info);
}

/* Densité — les vitrines respirent (16px), les dashboards restent compacts (14px). */
:root[data-density="confort"], [data-density="confort"] {
  --fs: var(--fs-5); --lh-corps: var(--lh-confort);
}

/* ═══ THÈME SOMBRE — on échange les pôles, on inverse les doses, rien d'autre.
   Les doses s'inversent parce que la température ÉCLAIRCIT le charbon : la
   surface d'une carte devient plus claire que le fond de page. Les NOMS gardent
   leur sémantique (n-0 = surface, n-2 = fond). marques/<id>.css, chargé APRÈS,
   redéfinit ces doses et les encres d'aplat avec les valeurs de sa marque. */
:root[data-theme="dark"] {
  --pole-fond: var(--marque-encre);
  --pole-ink:  var(--marque-papier);
  --nt-0: 11%; --nt-1: 8%; --nt-2: 5%; --nt-3: 15%;
  --soft-amt: 22%;
  /* Les seeds s'éclaircissent VERS LE PAPIER (chaud), jamais vers #ffffff :
     mélanger vers le blanc pur redonne des seeds désaturés et froids. */
  --seed-primary: color-mix(in srgb, var(--marque-primary) 52%, var(--marque-papier));
  --seed-accent:  color-mix(in srgb, var(--marque-accent)  62%, var(--marque-papier));
  --seed-success: color-mix(in srgb, var(--marque-success) 55%, var(--marque-papier));
  --seed-warning: color-mix(in srgb, var(--marque-warning) 60%, var(--marque-papier));
  --seed-danger:  color-mix(in srgb, var(--marque-danger)  62%, var(--marque-papier));
  --seed-info:    color-mix(in srgb, var(--marque-info)    58%, var(--marque-papier));
  --seed-signal:  var(--marque-signal);   /* déjà clair : il ne s'éclaircit pas */
  --nav:   color-mix(in srgb, var(--marque-primary) 16%, var(--marque-encre));
  /* --nav-2 n'est PLUS redéfini ici : la formule mix(papier 10%, nav) du bloc
     primitives se résout déjà sur le --nav sombre ci-dessus (1,34:1). */

  /* La surface d'une carte est RELEVÉE de 7 % vers le papier : sans ça, carte et
     fond de page tenaient à 1,02:1 en sombre. --pole-ombre reste l'encre (non
     échangé), donc les ombres restent des ombres et non un halo blanc. */
  --surface: color-mix(in srgb, var(--marque-papier) 7%, var(--n-0));
  --fond-appli: var(--n-2);                /* en sombre, n-2 est déjà le plus foncé : pas d'assombrissement */
  --voile-1: color-mix(in srgb, var(--pole-ink) 7%, transparent);
  --voile-2: color-mix(in srgb, var(--pole-ink) 11%, transparent);
  --voile-3: color-mix(in srgb, var(--pole-ink) 16%, transparent);
  /* Ombres sombres : plus denses et sans terme de highlight (une ombre claire sur
     du charbon se verrait comme un liseré). Le luisant des contrôles s'efface presque. */
  --sh-1: 0 1px 2px color-mix(in srgb, var(--pole-ombre) 40%, transparent);
  --sh-2: 0 2px 8px color-mix(in srgb, var(--pole-ombre) 45%, transparent);
  --sh-3: 0 3px 16px color-mix(in srgb, var(--pole-ombre) 65%, transparent);
  --anneau: 0 0 0 1px color-mix(in srgb, var(--pole-ombre) 40%, transparent);
  --luisant: inset 0 1px 0 color-mix(in srgb, var(--marque-papier) 12%, transparent);
}

/* Repli sans color-mix — défaut « maison ». marques/<id>.css émet le sien.
   Sans ce bloc, une WebView ancienne perd fond, texte ET bordures d'un coup. */
@supports not (color: color-mix(in srgb, red 50%, white)) {
  :root { --n-0:#fcf9f3; --n-1:#f8f4ed; --n-2:#f4eee4; --n-3:#eee7da; --n-4:#d8cfbf;
          --n-5:#c4bcae; --n-6:#847c6f; --ink:#211C12; --ink-2:#3f382d; --muted:#645d51;
          --faint:#9c968c; --nav:#242229; --nav-2:#3a3842; --fond-appli:#efe8db; }
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* Les trois seuls endroits où le navigateur réintroduit de lui-même une taille
   non entière (0.8333em) et sa propre pile de polices — mesurés dans Chrome :
   un <input type=checkbox> rendait Arial 13,3333px, un <sub> 11,6667px. */
html { font-family: var(--ff); }
input, button, select, textarea, optgroup { font-family: inherit; }
small { font-size: var(--fs-3); }
/* Les cases et boutons radio ne dessinent aucun glyphe, mais leur font-size par
   défaut (13,3333px) est la dernière valeur non entière de la page et, sur
   plusieurs moteurs, elle dimensionne le contrôle. On la met sur l'échelle. */
input[type=checkbox], input[type=radio] { font-size: var(--fs-4); inline-size: 16px; block-size: 16px; margin: 0; }
/* Une ligne, et la case cesse d'être bleu système sur 11 marques vertes, or, teal et brunes. */
:root { accent-color: var(--color-primary); }
sub, sup { font-size: var(--fs-1); line-height: 0; }
body { margin: 0; background: var(--bg-glow) var(--bg); color: var(--text);
  font: var(--fs)/var(--lh-corps) var(--ff); letter-spacing: var(--ls-tx-2);
  font-optical-sizing: auto;            /* l'axe opsz de DM Sans travaille seul */
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
  font-variant-numeric: lining-nums;
  /* Pas de faux italique. On GARDE le faux gras : font-synthesis:none
     supprimerait silencieusement l'emphase des <em> des ~40 surfaces existantes. */
  font-synthesis: weight;
  text-wrap: pretty; -webkit-font-smoothing: antialiased; }
a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
p, li { text-wrap: pretty; }
.prose { max-inline-size: var(--mesure); text-wrap: pretty; hyphens: auto; } /* lang="fr" requis */

/* Données. Deux régimes, jamais mélangés dans une même page : aligné (tableau,
   KPI) = tabulaire ; prose de vitrine = elzévirien, les chiffres descendent sous
   la ligne et se fondent dans le texte. Geste « papier » à zéro octet. */
.num, .tabular { font-variant-numeric: tabular-nums lining-nums; }
[data-density="confort"] .prose { font-variant-numeric: proportional-nums oldstyle-nums; }
.mono, code, .kbd, .ref { font-family: var(--ff-mono); font-variant-ligatures: none;
  font-variant-numeric: tabular-nums slashed-zero; }

/* ---------- Typo ----------
   Titres structurants en CONDENSÉ : une seule voix display dans tout le système,
   plus aucun font-stretch, donc plus aucun clamp silencieux de police variable. */
.titre-section, .h1, .h2 { font-family: var(--ff-display); font-weight: var(--seed-display-fw);
  text-wrap: balance; margin: 0; }
.titre-section { font-size: var(--fs-9); line-height: var(--lh-titre); letter-spacing: var(--ls-cd-3); }
.h1 { font-size: var(--fs-7); line-height: var(--lh-dense); letter-spacing: var(--ls-cd-2); }
.h2 { font-size: var(--fs-6); line-height: var(--lh-dense); letter-spacing: var(--ls-cd-2); }
/* .h3 repasse en grotesque : à 16px un condensé devient sec et se dégrade sous
   DirectWrite. Le condensé est réservé au ≥20px et aux étiquettes ≥12px. */
.h3 { font-family: var(--ff); font-size: var(--fs-5); font-weight: var(--fw-fort);
  line-height: var(--lh-etiquette); letter-spacing: var(--ls-tx-2); margin: 0; }
:root[data-density="confort"] .titre-section { font-size: var(--fs-10); letter-spacing: var(--ls-cd-4); }
:root[data-density="confort"] .h1 { font-size: var(--fs-8); letter-spacing: var(--ls-cd-3); }
:root[data-density="confort"] .h2 { font-size: var(--fs-7); }

/* HERO — grotesque LÉGER, aéré. Ce n'est pas un titre display, c'est une phrase :
   seule place du 300, et jamais sous 32px. */
.hero-titre { font-family: var(--ff); font-weight: var(--fw-leger); font-size: var(--fs-10);
  line-height: var(--lh-hero); letter-spacing: var(--ls-tx-4); text-wrap: balance;
  margin: 0; max-inline-size: 16ch; }
.chapo { font-size: var(--fs-5); line-height: var(--lh-confort); color: var(--text-2);
  max-inline-size: var(--mesure-chapo); }

.sub { color: var(--text-muted); font-weight: var(--fw-normal); }
/* Le socle ne normalisait jamais b/strong : le navigateur les rendait en 700 par
   défaut = faux gras, alors que DM Sans plafonne à 600 dans le système. */
b, strong { font-weight: var(--fw-fort); }
/* LA SEULE CAPITALE DU SYSTÈME — 12px minimum, mono, --text-muted. À 10,5px en
   capitales traquées et à 2,56:1, un micro-texte lu 8 h/jour était exactement ce
   qu'il ne fallait pas faire. */
.eyebrow, .kpi-etq { font-family: var(--ff-mono); font-size: var(--fs-2);
  font-weight: var(--fw-normal); text-transform: uppercase; letter-spacing: var(--ls-caps);
  line-height: var(--lh-etiquette); color: var(--text-muted); }
.text-sm { font-size: var(--fs-3); } .text-xs { font-size: var(--fs-2); }
.dim { color: var(--text-muted); }
/* .faint reste pour les FILETS et icônes muettes, jamais pour du texte. */
.faint { color: var(--text-faint); }

/* ---------- Layout ---------- */
.zone { max-width: 1520px; margin: 0 auto; padding: var(--sp-5); }
.row { display: flex; align-items: center; gap: var(--sp-2); }
.row-wrap { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: var(--sp-2); }
.spacer { flex: 1; }
.grid { display: grid; gap: var(--sp-3); }

/* ---------- Boutons ----------
   La bordure passe de --border-strong (1,22:1) à --border-field (≥3:1) : c'est le
   correctif d'accessibilité le plus nombreux du chantier — 763 occurrences. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-block-size: var(--h-ctrl); padding: 0 var(--sp-4);      /* HAUTEUR déclarée, plus émergente */
  font: var(--fw-medium) var(--fs-4)/18px var(--ff); letter-spacing: var(--ls-btn);
  font-variant-numeric: tabular-nums lining-nums;
  border: 1px solid var(--border-field); border-radius: var(--r-2);
  background-color: var(--surface); color: var(--text); cursor: pointer; white-space: nowrap;
  box-shadow: var(--luisant), var(--sh-1);
  transition: background-color var(--t), border-color var(--t), color var(--t),
              box-shadow var(--t), transform var(--t-fast); }
/* --surface-muted valait --bg au pixel près : au survol, 763 boutons prenaient la
   couleur du body et se trouaient. Le survol se pose EN PLUS de la surface. */
.btn:hover { background-color: var(--surface); border-color: var(--border-hover);
  box-shadow: var(--luisant), var(--sh-2); transform: translateY(-1px); }
@supports (color: color-mix(in srgb, red 50%, transparent)) {
  .btn:hover { background-image: linear-gradient(var(--voile-1), var(--voile-1)); }
}
/* 1 px PLEIN et transitionné : un demi-pixel non transitionné floute le libellé en sous-pixel. */
.btn:active { transform: translateY(1px); box-shadow: var(--creux); transition-duration: 40ms; }
/* opacity:.55 multipliait --border-field (calibré à 3:1) → 1,8:1. Palette dédiée : forme franche, libellé au plancher. */
.btn:disabled, .btn[aria-disabled="true"] { opacity: 1; cursor: not-allowed; pointer-events: none;
  box-shadow: none; transform: none; background-color: var(--surface-muted); color: var(--text-muted);
  border-color: color-mix(in srgb, var(--border-field) 55%, var(--surface-muted)); }

/* La hiérarchie vient de l'APLAT, pas de la graisse : à 600 dans une rangée de 500,
   le primaire ne pèse pas plus, il DÉBORDE. Il garde donc la graisse commune. */
.btn-primary { background-color: var(--color-primary); color: var(--on-primary);
  border-color: var(--contour-primary); font-weight: var(--fw-medium);
  box-shadow: var(--luisant-aplat), var(--sh-1); }
.btn-primary:hover { background-color: var(--color-primary); border-color: var(--contour-primary);
  background-image: none;   /* l'aplat reste INTACT : l'éclaircir baisserait le contraste du libellé */
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--on-primary) 36%, transparent), var(--sh-2); }
.btn-primary:active { box-shadow: var(--creux); }
.btn-danger { color: var(--color-danger);
  border-color: color-mix(in srgb, var(--color-danger) 45%, var(--border-field)); }
.btn-danger:hover { background-color: var(--color-danger-bg); background-image: none; }
.btn-ghost { background-color: transparent; border-color: transparent; box-shadow: none; }
.btn-ghost:hover { background-color: transparent; box-shadow: var(--luisant); transform: none; }
@supports (color: color-mix(in srgb, red 50%, transparent)) {
  .btn-ghost:hover { background-image: linear-gradient(var(--voile-1), var(--voile-1)); }
}
.btn-sm  { min-block-size: var(--h-ctrl-sm); padding: 0 var(--sp-3);
           font-size: var(--fs-3); border-radius: var(--r-1); }
.btn-lg  { min-block-size: var(--h-ctrl-lg); padding: 0 var(--sp-5); font-size: var(--fs-5); }
.btn-icon{ min-block-size: var(--h-ctrl); inline-size: var(--h-ctrl); padding: 0; }

/* Chargement à LARGEUR CONSTANTE : le libellé reste dans le flux (color:transparent),
   donc le nom accessible est préservé et la rangée ne saute pas. */
@keyframes rotation { to { transform: rotate(1turn); } }
.btn[aria-busy="true"] { color: transparent; pointer-events: none; position: relative; }
.btn[aria-busy="true"]::after { content: ""; position: absolute; inset-block-start: 50%;
  inset-inline-start: 50%; margin: -7px 0 0 -7px; inline-size: 14px; block-size: 14px;
  border-radius: 50%; border: 2px solid color-mix(in srgb, var(--text) 18%, transparent);
  border-block-start-color: var(--text); animation: rotation .7s linear infinite; }
.btn-primary[aria-busy="true"]::after {
  border-color: color-mix(in srgb, var(--on-primary) 22%, transparent);
  border-block-start-color: var(--on-primary); }

/* ---------- Champs ---------- */
.field, input:not([type=checkbox]):not([type=radio]), select {
  font: inherit; color: var(--text); background: var(--surface);
  border: 1px solid var(--border-field); border-radius: var(--r-2);
  min-block-size: var(--h-ctrl); padding: 0 var(--sp-3); line-height: 1;
  box-shadow: var(--creux);            /* un champ est CREUSÉ, il ne flotte pas */
  transition: border-color var(--t), box-shadow var(--t); }
textarea { font: inherit; color: var(--text); background: var(--surface);
  border: 1px solid var(--border-field); border-radius: var(--r-2);
  min-block-size: calc(var(--h-ctrl) * 2); padding: var(--sp-2) var(--sp-3);
  box-shadow: var(--creux); transition: border-color var(--t), box-shadow var(--t); }
/* L'anneau était --color-primary-bg (mélange OPAQUE, 1,17:1) : seul anneau des
   champs → échec 1.4.11. On repasse à un anneau alpha ancré sur --focus. */
.field:focus, input:focus, select:focus, textarea:focus { outline: none;
  border-color: var(--focus);
  box-shadow: var(--creux), 0 0 0 3px color-mix(in srgb, var(--focus) 24%, transparent); }
/* Le placeholder est du texte : il ne descend pas sous --text-muted. */
.field::placeholder, input::placeholder, textarea::placeholder { color: var(--text-muted); }
label.check { display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; }

/* ---------- Cartes ---------- */
/* Le repos n'est PAS élevé : l'élévation redevient une information. */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-3);
  box-shadow: var(--anneau); }
.card-2 { box-shadow: var(--anneau), var(--sh-2); }
.card-pad { padding: var(--sp-4); }
.card-head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border); }

/* ---------- Stat / KPI ----------
   Le chiffre passe en condensé : c'est ce qui donne au tableau de bord sa voix
   de planche d'atelier, et ça remplace un 22px gras générique. */
.stats { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-2);
  padding: var(--sp-3) var(--sp-4); flex: 1 1 150px; box-shadow: var(--anneau);
  transition: background-color var(--t), border-color var(--t); }
/* Le survol change le PLAN TONAL, pas l'altitude : à -1px sur 12 vignettes la page frémit. */
.stat:hover { border-color: var(--border-hover); }
@supports (color: color-mix(in srgb, red 50%, transparent)) {
  .stat:hover { background-image: linear-gradient(var(--voile-1), var(--voile-1)); }
}
/* Un condensé 400 à 32px a moins d'encre que 40 lignes de grotesque : le chiffre-preuve
   était l'objet le plus PÂLE de l'écran. La police est bonne, l'échelle était fausse.
   Et --ls-cd-3 valait +.014em POSITIF sur un chiffre tabulaire, qui n'a pas de contre-forme. */
.stat b, .kpi-val { font-family: var(--ff-display); font-size: var(--fs-9);
  font-weight: var(--fw-normal); line-height: var(--lh-titre); letter-spacing: var(--ls-tx-2);
  display: block; font-variant-numeric: tabular-nums lining-nums; }
:root[data-density="confort"] .stat b, :root[data-density="confort"] .kpi-val { font-size: var(--fs-10); }
.stat span { color: var(--text-muted); font-size: var(--fs-2); }
.stat.ok b { color: var(--color-success); } .stat.warn b { color: var(--color-warning); }
.stat.danger b { color: var(--color-danger); } .stat.accent b { color: var(--color-primary); }

/* ---------- Badges & chips ---------- */
/* --fs-1 (11px) est RÉSERVÉ à l'unité d'un chiffre : une étiquette de statut lit à --fs-3.
   Le contour prend le --contour-* calculé par marque (dans les 2 thèmes) — l'ancien
   mix(couleur 30%, transparent) valait quelques % de son PROPRE aplat = invisible. */
.badge { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-3);
  font-weight: var(--fw-medium); letter-spacing: var(--ls-tx-1); line-height: var(--lh-etiquette);
  padding: 2px 8px; border-radius: var(--r-pill); border: 1px solid var(--border);
  background: var(--surface-muted); color: var(--text-muted); white-space: nowrap; }
.badge.ok { background: var(--color-success-bg); color: var(--color-success); border-color: var(--contour-success); }
.badge.warn { background: var(--color-warning-bg); color: var(--color-warning); border-color: var(--contour-warning); font-weight: var(--fw-fort); }
.badge.danger { background: var(--color-danger-bg); color: var(--color-danger); border-color: var(--contour-danger); }
.badge.accent { background: var(--color-primary-bg); color: var(--color-primary-hover); border-color: var(--contour-primary); }
.chip { display: inline-flex; align-items: center; font-size: var(--fs-3); font-weight: var(--fw-medium);
  padding: 3px 9px; border-radius: var(--r-pill); background: var(--surface-muted);
  border: 1px solid var(--border); color: var(--text-2); }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none;
  background: var(--text-muted); }
.dot.ok { color: var(--color-success); background: var(--color-success); }
.dot.danger { color: var(--color-danger); background: var(--color-danger); }
.dot.faint { color: var(--text-faint); background: var(--text-faint); }

/* ---------- Tableaux ----------
   Le condensé descend DANS le CRM : en-têtes de colonne et onglets. Correctif
   a11y au passage — les th étaient en --text-faint (2,56:1) ET en capitales.
   C'est là que meurt la majorité des 589 text-transform:uppercase. */
.table-wrap { overflow-x: auto; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-3); box-shadow: var(--anneau); }
table.ui { border-collapse: separate; border-spacing: 0; width: 100%; }
/* th en DM Sans (pas condensé) : à 12px un condensé se dégrade sous DirectWrite ET
   n'existe qu'en 400 vendoré, donc « medium » était du FAUX gras. La hiérarchie se
   fait ici en graisse + couleur. --filet-section sépare l'en-tête du corps. */
table.ui th { background: var(--surface-sunken); text-align: left;
  font-family: var(--ff); font-size: var(--fs-2); font-weight: var(--fw-fort);
  letter-spacing: var(--ls-tx-1); line-height: var(--lh-etiquette); text-transform: none;
  color: var(--text-muted); padding: 9px 12px; border-bottom: 1px solid var(--filet-section);
  position: sticky; top: 0; z-index: 1; }
table.ui td { padding: 7px 12px; border-bottom: 1px solid var(--filet);
  block-size: var(--ligne-h); vertical-align: middle;
  font-variant-numeric: tabular-nums lining-nums; }
/* Troncature 2 lignes + infobulle : une adresse « 12 bd Bineau — appt 118 m² · 5 p.
   · 1 290 000 € » cassait la ligne sur 8 hauteurs, l'œil se resynchronisait à chaque
   saut. La valeur complète reste dans title=. Bornée à 2 lignes, pas 1 : un nom propre
   coupé net trahit plus qu'il n'informe. */
table.ui td .tronq { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; }
/* Filet VERTICAL en opt-in (.grille) : registre planche de calcul, sans peser sur les listes. */
table.ui.grille th + th, table.ui.grille td + td { border-left: 1px solid var(--filet-colonne); }
td.n, th.n { text-align: right; }        /* nombres à droite, chiffres tabulaires */
table.ui tbody tr:last-child td { border-bottom: none; }
table.ui tbody tr { transition: background-color var(--t-fast); }
table.ui tbody tr:hover td { background: var(--surface-sunken); }

/* ---------- Bandeau (alerte contextuelle) ---------- */
.banner { position: relative; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-2); padding: var(--sp-3) var(--sp-4); padding-left: var(--sp-5); }
.banner::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--color-success); }
.banner.warn::before, .banner.danger::before { background: var(--color-danger); }
.banner.warn { background: var(--color-warning-bg); }
.banner.danger { background: var(--color-danger-bg); }

/* ---------- Onglets ---------- */
.tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border); }
.tab { border: 1px solid var(--border); border-bottom: none; background: var(--surface-muted);
  color: var(--text-muted); padding: 8px 16px; cursor: pointer;
  font-family: var(--ff); font-size: var(--fs-3); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-tx-1); border-radius: var(--r-1) var(--r-1) 0 0; }
.tab.on { background: var(--surface); color: var(--text); font-weight: var(--fw-fort);
  box-shadow: inset 0 -2px 0 var(--color-primary); }

/* ---------- Toast ---------- */
.toast { position: fixed; bottom: 20px; right: 20px; background: var(--text); color: var(--surface);
  padding: 9px 16px; border-radius: var(--r-2); box-shadow: var(--anneau), var(--sh-3); z-index: 50; }

/* ---------- Surfaces encre — hero de vitrine, bandeaux, pied de page ---------- */
.encre { background: var(--nav); color: var(--nav-ink); }
.encre .chapo, .encre .eyebrow { color: var(--nav-ink-2); }
.encre a { color: color-mix(in srgb, var(--seed-signal) 55%, var(--nav-ink)); }

/* ---------- Divers ---------- */
.hr { height: 1px; background: var(--border); border: 0; margin: var(--sp-4) 0; }
.empty { text-align: center; padding: var(--sp-6); color: var(--text-muted); }
.kbd { font-family: var(--ff-mono); font-size: var(--fs-2); background: var(--surface-muted);
  border: 1px solid var(--border-field); border-bottom-width: 2px; border-radius: 5px; padding: 1px 6px; }

/* ---------- Accessibilité — contrat commun, gelé pour toutes les marques ----------
   Ne jamais retirer dans un thème : c'est la partie structurelle du système. */
/* Anneau de focus : --focus (primary, ou signal sur surface d'encre). Reste un
   OUTLINE : box-shadow n'est pas rendu en contraste forcé Windows (parc CEE/OPCO)
   et il est rogné par .table-wrap / .rail{overflow} / .facette-panel. */
.rail, .encre, .cta, .barre-masse { --focus: var(--color-signal); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.btn:focus-visible, .tab:focus-visible, a:focus-visible { border-radius: var(--r-1); }

/* Lien d'évitement : premier enfant du <body>, invisible jusqu'au focus clavier. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--text); padding: 10px 16px;
  border: 1px solid var(--border-field); border-radius: 0 0 var(--r-2) 0; box-shadow: var(--sh-2); }
.skip-link:focus { left: 0; }

/* Utilitaire manquant du socle — le contrat aria-busy en dépend (LOT 4). */
.visuellement-cache { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* L'accent chaud ne porte JAMAIS du corps de texte. Ces classes bornent son usage.
   .on-signal est l'APLAT de marque (CTA) ; .txt-accent est la couleur de TEXTE. */
.on-accent { background: var(--color-accent); color: var(--on-accent); border-color: var(--contour-accent); }
.on-signal { background: var(--color-signal); color: var(--on-signal); border-color: var(--contour-signal); }
.txt-accent { color: var(--color-accent); font-weight: var(--fw-fort); }

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

/* ---------- Mobile ≤640px — TOUJOURS « screen and » : un max-width nu se déclenche en A4 ---------- */
@media screen and (max-width: 640px) {
  .zone { padding: var(--sp-3); }
  .row, .card-head { flex-wrap: wrap; }
  .grid { grid-template-columns: 1fr !important; }
  .stats { gap: var(--sp-2); }
  .stat { flex: 1 1 calc(50% - var(--sp-2)); min-width: 0; }
  .stat b, .kpi-val { font-size: var(--fs-7); }
  .hero-titre { font-size: var(--fs-8); letter-spacing: var(--ls-tx-3); }
  .titre-section { font-size: var(--fs-8); letter-spacing: var(--ls-cd-3); }
  .tabs { overflow-x: auto; scrollbar-width: none; }
  .tab { white-space: nowrap; }
  .toast { left: 12px; right: 12px; bottom: 12px; text-align: center; }
  /* <16px sur un champ = zoom automatique iOS au focus */
  .field, input:not([type=checkbox]):not([type=radio]), select, textarea { font-size: var(--fs-5); }
}

/* ---------- Tactile (pointeur grossier) — cibles ~44px, le desktop ne change pas ---------- */
@media screen and (pointer: coarse) {
  .btn { padding: 11px 16px; }
  .btn-sm { padding: 9px 13px; }
  .btn-icon { padding: 10px 12px; }
  .tab { padding: 11px 16px; }
  .field, input:not([type=checkbox]):not([type=radio]), select, textarea { padding: 10px 12px; }
  table.ui td { padding: 13px 12px; }
  /* Le padding seul ne garantit rien : un contrôle court reste sous la cible.
     min-height est le filet, et il vaut pour TOUT contrôle, socle ou couche au-dessus. */
  .btn, .btn-sm, .btn-icon, .tab, .field, select, summary,
  button:not(.chip-f button) { min-height: 44px; }
  label.check { min-height: 44px; align-items: center; }
  label.check input[type=checkbox], label.check input[type=radio] { width: 20px; height: 20px; }
}
