/* ============================================================
   heleneguillon.fr — feuille de style principale
   One-page, 12 sections. Mobile-first (cf. specs/spec-responsive.md).
   Toutes les valeurs proviennent de design-tokens.css.
   ============================================================ */

/* ---------- 0. Reset & bases ---------- */

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-serif);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol {
  margin: 0;
}
ul, ol { padding: 0; list-style: none; }
img, svg, picture { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: var(--space-2);
  top: calc(var(--space-2) * -6);
  z-index: 100;
  padding: var(--space-1) var(--space-3);
  background: var(--btn-bg);
  color: var(--btn-text);
  font-size: var(--fs-ui);
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--space-2); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- 1. Primitives de mise en page ---------- */

.section {
  padding-block: var(--section-padding);
}

.container {
  width: 100%;
  max-width: calc(var(--content-max) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide {
  max-width: calc(var(--wide-max) + 2 * var(--gutter));
}
/* Bloc bordé par les seules gouttières (bannière 05) : la maquette
   la montre encadrée, la spec la veut pleine largeur — les gouttières
   réconcilient les deux. */
.container--bleed {
  max-width: none;
}

/* Grille « texte + illustration » des sections 02, 03, 04, 06.
   Mobile : une colonne, texte d'abord (ordre DOM = ordre de lecture).
   Desktop : deux colonnes, l'ordre visuel étant piloté par les
   modificateurs --media-first / --media-last (le DOM ne bouge pas). */
.split {
  display: grid;
  gap: clamp(var(--space-4), 5vw, var(--space-8));
  align-items: center;
}

.split__text > * + * { margin-top: var(--space-3); }

.split__media { align-self: center; }

/* ---------- 2. Typographie ---------- */

.h1,
.h2 {
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}

.h1 { font-size: var(--fs-hero); }
.h2 { font-size: var(--fs-h2); color: var(--color-vermillon); }
.h2--navy { color: var(--text); }

.h3 {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
}

/* Accent italique Light — « force de vente », « bonne cible »… */
.accent {
  font-style: italic;
  font-weight: var(--fw-light);
}

.lead {
  font-size: var(--fs-lead);
  font-weight: var(--fw-regular);
  line-height: var(--lh-snug);
  text-wrap: pretty;
}

.body > * + * { margin-top: var(--space-3); }
.body p { text-wrap: pretty; }

.eyebrow {
  font-size: var(--fs-h2);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  color: var(--color-vermillon);
  text-align: center;
}

.text-vermillon { color: var(--color-vermillon); }
.text-navy { color: var(--color-navy); }

/* ---------- Curseur suiveur « Smoooth » — corrélé au logo (ÉCART DESIGN-BAR) -
   DESIGN-BAR exclut les curseurs custom → écart explicite d'Hélène. Principe
   Smoooth Studio : un POINT précis (marine) à la position exacte + l'ÉTOILE du
   logo {g} (marine) qui TRAÎNE avec inertie (lerp/rAF, dans main.js) et GROSSIT
   sur les clickables. Corrélation au logotype = la même étoile 4 branches.
   Armé par main.js (.has-cursor) UNIQUEMENT sur pointeur fin, hors mouvement
   réduit et hors contraste renforcé → sinon curseur natif (aucun masquage, donc
   robuste sans JS). Position:fixed → CLS 0 ; le rAF s'arrête au repos. */
.has-cursor, .has-cursor * { cursor: none; }
.cursor-dot, .cursor-star {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 9998;
  opacity: 0;
  will-change: transform, opacity;
  transition: opacity var(--dur) var(--ease);   /* fondu seul ; le transform est piloté par rAF */
}
.cursor-dot {
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;                     /* centré sur le point exact */
  border-radius: var(--radius-pill);
  background: var(--color-navy);
}
.cursor-star {
  /* Boîte rendue à la taille MAX (40px) : l'étoile n'est jamais agrandie
     au-delà de sa rastérisation native → pas de flou au survol. Le JS ne fait
     que RÉDUIRE (repos ~0,55 → survol 1,0). */
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;                        /* centré */
}
.cursor-star svg { display: block; width: 100%; height: 100%; }
.cursor-star svg path { fill: var(--color-navy); }
.measure { max-width: 34ch; }
.measure-wide { max-width: 46ch; }

/* ---------- 3. Boutons & puces ---------- */

/* Les angles vifs et l'aplat marine viennent de l'artboard : ils ne bougent
   pas. Le relief est cherché dans le mouvement, pas dans la forme — d'où
   l'absence de rayon, d'ombre portée et de dégradé.
   Le filet marine est invisible sur le fond marine ; il ne sert qu'à donner
   une arête au bouton quand il s'inverse au survol.
   Trois registres commutables par la classe de <body> :
   .btn-fill (par défaut) · .btn-rule · .btn-arrow */
/* La bordure est TOUJOURS présente, jamais retirée : au repos elle est
   marine sur fond marine, donc invisible. C'est elle qui tient le bouton
   quand il devient crème sur une section claire — sans elle il se
   confondrait avec la page. On ne change que sa COULEUR : la supprimer au
   survol ferait rétrécir le bouton de 2px sous le curseur. */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  background: var(--btn-bg);
  color: var(--btn-text);
  border: var(--hairline) solid var(--btn-bg);
  border-radius: 0;               /* la maquette est à angles vifs */
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }

/* Repli universel : si aucune variante n'est active, on garde un survol
   (assombrissement), jamais un bouton mort. Sous `hover: hover` comme les
   autres — sinon il resterait collé après un tap au doigt. */
@media (hover: hover) {
  .btn:hover { background-color: var(--color-navy-700); }
}

/* --- A · bascule franche en crème (défaut) ---
   Pas de pseudo-élément, pas de balayage : une simple bascule de couleurs
   en 160ms, qui se lit comme instantanée sans être sèche. L'ancien
   remplissage animé laissait forcément un liseré marine — `inset: 0` sur
   un pseudo-élément couvre la boîte de padding, pas celle de bordure.
   Crème sur marine 13,7:1 au repos, marine sur crème 13,7:1 au survol :
   les deux états sont AAA. */

/* --- B · un filet crème se trace sous le libellé --- */
.btn-rule .btn::after {
  content: "";
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: calc(var(--space-2) - 2px);
  height: var(--hairline);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--dur) var(--ease);
}
/* --- C · un chevron avance --- */
.btn-arrow .btn::after {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-right: var(--hairline) solid currentColor;
  border-bottom: var(--hairline) solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--dur) var(--ease);
}

/* État actif de la variante A. Le focus clavier est déclaré HORS de la
   requête `hover` : un clavier ne survole pas, et l'état doit exister même
   sur un appareil tactile branché à un clavier. */
.btn-fill .btn:focus-visible {
  background-color: var(--color-cream);
  color: var(--color-navy);
  border-color: var(--btn-bg);
}
/* Sur les surfaces vermillon, le bouton crème se détache tout seul : on
   efface le filet pour qu'aucun liseré marine ne subsiste. Sur les
   sections claires il reste, sinon le bouton disparaîtrait dans la page. */
.btn-fill .hero .btn:focus-visible,
.btn-fill .manifesto__block .btn:focus-visible,
.btn-fill .header:not(.is-scrolled) .btn:focus-visible {
  border-color: var(--color-cream);
}

/* Le survol, lui, ne s'applique qu'aux pointeurs fins : au doigt, un état
   « survolé » resterait collé après le tap. */
@media (hover: hover) {
  .btn-fill .btn:hover {
    background-color: var(--color-cream);
    color: var(--color-navy);
    border-color: var(--btn-bg);
  }
  .btn-fill .hero .btn:hover,
  .btn-fill .manifesto__block .btn:hover,
  .btn-fill .header:not(.is-scrolled) .btn:hover {
    border-color: var(--color-cream);
  }

  .btn-rule .btn:hover { background-color: var(--color-navy-700); }
  .btn-rule .btn:hover::after,
  .btn-rule .btn:focus-visible::after { transform: scaleX(1); }

  .btn-arrow .btn:hover { background-color: var(--color-navy-700); }
  .btn-arrow .btn:hover::after,
  .btn-arrow .btn:focus-visible::after {
    transform: rotate(-45deg) translate(0.15rem, 0.15rem);
  }
}

