/* CAPEO — site vitrine, page unique en cinq écrans (spec 2026-10-10).
   Toutes les couleurs viennent de tokens.css, généré depuis
   docs/design/capeo_design_tokens.json : aucune valeur de couleur n'est écrite
   ici. Les teintes dérivées passent par color-mix(). */

/* ── Polices de la marque, servies depuis le domaine ───────────────────────── */
@font-face { font-family: "Archivo Black"; src: url("/fonts/archivo-black-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Inter"; src: url("/fonts/inter-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Inter"; src: url("/fonts/inter-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Inter"; src: url("/fonts/inter-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("/fonts/jetbrains-mono-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }

/* ── Variables propres au site ─────────────────────────────────────────────── */
:root {
  color-scheme: dark;
  /* Inter juste après la police de marque : ni Archivo Black ni JetBrains Mono
     n'ont le glyphe U+202F (espace fine insécable, posée avant ; ! ?). Sans ce
     repli, c'est la police du système qui la dessinerait, à une autre largeur. */
  --police-titre: "Archivo Black", "Inter", "Arial Black", system-ui, sans-serif;
  --police-texte: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --police-mono: "JetBrains Mono", "Inter", ui-monospace, "SF Mono", Menlo, monospace;
  --texte-attenue: color-mix(in srgb, var(--c-text-primary) 72%, transparent);
  --coque: color-mix(in srgb, var(--c-background) 50%, black);
  --gouttiere: clamp(16px, 3vw, 40px);
  --largeur-max: 1360px;
  --hauteur-nav: 64px;
}

/* ── Remise à zéro ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--c-background);
  color: var(--c-text-primary);
  font: 400 1.125rem/1.55 var(--police-texte);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}
/* `hidden` doit cacher quoi que dise la feuille : `.coach__onglets { display: flex }` et
   `.exemple { display: grid }` l'emporteraient sur la règle du navigateur. */
[hidden] { display: none !important; }
img, svg { display: block; max-width: 100%; }
picture { display: contents; }
a { color: inherit; }
h1, h2, h3, p, ul, ol, figure, figcaption { margin: 0; }
ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }
:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 3px; border-radius: var(--r-small); }
/* Les ancres passent sous la barre fixe : le titre visé n'est jamais caché. */
section[id] { scroll-margin-top: var(--hauteur-nav); }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

/* ── Utilitaires ───────────────────────────────────────────────────────────── */
.enveloppe { width: min(100% - 2 * var(--gouttiere), var(--largeur-max)); margin-inline: auto; }
.mono { font-family: var(--police-mono); font-weight: 500; font-size: .8125rem; letter-spacing: .18em; text-transform: uppercase; }
.accent { color: var(--c-primary); }
.section { padding-block: clamp(var(--s-10), 11vw, 160px); }
.visuellement-cache {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.surtitre {
  font-family: var(--police-mono); font-weight: 500; font-size: .8125rem; line-height: 1.4;
  letter-spacing: .18em; text-transform: uppercase; color: var(--texte-attenue);
}
.chapeau { font-size: clamp(1.125rem, 1.6vw, 1.25rem); line-height: 1.5; color: var(--texte-attenue); max-width: 46ch; text-wrap: pretty; }
/* Les grands titres. Chaque taille plafonne son plancher en `vw` : un plancher en `rem`
   doublerait à 200 % de texte, et un mot en capitales ne se coupe pas (le banc
   `test_les_titres_tiennent_dans_leur_colonne_…` rejoue la cascade, mot le plus long à l'appui). */
.titre-ecran {
  display: grid; font-family: var(--police-titre); font-weight: 400; text-transform: uppercase;
  font-size: clamp(min(3rem, 12vw), 8.4vw, 7.5rem); line-height: .98; letter-spacing: -.015em;
}
.titre-ecran .surtitre { margin-bottom: var(--s-5); }
/* Le surtitre de l'intro est le mot « CAPEO » du logo (tracés vectoriels : la police du logo
   n'existe pas en fichier), dérivé du lockup par le générateur. Sa taille suit la fenêtre, non
   le corps du texte : à 200 % elle ne double pas. Un peu plus grand que l'ancien surtitre mono. */
.titre-ecran .surtitre img { height: clamp(18px, 2.6vw, 30px); width: auto; margin-inline: auto; }
/* Sur une photo, le surtitre est en texte principal : l'atténué y perd son contraste. */
.ecran .surtitre { color: var(--c-text-primary); }
/* Une ligne de titre est un masque : sous `.js-anime`, son texte en monte (§ 5). */
.ligne { display: block; }
.ligne > span { display: block; }
.titre-section {
  font-family: var(--police-titre); font-weight: 400; text-transform: uppercase;
  font-size: clamp(min(2.25rem, 9.6vw), 5.5vw, 5rem); line-height: 1.02; letter-spacing: -.01em; text-wrap: balance;
}
/* Le trait signature (CapeoSigLine) : 28 × 3 en accent sous chaque titre de section. */
.titre-section::after { content: ""; display: block; width: 28px; height: 3px; margin-top: var(--s-5); background: var(--c-primary); }
.titre-final {
  font-family: var(--police-titre); font-weight: 400; text-transform: uppercase;
  font-size: clamp(min(2.5rem, 10.3vw), 7vw, 6.5rem); line-height: 1.02; text-wrap: balance; max-width: 14ch;
}
.reassurance { font-size: .875rem; color: var(--texte-attenue); }

/* ── Lien d'évitement ──────────────────────────────────────────────────────── */
.evitement {
  position: absolute; left: var(--s-4); top: -100px; z-index: 100;
  background: var(--c-primary); color: var(--c-background);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-pill); font-weight: 600; text-decoration: none;
}
.evitement:focus { top: var(--s-4); }

/* ── Boutons ───────────────────────────────────────────────────────────────── */
.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: var(--touch-min); padding: 0 var(--s-6); border-radius: var(--r-pill);
  font: 600 1rem/1 var(--police-texte); text-decoration: none; cursor: pointer;
  transition: transform var(--m-fast) var(--m-curve);
}
.bouton--principal { background: var(--c-primary); color: var(--c-background); }
.bouton--secondaire { border: 1px solid var(--c-border-strong); color: var(--c-text-primary); }
@media (hover: hover) {
  .bouton--principal:hover { transform: translateY(-2px); }
  .bouton--secondaire:hover { border-color: var(--c-primary); }
}
.bouton:active { transform: scale(.98); }

/* ── Navigation ────────────────────────────────────────────────────────────── */
/* Transparente tant que le titre de l'intro n'a pas atteint la barre ; au-delà, un fond
   `background` opaque et un filet (spec § 3.0 : jamais de verre flouté). Seuls transform et opacity s'animent : le fond et
   le filet sont portés par un pseudo-élément dont seule l'opacité varie, en 240 ms. */
.navigation {
  position: fixed; inset: 0 0 auto; z-index: 50; height: var(--hauteur-nav);
  isolation: isolate;
}
.navigation::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--c-background);
  border-bottom: 1px solid var(--c-border);
  opacity: 0;
}
.navigation.est-collee::before, .sans-js .navigation::before, .page:not(.page-accueil) .navigation::before {
  opacity: 1;
}
/* Le fond ne s'anime qu'une fois la barre dans son premier état : le script pose `est-animee`
   après le premier rappel de l'observateur. Sans cela, le passage de `sans-js` à `js` faisait
   redescendre le fond de 1 à 0 sous les yeux, dès le premier rendu (mesuré, 7 chargements
   sur 10 sans bridage). */
