/* ============================================================================
   CHASSIS AMORCE v1 — 21/09/2026
   Porté depuis la dissection de 20 sites Beaucoup Studio (CATALOGUE-13).
   À coller dans le <style> de toute maquette, AVANT la DA du projet.
   Rien ici n'est décoratif : c'est la plomberie qu'on n'écrit plus jamais.
   ========================================================================== */

/* --- 1. L'ÉCHELLE — UNE SEULE SOURCE, ET ELLE RESPECTE L'UTILISATEUR ------

   CORRIGÉ LE 21/09/2026. La version précédente de ce fichier posait
       html { font-size: calc((100vw / var(--size)) * 10) }
   C'est l'échec WCAG F94, vérifié mot pour mot sur w3.org :
       « Attempts to use browser controls to zoom or adjust text-size
         will not work. »
   Démonstration : à 200 % de zoom le viewport CSS est divisé par deux, donc
   100vw aussi, donc la racine aussi — et le rendu final est RIGOUREUSEMENT
   identique. Le zoom ne fait rien. Le réglage « taille du texte » du
   navigateur non plus, puisqu'on écrase la racine.
   Beaucoup et Obys sont tous les deux dans ce cas. On ne les copie pas là.

   La forme ci-dessous garde tout ce qu'on voulait — UNE source de vérité,
   fluide, sans media query typographique — et rend le zoom au visiteur :
   les deux bornes sont en rem, et le rem POSÉ SUR LA RACINE se résout
   contre la taille par défaut du navigateur. Si le visiteur la met à 24px,
   plancher et plafond montent avec lui.

   Le terme du milieu donne la fluidité, les bornes donnent la sécurité.
   UN SEUL clamp() dans tout le projet, ici. Jamais un par composant :
   c'est ça, le vrai défaut (941 clamp() en dur mesurés sur notre parc le
   21/09, contre 15 chez un Site of the Day).

   HONNÊTETÉ SUR CE QUE ÇA VAUT. Cette forme ne rend pas exactement 200 %
   de grossissement : elle rend x1,80 quand le visiteur double sa taille par
   défaut, et le reste s'obtient en zoomant un cran de plus. Ce n'est donc
   pas une conformité parfaite à 1.4.4 — c'est la sortie de F94, qui rendait
   x1,00, c'est-à-dire rien du tout. Pour un site où l'accessibilité est
   contractuelle (public, santé, senior), prendre la version rem pure
   (font-size: 100%) et porter la fluidité sur la grille uniquement.        */

/* « Texte plus grand » (Paramètres de l'espace) : un multiplicateur sur la SEULE définition de la racine, pas une seconde. */
html.texte-grand { --echelle-texte: 1.125; }

html {
  /* ~15px au téléphone → 17,6px à 1440 → 20px plafond. Recalé si besoin,
     mais UNE fois, et jamais dupliqué ailleurs dans le projet.

     Le dosage n'est pas libre : la part en vw DILUE la préférence de
     l'utilisateur, parce qu'elle ne bouge pas quand il change sa taille
     par défaut. Mesuré à 1440px, réglage navigateur passé de 16 à 32px :
         0.60vw -> le texte ne fait que x1,58
         0.35vw -> x1,73
         0.25vw -> x1,80   <- retenu
         0    vw -> x2,00  (rem pur, mais plus aucune fluidité)
     Ne pas monter la part en vw sans savoir ce qu'on abandonne.            */
  font-size: calc(clamp(0.9rem, 0.875rem + 0.25vw, 1.25rem) * var(--echelle-texte, 1));
  line-height: 1.3;
  max-width: 2560px;
  margin-inline: auto;
  overscroll-behavior: none;
}

/* --size reste disponible pour la GRILLE et les espacements (voir §3), où
   la proportionnalité au viewport ne pose aucun problème d'accessibilité :
   F94 ne concerne que le texte.                                            */
:root { --size: 375 }
@media (min-width:  768px) { :root { --size: 768  } }
@media (min-width: 1024px) { :root { --size: 1024 } }
@media (min-width: 1200px) { :root { --size: 1440 } }
@media (min-width: 1920px) { :root { --size: 1920 } }  /* cap ultra-large */