/* --- CTA de prise de RDV : un chevron qui avance (additif à btn-fill) ---
   Affordance directionnelle « on avance / on réserve » sur les seuls CTA
   `data-booking`, indépendamment de la variante de <body>. Reprend la
   mécanique de .btn-arrow, en SURCOUCHE : l'inversion crème de btn-fill est
   conservée. `currentColor` → le chevron s'inverse avec le libellé. Aucune
   ombre ni rebond : un simple filet chevron qui glisse. Le bouton étant en
   inline-flex avec `gap`, le chevron se pose après le libellé, espacé. */
.btn[data-booking]::after {
  content: "";
  flex: none;
  width: 0.4rem;
  height: 0.4rem;
  border-right: var(--hairline) solid currentColor;
  border-bottom: var(--hairline) solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--dur) var(--ease);
}
.btn[data-booking]:focus-visible::after {
  transform: rotate(-45deg) translate(0.15rem, 0.15rem);
}
@media (hover: hover) {
  .btn[data-booking]:hover::after {
    transform: rotate(-45deg) translate(0.15rem, 0.15rem);
  }
}

/* CTA pleine largeur sous 480px (spec §2) */
.btn--block { width: 100%; }

.btn--compact {
  padding: var(--space-1) var(--space-3);
  font-size: var(--fs-ui);
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
/* Les puces ne sont PAS interactives (des <li>, pas des liens) : aucun
   effet de survol ne leur est appliqué — il suggérerait une affordance
   qui n'existe pas. Leur présence est gagnée à l'entrée, pas au survol.

   ⚠️ Point de contraste le plus faible du site : vermillon sur crème =
   3,15:1, pour un corps d'environ 16px. Le seuil AA du texte courant est
   4,5:1 — ce couple ne l'atteint pas, et aucune graisse n'y changera rien
   (la graisse ne modifie pas le ratio, seulement la perception). Le token
   --color-vermillon-deep (4,81:1) existe et corrigerait le point ; il est
   déjà employé pour le petit texte du pied de page. Non appliqué ici :
   ce serait un écart visible à la maquette, à trancher. */
.pill {
  position: relative;
  z-index: 0;                       /* contexte d'empilement pour le ::before */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-1) var(--space-2);
  color: var(--color-vermillon);
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  line-height: 1.2;
}
/* Le fond de la puce est porté par un ::before : au survol on ne grossit QUE
   LUI, jamais le texte → aucune distorsion (scaler un texte le rasterise et le
   ramollit). Le badge « gonfle » derrière un texte qui reste net.
   ⚠️ Écart à la décision « aucun hover sur les puces » (des <li>, pas des
   liens) : PAS de cursor:pointer — le curseur custom reste le petit point. */
.pill::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-cream);
  border-radius: var(--radius-pill);
  transition: transform var(--dur) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .pill:hover::before { transform: scale(1.06); }
}

/* ---------- 4. Illustrations ---------- */

.illus {
  position: relative;
  overflow: hidden;
  background: var(--color-vermillon);   /* fond intégré aux visuels */
}
/* <picture> est un conteneur inline sans hauteur propre : il doit
   relayer la hauteur du cadre, sinon `height: 100%` sur l'img retombe
   sur auto et l'illustration ne remplit pas la section. */
.illus picture,
.hero__media picture {
  display: block;
  width: 100%;
  height: 100%;
}
.illus img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;             /* sujets centrés dans les masters 16:9 */
}

/* Cadre carré des sections 02/03/04/06 (maquette : carré ~640px) */
.illus--square {
  aspect-ratio: 1 / 1;
}

/* Parallaxe des cadres : le `scale(1.06)` n'existe que lorsque main.js a
   activé l'effet (classe posée hors mouvement réduit / pointeur fin) —
   sinon l'illustration garde son cadrage exact, sans zoom parasite. Le
   `.illus` porte déjà `overflow: hidden`, qui borne le glissement.
   `--px/--py` sont pilotés par main.js ; 0 au repos. */
.parallax-on .illus--square img {
  transform: scale(1.06) translate(var(--px, 0), var(--py, 0));
  will-change: transform;
}
/* Parallaxe au SCROLL (mobile / tactile, sans curseur) : même échelle, mais
   glissement VERTICAL piloté par main.js selon la position dans le viewport
   (--py). Préserve l'effet là où il n'y a pas de survol. */
.parallax-scroll .illus--square img {
  transform: scale(1.06) translate(0, var(--py, 0));
  will-change: transform;
}

/* ---------- 5. Header ---------- */

.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: center;
  min-height: var(--header-h);
  transition: background var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.header__logo {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                     /* cible tactile */
  color: var(--color-cream);            /* sur le hero vermillon */
  transition: color var(--dur) var(--ease);
}
.header__logo svg {
  width: clamp(2.25rem, 4vw, 3.25rem);
  height: auto;
  transition: transform var(--dur) var(--ease);
}
/* Même signature que le logo footer : léger agrandissement + l'étoile qui
   pivote sur son propre centre (fill-box). Focus clavier hors requête hover
   (parité) ; survol pointeur gaté @media (hover:hover). */
.header__logo .logo-star {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease);
}
.header__logo:focus-visible svg { transform: scale(1.06); }
.header__logo:focus-visible .logo-star { transform: rotate(180deg); }
@media (hover: hover) {
  .header__logo:hover svg { transform: scale(1.06); }
  .header__logo:hover .logo-star { transform: rotate(180deg); }
}

/* Au scroll : fond crème, logo vermillon (le CTA reste marine) */
.header.is-scrolled {
  background: color-mix(in srgb, var(--color-cream) 92%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 1px 0 var(--color-border);
}
.header.is-scrolled .header__logo { color: var(--color-vermillon); }

/* ---------- 6. Section 01 — Hero ----------

   Le visuel est l'artboard 01 complet. Relevé sur le fichier (balayage
   par bandes de 10 % de hauteur), la zone d'aplat vermillon uni s'étend
   depuis le bord gauche jusqu'à :
     y 0-10 % → 44 %   ·   y 20-30 % → 37,8 %  (le plus étroit)
     y 40-50 % → 45,8 %  ·  y 60-70 % → 58,9 %
     y 80-100 % → 11 % puis 5,5 %  (les roses remontent)
   D'où les deux règles de sécurité : le texte reste dans les ~37 % de
   gauche, et au-dessus des 80 % de hauteur. Aucun voile ni dégradé —
   la lisibilité vient du cadrage, l'aplat doit rester pur.            */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* même vermillon que celui du visuel : le raccord est invisible */
  background: var(--color-vermillon);
  color: var(--color-cream);
  /* Le premier écran occupe exactement une hauteur de fenêtre : rien de la
     section suivante ne doit apparaître au chargement.
     `svh` et non `vh` : sur mobile, `vh` compte la fenêtre SANS la barre du
     navigateur, ce qui pousserait le CTA sous la ligne de flottaison. Et
     surtout pas `dvh`, dont la valeur change pendant le défilement et ferait
     sauter la mise en page. `vh` reste en repli pour les moteurs anciens.
     `min-height` et non `height` : sur une fenêtre courte, le contenu doit
     pouvoir déborder plutôt que d'être rogné. */
  min-height: 100vh;
  min-height: 100svh;
}

/* Mobile : l'illustration reste le fond de la section, mais s'affiche
   ENTIÈRE (aucun recadrage) ; le vermillon de la section prolonge celui
   du visuel sans couture visible, et le texte se pose dessous — sur le
   même aplat, donc toujours « sur » l'illustration. */
.hero__media {
  position: relative;
  z-index: 0;
}
.hero__media picture { display: block; }
.hero__media img {
  width: 100%;
  height: auto;
}

/* Voile de lisibilité — un très léger renfort vermillon ancré à GAUCHE,
   fondu à 0 avant la moitié. Sur l'aplat déjà plein, il est neutre ; il
   n'agit que là où l'image s'éclaircit (roses, chevalière) en dérivant
   sous le texte au fil de la parallaxe, garantissant que la zone de
   texte crème reste dominée par le vermillon (plancher AA grand texte).
   Teinte UNIQUE (--color-vermillon) : raccord invisible, aucun décalage
   de nuance. Utilitaire de contraste, pas d'ornement (ordre d'arbitrage
   DESIGN-BAR : priorité 1). Calque posé au-dessus de l'image mais sous
   .hero__content (z-1) ; opacité seule → aucun coût CLS/LCP. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    105deg,
    rgba(232, 79, 55, 0.45) 0%,     /* --color-vermillon #e84f37 @ .45 */
    rgba(232, 79, 55, 0.28) 24%,
    rgba(232, 79, 55, 0) 48%
  );
}
@media (forced-colors: active) {
  .hero__media::after { display: none; }
}