.navigation.est-animee::before { transition: opacity var(--m-standard) var(--m-curve); }
.navigation__barre { height: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.navigation__logo { display: inline-flex; flex: none; min-height: var(--touch-min); align-items: center; }
.navigation__logo img { height: 34px; width: auto; }
/* Le lockup grossit (retouche du porteur, 2026-10-14) : 40 px de haut sur ordinateur ; sur
   téléphone, 34 px, la hauteur qui garde le lockup, « Tarifs » et « Télécharger » sur une
   ligne dès 320 px dans la barre de 64 px. */
@media (min-width: 900px) { .navigation__logo img { height: 40px; } }
.navigation__liens { display: flex; align-items: center; gap: var(--s-7); }
/* Le corps de la barre est plafonné en `vw` : à 200 % de texte, ses cinq liens à 30 px ne
   tiendraient pas sur une ligne de 900 px (« Pour qui » passait sur deux lignes, hors de la
   barre de 64 px). À 100 %, rien ne change ; à 1 440 px, le texte double encore. */
.navigation__liens .lien-texte {
  display: inline-flex; align-items: center; min-height: var(--touch-min); white-space: nowrap;
  text-decoration: none; font-size: min(.9375rem, 2.9vw); color: var(--c-text-primary);
}
@media (hover: hover) {
  .navigation__liens .lien-texte:hover { color: var(--c-primary); }
}
.navigation .bouton { min-height: var(--touch-min); padding-inline: var(--s-5); font-size: min(.9375rem, 2.9vw); white-space: nowrap; }
/* Sur téléphone : le lockup, « Tarifs » et « Télécharger », sans menu (spec § 3.0). Même
   plafond, à la mesure de deux liens : à 200 % sur 390 px, « Tarifs » et « Télécharger » à
   30 px sortiraient de l'écran. À 100 %, rien ne change dès 385 px. */
@media (max-width: 899px) {
  .navigation__liens .lien-large { display: none; }
  .navigation__liens { gap: var(--s-4); }
  .navigation__liens .lien-texte, .navigation .bouton { font-size: min(.9375rem, 3.9vw); }
  .navigation .bouton { padding-inline: var(--s-4); }
}

/* ── Pied de page ──────────────────────────────────────────────────────────── */
.pied { border-top: 1px solid var(--c-border); padding-block: var(--s-9) var(--s-8); color: var(--c-text-secondary); font-size: .9375rem; }
.pied__grille { display: grid; gap: var(--s-7); }
@media (min-width: 768px) { .pied__grille { grid-template-columns: 1fr 2fr; align-items: start; } }
.pied__marque img { height: 28px; width: auto; }
.pied__signature { margin-top: var(--s-3); color: var(--c-text-primary); }
.pied__liens { display: flex; flex-wrap: wrap; gap: 0 var(--s-6); }
.pied__liens a { display: inline-flex; align-items: center; min-width: var(--touch-min); min-height: var(--touch-min); text-decoration: none; }
@media (hover: hover) {
  .pied__liens a:hover { color: var(--c-text-primary); }
}
.pied__legal { grid-column: 1 / -1; padding-top: var(--s-6); border-top: 1px solid var(--c-border); font-size: .8125rem; }

/* ── En-tête des pages intérieures ─────────────────────────────────────────── */
.page-entete {
  display: grid; gap: var(--s-6); justify-items: center; text-align: center;
  padding: calc(var(--hauteur-nav) + clamp(var(--s-9), 8vw, 120px)) 0 var(--s-9);
}
.page-entete h1 {
  font-family: var(--police-titre); font-weight: 400; text-transform: uppercase;
  font-size: clamp(min(2.5rem, 10.3vw), 7vw, 5.5rem); line-height: 1.02; text-wrap: balance;
}
.page-entete .chapeau { text-wrap: balance; }

/* ── Texte légal ───────────────────────────────────────────────────────────── */
.texte-legal { max-width: 72ch; display: grid; gap: var(--s-8); padding-bottom: var(--s-10); }
.texte-legal section { display: grid; gap: var(--s-3); }
.texte-legal h2 { font: 600 1.25rem/1.3 var(--police-texte); }
.texte-legal p { color: var(--texte-attenue); }
.texte-legal a { color: var(--c-primary); }

/* ── Page 404 ──────────────────────────────────────────────────────────────── */
.page-404 main { min-height: 80svh; display: grid; place-content: center; justify-items: center; gap: var(--s-6); text-align: center; padding: var(--hauteur-nav) var(--gouttiere) 0; }
.liens-404 { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center; }

/* ── Badges des stores ─────────────────────────────────────────────────────── */
/* Les fichiers officiels, jamais recolorés ni grisés : le « bientôt » est porté
   par la puce voisine, pas par le badge. */
.badges { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
.badges__etat { flex-basis: 100%; color: var(--c-primary); }
.badge { display: inline-flex; align-items: center; height: 48px; border-radius: var(--r-small); }
.badge img { height: 100%; width: auto; }
/* Le PNG de Google (646 × 250) porte 29 px transparents en haut et en bas, et
   aucun sur les côtés (mesuré sur le fichier). On agrandit l'image de 250 / 192
   et on reprend cette marge, pour que les deux badges aient la même hauteur
   visible : 48 px, la cible tactile minimale. */
.badge--google { height: 62.5px; margin-block: -7.25px; }
a.badge { transition: transform var(--m-fast) var(--m-curve); }
@media (hover: hover) { a.badge:hover { transform: translateY(-2px); } }

/* ── QR code, sur ordinateur seulement ─────────────────────────────────────── */
.qr { display: none; }
@media (min-width: 1024px) {
  .qr { display: grid; justify-items: center; gap: var(--s-3); color: var(--c-text-secondary); }
  .qr svg { width: 132px; height: auto; border-radius: var(--r-small); shape-rendering: crispEdges; }
}

/* ── Page de téléchargement ────────────────────────────────────────────────── */
.telecharger { display: grid; justify-items: center; gap: var(--s-8); text-align: center; padding-block: clamp(var(--s-10), 14vw, 200px); }
.telecharger .badges, .ecran--intro .badges, .tarifs .badges { justify-content: center; }
.telecharger .badges__etat, .ecran--intro .badges__etat, .tarifs .badges__etat { text-align: center; }
.page-telecharger .telecharger { padding-top: calc(var(--hauteur-nav) + var(--s-10)); }

/* ── Le téléphone, dessiné en CSS (aucune image d'iPhone) ──────────────────── */
.telephone {
  position: relative; aspect-ratio: 440 / 956;
  border-radius: 14% / 6.5%; background: var(--coque); box-shadow: 0 0 0 1px var(--c-border);
}
/* L'îlot : les captures n'en portent pas. */
.telephone::before {
  content: ""; position: absolute; z-index: 2; top: 3.4%; left: 50%; width: 27%; height: 3.1%;
  translate: -50% 0; border-radius: var(--r-pill); background: var(--coque);
}
/* L'écran est posé en absolu, avec la marge de 2,4 % de la coque. Un `padding: 2.4%` se
   calcule sur la largeur du BLOC CONTENEUR (la colonne de la grille), pas sur celle du
   téléphone : mesuré à 1 440 px, il valait 34,5 px au lieu de 7,2 et rognait 14 % de la
   capture sur les côtés. En hauteur, 2,4 % de la largeur font 1,105 % (rapport 440 / 956). */
.telephone__ecran {
  position: absolute; left: 2.4%; top: 1.105%; display: block; width: 95.2%; height: 97.79%;
  border-radius: 12% / 5.6%; overflow: hidden; object-fit: cover; object-position: top;
}

/* ── 01 et 02 · Les écrans plein cadre ─────────────────────────────────────── */
/* `overflow: clip`, non `hidden` : `hidden` ferait de l'écran un conteneur de défilement,
   et la parallaxe (`animation-timeline: view()`) y chercherait son défilement, qui
   n'existe pas. */
.ecran { position: relative; min-height: 100svh; display: grid; align-items: end; isolation: isolate; overflow: clip; }
.ecran__fond { position: absolute; inset: 0; z-index: -1; }
.ecran__photo { width: 100%; height: 100%; object-fit: cover; }
/* Ce voile n'est pas décoratif : il porte la lisibilité du texte sur la photo
   (doctrine « texte sur photo », contraste mesuré par zone de texte, au 95e centile du
   fond). Un dégradé droit de 25 à 88 % laissait « POUR QUI » à 3,3:1 sur la lueur de
   l'horizon, à 1 440 px et 200 % de texte ; resserré sous le tiers, il le porte à 4,6:1. */
.ecran__voile {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--c-background) 25%, transparent) 0%,
    color-mix(in srgb, var(--c-background) 35%, transparent) 30%,
    color-mix(in srgb, var(--c-background) 78%, transparent) 55%,
    color-mix(in srgb, var(--c-background) 90%, transparent) 100%);
}
/* Le bas laisse la hauteur de la barre en plus : une ancre pose le haut de l'écran sous la
   barre, et le texte, en bas, resterait sinon sous le pli. */
