/* =============================================================
   THÈME CHALEUREUX — echelle.css

   CE QU'UN THÈME DÉCIDE. Type, rythme, formes, mouvement, proportions.
   Extrait de `theme/tokens.css` §B le 17 août 2026 : ces valeurs y
   étaient annoncées « identiques sur tout le parc », ce qui a cessé
   d'être vrai le jour où un second thème est devenu du travail engagé.

   CE QU'UN THÈME NE DÉCIDE PAS, et qui reste dans le socle :
     · les huit bornes WCAG et les constantes de couleur ;
     · toute la dérivation `oklch()` et les huit rôles ;
     · les graines client (`--brand-l/c/h`, `--radius`, `--space-unit`) ;
     · les invariants de mécanisme : plans (z-index), `--blend-objet`,
       `--page-min-h`, position au repos du lien d'évitement.

   La couleur est hors de portée d'un thème, et c'est une contrainte
   MÉCANIQUE, pas une convention : `test/contrast.test.js` balaye tout le
   domaine de graines sur le socle. Une couleur écrite ici échapperait à
   la matrice, et la liste fermée des huit rôles se dédoublerait.
   `test/themes.test.js` refuse tout littéral de couleur dans ce fichier.

   Un thème décide COMBIEN de couleur il emploie — c'est `theme.css` qui
   le dit, en composant les rôles du socle. Jamais laquelle.
   ============================================================= */