.hero__content {
  position: relative;
  z-index: 1;
  padding: var(--space-6) var(--gutter) var(--section-padding);
}
.hero__text > * + * { margin-top: var(--space-4); }

/* Entrée du hero — accrochée au MÊME verrou que les apparitions au
   défilement : la classe .js n'est posée par main.js que si le mouvement
   n'est pas réduit. Sans JS, sans observer, ou en mouvement réduit, tout
   est déjà à sa place. Transform + opacité seulement : aucun décalage de
   mise en page possible.
   `backwards` et non `both` : l'animation tient l'état de DÉPART pendant
   son délai, mais ne fige pas l'état d'arrivée — sinon elle écraserait le
   `transform` de :active et le bouton ne s'enfoncerait plus au clic. */
.js .hero__text > * {
  animation: hero-rise var(--dur-slow) var(--ease) backwards;
}
.js .hero__text > *:nth-child(2) { animation-delay: 120ms; }

/* Le TITRE n'est pas animé, volontairement : c'est l'élément LCP de la
   page. L'animer, c'est retarder de 700ms la peinture de ce que le
   visiteur est venu lire — un coût de performance réel pour un gain
   décoratif. Il est là d'emblée, et le reste se pose autour de lui.
   La liste de puces n'entre pas en bloc : ce sont les puces elles-mêmes. */
.js .hero__text > .h1,
.js .hero__text > .pills,
.js .hero__text > p:last-child { animation: none; }

.js .pills > li {
  animation: hero-rise var(--dur-slow) var(--ease) backwards;
}
.js .pills > li:nth-child(1) { animation-delay: 240ms; }
.js .pills > li:nth-child(2) { animation-delay: 320ms; }

/* Le CTA se POSE, il ne surgit pas : 3 % d'échelle et rien de plus.
   Pas de dépassement élastique — le registre de la page est sérieux. */
.js .hero__text > p:last-child .btn {
  animation: cta-settle var(--dur-slow) var(--ease) 400ms backwards;
}

/* Filet de sécurité, comme pour les apparitions au défilement : si les
   animations ne s'exécutent pas (onglet jamais rendu, moteur exotique),
   `backwards` laisserait le contenu figé à opacité 0. main.js pose cette
   classe une fois la séquence écoulée.
   Ce bloc doit rester APRÈS toutes les règles d'entrée : à spécificité
   égale, c'est l'ordre qui tranche. */
.entry-done .hero__text > *,
.entry-done .pills > li,
.entry-done .hero__text > p:last-child .btn { animation: none; }

@keyframes hero-rise {
  from { opacity: 0; transform: translateY(var(--space-2)); }
  to   { opacity: 1; transform: none; }
}
@keyframes cta-settle {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.hero__title {
  color: var(--color-cream);   /* « Votre » et « À la demande. » en crème */
  text-wrap: initial;          /* les retours de ligne sont explicites (<br>) */
}
/* Bicolore, par lisibilité : seul l'accent « force de vente » reste marine —
   il tombe sur l'aplat vermillon uni (~5,6:1), là où le marine gagne. Les
   mots d'encadrement débordent, eux, sur l'image sombre (surtout « À la
   demande. ») où le marine s'effondrait (~1:1) : le crème (clair) y tient
   mieux. Scopé au hero → n'affecte pas les accents du manifeste. */
.hero__title .accent {
  color: var(--color-navy);
}

/* H1 : deux graisses, fidèle à la maquette. « Votre » / « À la demande. »
   en Medium (via .h1) ; « force de vente » en Light Italic (.accent),
   seule italique réellement dessinée de Juana. L'accent est porté par
   l'italique, pas par une graisse d'emphase. */

/* Sous-titre marine sur vermillon : 4,37:1. Au-dessus de 1280px de large
   il dépasse 24px, donc il relève du « grand texte » (seuil 3:1) et la
   graisse est libre — d'où le passage en Regular, demandé au rendu.
   En dessous, il passe sous 24px et relèverait du seuil 4,5:1, que ce
   couple de la charte ne peut pas atteindre quelle que soit la graisse :
   c'est une limite de la palette, pas un réglage.
   Pas de borne de largeur en mobile : la colonne est déjà étroite. La
   borne desktop est posée au palier ≥768px, en vw (surtout pas en `ch` :
   le zéro de Juana fait 0,71em, un `33ch` vaut 635px et ne borne rien). */
.hero__subtitle {
  color: var(--text-on-vermillon);   /* crème */
  font-weight: var(--fw-regular);
}

/* Repère de défilement — bord droit à mi-hauteur, comme l'artboard 01.
   Mesuré sur le master : à cet emplacement l'illustration est à 100 %
   d'aplat uni à 1200, 1440 et 1920px (contraste crème/vermillon 3,08,
   au-dessus du seuil 3:1 exigé par WCAG 1.4.11 pour un élément
   d'interface non textuel). La position de la maquette est donc conservée
   — seule la forme change.
   Masqué sous 1200px : au doigt le défilement est instinctif, et un
   second lien y concurrencerait le CTA pleine largeur.
   Trois variantes commutables par la classe de <body> :
   .cue-a (sceau) · .cue-b (filet) · .cue-c (micro-typo, par défaut). */
.hero__scroll {
  display: none;
}

/* ---------- 7. Section 05 — Bannière-manifeste ---------- */

.manifesto {
  padding-block: var(--section-padding);
}
.manifesto__block {
  background: var(--color-vermillon);
  color: var(--color-cream);
  padding: clamp(var(--space-6), 8vw, var(--space-12)) var(--gutter);
  text-align: center;
}
.manifesto__block > * + * { margin-top: var(--space-4); }

.manifesto__title {
  font-size: var(--fs-display);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
  max-width: 18ch;
  margin-inline: auto;
}
.manifesto__subtitle {
  font-weight: var(--fw-regular);       /* graisse réduite (demande) ; grand texte
                                           → AA 3:1 tenu sur vermillon même en Regular */
  max-width: 62ch;
  margin-inline: auto;
}

/* ---------- 8. Section 07 — Méthode (frise 5 étapes) ---------- */

.method__title {
  margin-bottom: var(--space-8);
  text-align: center;            /* centré au-dessus de la frise, elle-même centrée */
}

.steps {
  display: grid;
  gap: var(--space-6);
}

/* Index d'étape : sert à cascader la révélation et le tracé du trait
   (1 → 5), pour traduire le déroulé du process. */
.step:nth-child(1) { --step-i: 0; }
.step:nth-child(2) { --step-i: 1; }
.step:nth-child(3) { --step-i: 2; }
.step:nth-child(4) { --step-i: 3; }
.step:nth-child(5) { --step-i: 4; }

/* Révélation séquentielle : chaque étape entre après la précédente. Le
   décalage s'ajoute au mécanisme .reveal existant, il n'en crée pas d'autre. */
.js .step.reveal { transition-delay: calc(var(--step-i, 0) * 90ms); }

.step {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  align-items: start;
}

/* Pastille : disque vermillon + anneau fin concentrique */
.step__node {
  position: relative;
  display: grid;
  place-items: center;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: var(--radius-pill);
  background: var(--color-vermillon);
  color: var(--color-cream);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);   /* chiffre plus présent dans la pastille */
  line-height: 1;
  flex: none;
}
/* Anneau concentrique. Sa transition sert au focus au survol (il respire). */
.step__node::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--space-1));
  border: var(--hairline) solid var(--color-vermillon);
  border-radius: var(--radius-pill);
  transition: inset var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.step__title { transition: color var(--dur) var(--ease); }

/* Repérage au survol (pointeur fin) : l'étape pointée prend le focus —
   l'anneau s'ouvre, le titre passe au vermillon pour rejoindre sa pastille.
   Pas de `cursor: pointer` ni de soulèvement : l'étape n'est pas un lien,
   c'est une aide de lecture, pas une fausse affordance cliquable. */
@media (hover: hover) and (pointer: fine) {
  .step:hover .step__node::before { inset: calc(-1 * var(--space-2)); }
  .step:hover .step__title { color: var(--color-vermillon); }
}

/* Trait de liaison vertical (mobile) — ancré sur l'étape, pas sur la
   pastille : la hauteur suit la hauteur réelle du texte de l'étape et
   s'arrête juste avant l'anneau suivant. */
.step:not(:last-child)::before {
  content: "";
  position: absolute;
  top: 4rem;                       /* sous la pastille (3.25rem) et son anneau */
  left: 1.625rem;                  /* centre de la pastille */
  width: var(--hairline);
  height: calc(100% + var(--space-6) - 4.75rem);
  background: var(--color-vermillon);
}
/* Tracé du trait vers le bas, à l'entrée de l'étape (mobile). Gaté `.js` :
   sans JS ou en mouvement réduit, le trait reste plein (scaleY 1). */