.ecran__contenu { padding-block: calc(var(--hauteur-nav) + var(--s-8)) calc(var(--hauteur-nav) + var(--s-6)); display: grid; gap: var(--s-5); justify-items: start; }

/* L'intro, « comme actuellement » (demande du porteur) : le texte centré dans les deux
   axes, sur la photo. */
.ecran--intro { align-items: center; text-align: center; }
.ecran--intro .ecran__contenu { justify-items: center; }
.ecran--intro .ecran__photo { object-position: 50% 22%; transform-origin: 50% 30%; }
/* Le voile de l'intro, pour un texte au centre : léger dans le haut, où se tiennent le ciel
   et l'athlète ; plus dense dans la bande du titre et du chapeau, de 28 à 70 % de la
   hauteur ; presque plein sous la mention. Contraste mesuré par zone (95e centile du fond),
   à 500, 768 et 1 440 px, à 100 et 200 % : rapport de la tâche 3.
   De 40 % en haut, il monte à 46 % à 28 %. Avant la décision Q1 (tâche 5), la barre restait
   transparente pendant toute la descente de l'intro, et la lueur de l'horizon passait sous ses
   liens : « Pour qui » y tombait à 4,38:1 à 900 px et 150 % de texte (liens de 22,5 px).
   Depuis, la barre prend son fond dès que le titre l'atteint, mais l'horizon peut encore passer
   sous ses liens dans les premiers pixels de défilement : les 46 % restent, en marge de sécurité,
   et n'assombrissent que la photo. */