:root{

  /* ---- ESPACEMENT ------------------------------------------
     Intra-groupe = 1×, inter-groupe = 3× (grammaire de densité).
     `--space-unit` est une GRAINE CLIENT et vit dans le socle : ce sont
     les rapports qui appartiennent au thème, pas l'unité. Un client à
     grande unité obtient une page plus aérée, aux mêmes proportions. */
  --space-1: var(--space-unit);
  --space-2: calc(var(--space-unit) * 1.5);
  --space-3: calc(var(--space-unit) * 2.5);
  --space-4: calc(var(--space-unit) * 4);
  --space-5: calc(var(--space-unit) * 6);

  /* Grammaire de densité — le seul rapport qui compte est celui que le
     lecteur VOIT. Entre deux sections il voit la SOMME des deux
     padding-block, jamais un seul : le token porte donc la moitié de
     l'écart voulu. Vérifié sur l'écart rendu par test/density.test.js —
     un ratio contrôlé sur le token est un ratio non contrôlé. */
  --rhythm-intra: var(--space-2);                  /* 1× — entre frères d'un groupe */
  --rhythm-group: calc(var(--rhythm-intra) * 3);   /* 3× — écart RENDU entre groupes */
  --section-pad:  calc(var(--rhythm-group) / 2);   /* posé des deux côtés            */

  /* ---- LES PILES DE POLICES ---------------------------------
     Descendues du socle le 18 août 2026. Le socle dit quelles familles
     existent (liste fermée, binaires, licences) ; le THÈME dit laquelle
     mène ; la PEAU du client peut l'écraser. Chaque pile finit sur un
     repli système, pour un rendu qui tient sans binaires.

     POIDS RÉEL de ce thème, MESURÉ (woff2, latin) :
       Bricolage Grotesque  700 ......... 21,9 Ko
       Hanken Grotesk       400–600 ..... 33,9 Ko   (variable, un fichier)
       IBM Plex Mono        400 ......... 14,4 Ko
       IBM Plex Mono        700 ......... 14,6 Ko
       TOTAL / page ..................... 84,7 Ko
     C'est la première dépense de poids, très au-dessus du JS, et le
     plafond de test/fonts.test.js est de 90 Ko PAR THÈME : c'est le
     visiteur d'une page qui paie, et il n'en charge qu'un.

     Les 84,7 Ko remplacent les « ~76 Ko » annoncés jusqu'ici, qui
     n'avaient jamais été mesurés. L'écart tient à deux erreurs de ce
     chiffre de brief : il comptait un fichier mono à 500, poids que ce
     thème ne demande NULLE PART, et ignorait que le second fichier mono
     est demandé. Il coûte 14,6 Ko à lui seul.

     Le mono est demandé à TROIS poids, et le fichier 700 sert les deux
     plus hauts : 400 hérité du corps, 600 à l'enseigne, 700 au « + » de
     la FAQ. C'est le 600 qui pèse — l'enseigne est sur toutes les pages,
     404 comprise, là où la FAQ n'est que sur l'accueil ; et 600 n'étant
     pas déposé pour cette famille, la sélection CSS monte au 700.
     Mesuré, pas déduit : `document.fonts` donne « IBM Plex Mono 700 →
     loaded » sur toutes les pages du parc, dans les DEUX thèmes. */
  --font-title: "Bricolage Grotesque", "Schibsted Grotesk", "Space Grotesk",
                system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-text:  "Hanken Grotesk", "Public Sans", "Figtree", "Instrument Sans",
                system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono:  "IBM Plex Mono", "Spline Sans Mono", "JetBrains Mono",
                ui-monospace, "SFMono-Regular", "Menlo", monospace;

  /* ---- TYPE -------------------------------------------------
     Échelle modulaire, plancher 17px mobile. */
  --text-xs:   0.8125rem;
  --text-sm:   0.9375rem;
  --text-base: 1.0625rem;
  --text-lg:   1.25rem;
  --text-xl:   1.6rem;
  --text-2xl:  clamp(1.9rem, 1.2rem + 3vw, 2.6rem);
  --text-3xl:  clamp(2.4rem, 1.4rem + 5vw, 4rem);
  --lh-tight:  1.08;
  --lh-snug:   1.28;
  --lh-base:   1.55;
  --tracking-title: -0.01em;
  --tracking-mono:  0.02em;

  /* ---- FORMES -----------------------------------------------
     `--radius` est une graine client ; ce qui en dérive est du thème. */
  --radius-sm: calc(var(--radius) * 0.5);
  --radius-pill: 999px;

  /* Graisses */
  --weight-regular: 400;
  --weight-medium:  600;
  --weight-bold:    700;

  /* Traits & focus */
  --hair: 1px;
  --hair-2: calc(var(--hair) * 2);   /* filet porteur de sens : porte, .point */
  --hair-3: calc(var(--hair) * 3);   /* filet de mise en avant : .feature__lock */
  --focus-ring-w: 3px;
  --underline-offset: 0.18em;

  /* Opacités nommées — une opacité anonyme est une décision de design
     qu'aucune peau ne peut reprendre. */
  --opacity-sep:  0.5;    /* séparateur « · » de l'enseigne          */
  --opacity-mono: 0.8;    /* surtitre mono sur aplat plein           */

  /* ---- PROPORTIONS DE COMPOSANTS ---------------------------- */
  --tap-target: calc(var(--space-unit) * 5.5);   /* cible tactile minimale */
  --press-y: 1px;                                 /* enfoncement au clic    */
  --badge-pad-y: calc(var(--space-1) * 0.5);
  --rotate-open: 45deg;                           /* « + » de la FAQ → « × » */
  /* Opacité du fond d'en-tête. Le flou fait le travail de lisibilité ;
     l'opacité ne dose que ce qu'on devine du contenu qui passe dessous.
     75 % a été essayé et écarté : les fantômes redevenaient perceptibles.
     Le verre dépoli est une signature de CE thème — un thème sobre poserait
     un en-tête plat, et c'est exactement ce que ce fichier rend possible. */
  --header-tint: 88%;
  --header-saturate: 1.1;
  --header-blur: 10px;                            /* verre dépoli de l'en-tête */
  --hero-split: 1.05fr 0.95fr;                    /* asymétrie du hero       */
  --feature-split: 0.9fr 1.1fr;                   /* asymétrie de la bande texte */

  /* Icônes — tracé seul, jamais de remplissage. La taille suit la
     typographie (em) pour qu'un client à grande échelle ait de grandes
     icônes sans qu'on y touche. Les douze TRACÉS, eux, sont partagés :
     ils vivent dans theme/icons.mjs et n'appartiennent à aucun thème. */
  --icon-size: 1.5em;
  --icon-stroke: 1.75;
  --icon-size-lg: 2.25rem;

  /* Formats de média */
  --ratio-media: 16/10;
  --ratio-media-tall: 4/5;
  --ratio-media-wide: 21/9;
  --dot-size: 0.7em;

  /* Marge du média « objet » — le visuel détouré respire dans son cadre.
     La FUSION elle-même (`--blend-objet: multiply`) reste dans le socle :
     c'est un mécanisme dont l'exactitude est mesurée sur tout le domaine
     de graines par test/media.test.js, pas un choix d'allure. */
  --media-objet-pad: var(--space-4);

  /* ---- MOUVEMENT --------------------------------------------
     « Micro-animations sobres » est la définition de ce thème (doc 04
     §4.2). Un thème qui n'anime pas met ces durées à zéro ; il n'a pas à
     retirer les transitions une par une dans son exécution. */
  --dur-fast: 150ms;
  --dur: 250ms;
  --dur-slow: 400ms;
  --dur-breath: 3200ms;
  --ease: cubic-bezier(0.2, 0.6, 0.2, 1);
  --reveal-y: 8px;
  --zoom: 1.03;

  /* ---- MESURE ----------------------------------------------- */
  --measure: 62ch;
  --page-max: 72rem;

  /* ---- DÉGAGEMENTS DES BARRES -------------------------------
     Deux majorations, et elles appartiennent au thème parce qu'elles
     majorent des hauteurs que SON échelle produit.

     Dégagement au-dessus d'une ancre. La barre est collante : sans lui,
     TOUTE ancre — y compris le lien d'évitement — dépose sa cible SOUS la
     barre, qui la recouvre. L'ancre a bien fonctionné, et le visiteur voit
     autre chose que ce qu'il visait.

     Ce n'est PAS la hauteur de la barre recopiée : §3 interdit cette
     seconde source de vérité, et Barre.astro documente déjà le refus. La
     hauteur réelle varie avec les graines du client et avec le retour à la
     ligne de l'enseigne en mobile — elle n'est pas connaissable ici.

     C'est une MAJORATION, volontairement large : trop de dégagement laisse
     de l'air au-dessus de la cible, trop peu la cache. Les deux ne se
     valent pas. `npm run shots` mesure la barre RENDUE aux deux largeurs
     et refuse le build si ce dégagement passe dessous — l'estimation est
     posée ici, la garantie est prise au navigateur. */
  --ancre-degagement: 8rem;

  /* Dégagement SOUS le contenu, pour l'appel à l'action ancré en bas d'écran
     (`.cta-bas`, sous 52 rem uniquement). Il est porté par le PIED DE PAGE, en
     utilitaire, et seulement là où la barre est rendue : posé sur `body`, il
     laissait une couture de surface claire entre le pied et la barre, et 8 rem
     de vide sous le pied de la 404, qu'aucune barre ne justifiait.

     Même refus que ci-dessus : ce n'est pas la hauteur de la barre recopiée.
     Elle dépend du libellé du client — donc de la rédaction — et de
     `--tap-target`, qui suit `--space-unit`, une GRAINE. Un rem ne peut pas
     suivre ça.

     Posé à 6rem au premier jet, et REFUSÉ par la mesure : 97 px rendus contre
     96, sur la peau Boréal et sur elle seule. Une majoration à un pixel près
     n'est pas une majoration. `npm run shots` mesure le RECOUVREMENT réel
     entre la barre et la fin du pied, aux deux largeurs, et refuse le build. */
  --cta-degagement: 8rem;

}

/* Le mouvement se coupe à la demande du système d'exploitation. Le bloc vit
   ICI parce qu'il pose des VALEURS de durée, et que les durées sont du thème :
   laissé dans le socle, il aurait redéclaré des tokens qu'il ne possède pas.
   `tools/shots.mjs` émule cette préférence pour geler le point de l'enseigne
   avant toute comparaison pixel à pixel. */
@media (prefers-reduced-motion: reduce){
  :root{ --dur-fast: 0ms; --dur: 0ms; --dur-slow: 0ms; --dur-breath: 0ms; }
}