.js .step:not(:last-child)::before {
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform var(--dur-slow) var(--ease);
}
.js .step.is-visible:not(:last-child)::before {
  transform: scaleY(1);
  transition-delay: calc(var(--step-i, 0) * 90ms + 140ms);
}

.step__body > * + * { margin-top: var(--space-1); }
.step__title { color: var(--text); }
.step__desc { color: var(--text); }

/* --- Chorégraphie ÉLABORÉE par étape (à l'entrée à l'écran) ----------------
   Chaque étape ne se révèle plus EN BLOC : ses éléments se déclinent en
   séquence — la pastille se POSE (scale + anneau qui apparaît), le titre
   MONTE, la description SUIT, le trait se TRACE vers l'étape suivante. Le
   tout cascadé 1→5 via --step-i. Déclencheur = IntersectionObserver (état
   final TOUJOURS visible : pas de scroll-lié, qui risquerait de figer le
   contenu — cf. décision titres §14). Gaté `.js` : sans JS / mouvement
   réduit, tout est déjà en place. Transform + opacité seuls → CLS 0.
   N'écrase NI le survol (couleur du titre, anneau) NI le tracé du connecteur. */

/* L'étape devient le simple DÉCLENCHEUR : elle ne se translate plus elle-même. */
.js .step.reveal { opacity: 1; transform: none; }

/* États de départ des sous-éléments. */
.js .step.reveal .step__node {
  opacity: 0;
  transform: scale(0.7);
  transform-origin: center;
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.js .step.reveal .step__node::before { opacity: 0; }   /* l'anneau apparaît (inset géré par le survol) */
.js .step.reveal .step__title {
  opacity: 0;
  transform: translateY(var(--space-2));
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease),
              color var(--dur) var(--ease);              /* color conservé pour le survol */
}
.js .step.reveal .step__desc {
  opacity: 0;
  transform: translateY(var(--space-2));
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}

/* Arrivée échelonnée : pastille → anneau → titre → description, + cascade 1→5. */
.js .step.reveal.is-visible .step__node {
  opacity: 1; transform: scale(1);
  transition-delay: calc(var(--step-i, 0) * 90ms);
}
.js .step.reveal.is-visible .step__node::before {
  opacity: 1;
  transition-delay: calc(var(--step-i, 0) * 90ms + 60ms);
}
.js .step.reveal.is-visible .step__title {
  opacity: 1; transform: none;
  transition-delay: calc(var(--step-i, 0) * 90ms + 110ms);
}
.js .step.reveal.is-visible .step__desc {
  opacity: 1; transform: none;
  transition-delay: calc(var(--step-i, 0) * 90ms + 190ms);
}

/* Mouvement réduit : tout en place d'emblée (défense si la préférence bascule
   après chargement, .js déjà posée). */
@media (prefers-reduced-motion: reduce) {
  .js .step.reveal .step__node,
  .js .step.reveal .step__node::before,
  .js .step.reveal .step__title,
  .js .step.reveal .step__desc { opacity: 1; transform: none; }
}

/* ---------- 9. Sections 08/09 — Témoignages (carrousel) ---------- */

.testimonials__eyebrow { margin-bottom: var(--space-8); }

.carousel {
  position: relative;
}
.carousel__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  gap: var(--gutter);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-inline: contain;
}
.carousel__track::-webkit-scrollbar { display: none; }

.testimonial {
  scroll-snap-align: center;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
}
.testimonial__client {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  color: var(--color-vermillon);
}
.testimonial__quote {
  font-size: var(--fs-lead);
  font-weight: var(--fw-light);
  line-height: var(--lh-snug);
  max-width: 46ch;
  text-wrap: pretty;
}
/* Filet séparateur : détache le verbatim de l'attribution, vocabulaire des
   filets 1px de la charte. Court et centré. */
.testimonial__author { display: grid; gap: var(--space-1); }
.testimonial__author::before {
  content: "";
  width: 2rem;
  height: var(--hairline);
  background: var(--color-vermillon);
  margin: 0 auto var(--space-2);
}
.testimonial__name {
  font-weight: var(--fw-semibold);
  color: var(--color-vermillon);
  font-size: var(--fs-ui);
}
.testimonial__role {
  font-size: var(--fs-ui);
  line-height: var(--lh-snug);
}

.testimonial__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
  font-size: var(--fs-ui);
  text-decoration: none;
}
.testimonial__link .icon-circle {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: var(--hairline) solid var(--color-vermillon);
  border-radius: var(--radius-pill);
  color: var(--color-vermillon);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.testimonial__link:hover .icon-circle,
.testimonial__link:focus-visible .icon-circle {
  background: var(--color-vermillon);
  color: var(--color-cream);
  transform: scale(1.1);
}
.testimonial__link svg { width: 1.125rem; height: 1.125rem; }

/* Barre de contrôle : flèches ← ●● → centrées sous le témoignage. Les
   flèches ajoutent une affordance de navigation explicite (souris/clavier)
   là où il n'y avait que puces + swipe. */
.carousel__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-8);
}
.carousel__arrow {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  background: none;
  border: var(--hairline) solid var(--color-vermillon);
  border-radius: var(--radius-pill);
  color: var(--color-vermillon);
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease),
              opacity var(--dur) var(--ease);
}
.carousel__arrow svg { width: 1.125rem; height: 1.125rem; }
.carousel__arrow:disabled { opacity: 0.3; cursor: default; }
@media (hover: hover) {
  .carousel__arrow:not(:disabled):hover {
    background: var(--color-vermillon);
    color: var(--color-cream);
  }
}

.carousel__dots {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
}
.carousel__dot {
  width: 44px;
  height: 44px;
  padding: 0;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  cursor: pointer;
}
.carousel__dot::before {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  border: var(--hairline) solid var(--color-vermillon);
  border-radius: var(--radius-pill);
  transition: background var(--dur) var(--ease);
}
.carousel__dot[aria-selected="true"]::before {
  background: var(--color-vermillon);
}

/* ---------- 10. Section 10 — Tarif ---------- */

.pricing__title { text-align: center; margin-bottom: var(--space-8); }

.price {
  font-size: var(--fs-price);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--color-vermillon);
}
/* Le nombre compte de 0 à 450 à l'entrée (main.js). Largeur réservée pour
   3 chiffres tabulaires, aligné à droite : le « € » ne bouge pas pendant le
   comptage → aucun décalage de mise en page (CLS 0). */
.price__value {
  display: inline-block;
  min-width: 3ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.price__unit { color: var(--text); }
.price__sep {
  font-weight: var(--fw-light);
  color: var(--text);
}

.pricing__conditions {
  margin-top: var(--space-3);
  font-size: var(--fs-lead);
  font-weight: var(--fw-light);
  line-height: var(--lh-snug);
  max-width: 30ch;
}
.pricing__conditions strong { font-weight: var(--fw-semibold); }

.pricing__cards {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

/* Index de carte : cascade la révélation 1 → 2 → 3, même principe que la
   frise (§8). */
.pricing__cards .card:nth-child(1) { --card-i: 0; }
.pricing__cards .card:nth-child(2) { --card-i: 1; }
.pricing__cards .card:nth-child(3) { --card-i: 2; }

.card {
  position: relative;
  z-index: 0;                       /* contexte d'empilement pour le ::before */
  padding: clamp(var(--space-3), 3vw, var(--space-6));
}
/* Bordure + fond de la carte portés par un ::before : au survol on ne grossit
   QUE lui → le texte n'est jamais scalé, aucune distorsion (même principe que
   les puces). Le repli sans JS (fond cream-200 au survol) passe aussi par ici. */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border: var(--hairline) solid var(--color-vermillon);
  border-radius: var(--radius-lg);
  background-color: transparent;
  transition: background-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
/* Avec JS : opacité + transform cascadés (révélation), le fond restant
   instantané au survol grâce à un délai PAR propriété (0s sur le fond). */
.js .card.reveal {
  transition-property: opacity, transform;
  transition-duration: var(--dur-slow), var(--dur-slow);
  transition-timing-function: var(--ease), var(--ease);
  transition-delay: calc(var(--card-i, 0) * 90ms),
                    calc(var(--card-i, 0) * 90ms);
}
/* Repérage au survol (pointeur fin) : la carte pointée prend un fond
   crème-200, le token dédié aux survols. Pas de `cursor: pointer` ni de
   soulèvement : la carte n'est pas cliquable, c'est une aide de lecture. */
@media (hover: hover) and (pointer: fine) {
  /* ⚠️ Écart à la décision « sans soulèvement » : léger scale (demande). Pas de
     cursor:pointer. On grossit le ::before (fond + bordure), jamais le texte. */
  .card:hover::before {
    background-color: var(--color-cream-200);
    transform: scale(1.02);
  }
}
.card__title {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--color-vermillon);
  margin-bottom: var(--space-3);
}
.card__body > * + * { margin-top: var(--space-2); }

.pricing__footer {
  display: grid;
  gap: var(--space-4);
  align-items: center;
  margin-top: var(--space-8);
}
.pricing__legal {
  font-size: var(--fs-small);
  line-height: var(--lh-normal);
}

/* ---------- 11. Section 11 — Formulaire lead magnet ---------- */

/* Section inversée en fond CRÈME (était vermillon plein). Le texte repasse
   donc en marine, le titre reprend le vermillon des autres titres de
   section, et le formulaire est redélimité pour le fond clair (ci-dessous). */
.leadmagnet {
  background: var(--color-cream);
  color: var(--color-navy);
  padding-block: var(--section-padding);
}
/* Plus d'override : le titre reprend le vermillon de `.h2`, comme les
   autres sections. */
.leadmagnet__body {
  margin-top: var(--space-3);
  max-width: 42ch;
  font-weight: var(--fw-light);   /* allégé (marine sur crème = 13,7:1, tient) */
}

.form {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-6);
}
.form__row { display: grid; gap: var(--space-2); }