.ecran--intro .ecran__voile {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--c-background) 40%, transparent) 0%,
    color-mix(in srgb, var(--c-background) 46%, transparent) 28%,
    color-mix(in srgb, var(--c-background) 62%, transparent) 45%,
    color-mix(in srgb, var(--c-background) 72%, transparent) 70%,
    color-mix(in srgb, var(--c-background) 92%, transparent) 100%);
}
.ecran--intro .chapeau { max-width: 34ch; text-wrap: balance; }
.ecran--intro .badges { margin-top: var(--s-3); }

/* 02 : plus court que l'écran, le texte centré dans sa hauteur. Avec 100svh et le texte en bas,
   la descente depuis l'intro montrait un écran de photo vide avant le titre ; à 75svh, le texte
   est là dès que la section entre (retouche du porteur 2026-10-14). */
.ecran--pour-qui { min-height: 75svh; align-items: center; }
.ecran--pour-qui .ecran__contenu { padding-block: var(--s-10); }
.ecran--pour-qui .ecran__photo { object-position: 64% 50%; }
/* Le texte de 02 n'est plus en bas mais au milieu : le voile en bas, dense, ne sert plus à rien, et
   le haut, léger, laissait « POUR QUI » à 3,5:1 sur la lueur de l'horizon (1 440 px, 100 %).
   Plus dense dans la bande du texte, de 25 à 70 % de la hauteur, presque plein au pied de la
   section (elle se fond dans le fond de page). Contraste mesuré par zone de texte (5e centile du pire
   pixel), à 500, 768 et 1 440 px, à 100 et 200 % : rapport de la retouche du 2026-10-14. */
.ecran--pour-qui .ecran__voile {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--c-background) 28%, transparent) 0%,
    color-mix(in srgb, var(--c-background) 48%, transparent) 25%,
    color-mix(in srgb, var(--c-background) 60%, transparent) 45%,
    color-mix(in srgb, var(--c-background) 68%, transparent) 70%,
    color-mix(in srgb, var(--c-background) 92%, transparent) 100%);
}
/* « L'EXCELLENCE. », 8,39 em, est le plus long mot du titre : le plancher plafonné en `vw` le
   tient dans sa colonne dès 320 px, à 100 et à 200 % (le banc des titres le rejoue). */
.ecran--pour-qui .titre-ecran { font-size: clamp(min(2.5rem, 10.3vw), 6.6vw, 6.5rem); }
.reperes { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6); margin-top: var(--s-3); color: var(--c-text-primary); }

/* ── 03 · Les quatre disciplines, en cartes photo ─────────────────────────── */
/* Retouche du porteur (2026-10-14) : la mosaïque cède la place au style des autres écrans, un
   en-tête de section puis quatre cartes. Sur téléphone, elles se feuillettent au doigt : un
   défilement horizontal qui s'accroche carte à carte, la suivante dépassant du bord. */
.disciplines { display: grid; gap: var(--s-9); }
.disciplines__entete { display: grid; gap: var(--s-5); justify-items: start; }
/* Le défilement déborde de la gouttière jusqu'au bord de l'écran (les cartes y glissent au lieu
   d'y être rognées) et la reprend en marge intérieure : la première carte reste alignée sur le
   texte. Le `padding-block` laisse la place du survol (la carte monte de 4 px). */
.cartes {
  display: flex; gap: var(--s-4); overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding-inline: var(--gouttiere);
  margin-inline: calc(-1 * var(--gouttiere)); padding: var(--s-2) var(--gouttiere);
  scrollbar-width: thin; scrollbar-color: var(--c-border-strong) transparent;
}
/* Le conteneur défilant se prend au clavier (`tabindex="0"`) : son anneau de focus reste dans
   ses bords, que le corps rogne à gauche et à droite. */