/* --- 2. VIEWPORT CORRIGÉ ---------------------------------------------------
   --vw exclut la barre de défilement, --vh est figé au chargement pour que
   la barre d'adresse mobile ne fasse plus sauter les hauteurs.
   Les deux sont écrasés par chassis.js.                                     */

:root {
  --vw: 100vw;
  --vh: calc(var(--vh-initial, 1vh) * 100);
}
.h-screen { height: var(--vh) }

/* --- 3. GRILLE 24 COLONNES (6 en mobile) ----------------------------------
   24 et pas 12 : c'est ce qui autorise les départs asymétriques
   (col 6 → 23, col 9 → 18) et donne une page COMPOSÉE, pas centrée.        */

:root {
  --gap: 1rem;
  --pad: 2rem;                                        /* marge de page */
  --column:        calc(var(--vw)/24 - var(--pad)*2/24 - var(--gap)*23/24);
  --column-mobile: calc(var(--vw)/6  - var(--pad)*2/6  - var(--gap)*5/6);
}
.grid-w {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--gap);
  padding-inline: var(--pad);
}
@media (min-width: 1024px) { .grid-w { grid-template-columns: repeat(24, 1fr) } }
.col-full { grid-column: 1 / -1 }

/* Placement de colonne SÛR : pleine largeur en mobile, placé en desktop.
   Un `grid-column:6/19` écrit en dur crée des colonnes implicites sur la
   grille mobile à 6 colonnes et fait exploser la mise en page — vérifié.
   Usage :  <p class="col" style="--cs:6; --ce:19">…</p>                    */
.col { grid-column: 1 / -1 }
@media (min-width: 1024px) { .col { grid-column: var(--cs, 1) / var(--ce, -1) } }

/* La colonne sert aussi d'unité de respiration verticale : le rythme
   horizontal et vertical deviennent le même nombre.                        */
.breathe    { padding-block: calc(var(--column) + 1rem) }
.breathe-lg { padding-block: calc(var(--column) * 2) }

/* --- 4. L'ÉCHELLE TYPO — UN TOKEN PAR RÔLE, PAS UNE TAILLE PAR BLOC -------

   REFAIT LE 21/09/2026, après contre-épreuve. L'ancienne version était une
   échelle de 15 paliers nommés en pixels (.t-10 … .t-180) calée sur
   1rem = 10px. Deux raisons de l'abandonner :

   1. La racine n'est plus en vw pur (voir §1), donc 1rem ne vaut plus 10px.
   2. Surtout : un palier n'est pas un rôle. Quinze paliers disponibles, ça
      veut dire quinze occasions d'en inventer un seizième. Mesure du 21/09
      sur notre parc : intent-amorce déclare .52rem, .53rem, .54rem ET
      .55rem — quatre valeurs qu'aucun œil ne distingue, et que personne n'a
      choisies. 941 valeurs écrites en dur au total, pour 13 à 42 tokens.
      Un Site of the Day mesuré le même jour : 316 tokens, 1449 usages.

   LA RÈGLE : un token par RÔLE DE CONTENU, décidé UNE FOIS au CONCEPT.md,
   puis gelé. Le nombre se compte, il ne se devine pas — on liste les rôles
   réels du projet (titre, accroche, corps, prix, mention, étiquette…) et
   on a autant de tokens que de rôles. Plus de tokens que de rôles = des
   doublons à fusionner. Moins = du contenu qu'on écrase.
       vitrine / portfolio  : 5 à 6 rôles, ratio large   1.333 à 1.618
       site commercial dense: 7 à 10 rôles, ratio serré  1.125 à 1.25
   Repères vérifiés : GOV.UK 6-7 tailles, Material 3 15 rôles.

   PAS DE clamp() ICI. La racine est déjà fluide, donc tout ce qui est en
   rem suit déjà. Remettre du vw à l'étage de l'élément, c'est justement
   redonner à chaque texte sa propre vitesse de croissance — le défaut qui
   fait varier le rapport titre/texte de 166 %% sur rougebarre.

   POUR LE MOBILE : on ne change pas la taille, on change de PALIER.
       <h1 class="fs-h1 lg:fs-display-xl">
   C'est ce que fait Beaucoup (heading-60 md:heading-64 lg:heading-128) :
   une seule échelle, un cran différent selon la largeur.
   ========================================================================= */