.field { display: grid; gap: var(--space-1); }
.field input {
  width: 100%;
  min-height: 3.25rem;
  padding: var(--space-2) var(--space-3);
  background: var(--color-cream);
  color: var(--color-navy);
  /* Sur fond crème, le champ n'était plus délimité (il l'était par contraste
     avec le vermillon) : on le borde du filet de la charte. */
  border: var(--hairline) solid var(--color-border);
  border-radius: 0;
  font-family: inherit;
  font-size: 1rem;                       /* ≥16px : pas de zoom auto iOS */
  transition: border-color var(--dur-fast) var(--ease);
}
.field input:focus { border-color: var(--color-navy); }
.field input::placeholder {
  color: var(--color-navy);
  opacity: 0.72;
}
.field input:focus-visible {
  outline: 2px solid var(--color-navy);
  outline-offset: 2px;
}
.field input[aria-invalid="true"] {
  border-color: var(--color-navy);
  box-shadow: inset 0 0 0 2px var(--color-navy);
}
.field__error {
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--color-navy);
  min-height: 1.2em;
}

/* Honeypot anti-spam (cf. spec-formulaire §2) */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2);
  align-items: start;
  margin-top: var(--space-1);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
}
.consent input {
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.2rem 0 0;
  accent-color: var(--color-navy);
}
.consent a {
  display: inline-block;
  padding-block: var(--space-1);
  color: var(--color-navy);
}

.form__note {
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--color-navy);
}

.form__status {
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  min-height: 1.4em;
}

/* ---------- 12. Section 12 — CTA final + pied de page ---------- */

.final {
  text-align: center;
  padding-block: var(--section-padding) var(--space-8);
}
.final > * + * { margin-top: var(--space-4); }
.final__title {
  font-size: var(--fs-hero);
  font-weight: var(--fw-regular);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--color-vermillon);
  text-wrap: balance;
  max-width: 20ch;
  margin-inline: auto;
}
.final__subtitle { max-width: 46ch; margin-inline: auto; }

.footer {
  padding-bottom: var(--space-6);
  /* Teinte unifiée sur le vermillon VIF (demande) : baseline, contacts et
     ligne légale rejoignent le nom/les intitulés. ⚠️ Compromis contraste :
     sur ce petit texte, vermillon vif sur crème = 3,15:1 (le token
     --color-vermillon-deep tenait 4,8:1 / AA). Choix esthétique assumé. */
  color: var(--color-vermillon);
}
.footer__top {
  display: grid;
  gap: var(--space-6);
  padding-block: var(--space-8);
  border-top: var(--hairline) solid var(--color-border);
}
/* Logo {g} du footer : lien « retour au header » (miroir du .header__logo).
   Le <a> est le conteneur (cible 44px) ; le <svg> porte la taille + le
   grossissement au survol ; l'étoile pivote sur elle-même (« s'anime »). */
.footer__logo {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                     /* cible tactile */
  color: var(--color-vermillon);
}
.footer__logo svg {
  width: clamp(3.5rem, 8vw, 5.5rem);
  height: auto;
  transition: transform var(--dur) var(--ease);
}
/* « s'anime » : l'étoile du {g} (1er tracé) pivote sur son PROPRE centre au
   survol — écho de l'animation d'intro. fill-box verrouille le centre malgré
   le scale(0.1,-0.1) du <g> parent (même leçon que le sceau / logo-anim). */
.footer__logo .logo-star {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease);
}
/* Focus clavier : même affordance, hors requête hover (un clavier ne survole
   pas). Le survol pointeur est gaté @media (hover:hover) ci-dessous. */
.footer__logo:focus-visible svg { transform: scale(1.06); }
.footer__logo:focus-visible .logo-star { transform: rotate(180deg); }
@media (hover: hover) {
  .footer__logo:hover svg { transform: scale(1.06); }
  .footer__logo:hover .logo-star { transform: rotate(180deg); }
}

/* Repli du formulaire HubSpot avant consentement (section 11, fond crème) :
   un simple encadré au filet, texte marine, avec le bouton d'autorisation. */
.hs-embed__fallback {
  border: var(--hairline) solid var(--color-border);
  padding: var(--space-4);
  color: var(--color-navy);
}
.hs-embed__fallback > * + * { margin-top: var(--space-3); }

/* « Gérer les cookies » (pied de page) : un <button> rendu comme les liens
   voisins, pour tenir sur la même ligne légale. */
.footer__cookie-link {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.footer__brand > * + * { margin-top: var(--space-2); }
.footer__name {
  font-size: var(--fs-ui);
  font-weight: var(--fw-bold);
  color: var(--color-vermillon);
}
.footer__baseline {
  font-size: var(--fs-small);
  font-weight: var(--fw-light);
  max-width: 32ch;
}
.footer__heading {
  font-size: var(--fs-ui);
  font-weight: var(--fw-semibold);
  color: var(--color-vermillon);
  margin-bottom: var(--space-1);
}
.footer__contact a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-small);
  text-decoration: none;
}
.footer__contact a:hover { text-decoration: underline; }

.footer__social {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
  color: var(--color-vermillon);
  /* Sans ça, le lien hérite du soulignement par défaut : le tiret « — »
     ET l'underline formaient un double trait près du logo LinkedIn. Le
     tiret cadratin, centré verticalement, suffit comme unique trait. */
  text-decoration: none;
}
.footer__social:hover { text-decoration: none; }
.footer__social svg {
  width: 1.25rem;
  height: 1.25rem;
  transition: transform var(--dur) var(--ease);
}
/* Grossissement au survol/focus du logo LinkedIn (c'est un lien → affordance
   légitime). */
.footer__social:focus-visible svg { transform: scale(1.1); }
@media (hover: hover) and (pointer: fine) {
  .footer__social:hover svg { transform: scale(1.1); }
}

.footer__seal {
  width: clamp(7rem, 18vw, 11rem);
  justify-self: center;
}

.footer__legal {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: var(--hairline) solid var(--color-vermillon);
  font-size: var(--fs-xsmall);
  line-height: var(--lh-normal);
  text-align: center;
}
.footer__legal a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                     /* cible tactile */
  text-decoration: none;
}
.footer__legal a:hover { text-decoration: underline; }
.footer__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-1) var(--space-2);
}

/* ---------- 13. Pages légales ---------- */

/* Header dans le flux (sticky) sur les pages légales : pas de
   compensation de hauteur nécessaire. */
.legal-page .header {
  position: sticky;
  background: var(--bg);
  box-shadow: 0 1px 0 var(--color-border);
}
.legal-page .header__logo { color: var(--color-vermillon); }