.cartes:focus-visible { outline-offset: -2px; }
.carte {
  position: relative; isolation: isolate; overflow: hidden; display: grid; align-items: end;
  aspect-ratio: 4 / 5; flex-grow: 0; flex-shrink: 0; flex-basis: 78%; scroll-snap-align: start;
  /* À 200 % de texte, 78 % ne laisse plus la place du mot « MUSCULAIRE » : la carte prend alors
     15 rem, ou toute la largeur si elle est plus étroite. À 100 %, 15 rem ne joue jamais. */
  min-width: min(15rem, 100%);
  border: 1px solid var(--c-border); border-radius: var(--r-large); background: var(--c-surface);
  transition: transform var(--m-standard) var(--m-curve);
}
@media (min-width: 640px) { .carte { flex-basis: 44%; } }
.carte__photo {
  position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--m-standard) var(--m-curve);
}
/* La traction impose son cadrage (lockedAlignment de l'app : Alignment(0, -0.75)). */
.carte--agres .carte__photo { object-position: 50% 12.5%; }
.carte--em .carte__photo { object-position: 50% 42%; }
/* Deux voiles pour la lisibilité, non pour le décor : un voile plat de 0,3 et un dégradé
   qui part à 25 % et finit à 96 % (les valeurs mesurées pour 4,5:1 par pixel sur ces
   photos, site du 2026-10-09). Le texte est en texte principal, jamais en olive. */
.carte::before, .carte::after { content: ""; position: absolute; inset: 0; z-index: -1; }
.carte::before { background: var(--c-background); opacity: .3; }
.carte::after { background: linear-gradient(180deg, transparent 25%, color-mix(in srgb, var(--c-background) 96%, transparent) 100%); }
/* `min-width: 0` : sans lui, le mot le plus long du texte élargit la case au-delà de la
   carte, qui le rogne. */
.carte__texte { display: grid; gap: var(--s-1); min-width: 0; padding: var(--s-5); }
/* Le plancher du titre se plafonne en `vw` (sans quoi « MUSCULAIRE », un mot en capitales qui ne
   se coupe pas, sortirait de la carte à 200 % de texte) et monte avec le texte tant que la
   carte a la place : 36 px à 200 % sur 360 px, où la carte prend toute la largeur. */
.carte h3 {
  font-family: var(--police-titre); font-weight: 400; text-transform: uppercase; line-height: 1.05;
  font-size: clamp(min(1.125rem, 10vw), 4vw, 1.75rem);
}
.carte p { font-size: clamp(.875rem, 1.1vw, 1rem); line-height: 1.4; color: var(--texte-attenue); overflow-wrap: break-word; }
/* À partir de 900 px, les quatre cartes tiennent sur une ligne. */
@media (min-width: 900px) {
  .cartes {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4);
    overflow: visible; scroll-snap-type: none; margin-inline: 0; padding: 0;
  }
  .carte { min-width: 0; }
  .carte h3 { font-size: clamp(min(1.125rem, 2.2vw), 2.2vw, 1.75rem); }
}
@media (hover: hover) {
  .carte:hover { transform: translateY(-4px); }
  .carte:hover .carte__photo { transform: scale(1.04); }
}

/* ── 04 · Le coach ─────────────────────────────────────────────────────────── */
/* Une grille à zones, dans l'ordre du DOM : l'en-tête (surtitre, titre, chapeau), le téléphone,
   les exemples (onglets, conversations, « Il propose, tu décides. »). Sous 900 px, les trois
   zones se suivent, et le téléphone n'est qu'un aperçu : la capture, recadrée net en bas, montre
   le haut du planning sur environ un tiers de l'écran, juste sous le titre, avant les onglets.
   À partir de 900 px, le téléphone entier occupe la colonne de gauche sur toute la hauteur ;
   l'en-tête et les exemples, centrés en bloc, sont à droite (retouche du 2026-10-14). */
.coach {
  display: grid; gap: var(--s-7); align-items: start;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "entete" "telephone" "exemples";
}
.coach__entete, .coach__exemples { display: grid; gap: var(--s-6); align-content: start; min-width: 0; }
.coach__entete { grid-area: entete; }
.coach__exemples { grid-area: exemples; }
/* L'aperçu : une hauteur fixe qui rogne le téléphone, dont seule la capture est un contenu (son
   texte alternatif décrit la semaine entière). `svh`, non `vh` : la barre d'adresse d'un
   téléphone ne rogne pas l'aperçu au défilement. */
.coach__telephone { grid-area: telephone; display: grid; justify-items: center; height: clamp(220px, 34svh, 340px); overflow: hidden; }
.coach__telephone .telephone { width: min(64vw, 300px); }
@media (min-width: 900px) {
  .coach {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    grid-template-rows: 1fr auto auto 1fr;
    grid-template-areas: "telephone ." "telephone entete" "telephone exemples" "telephone .";
    gap: 0; column-gap: var(--s-10);
  }
  .coach__telephone { height: auto; overflow: visible; align-self: center; }
  .coach__exemples { margin-top: var(--s-6); }
  .coach .titre-section { font-size: clamp(min(2.25rem, 4.4vw), 4.4vw, 4rem); }
}
/* Les onglets : la puce choisie est cerclée d'accent sur fond `surface`, jamais remplie
   d'accent (un seul bouton d'accent par écran, spec § 1). */