:root {
  /* ratio 1.333 (vitrine). Site commercial dense : repasser en 1.25. */
  --fs-label      : 0.75rem;
  --fs-small      : 0.8rem;
  --fs-body       : 1.0rem;
  --fs-lead       : 1.333rem;
  --fs-h3         : 1.777rem;
  --fs-h2         : 2.369rem;
  --fs-h1         : 3.157rem;
  --fs-display    : 4.209rem;
  --fs-display-xl : 5.61rem;
}

/* Les classes portent AUSSI la loi de tracking : le resserrement croît
   avec la taille, et s'inverse en positif sur les petites capitales.   */
.fs-label{font-size:var(--fs-label);letter-spacing:0.06em;line-height:1.2;text-transform:uppercase;}
.fs-small{font-size:var(--fs-small);letter-spacing:0em;line-height:1.45;}
.fs-body{font-size:var(--fs-body);letter-spacing:-0.005em;line-height:1.55;}
.fs-lead{font-size:var(--fs-lead);letter-spacing:-0.012em;line-height:1.4;}
.fs-h3{font-size:var(--fs-h3);letter-spacing:-0.02em;line-height:1.25;}
.fs-h2{font-size:var(--fs-h2);letter-spacing:-0.028em;line-height:1.15;}
.fs-h1{font-size:var(--fs-h1);letter-spacing:-0.034em;line-height:1.05;}
.fs-display{font-size:var(--fs-display);letter-spacing:-0.04em;line-height:1.0;}
.fs-display-xl{font-size:var(--fs-display-xl);letter-spacing:-0.045em;line-height:0.95;}

@media (min-width: 768px) {
  .md\:fs-label{font-size:var(--fs-label);letter-spacing:0.06em;line-height:1.2;text-transform:uppercase;}
  .md\:fs-small{font-size:var(--fs-small);letter-spacing:0em;line-height:1.45;}
  .md\:fs-body{font-size:var(--fs-body);letter-spacing:-0.005em;line-height:1.55;}
  .md\:fs-lead{font-size:var(--fs-lead);letter-spacing:-0.012em;line-height:1.4;}
  .md\:fs-h3{font-size:var(--fs-h3);letter-spacing:-0.02em;line-height:1.25;}
  .md\:fs-h2{font-size:var(--fs-h2);letter-spacing:-0.028em;line-height:1.15;}
  .md\:fs-h1{font-size:var(--fs-h1);letter-spacing:-0.034em;line-height:1.05;}
  .md\:fs-display{font-size:var(--fs-display);letter-spacing:-0.04em;line-height:1.0;}
  .md\:fs-display-xl{font-size:var(--fs-display-xl);letter-spacing:-0.045em;line-height:0.95;}
}
@media (min-width: 1024px) {
  .lg\:fs-label{font-size:var(--fs-label);letter-spacing:0.06em;line-height:1.2;text-transform:uppercase;}
  .lg\:fs-small{font-size:var(--fs-small);letter-spacing:0em;line-height:1.45;}
  .lg\:fs-body{font-size:var(--fs-body);letter-spacing:-0.005em;line-height:1.55;}
  .lg\:fs-lead{font-size:var(--fs-lead);letter-spacing:-0.012em;line-height:1.4;}
  .lg\:fs-h3{font-size:var(--fs-h3);letter-spacing:-0.02em;line-height:1.25;}
  .lg\:fs-h2{font-size:var(--fs-h2);letter-spacing:-0.028em;line-height:1.15;}
  .lg\:fs-h1{font-size:var(--fs-h1);letter-spacing:-0.034em;line-height:1.05;}
  .lg\:fs-display{font-size:var(--fs-display);letter-spacing:-0.04em;line-height:1.0;}
  .lg\:fs-display-xl{font-size:var(--fs-display-xl);letter-spacing:-0.045em;line-height:0.95;}
}