.prose { max-width: 72ch; }
.prose > * + * { margin-top: var(--space-3); }
.prose h1 {
  font-size: var(--fs-h2);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  color: var(--color-vermillon);
}
.prose h2 {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--color-vermillon);
  margin-top: var(--space-6);
}
.prose ul { list-style: disc; padding-left: var(--space-3); }
.prose li + li { margin-top: var(--space-1); }
.prose a { color: var(--color-vermillon-deep); }
.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}
.prose th, .prose td {
  padding: var(--space-1) var(--space-2);
  border: var(--hairline) solid var(--color-border);
  text-align: left;
  vertical-align: top;
}
.prose .note {
  padding: var(--space-3);
  border-left: 3px solid var(--color-vermillon);
  background: var(--color-cream-200);
  font-size: var(--fs-small);
}
.table-scroll { overflow-x: auto; }
.back-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--space-6);
  font-size: var(--fs-ui);
  color: var(--color-vermillon-deep);
}

/* ============================================================
   BREAKPOINTS
   ============================================================ */

/* ---------- ≥ 480px (sm) : le CTA reprend sa largeur auto ---------- */
@media (min-width: 480px) {
  .btn--block { width: auto; }
  .form .btn--block { width: auto; }
}

/* ---------- ≥ 768px (md) : tablette ---------- */
@media (min-width: 768px) {

  /* Sections texte + illustration : 2 colonnes */
  .split {
    grid-template-columns: 1fr 1fr;
  }
  .split--media-first .split__media { order: -1; }

  /* Hero desktop : l'illustration passe en fond plein cadre, le texte se
     pose dessus sur la zone d'aplat vermillon. La hauteur pleine fenêtre
     (100svh, déclarée plus haut) sert aussi la lisibilité : plus la section
     est haute, plus le bloc de texte occupe une petite fraction de l'image
     et reste dans la bande centrale, loin des roses du bas. */
  .hero {
    display: grid;
    align-items: center;
  }
  .hero__media {
    position: absolute;
    inset: 0;
    overflow: hidden;           /* borne le glissement parallaxe */
  }
  .hero__media picture,
  .hero__media img {
    width: 100%;
    height: 100%;
  }
  /* Point focal horizontal — arbitrage : trop à gauche, la chevalière sort
     du champ ; trop à droite, l'aplat se réduit sous le texte. À ce palier
     le recadrage est fortement piloté par la hauteur (fenêtre étroite),
     30 % garde les deux. Y à 22 % : rogne par le bas (les roses). */
  .hero__media img {
    object-fit: cover;
    object-position: 30% 22%;
    /* Surdimensionnement : la parallaxe a besoin de jeu pour glisser sans
       révéler de bord vide. 106 % laisse ~43px de chaque côté, très au-delà
       de l'amplitude (±10px) posée par main.js. Le glissement s'ajoute en
       translate via la variable, sans casser cette échelle. */
    transform: scale(1.06) translate(var(--px, 0), var(--py, 0));
    will-change: transform;
  }
  .hero__content {
    padding-block: var(--header-h) var(--space-8);
    padding-inline: 3.4vw;      /* marge gauche de l'artboard : 64/1920 */
  }
  /* Colonne de texte bornée à l'aplat. */
  .hero__text {
    width: min(44vw, 52rem);
  }
  /* Le sous-titre est le seul bloc dont la largeur naturelle dépasserait
     l'aplat à sa hauteur : borné en vw (jamais en `ch`, le zéro de Juana
     fait 0,71em). L'aplat de l'image fournie tient 45,2 % à la hauteur du
     sous-titre : 3,4 + 38 = 41,4 % laisse ~4 points de marge. */
  .hero__subtitle {
    max-width: min(38vw, 46rem);
  }

  /* Frise : 2 lignes (3 + 2) — spec §3-07 */
  .steps {
    grid-template-columns: repeat(6, 1fr);
    gap: var(--space-8) var(--space-4);
  }
  .step {
    grid-column: span 2;
    grid-template-columns: 1fr;
    justify-items: center;
    align-content: start;        /* pastille + corps tassés en haut : les
                                    titres s'alignent sur le même axe quelle
                                    que soit la longueur des descriptions */
    text-align: center;
  }
  .step:nth-child(4) { grid-column: 2 / span 2; }
  .step:nth-child(5) { grid-column: 4 / span 2; }
  /* Frise sur 2 lignes : le trait VERTICAL (mobile) est retiré. Les traits
     HORIZONTAUX intra-rangée sont posés dans un bloc BORNÉ à la tablette
     ci-dessous : leur sélecteur (3 pseudo-classes) est plus spécifique que
     celui du desktop, donc laissé ouvert il écraserait la géométrie desktop
     (trait décalé de 10px). Il ne doit vivre que dans l'intervalle tablette. */
  .step:not(:last-child)::before { content: none; }

  /* Tarif : les 3 cartes restent empilées tant que la colonne serait
     trop étroite (< 1000px) — voir le palier suivant. */
  .pricing__footer {
    grid-template-columns: auto 1fr;
    gap: var(--space-6);
  }

  /* Formulaire : Prénom / Nom sur une ligne */
  .form__row--two { grid-template-columns: 1fr 1fr; }

  /* Pied de page : 3 zones */
  .footer__top {
    grid-template-columns: 1.4fr 1fr auto;
    align-items: start;
  }
  .footer__seal { justify-self: end; }
}

/* ---------- Frise : connecteurs intra-rangée, TABLETTE UNIQUEMENT ----------
   Bloc borné à [768, 1200[ : le sélecteur à 3 pseudo-classes est plus
   spécifique que celui du desktop ; hors de cette borne il décalerait le
   trait desktop de 10px (bug corrigé). Relie 1→2→3 (rangée 1) et 4→5
   (rangée 2) ; le raccord cross-rangée 3→4 est sauté via :not(:nth-child(3)),
   il ne reste donc de connecteur qu'après les étapes 1, 2 et 4.
   Géométrie centre-à-centre, pastille tablette 3,25rem (rayon 1,625rem +
   0,5rem de respiration = 2,125rem). */
@media (min-width: 768px) and (max-width: 1199.98px) {
  .step:not(:last-child):not(:nth-child(3))::after {
    content: "";
    position: absolute;
    top: 1.625rem;                    /* centre de la pastille 3,25rem */
    left: calc(50% + 2.125rem);
    width: calc(100% + var(--space-4) - 4.25rem);
    height: var(--hairline);
    background: var(--color-vermillon);
  }
  /* Tracé en cascade, comme mobile et desktop. Gaté `.js` : plein sans JS. */
  .js .step:not(:last-child):not(:nth-child(3))::after {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-slow) var(--ease);
  }
  .js .step.is-visible:not(:last-child):not(:nth-child(3))::after {
    transform: scaleX(1);
    transition-delay: calc(var(--step-i, 0) * 90ms + 140ms);
  }
}