.coach__onglets { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.coach__onglets button {
  min-height: var(--touch-min); padding: 0 var(--s-5); border: 1px solid var(--c-border);
  border-radius: var(--r-pill); background: transparent; color: var(--texte-attenue);
  font: 600 .9375rem/1.2 var(--police-texte); cursor: pointer; touch-action: manipulation;
  transition: transform var(--m-fast) var(--m-curve);
}
/* Sur téléphone, les trois onglets se partagent la ligne. Avec 20 px de marge, ils mesuraient
   343 px pour 351 à 390 px : « Remplacer » partait seul à la ligne (relecture Impeccable). Avec
   12 px et `flex: 1 1 auto`, ils tiennent sur une ligne dès 360 px et la remplissent ; à 200 %
   de texte, ils repassent à la ligne, et le dernier prend toute la largeur. */
@media (max-width: 639px) {
  .coach__onglets button { flex: 1 1 auto; padding-inline: var(--s-3); }
}
.coach__onglets button[aria-selected="true"] { border-color: var(--c-primary); background: var(--c-surface); color: var(--c-text-primary); }
@media (hover: hover) {
  .coach__onglets button:hover { color: var(--c-text-primary); }
}
.coach__onglets button:active { transform: scale(.98); }
.exemple { display: grid; gap: var(--s-4); max-width: 36rem; }
.exemple:focus-visible { outline-offset: var(--s-3); border-radius: var(--r-large); }
/* Sans script, les trois exemples se suivent, séparés d'un filet ; avec le script, un seul
   est visible et ne porte pas ce filet. */
.exemple:not([hidden]) ~ .exemple:not([hidden]) { padding-top: var(--s-6); border-top: 1px solid var(--c-border); }
.exemple__legende { color: var(--c-text-secondary); font-size: .75rem; }
.conversation { display: grid; gap: var(--s-3); }
.bulle { max-width: min(34ch, 90%); padding: var(--s-4) var(--s-5); border-radius: var(--r-large); font-size: 1rem; line-height: 1.45; }
.bulle--toi { justify-self: end; background: var(--c-primary-container); border-bottom-right-radius: var(--r-small); }
.bulle--coach { justify-self: start; display: grid; gap: var(--s-2); background: var(--c-surface); border: 1px solid var(--c-border); border-bottom-left-radius: var(--r-small); }
/* Le nom du coach est précédé de l'emblème, jamais d'un visage, et jamais d'un `<img>`. */
.bulle__auteur {
  display: flex; align-items: center; gap: var(--s-2); color: var(--c-text-secondary);
  font: 500 .6875rem/1.2 var(--police-mono); letter-spacing: .16em; text-transform: uppercase;
}
.bulle__auteur::before { content: ""; flex: none; width: 18px; height: 18px; background: url("/marque/capeo_mark_light.svg") center / contain no-repeat; }
/* La carte de proposition, comme celle de l'app (`coach_cards.dart`) : une surface, un filet
   et le rayon de 12, sans libellé. Le trait d'accent vertical de 3 px sur son bord gauche est
   retiré : Impeccable le nomme (un filet de couleur de plus d'un pixel sur le bord d'une
   carte), et la carte de l'app n'en porte pas. */
.proposition {
  justify-self: start; padding: var(--s-4) var(--s-5); border: 1px solid var(--c-border);
  border-radius: var(--r-medium); background: var(--c-surface-subtle); font-size: 1rem; font-weight: 600;
}
.coach__decision { font-size: 1.125rem; font-weight: 600; }

/* ── 05 · Les tarifs ───────────────────────────────────────────────────────── */
.tarifs { display: grid; justify-items: center; gap: var(--s-6); text-align: center; }
.tarifs .chapeau { text-wrap: balance; }
.tarifs .titre-section::after { margin-inline: auto; }
/* Les deux formules sont côte à côte à toutes les largeurs, dès 320 px : aucune requête de
   fenêtre ne les empile (retouche R10). Le bloc fait 40 rem, soit 640 px à 100 % ; il suit la
   taille du texte, comme le prix, qui ne doit jamais sortir de sa carte. Chaque carte prend
   trois lignes de la grille (`subgrid`) : la tête, le prix et l'équivalent de l'une sont
   alignés sur ceux de l'autre, même quand la pastille passe sous le nom. */
.formules {
  --ecart: var(--s-3);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ecart);
  container-name: formules; container-type: inline-size;
  width: 100%; max-width: 40rem; margin-top: var(--s-4); text-align: start;
}
@media (min-width: 640px) { .formules { --ecart: var(--s-4); } }
.formule {
  display: grid; grid-row: span 3; grid-template-rows: subgrid; gap: var(--ecart);
  padding: var(--s-4); border: 1px solid var(--c-border); border-radius: var(--r-panel); background: var(--c-surface);
}
@media (min-width: 640px) { .formule { padding: var(--s-5); } }
.formule--avant { border-color: var(--c-primary); }
.formule__tete { display: flex; flex-wrap: wrap; align-content: start; align-items: center; justify-content: space-between; gap: var(--s-1) var(--s-3); }
.formule__nom { font: 600 1.125rem/1.2 var(--police-texte); }
/* Archivo Black ; « 99,99 € » (insécable) y mesure 4,0011 em. Le plancher est plafonné en `vw` :
   un plancher en `rem` doublerait à 200 % (banc `test_les_mots_d_une_carte_tiennent_...`). */