/* Rendu réel de l'échelle, racine = 17,6px à 1440 :
     --fs-label         0.75rem  =    13.2px
     --fs-small          0.8rem  =    14.1px
     --fs-body           1.0rem  =    17.6px
     --fs-lead         1.333rem  =    23.5px
     --fs-h3           1.777rem  =    31.3px
     --fs-h2           2.369rem  =    41.7px
     --fs-h1           3.157rem  =    55.6px
     --fs-display      4.209rem  =    74.1px
     --fs-display-xl    5.61rem  =    98.7px
   Le rapport entre deux rôles voisins vaut 1,333 À TOUTE LARGEUR.    */


/* RETIRÉ LE 22/09/2026 — les classes .md:t-10 … .lg:t-180 vivaient encore ici.
   Elles étaient calées sur « 1rem = 10px », vrai seulement avec l'ancienne
   racine en vw pur, celle que la correction F94 a supprimée le 21/09. Depuis,
   la racine vaut 17,6px à 1440 : un .lg:t-40 rendait donc 4rem = 70px au lieu
   des 40px annoncés par son nom. Le §4 les déclarait abandonnées, le fichier
   les servait quand même. Utiliser les rôles fs-* ci-dessus.                 */

/* Règle de fond : un titre ne prend JAMAIS line-height 1.4. */

/* --- 5. COULEURS EN TRIPLETS RGB ------------------------------------------
   Nommées par ce qu'elles SONT, pas par leur rôle : c'est ce qui rend le
   reskin d'un projet à l'autre trivial. Le noir n'est jamais pur.
   À remplacer projet par projet, en gardant les noms de slot.              */

:root {
  --c-ink:    18 17 16;     /* jamais 0 0 0 */
  --c-paper: 247 246 243;   /* jamais 255 255 255 */
  --c-white: 255 255 255;
  --c-accent: 0 0 255;      /* UNE seule couleur franche */
  --c-muted: 140 136 130;
  --c-line:  225 222 216;
}
.ink    { color: rgb(var(--c-ink)) }
.on-ink { background: rgb(var(--c-ink)); color: rgb(var(--c-paper)) }
body    { background: rgb(var(--c-paper)); color: rgb(var(--c-ink)) }

/* --- 6. LES COURBES DE MARQUE ---------------------------------------------
   Cinq courbes, jamais une de plus, jamais un cubic-bezier inline.
   --e-momentum démarre sur un point de contrôle NÉGATIF : l'élément recule
   d'un cheveu avant de partir. C'est l'élan physique, perçu sans être vu.  */

:root {
  --e-out:      cubic-bezier(.23, 1, .32, 1);        /* le cheval de trait */
  --e-out-soft: cubic-bezier(.25, .46, .45, .9);
  --e-in-out:   cubic-bezier(.86, 0, .07, 1);
  --e-back:     cubic-bezier(.175, .885, .32, 1.275);
  --e-momentum: cubic-bezier(-.02, .55, .3, 1);      /* élan négatif */

  /* UNE durée dominante, deux variantes. Pas dix. */
  --d:      .4s;
  --d-slow: .8s;
  --d-fast: .2s;

  --control-h: 3.5rem;   /* hauteur unique de TOUS les boutons et labels */
  --radius:    1rem;
}

/* --- 7. Z-INDEX NOMMÉS — plus jamais de nombre magique -------------------- */
:root { --z-base: 1; --z-sticky: 10; --z-header: 15; --z-loader: 30; --z-modal: 40 }

/* --- 8. LE SOCLE DE REVEAL (piloté par data-reveal, voir chassis.js) ------ */
[data-reveal] {
  opacity: 0;
  transform: translateY(2.2rem);
  filter: blur(8px);
  transition:
    opacity   var(--d-slow) var(--e-out) var(--reveal-delay, 0s),
    transform var(--d-slow) var(--e-out) var(--reveal-delay, 0s),
    filter    var(--d-slow) var(--e-out) var(--reveal-delay, 0s);
  will-change: opacity, transform;
}
[data-reveal].is-in { opacity: 1; transform: none; filter: none }

/* --- 9. prefers-reduced-motion : OBLIGATOIRE ------------------------------
   Beaucoup ne le fait NULLE PART. C'est leur faute, pas un modèle.
   Interdit dur du socle Amorce : aucune livraison sans ce bloc.            */

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

/* --- 10. Accessibilité minimale non négociable ---------------------------- */
:focus-visible { outline: 2px solid rgb(var(--c-accent)); outline-offset: 3px }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