/* ---------- ≥ 1000px : la grille de cartes devient tenable ---------- */
@media (min-width: 1000px) {
  .pricing__cards { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- ≥ 1200px (lg) : desktop ---------- */
@media (min-width: 1200px) {

  /* Frise horizontale à 5 nœuds reliés par un trait */
  .steps {
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-4);
  }
  .step,
  .step:nth-child(4),
  .step:nth-child(5) {
    grid-column: auto;
  }
  .step__node { width: 4.5rem; height: 4.5rem; }

  /* Trait de liaison horizontal : ancré sur la colonne (et non sur la
     pastille) pour tomber pile entre deux nœuds quelle que soit la
     largeur — centre à centre = 100% de colonne + la gouttière de grille,
     moins 2 × (rayon 2.25rem + 0.5rem de respiration). */
  .step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 2.25rem;
    left: calc(50% + 2.75rem);
    width: calc(100% + var(--space-4) - 5.5rem);
    height: var(--hairline);
    background: var(--color-vermillon);
  }
  /* Tracé du trait gauche → droite, en cascade 1 → 5 : la frise se
     construit dans l'ordre du process. Gaté `.js` (plein sans JS / réduit). */
  .js .step:not(:last-child)::after {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-slow) var(--ease);
  }
  .js .step.is-visible:not(:last-child)::after {
    transform: scaleX(1);
    transition-delay: calc(var(--step-i, 0) * 90ms + 140ms);
  }
  .step__body { margin-top: var(--space-4); }

  /* Au-delà de 1200px le recadrage est doux : on ramène le point focal à
     gauche pour conserver l'intégralité de l'aplat sous le texte — le
     visage reste largement dans le champ. Le `scale(1.06)` de la parallaxe
     est répété ici, sinon cette règle l'écraserait. */
  .hero__media img {
    object-position: 0% 22%;
    transform: scale(1.06) translate(var(--px, 0), var(--py, 0));
  }

  /* --- Repère de défilement : socle commun aux 3 variantes ---
     Bord droit, à mi-hauteur — position d'origine de l'artboard. */
  .hero__scroll {
    position: absolute;
    z-index: 2;
    right: 2.4vw;
    top: 50%;
    transform: translateY(-50%);
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--space-2);
    min-width: 44px;
    min-height: 44px;
    padding: var(--space-1);
    color: var(--color-cream);
    text-decoration: none;
    opacity: 0.88;
    transition: opacity var(--dur) var(--ease);
  }
  .hero__scroll:hover,
  .hero__scroll:focus-visible { opacity: 1; }

  /* Le libellé n'est visible qu'en variante C ; ailleurs il porte le nom
     accessible du lien. */
  .hero__scroll__label {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  /* Rotation portée par la règle de base : sans elle, le chevron
     redeviendrait un carré dès que l'animation est neutralisée. */
  .hero__scroll__arrow {
    display: none;
    transform: rotate(45deg);
  }

  /* --- A · le sceau de marque en rotation lente ---
     Le SVG sert de masque : la forme est reprise, la couleur vient du
     token (le fichier est vermillon, invisible sur l'aplat vermillon). */
  /* 4rem, pas davantage : au-delà de 88px de large (sceau + gouttières)
     le repère mord sur l'épaulière de la chevalière à 1440px et le
     contraste tombe à 1,6. Mesuré, pas estimé. */
  .cue-a .hero__scroll__mark {
    width: 4rem;
    height: 4rem;
    background: currentColor;
    -webkit-mask: url("../img/sceau_hg_vermillon.svg") center / contain no-repeat;
            mask: url("../img/sceau_hg_vermillon.svg") center / contain no-repeat;
    animation: cue-orbit var(--dur-orbit) linear infinite;
  }
  .cue-a .hero__scroll__arrow {
    display: block;
    width: 0.5rem;
    height: 0.5rem;
    border-right: var(--hairline) solid currentColor;
    border-bottom: var(--hairline) solid currentColor;
    animation: cue-nudge 2.4s var(--ease) infinite;
  }

  /* --- B · filet parcouru par un segment ---
     Même vocabulaire que les filets de la frise (07) et des cartes (10). */
  .cue-b .hero__scroll__mark {
    position: relative;
    width: var(--hairline);
    height: 6rem;
    background: color-mix(in srgb, currentColor 30%, transparent);
    overflow: hidden;
  }
  .cue-b .hero__scroll__mark::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 2rem;
    background: currentColor;
    animation: cue-travel 2.6s var(--ease) infinite;
  }

  /* --- C · micro-typo verticale + filet + chevron (par défaut) ---
     Registre éditorial, cohérent avec une page entièrement en Juana. */
  /* Retour client : « à peine perceptible ». Le contraste ne peut pas
     aider — crème sur vermillon plafonne à 3,15:1, c'est le maximum de la
     charte. Le gain vient donc de la taille (12 → 14px), de la graisse
     (500 → 600), de l'allongement du filet et de l'amplitude du chevron.
     L'opacité passe à 1 : la retenue ne doit plus se payer en lisibilité. */
  .cue-c .hero__scroll { opacity: 1; }
  .cue-c .hero__scroll__label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    writing-mode: vertical-rl;
    text-transform: uppercase;
    letter-spacing: 0.32em;
    text-indent: 0.32em;        /* compense l'interlettrage de fin */
    font-size: var(--fs-small);
    font-weight: var(--fw-semibold);
  }
  .cue-c .hero__scroll__mark {
    width: var(--hairline);
    height: 3.5rem;
    background: color-mix(in srgb, currentColor 60%, transparent);
  }
  .cue-c .hero__scroll__arrow {
    display: block;
    width: 0.5rem;
    height: 0.5rem;
    border-right: var(--hairline) solid currentColor;
    border-bottom: var(--hairline) solid currentColor;
    animation: cue-nudge 2.6s var(--ease) infinite;
  }

  /* Tarif : prix à gauche, conditions à droite (cf. artboard 10) */
  .pricing__head {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: end;
    gap: var(--space-6);
  }

  /* Pied de page : ligne légale sur 3 zones, alignées EN HAUT — l'item
     central fait 2 lignes ; `start` évite que les items courts (© et liens)
     flottent au milieu. */
  .footer__legal {
    grid-template-columns: auto 1fr auto;
    align-items: start;
    text-align: left;
    gap: var(--space-4);
  }
  .footer__legal-center { text-align: center; }
  .footer__links { justify-content: flex-end; }
}

/* Repère de défilement — le chevron est déjà tourné de 45° au repos :
   les keyframes conservent cette rotation, sinon il se redresse. */
@keyframes cue-orbit {
  to { transform: rotate(360deg); }
}
@keyframes cue-travel {
  0%   { transform: translateY(-100%); }
  100% { transform: translateY(300%); }
}
@keyframes cue-nudge {
  0%, 100% { transform: rotate(45deg) translate(0, 0); }
  50%      { transform: rotate(45deg) translate(0.25rem, 0.25rem); }
}

/* ---------- 14. Animations d'apparition ---------- */

/* Le masquage n'est appliqué que si le JS a pris la main (classe .js sur
   <html>) : sans JS, ou si l'observer n'est pas disponible, le contenu
   reste visible — jamais de section blanche. */
.js .reveal {
  opacity: 0;
  transform: translateY(var(--space-3));
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* --- Cascade : le contenu d'un bloc entre élément par élément ---
   Le bloc `.reveal` reste le déclencheur observé mais ne fond plus lui-même ;
   ses enfants directs montent l'un après l'autre (titre, puis corps, puis
   CTA…). Transform + opacité SEULS → aucun décalage de mise en page (le
   transform ne reflue pas). Le filet de sécurité 4 s (main.js) pose
   is-visible partout si l'observer ne tourne pas ; sous mouvement réduit, la
   classe `.js` n'est pas posée → tout reste visible d'emblée. */
.js .reveal--cascade { opacity: 1; transform: none; }
.js .reveal--cascade > * {
  opacity: 0;
  transform: translateY(var(--space-2));
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}
.js .reveal--cascade.is-visible > * { opacity: 1; transform: none; }
.js .reveal--cascade.is-visible > *:nth-child(2) { transition-delay: 90ms; }
.js .reveal--cascade.is-visible > *:nth-child(3) { transition-delay: 180ms; }
.js .reveal--cascade.is-visible > *:nth-child(4) { transition-delay: 270ms; }

/* Sections texte + illustration (02, 03, 04, 06, 11), sans toucher au HTML.
   Les WRAPPERS (bloc, .split__text, .body) ne bougent pas ; ce sont les
   FEUILLES qui défilent vers le haut, en cascade : le TITRE, puis CHAQUE
   paragraphe l'un après l'autre, puis l'illustration. */
.js .split.reveal,
.js .split.reveal .body { opacity: 1; transform: none; }

.js .split.reveal .split__text > .h2,
.js .split.reveal .split__text > .body > *,
.js .split.reveal .split__text > p,          /* 11 : le chapô est un <p> direct */
.js .split.reveal .split__media {
  opacity: 0;
  transform: translateY(var(--space-3));
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}
.js .split.reveal.is-visible .split__text > .h2,
.js .split.reveal.is-visible .split__text > .body > *,
.js .split.reveal.is-visible .split__text > p,
.js .split.reveal.is-visible .split__media { opacity: 1; transform: none; }

/* Cascade : titre (0) → paragraphes → illustration. */
.js .split.reveal.is-visible .split__text > p,
.js .split.reveal.is-visible .split__text > .body > *:nth-child(1) { transition-delay: 80ms; }
.js .split.reveal.is-visible .split__text > .body > *:nth-child(2) { transition-delay: 150ms; }
.js .split.reveal.is-visible .split__text > .body > *:nth-child(3) { transition-delay: 220ms; }
.js .split.reveal.is-visible .split__media { transition-delay: 120ms; }

/* --- Titres : révélation par BALAYAGE MASQUÉ (clip-path) --------------
   Montée en raffinement du fondu-montée : le H2 se découvre derrière une
   arête qui glisse depuis la gauche. clip-path se compose sur le GPU et ne
   provoque AUCUN reflux → CLS 0. Les titres BALAIENT quand le corps MONTE :
   deux gestes distincts qui hiérarchisent titre et texte. Gaté `.js` : sans
   JS — et donc sous mouvement réduit, où .js n'est pas posée — le titre
   reste entièrement visible. Trois déclencheurs selon le contexte : le H2
   est lui-même la cible .reveal (07, 10), ou un ancêtre .split.reveal
   (02,03,04,06,11) / .reveal--cascade (05) porte le is-visible. */
.js .wipe {
  opacity: 1;                       /* le clip masque, pas l'opacité */
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--dur-slow) var(--ease);
}
.js .h2.wipe,
.js .manifesto__title.wipe { transform: none; }   /* pur balayage, sans translation */