.formule__prix { font-family: var(--police-titre); font-size: clamp(min(1.625rem, 7.5vw), 7.5vw, 1.625rem); line-height: 1; }
@media (min-width: 640px) { .formule__prix { font-size: clamp(min(2.25rem, 5.6vw), 5.6vw, 2.25rem); } }
/* La période passe sous le prix, en petit. */
.formule__prix small { display: block; margin-top: var(--s-2); font: 500 .875rem/1.3 var(--police-texte); color: var(--c-text-secondary); }
.formule__equivalent { font-size: 1rem; line-height: 1.4; color: var(--texte-attenue); }
/* Seule exception : le texte agrandi. La requête est en `em`, l'unité du texte du conteneur :
   à 200 %, 15 em valent 540 px, et les deux cartes s'empilent quand elles ne tiennent plus
   côte à côte (« engagement. », 199 px à 200 %, est le premier mot à sortir : il lui faut un
   bloc de 478 px) ; à 100 %, 15 em valent 270 px, sous le bloc du plus petit téléphone (288 px
   à 320 px), qui garde donc ses deux colonnes. Empilé, le prix a toute la largeur : il regrossit
   pour garder sa place devant le texte, agrandi lui aussi. Le banc rejoue ce calcul de 320 à
   1 440 px, à 100 et 200 %. */
@container formules (max-width: 15em) {
  .formule { grid-column: 1 / -1; }
  .formule__prix { font-size: clamp(min(2.25rem, 14vw), 14vw, 2.25rem); }
}
/* La pastille d'économie, en accent sur son conteneur : jamais remplie d'accent, le
   bouton d'accent de l'écran est celui de la barre. */
.economie { padding: 1px var(--s-2); border-radius: var(--r-pill); background: var(--c-primary-container); color: var(--c-primary); font: 600 .75rem/1.5 var(--police-texte); }
.formules__notice { max-width: 62ch; font-size: .875rem; line-height: 1.6; color: var(--c-text-secondary); text-wrap: pretty; }
.tarifs__telecharger { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s-8); margin-top: var(--s-4); }
.tarifs__faq { width: 100%; max-width: 820px; margin-top: var(--s-8); text-align: start; }
.faq { display: grid; gap: var(--s-3); }
.faq details { border: 1px solid var(--c-border); border-radius: var(--r-large); background: var(--c-surface); }
.faq summary { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); min-height: var(--touch-min); padding: var(--s-4) var(--s-6); list-style: none; cursor: pointer; font-weight: 600; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; content: "+" / ""; flex: none; font-family: var(--police-mono); font-size: 1.25rem; line-height: 1; color: var(--c-primary); transition: transform var(--m-standard) var(--m-curve); }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq__reponse { display: grid; gap: var(--s-3); padding: 0 var(--s-6) var(--s-6); color: var(--texte-attenue); }
.faq__reponse a { color: var(--c-primary); }

/* ── Le mouvement (spec § 5) ───────────────────────────────────────────────── */
/* Rien n'est caché hors de `html.js-anime`, que le script ne pose que si
   `IntersectionObserver` existe et que la réduction des mouvements n'est pas demandée ;
   et rien ne bouge hors de `prefers-reduced-motion: no-preference`. Les entrées tiennent
   leur état de départ par `backwards` seulement : la fin rend la main au style de la
   carte, dont le survol peut alors jouer. Courbe : celle des tokens. */
@keyframes monter-ligne { from { transform: translateY(105%); } }
@keyframes reculer { from { transform: scale(var(--recul, 1.08)); } }
@keyframes entrer { from { opacity: 0; transform: translateY(var(--depart, 16px)) scale(var(--echelle, 1)); } }
@keyframes apparaitre { from { opacity: 0; } }
@keyframes parallaxe { from { transform: translateY(-6%); } to { transform: translateY(6%); } }