/* Neutralise le fondu-montée hérité (les blocs existants sont plus
   spécifiques : on remonte la spécificité à l'identique). */
.js .split.reveal .split__text > .h2.wipe {
  opacity: 1; transform: none;
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--dur-slow) var(--ease);
}
.js .reveal--cascade > .wipe {
  opacity: 1; transform: none;
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--dur-slow) var(--ease);
}

/* Ouverture du balayage, quel que soit le déclencheur. */
.js .wipe.is-visible,
.js .split.reveal.is-visible .split__text > .h2.wipe,
.js .reveal--cascade.is-visible > .wipe {
  clip-path: inset(0 0 0 0);
}

/* --- Filet-accent qui se trace sous les deux titres CENTRÉS (07, 10) ---
   Reprend le vocabulaire hairline des boutons (.btn-rule) et relie titres
   et CTA. UNIQUEMENT sous les titres centrés, qui ont déjà une grande marge
   basse (--space-8) : le filet vit dans cet espace, sans rien réserver ni
   décaler → CLS 0. Visible d'emblée sans JS ; il ne se « trace » (scaleX
   0→1) que sous .js, quand le titre entre (is-visible). */
.method__title,
.pricing__title { position: relative; }
.method__title::after,
.pricing__title::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(-1 * var(--space-3));
  width: var(--space-6);
  height: var(--hairline);
  background: currentColor;
  transform: translateX(-50%) scaleX(1);
  transform-origin: 50% 50%;
}
.js .method__title.reveal::after,
.js .pricing__title.reveal::after {
  transform: translateX(-50%) scaleX(0);
  transition: transform var(--dur-slow) var(--ease);
}
.js .method__title.reveal.is-visible::after,
.js .pricing__title.reveal.is-visible::after {
  transform: translateX(-50%) scaleX(1);
}

/* Note : une variante « balayage piloté par le scroll » (animation-timeline:
   view()) a été écartée volontairement. Sur un moteur qui ne fait pas
   progresser le timeline — ou un contexte qui gèle les animations — le clip
   resterait bloqué à l'état masqué et les titres deviendraient INVISIBLES.
   Le déclenchement par IntersectionObserver ci-dessus reste plus sûr :
   révélé une fois pour toutes à l'entrée, jamais de titre bloqué. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .js .reveal--cascade > *,
  .js .split.reveal .split__text > .h2,
  .js .split.reveal .split__text > .body > *,
  .js .split.reveal .split__text > p,
  .js .split.reveal .split__media { opacity: 1; transform: none; }
  /* Balayage des titres + filet-accent : état final d'emblée, aucun geste
     (garde le cas où la préférence bascule après chargement, .js déjà posée). */
  .js .wipe { clip-path: none; animation: none; }
  .js .method__title.reveal::after,
  .js .pricing__title.reveal::after { transform: translateX(-50%) scaleX(1); }
  /* Logos header + footer : ni grossissement ni pivot d'étoile en mouvement réduit. */
  .header__logo:hover svg, .header__logo:focus-visible svg,
  .header__logo:hover .logo-star, .header__logo:focus-visible .logo-star,
  .footer__logo:hover svg, .footer__logo:focus-visible svg,
  .footer__logo:hover .logo-star, .footer__logo:focus-visible .logo-star { transform: none; }
  .carousel__track { scroll-behavior: auto; }

  /* Repère de défilement : l'élément reste, le mouvement s'arrête.
     Les sélecteurs sont préfixés par la variante : sans ça leur
     spécificité (0,2,0) l'emporterait sur un `.hero__scroll__arrow` nu
     et la règle ne servirait à rien. Le segment de la variante B est
     ramené à une position visible — sinon il resterait figé hors du filet. */
  .cue-a .hero__scroll__mark,
  .cue-a .hero__scroll__arrow,
  .cue-c .hero__scroll__arrow { animation: none; }
  .cue-b .hero__scroll__mark::after {
    animation: none;
    top: 25%;
  }
}

/* ---------- 15. Défilement panneau par panneau ----------

   `proximity` et NON `mandatory` — décision fondée sur la mesure, pas sur
   le goût. Hauteur des 12 panneaux comparée à celle de la fenêtre :
     1440x900  → 0/12 dépassent
     1920x1080 → 0/12 dépassent
     1280x620  → 8/12 dépassent, jusqu'à x1,35 (le tarif fait 836px)
   Avec `mandatory`, un panneau plus haut que l'écran voit son bas devenir
   inatteignable : le navigateur force l'atterrissage sur un point de
   calage et saute le contenu intermédiaire. `proximity` cale la fin de
   course quand on en est proche, mais laisse toujours s'arrêter où l'on
   veut. Le défilement reste celui de l'utilisateur : ce n'est pas du
   détournement de molette, que DESIGN-BAR.md exclut.

   Bornes : au-dessus de 700px de haut seulement (en dessous, presque
   aucun panneau ne tient et le calage n'aurait plus de sens), et à partir
   de 768px de large — au doigt, calage et défilement inertiel se
   contrarient. */
@media (min-width: 768px) and (min-height: 700px) {
  html { scroll-snap-type: y proximity; }

  .hero,
  main > section,
  .footer {
    scroll-snap-align: start;
    /* Un panneau plus haut que la fenêtre ne doit jamais être « sauté » :
       on autorise explicitement l'arrêt à l'intérieur. */
    scroll-snap-stop: normal;
  }
}

/* Mouvement réduit : aucun calage, le défilement reste strictement celui
   demandé par l'utilisateur. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-snap-type: none; }
}

/* ---------- 16. Navigation latérale par panneau (scrollspy) ----------
   Colonne de puces à droite, une par panneau, construite par main.js à
   partir des sections. Desktop seulement (≥1024px) : au doigt / en tablette,
   le défilement panneau-par-panneau suffit.
   Teinte MARINE : c'est le seul ton qui tient le seuil 3:1 (WCAG 1.4.11,
   composant d'interface) à la fois sur crème (13,7:1) ET sur le vermillon
   de la section 05 (4,4:1), la nav étant fixe au-dessus de fonds variés. */
.section-nav { display: none; }

@media (min-width: 1024px) {
  .section-nav {
    display: block;
    position: fixed;
    right: clamp(0.5rem, 1.4vw, 1.25rem);
    top: 50%;
    transform: translateY(-50%);
    z-index: 40;
    transition: opacity var(--dur) var(--ease);
  }
  /* Masquée tant que le hero est à l'écran : là, c'est l'indicateur de
     scroll qui sert d'affordance (les deux sont au bord droit). main.js
     pose `.is-hidden` via un observer sur le hero. */
  .section-nav.is-hidden {
    opacity: 0;
    pointer-events: none;
  }
  .section-nav ul {
    display: grid;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .section-nav__link {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    min-height: 24px;                 /* cible tactile AA (2.5.8) */
    padding-block: 2px;
    color: var(--color-navy);
    text-decoration: none;
  }
  .section-nav__link::after {         /* la puce */
    content: "";
    flex: none;
    width: 0.55rem;
    height: 0.55rem;
    border: var(--hairline) solid currentColor;
    border-radius: var(--radius-pill);
    transition: background-color var(--dur) var(--ease),
                transform var(--dur) var(--ease);
  }
  .section-nav__link[aria-current="true"]::after {
    background-color: currentColor;
    transform: scale(1.2);
  }
  /* Survol / focus : la puce GROSSIT (contour qui s'agrandit). Placé APRÈS
     l'état actif → survoler la puce active l'emporte (1.2 → 1.4) tout en
     gardant son remplissage : survoler = toujours la plus grosse. On ne
     grossit que le ::after (transform), la zone cliquable 24px est intacte. */
  .section-nav__link:hover::after,
  .section-nav__link:focus-visible::after {
    transform: scale(1.4);
  }
  .section-nav__label {               /* révélé au survol / focus */
    font-size: var(--fs-small);
    line-height: 1;
    white-space: nowrap;
    opacity: 0;
    transform: translateX(0.4rem);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
    pointer-events: none;
  }
  .section-nav__link:hover .section-nav__label,
  .section-nav__link:focus-visible .section-nav__label {
    opacity: 1;
    transform: none;
  }
}

/* ---------- 17. Impression ---------- */

@media print {
  .header, .hero__scroll, .carousel__dots { display: none; }
  body { background: #fff; color: #000; }
}