@media (prefers-reduced-motion: no-preference) {
  /* Le masque des lignes : un peu de marge en haut et en bas, pour les accents des
     capitales (« RÉVÈLE ») et la cédille (« CONÇU »). */
  .js-anime .ligne { overflow: hidden; padding-block: .16em .12em; margin-block: -.16em -.12em; }

  /* 01, au chargement : la photo recule, les lignes montent de leur masque à 60 ms
     d'écart. Le chapeau, les badges et la mention ne partent jamais d'une opacité nulle
     (la photo est l'élément du LCP). */
  .js-anime .ecran--intro .ecran__photo { animation: reculer 1600ms var(--m-curve) backwards; }
  .js-anime .ecran--intro .ligne > span { animation: monter-ligne 500ms var(--m-curve) backwards; }
  .js-anime .ecran--intro .ligne + .ligne > span { animation-delay: 60ms; }

  /* 02, à l'entrée : la photo est visible dès qu'elle arrive (le rideau qui la couvrait est
     supprimé, retouche du porteur 2026-10-14) et recule ; les lignes montent, puis le texte et
     les repères, à 80 ms d'écart. */
  .js-anime .ecran--pour-qui:not(.est-vu) .ligne > span { transform: translateY(105%); }
  .js-anime .ecran--pour-qui:not(.est-vu) :where(.surtitre, .chapeau, .reperes li) { opacity: 0; }
  .js-anime .ecran--pour-qui.est-vu .ecran__photo { --recul: 1.1; animation: reculer 1100ms var(--m-curve) backwards; }
  .js-anime .ecran--pour-qui.est-vu .ligne > span { animation: monter-ligne 500ms var(--m-curve) 240ms backwards; }
  .js-anime .ecran--pour-qui.est-vu .ligne + .ligne > span { animation-delay: 300ms; }
  /* `:where()` : la règle commune ne pèse que (0,3,0), et le délai propre du chapeau et des
     repères l'emporte (`:is()` prenait le poids de `.reperes li` et le chapeau partait
     avant les lignes). Ordre : surtitre, lignes, chapeau, repères. */
  .js-anime .ecran--pour-qui.est-vu :where(.surtitre, .chapeau, .reperes li) { animation: entrer 600ms var(--m-curve) 160ms backwards; }
  .js-anime .ecran--pour-qui.est-vu .chapeau { animation-delay: 420ms; }
  .js-anime .ecran--pour-qui.est-vu .reperes li:nth-child(1) { animation-delay: 500ms; }
  .js-anime .ecran--pour-qui.est-vu .reperes li:nth-child(2) { animation-delay: 580ms; }
  .js-anime .ecran--pour-qui.est-vu .reperes li:nth-child(3) { animation-delay: 660ms; }

  /* 03, à l'entrée : les cartes en cascade, à 70 ms d'écart (la mosaïque comptait cinq tuiles,
     son titre en tête : les quatre cartes sont maintenant les quatre premiers enfants). */
  .js-anime section:not(.est-vu) .carte { opacity: 0; }
  .js-anime section.est-vu .carte { --echelle: .97; animation: entrer 600ms var(--m-curve) backwards; }
  .js-anime section.est-vu .carte:nth-child(2) { animation-delay: 70ms; }
  .js-anime section.est-vu .carte:nth-child(3) { animation-delay: 140ms; }
  .js-anime section.est-vu .carte:nth-child(4) { animation-delay: 210ms; }

  /* 04, à l'entrée : le téléphone monte de 40 px. Au changement d'onglet, dans le panneau
     entrant, la bulle « Toi », la bulle « Coach » puis la carte montent de 8 px, à 0, 240
     et 480 ms. */
  .js-anime .coach:not(.est-vu) .coach__telephone { opacity: 0; }
  .js-anime .coach.est-vu .coach__telephone { --depart: 40px; animation: entrer 800ms var(--m-curve) backwards; }
  .js-anime .exemple.entre :is(.bulle, .proposition) { --depart: 8px; animation: entrer var(--m-slow) var(--m-curve) backwards; }
  .js-anime .exemple.entre .bulle--coach { animation-delay: 240ms; }
  .js-anime .exemple.entre .proposition { animation-delay: 480ms; }

  /* 05, à l'entrée : les cartes montent de 16 px, à 80 ms d'écart. La réponse d'une
     question apparaît en fondu ; sa hauteur ne s'anime pas. */
  .js-anime .tarifs:not(.est-vu) .formule { opacity: 0; }
  .js-anime .tarifs.est-vu .formule { animation: entrer 600ms var(--m-curve) backwards; }
  .js-anime .tarifs.est-vu .formule + .formule { animation-delay: 80ms; }
  .faq details[open] .faq__reponse { animation: apparaitre var(--m-standard) var(--m-curve); }
}

/* La parallaxe des photos 01 et 02, liée au défilement. Sans prise en charge, la photo
   reste immobile. Le fond déborde de 8 % en haut et en bas, assez pour ses ±6 %. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .ecran__fond { inset: -8% 0; animation: parallaxe linear both; animation-timeline: view(); animation-range: cover; }
  }
}

/* ── Passage d'une page à l'autre ──────────────────────────────────────────── */
@view-transition { navigation: auto; }
/* La barre fixe a son propre nom de transition : sans lui, elle se fond avec la
   racine à chaque changement de page ; avec lui, elle reste en place et seuls
   ses liens changent d'état. */
.navigation { view-transition-name: navigation; }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root), ::view-transition-new(root),
  ::view-transition-old(navigation), ::view-transition-new(navigation) { animation-duration: var(--m-slow); animation-timing-function: var(--m-curve); }
}

/* ── Réduction des animations ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* ── Contraste renforcé et couleurs forcées ────────────────────────────────── */
/* Les couleurs forcées (Windows) remplacent les fonds et les ombres : ce qui ne
   tenait que par un fond ou par `box-shadow` doit garder un trait. */
@media (forced-colors: active) {
  .bouton, .coach__onglets button { border: 1px solid ButtonText; }
  .coach__onglets button[aria-selected="true"] { border: 2px solid Highlight; }
  .economie { border: 1px solid CanvasText; }
  .telephone { outline: 1px solid CanvasText; }
  .carte, .bulle, .proposition { border: 1px solid CanvasText; }
  /* Le texte d'une carte garde un fond à lui : le voile, un fond, est remplacé. Les
     photos des écrans plein cadre sont un décor, que le texte ne peut plus couvrir. */
  .carte .carte__texte { background: Canvas; }
  .ecran__fond { display: none; }
  .navigation::before { opacity: 1; }
  /* Le lockup et le mot « CAPEO » de l'intro sont clairs, dessinés pour le fond de la marque :
     sur un fond de système clair ils disparaîtraient. Ils gardent donc leur fond, et leurs
     couleurs. */
  .navigation__logo img, .pied__marque img, .titre-ecran .surtitre img {
    forced-color-adjust: none; background: var(--c-background);
    box-shadow: 0 0 0 var(--s-2) var(--c-background); border-radius: var(--r-small);
  }
}
/* Le contraste renforcé retire les filets décoratifs et le texte atténué. */
@media (prefers-contrast: more) {
  :root { --c-border: var(--c-border-strong); --texte-attenue: var(--c-text-primary); }
}
