/* Design system v1 — variante « classique », tokens de forme v2 (11.1),
 * variante de layout « éditorial » (11.2), token de mouvement (11.12).
 *
 * Feuille unique, partagée par tous les cabinets. Tout ce qui distingue un
 * cabinet d'un autre arrive par les custom properties du thème, injectées dans
 * un bloc `:root` APRÈS cette feuille (ordre du head rétabli en 11.22) :
 * --color-bg, --color-surface,
 * --color-text, --color-primary, --color-on-primary, --font-heading,
 * --font-body, --scale, --radius, --radius-sm, --radius-lg, --space-scale,
 * --heading-transform, --heading-tracking, --logo-height, --color-muted,
 * --rule-mix-classic, --rule-mix-editorial, --container-max, et les trois
 * échelons de titre --step-3, --step-4, --hero-title-colonne (11.10).
 * Le `:root` inline et celui d'ici ont la MÊME spécificité : c'est l'ordre du
 * document qui tranche, et il donne le dernier mot au cabinet. Toute valeur
 * écrite en `:root` ci-dessous est donc un REPLI — servi seulement si le bloc
 * inline ne porte pas la variable (thème antérieur, cabinet sans thème) :
 * --container-max (11.21) et les trois échelons de titre (11.10) sont dans ce
 * cas, et leur liste est close par test. `layout-editorial` redéfinit
 * --container-max sur le body pour sa colonne de revue, et gagne alors par
 * spécificité, quel que soit l'ordre. Les tokens structurels arrivent en
 * classes sur <body> :
 * layout-classique | layout-editorial,
 * btn-plein | btn-contour, hero-sobre | hero-bandeau | hero-encre,
 * hero-geo-scinde | hero-geo-empile | hero-geo-plein,
 * motion-aucun | motion-discret | motion-marque | motion-soutenu,
 * footer-colonnes | footer-bandeau,
 * sep-aucun | sep-filet | sep-bandeau | sep-encoche,
 * header-statique | header-adherent,
 * header-confort | header-compact.
 * header-confort pose les custom properties de densité verticale (11.17,
 * --header-min-h et --header-logo-max depuis 11.18).
 *
 * Règle de contraste : le texte n'utilise jamais --color-primary, dont le seul
 * couple validé sur le fond de page l'est au seuil « grand texte / éléments
 * d'interface » (3:1). La couleur principale ne sert donc qu'aux éléments non
 * textuels — filets, liserés, contour de focus — et aux boutons pleins, où elle
 * est associée à --color-on-primary, couple validé à 4,5:1 par le modèle.
 */

/* ─── Mini-reset ──────────────────────────────────────────── */

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

body,
h1,
h2,
h3,
h4,
p,
ul,
figure {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

img {
  display: block;
  max-width: 100%;
}

input,
textarea,
button {
  font: inherit;
  color: inherit;
}

/* ─── Variables locales ───────────────────────────────────── */

:root {
  /* Échelle typographique dérivée du token --scale du thème.
     Titres en clamp : min/max proportionnels à l'échelle (pas de nouveau
     champ, pas de media query). La fluide reste sous le max jusqu'à 1920 px. */
  --step--1: calc(1rem / var(--scale));
  --step-1: calc(1rem * var(--scale));
  --step-2: calc(var(--step-1) * var(--scale));
  /* Petit texte, avec un plancher de lisibilité indépendant de l'échelle :
     un avertissement déontologique ne descend jamais sous 14 px. Les
     surtitres (eyebrows) ont leur propre plancher, 13 px (11.17). */
  --step-small: max(0.875rem, var(--step--1));
  --step-eyebrow: max(0.8125rem, var(--step--1));

  /* --radius et --space-scale viennent du thème. Fallbacks = rendu v1
     (thème antérieur dont le :root inline ne les définirait pas encore). */
  --space: calc(clamp(1rem, 0.5rem + 2vw, 2rem) * var(--space-scale, 1));
  --measure: 68ch;

  /* Largeur du conteneur de corps. UNE seule source : la gouttière du héros
     s'en déduit (11.14), et la variante éditoriale n'a qu'à la redéfinir.
     Depuis 11.21 c'est un token de thème (« Largeur de page ») : la valeur
     ci-dessous est le repli v1, servi au seul :root inline qui ne la porterait
     pas. Elle ne peut plus écraser le thème — la feuille passe AVANT le bloc
     de tokens depuis 11.22, c'est ce qui rend ce repli honnête. La mesure de
     lecture reste --measure : élargir la page n'allonge aucune ligne. */
  --container-max: 64rem;

  /* Rythme vertical : UN clamp, deux multiplicateurs de --space-scale.
     1.2 × 2,5–5 rem reconstruit le pas héros (3–6 rem) sans seconde échelle. */
  --space-hero-mult: 1.2;
  --space-section-mult: 1;
  --space-block: clamp(2.5rem, 1.5rem + 5vw, 5rem);
  --hero-padding: calc(
    var(--space-block) * var(--space-scale, 1) * var(--space-hero-mult)
  );

  /* Filets : mix issu du thème (moyen = 20 % / 55 %, contrat v1). */
  --rule: color-mix(
    in srgb,
    var(--color-text) var(--rule-mix-classic, 20%),
    var(--color-bg)
  );

  /* Marqueur FAQ : primaire autorisée sur le glyphe, jamais sur le texte. */
  --faq-marker: var(--color-primary);

  /* Chrome d'en-tête (11.18) : la densité pose la hauteur, pas le logo.
     Confort est le défaut ; compact et adhérent redéfinissent.
     Encoche : amorce à x=0, arrêt à cette fraction. */
  --header-min-h: 68px;
  --header-pad-y: 0.5rem;
  --header-logo-max: 36px;
  --encoche-span: 72%;

  /* Trois axes de diversité (11.20). Les valeurs par défaut REPRODUISENT le
     rendu v1 : héros à gauche, photos naturelles, portraits libres, cartes à
     liseré. Chaque axe est un jeu de custom properties posé par une classe de
     body — et redéfini localement par les échantillons du styleguide, qui s'en
     immunisent ainsi sans copier une seule valeur. */
  --hero-align: start;
  --hero-align-margin: 0;
  --img-filter: none;
  --img-veil-alpha: 0;
  --portrait-width: auto;
  --portrait-ratio: auto;
  --portrait-fit: fill;
  --portrait-radius: 0;
  --card-border-top-width: 3px;
  --card-border-top-color: var(--color-primary);
  --card-shadow: none;

  /* Échelons de titre (section et héros) — REPLI de la feuille depuis la
     11.10. Le facteur --display-scale a disparu : chaque ampleur porte ses
     PROPRES bornes dans TITLE_IMPACT_TOKENS et le thème émet les trois
     échelons tels quels. Un facteur unique avait échoué deux fois — il ne peut
     pas être à la fois perceptible à 1280 px (11.21 : +21 % seulement) et
     borné sur téléphone (11.22 : le rendu mobile gonflait avec la base). Le
     piège de substitution s'en va avec lui : un échantillon du styleguide n'a
     plus à recalculer un échelon, il pose le clamp de son profil sur
     --hero-title-size, et ces trois déclarations reviennent dans le :root
     commun.

     Les valeurs ci-dessous sont celles du profil « mesure », c'est-à-dire le
     rendu v1 au pixel près. Ce n'est pas une seconde source : un test les
     compare, aux espaces près, à la sortie du modèle pour « mesure ». Elles
     servent quand une page arrive sans tokens — la feuille est un repli,
     jamais un concurrent (doctrine de cascade 11.22).

     --hero-title-size : le h1 se mesure sur le viewport ; le héros scindé le
     redéfinit sur --hero-title-colonne, en unités de conteneur. */
  --hero-title-size: var(--step-4);
  --step-3: clamp(
    calc(1.15rem * var(--scale) / 1.25),
    calc(0.95rem * var(--scale) / 1.25 + 0.85vw),
    calc(1.8rem * var(--scale) / 1.25)
  );
  --step-4: clamp(
    calc(1.55rem * var(--scale) / 1.25),
    calc(1.15rem * var(--scale) / 1.25 + 1.45vw),
    calc(2.6rem * var(--scale) / 1.25)
  );
  --hero-title-colonne: clamp(
    calc(1.55rem * var(--scale) / 1.25),
    calc(1.15rem * var(--scale) / 1.25 + 1.45cqi),
    calc(2.6rem * var(--scale) / 1.25)
  );
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  overflow-wrap: break-word;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-heading);
  font-weight: 600;
  line-height: 1.2;
  text-transform: var(--heading-transform, none);
  letter-spacing: var(--heading-tracking, 0);
}

h3 {
  font-size: var(--step-2);
}

/* Hiérarchie du styleguide : le reset annule les marges. Scopé pour
   ne pas pousser .card__title ni .profile__name. */
#specimen h3,
#tokens-forme h3,
#mouvement h3 {
  margin-top: 1.75rem;
}

h4 {
  font-size: var(--step-1);
}

p + p {
  margin-top: 1rem;
}

a {
  color: var(--color-text);
}

/* ─── Accessibilité plancher ──────────────────────────────── */

:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 30;
  padding: 0.75rem 1rem;
  background: var(--color-primary);
  color: var(--color-on-primary);
  transform: translateY(-150%);
}

.skip-link:focus {
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    animation-timeline: none;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }
}

/* ─── Enveloppes ──────────────────────────────────────────── */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space);
}

.container--narrow {
  /* Largeur de mesure conservée, bord GAUCHE calé sur le conteneur de page
     (doctrine 11.14 précisée en 11.17). Sous éditorial, --container-max
     vaut déjà la mesure : le calcul retombe sur le centrage actuel. */
  max-width: calc(var(--measure) + 4rem);
  margin-inline-start: max(0px, calc((100% - var(--container-max)) / 2));
  margin-inline-end: auto;
}

/* ─── En-tête et navigation ───────────────────────────────── */

.header {
  position: relative;
  z-index: 20;
  overflow: visible;
  min-height: var(--header-min-h, 68px);
  border-bottom: 1px solid var(--rule);
  background: var(--color-bg);
}

.header__inner {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem var(--space);
  padding-block: var(--header-pad-y, 0.5rem);
}

.header__name {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* Le lien du logo ne se comprime jamais sous sa taille de cible : le
     monogramme tombait à width:0 quand la rangée devenait trop étroite. */
  min-width: 44px;
  min-height: 44px;
  font-family: var(--font-heading);
  font-size: var(--step-1);
  text-decoration: none;
}

.header__logo {
  display: block;
  height: min(var(--logo-height, 2rem), var(--header-logo-max, 36px));
  max-height: var(--header-logo-max, 36px);
  width: auto;
  flex-shrink: 0;
}

.header__wordmark {
  min-width: 0;
}

.header__name--has-logo .header__wordmark {
  /* Silhouette desktop : le monogramme suffit ; le nom reste dans le DOM. */
  display: none;
}

.header__phone {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  white-space: nowrap;
}

body.header-confort {
  --header-min-h: 68px;
  --header-pad-y: 0.5rem;
  --header-logo-max: 36px;
}

body.header-compact {
  --header-min-h: 52px;
  --header-pad-y: 0.25rem;
  --header-logo-max: 28px;
}

body.header-compact .header__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le plancher de cible 44 px prime sur l'écart esthétique de densité
   (doctrine amendée à la recette 11.18) : en compact, les cibles de la barre
   font 44 px EXACTEMENT, faute de quoi le padding du bouton les poussait à
   47 px et le header à 56 px. La hauteur du header en découle :
   44 + 2 × --header-pad-y + 1 px de filet = 53 px, bordure incluse. */
body.header-compact .header .button,
body.header-compact .header__phone,
body.header-compact .nav-repli > summary {
  height: 44px;
  padding-block: 0;
}

body.header-adherent {
  --header-max-height: 64px;
}

/* min-height l'emporte sur max-height si plus grand : le plafond
   adhérent redéfinit --header-min-h pour rester à 64 px bordure incluse. */
body.header-adherent.header-confort {
  --header-min-h: 64px;
}

body.header-adherent .header {
  position: sticky;
  top: 0;
  max-height: var(--header-max-height, 64px);
  box-shadow: 0 1px 0 var(--rule), 0 4px 12px color-mix(in srgb, var(--color-text) 8%, transparent);
}

body.header-adherent .header__inner {
  max-height: 100%;
}

.nav-desktop {
  display: block;
}

.nav-repli {
  display: none;
}

.nav-repli > summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 1rem;
  font-family: var(--font-heading);
  cursor: pointer;
  list-style: none;
  background: var(--color-surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius, 4px);
}

.nav-repli > summary::-webkit-details-marker {
  display: none;
}

.nav-repli > summary::marker {
  content: "";
}

.nav {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.25rem 1.5rem;
}

/* Apparence du panneau replié : HORS media query, et une seule fois. Le
   <details> lui-même est `display: none` au-dessus du seuil, ce qui neutralise
   tout son sous-arbre — ces règles ne peignent donc rien tant qu'une bascule
   ne l'a pas montré. Les sortir des seuils est ce qui permet d'en avoir DEUX
   (11.22) sans recopier un corps de règle : seules les bascules `display`
   vivent dans les media queries ci-dessous. */
.nav-repli[open] > summary {
  background: var(--color-bg);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

.nav-repli > .nav {
  display: none;
  position: absolute;
  top: 100%;
  right: 0;
  left: 0;
  z-index: 21;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 0;
  padding: 0.5rem var(--space);
  background: var(--color-bg);
  border-bottom: 1px solid var(--rule);
  box-shadow: 0 8px 16px color-mix(in srgb, var(--color-text) 12%, transparent);
}

.nav-repli[open] > .nav {
  display: flex;
}

/* CONTRAT DE DENSITÉ DE LA RANGÉE DESKTOP (11.22), gravé :
     ≤ 4 entrées → repli à 42 rem ;
       5 entrées → repli à 53 rem (classe `nav-dense`, posée SERVEUR) ;
     > 5 entrées → HORS GARANTIE, recette visuelle obligatoire avant mise en
                   ligne.
   Histoire du seuil : 40 rem laissait déborder cinq liens entre 641 et 665 px
   (recette 11.8) ; 42 rem n'a fait que DÉPLACER la bande cassée en 673-711 px
   (recette 11.21), parce que cinq entrées ne tiennent pas dans la rangée avant
   849 px. D'où le second seuil, réservé aux cabinets denses. Le pixel exact,
   puisqu'un seuil se lit à l'unité (correctif 11.10) : `max-width` est
   INCLUSIF, donc 53 rem = 848 px replie jusqu'à 848 px INCLUS, et la rangée
   desktop ne revient qu'à 849 px. Un cabinet à deux entrées garderait sa
   rangée jusqu'au bout : le replier plus tôt serait une régression pour lui. */
@media (max-width: 42rem) {
  .header__wordmark {
    display: none;
  }

  .nav-desktop {
    display: none;
  }

  .nav-repli {
    display: block;
  }
}

@media (max-width: 53rem) {
  body.nav-dense .header__wordmark {
    display: none;
  }

  body.nav-dense .nav-desktop {
    display: none;
  }

  body.nav-dense .nav-repli {
    display: block;
  }
}

.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: none;
}

.nav__link:hover {
  text-decoration: underline;
}

/* ─── Héros ───────────────────────────────────────────────── */

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

.hero__eyebrow {
  font-size: var(--step-eyebrow);
  letter-spacing: var(--heading-tracking, 0);
  text-transform: var(--heading-transform, none);
  color: var(--color-muted);
}

.hero__title {
  margin-top: 0.5rem;
  font-size: var(--hero-title-size);
  /* CÉSURE FRANÇAISE, pas coupure brute (11.10) : `overflow-wrap: anywhere`
     tranchait « sereinement » n'importe où, ce qui n'est pas de la
     typographie. `hyphens: auto` coupe entre syllabes — le `lang="fr"` de
     <html> le rend possible. L'ultime recours reste le `overflow-wrap:
     break-word` du body, hérité : il n'est pas recopié ici. */
  hyphens: auto;
}

/* Alignement du bloc texte (11.20). Le token est posé par le body ; un
   échantillon du styleguide le redéfinit sur lui-même et l'emporte alors sur
   la valeur héritée du cabinet servi — un seul corps de règle par valeur. */
body.hero-align-gauche,
.hero--align-gauche {
  --hero-align: start;
  --hero-align-margin: 0;
}

body.hero-align-centre,
.hero--align-centre {
  --hero-align: center;
  --hero-align-margin: auto;
}

.hero__texte {
  text-align: var(--hero-align, start);
}

/* Le héros élargit le BLOC, jamais la colonne de lecture (leçon du gabarit
   bandeau, 11.13) : en géométrie « empile », le texte courrait sinon sur
   toute la largeur du conteneur. */
.hero__about {
  max-width: var(--measure);
  margin-top: 1.25rem;
  /* Centrer le TEXTE ne suffit pas : le bloc de mesure — et le filet du héros
     plein qui le ferme, large de 100 % — resterait calé à gauche. */
  margin-inline: var(--hero-align-margin, 0);
}

.hero .button {
  margin-top: 1.75rem;
}

/* Géométries du héros (11.14), indépendantes du traitement de fond. Les deux
   géométries à image portent leur rythme vertical sur le bloc texte, l'image
   occupant toute sa zone. */
.hero--scinde,
.hero--empile {
  padding-block: 0;
}

.hero--scinde .hero__texte,
.hero--empile .hero__texte {
  padding-block: var(--hero-padding);
}

/* Scindé : deux zones (texte | image). L'image n'est jamais derrière le
   texte — le contraste AA reste celui du moteur de thèmes. Empilement
   étroit sans media query de largeur. */
.hero--scinde .hero__inner {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  align-items: stretch;
}

/* Gouttière unique (correctif d'audit) : la colonne de texte fait exactement
   la moitié de la page, donc un conteneur de demi-largeur calé à droite
   commence là où commence le conteneur de corps — sans vw ni media query.
   Plus jamais un titre à 20 px quand le corps commence à 148 px.

   Ce conteneur est aussi un QUERY CONTAINER (11.10) : le titre du scindé se
   dimensionne sur la boîte qui le porte, jamais sur le viewport. C'est bien
   ce conteneur-ci et non la colonne de grille : lui est plafonné à la
   demi-largeur de page, elle continue de croître avec l'écran — un h1
   monumental en vw débordait sa demi-page (recette 11.22). En pile étroite
   (une seule colonne), le conteneur vaut la largeur de page et le cqi rejoint
   le vw : le rendu téléphone ne change pas. */
.hero--scinde .hero__texte > .container {
  max-width: calc(var(--container-max) / 2);
  margin-inline: auto 0;
  container-type: inline-size;
  --hero-title-size: var(--hero-title-colonne);
}

.hero--scinde .hero__media {
  min-height: 12rem;
}

/* Empilé : texte pleine largeur, puis l'image en bandeau large SOUS le bloc
   texte. Le recadrage 21:9 rogne le haut et le bas, d'où object-position. */
.hero--empile .hero__media {
  aspect-ratio: 21 / 9;
}

/* Éditorial : la page est UNE colonne, le bandeau y reste (décision 11.14) ;
   en classique il file d'un bord à l'autre. */
body.layout-editorial .hero--empile .hero__media {
  max-width: calc(var(--container-max) - var(--space) * 2);
  margin-inline: auto;
}

.hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Plein : héros purement typographique, aucune image rendue. Le titre
   reste --step-4 (clamp) : plus de plafond intérimaire 2rem + 4vw. */
.hero--plein {
  padding-block: calc(var(--hero-padding) * 1.4);
}

/* Filet de mesure sous le sous-titre : seule marque graphique d'un héros
   sans image. */
.hero--plein .hero__about::after {
  content: "";
  display: block;
  width: 100%;
  max-width: var(--measure);
  height: 2px;
  margin-top: calc(var(--space) * 0.75);
  background: var(--rule);
}

/* Traitements du héros. Les classes locales (.hero--sobre, .hero--bandeau,
   .hero--encre) servent aux échantillons ; live : :not(.hero--echantillon)
   pour que le styleguide ne soit pas contaminé par le cabinet servi. */
body.hero-sobre .hero:not(.hero--echantillon),
.hero.hero--sobre {
  background: var(--color-bg);
  color: var(--color-text);
}

body.hero-bandeau .hero:not(.hero--echantillon),
.hero.hero--bandeau {
  background: var(--color-surface);
}

body.hero-encre .hero:not(.hero--echantillon),
.hero.hero--encre {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

/* Trois niveaux : le a global pose --color-text et casserait l'héritage.
   Restreint au héros pour que le reste de la page reste en --color-text.
   `:not(.button)` (correctif d'audit) : un bouton n'est pas un lien de texte,
   il ne se souligne jamais — la règle de 11.1 le frappait aussi. */
body.hero-encre .hero:not(.hero--echantillon) a:not(.button),
.hero.hero--encre a:not(.button) {
  color: var(--color-on-primary);
  text-decoration: underline;
}

body.hero-encre .hero:not(.hero--echantillon) .hero__eyebrow,
.hero.hero--encre .hero__eyebrow {
  color: var(--color-on-primary);
}

/* Un anneau de focus primaire sur un fond primaire est invisible : dans un
   bloc encre, il passe en sur-primaire (correctif d'audit, a11y — c'est le
   CTA principal du héros). */
body.hero-encre .hero:not(.hero--echantillon) :focus-visible,
.hero.hero--encre :focus-visible {
  outline-color: var(--color-on-primary);
}

/* Le filet de mesure du héros plein se lit sur le fond primaire : --rule est
   mélangé au fond de PAGE, il y disparaîtrait. */
body.hero-encre .hero:not(.hero--echantillon) .hero__about::after,
.hero.hero--encre .hero__about::after {
  background: color-mix(in srgb, var(--color-on-primary) 60%, transparent);
}

/* Encre + plein : inverser le bouton, sinon fond primaire sur fond primaire. */
body.hero-encre .hero:not(.hero--echantillon) .button--primary,
.hero.hero--encre .button--primary {
  background: var(--color-on-primary);
  color: var(--color-primary);
  border-color: var(--color-on-primary);
}

body.hero-encre .hero:not(.hero--echantillon) .button--primary:hover,
.hero.hero--encre .button--primary:hover {
  background: var(--color-on-primary);
}

/* Le survol global souligne les boutons : dans un bloc encre, jamais (le
   repère au survol reste l'élévation, quand le mouvement est actif). */
body.hero-encre .hero:not(.hero--echantillon) .button:hover,
.hero.hero--encre .button:hover {
  text-decoration: none;
}

/* Encre + contour : --color-text sur primaire n'est pas une paire AA.
   Texte et bordure en --color-on-primary (jamais en primaire).
   L'épaisseur 2 px vient de la règle contour unique. */
body.hero-encre.btn-contour .hero:not(.hero--echantillon) .button--primary,
.hero.hero--encre .token-sample--btn-contour .button--primary,
.token-sample--btn-contour .hero.hero--encre .button--primary {
  background: transparent;
  color: var(--color-on-primary);
  border-color: var(--color-on-primary);
}

/* ─── Sections ────────────────────────────────────────────── */

.section {
  padding-block: calc(
    var(--space-block) * var(--space-scale, 1) * var(--space-section-mult)
  );
  scroll-margin-top: var(--space);
}

body.header-adherent .section,
body.header-adherent .hero,
body.header-adherent [id] {
  scroll-margin-top: calc(var(--logo-height, 2rem) + 1.5rem + var(--space));
}

.section--surface {
  background: var(--color-surface);
}

/* Séparateurs de sections (11.15). sep-aucun est un no-op.
   Sélecteurs multiples : rendu réel + échantillons du styleguide.
   Immunisation 11.17 : :not(.section--echantillon) — un seul corps de règle. */
body.sep-filet .hero + .section:not(.section--echantillon),
body.sep-filet .section + .section:not(.section--echantillon),
.token-sample--sep-filet .section + .section {
  border-top: 1px solid var(--rule);
}

body.sep-bandeau .hero + .section:not(.section--echantillon)::before,
body.sep-bandeau .section + .section:not(.section--echantillon)::before,
body.sep-encoche .hero + .section:not(.section--echantillon)::before,
body.sep-encoche .section + .section:not(.section--echantillon)::before,
.token-sample--sep-bandeau .section + .section::before,
.token-sample--sep-encoche .section + .section::before {
  content: "";
  display: block;
  height: 1.25rem;
  margin-block: calc(-0.5 * var(--space-block) * var(--space-scale, 1) * var(--space-section-mult)) 0;
  background: var(--color-surface);
}

body.sep-bandeau .hero + .section:not(.section--echantillon)::before,
body.sep-bandeau .section + .section:not(.section--echantillon)::before,
.token-sample--sep-bandeau .section + .section::before {
  margin-inline: calc(-1 * var(--space));
}

/* Encoche (11.17) : amorce nette à x=0 du .section, arrêt à --encoche-span.
   Plus de marge négative (lue comme un débordement). */
body.sep-encoche .hero + .section:not(.section--echantillon)::before,
body.sep-encoche .section + .section:not(.section--echantillon)::before,
.token-sample--sep-encoche .section + .section::before {
  margin-inline: 0 calc(100% - var(--encoche-span, 72%));
}

/* Section réduite à un en-tête de page : son filet ferme déjà le bloc, un
   second rythme vertical avant la section suivante creuserait un trou (visible
   dès qu'un cabinet n'a pas encore de coordonnées à afficher). */
.section--entete {
  padding-bottom: 0;
}

.section__title {
  font-size: var(--step-3);
}

/* Seul élément de signature de la variante : un filet sous les titres de
   section, à la place de tout effet décoratif. */
.section__title::after {
  content: "";
  display: block;
  width: 3rem;
  height: 2px;
  margin-top: 0.75rem;
  background: var(--color-primary);
}

.disclaimer {
  max-width: var(--measure);
  margin-top: 1.25rem;
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* ─── En-tête de page intérieure ──────────────────────────── */

/* Même grammaire que le héros (sur-titre, titre, filet), un cran plus bas :
   une page de domaine ou de contact n'ouvre pas sur un H1 nu. */
.page-header {
  margin-bottom: 1.75rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--rule);
}

.page-header__eyebrow {
  font-size: var(--step-eyebrow);
  letter-spacing: var(--heading-tracking, 0);
  text-transform: var(--heading-transform, none);
  color: var(--color-muted);
}

.page-header__title {
  margin-top: 0.5rem;
  font-size: var(--step-4);
}

/* Chapô : distinct du corps par la taille, l'interlignage et --color-muted
   (11.16 ; exception AA à la doctrine « tout texte = --color-text »). */
.lede {
  max-width: var(--measure);
  font-size: var(--step-1);
  line-height: 1.45;
  color: var(--color-muted);
}

.lede + p {
  margin-top: 1.5rem;
}

/* ─── Domaines d'intervention ─────────────────────────────── */

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space);
  margin-top: 2rem;
}

.card {
  flex: 1 1 16rem;
  padding: 1.5rem;
  border: 1px solid var(--rule);
  border-top: var(--card-border-top-width) solid var(--card-border-top-color);
  border-radius: var(--radius, 4px);
  background: var(--color-bg);
  box-shadow: var(--card-shadow, none);
}

/* Style des cartes (11.20). Aucun sélecteur `body.card-* .card` : les valeurs
   passent par des tokens, si bien que l'aplatissement éditorial (plus bas)
   les écrase sans lutte de spécificité — c'est le NO-OP documenté. */
body.card-lisere,
.card--lisere {
  --card-border-top-width: 3px;
  --card-border-top-color: var(--color-primary);
  --card-shadow: none;
}

body.card-ombre,
.card--ombre {
  --card-border-top-width: 1px;
  --card-border-top-color: var(--rule);
  --card-shadow:
    0 1px 2px color-mix(in srgb, var(--color-text) 8%, transparent),
    0 6px 16px color-mix(in srgb, var(--color-text) 12%, transparent);
}

body.card-sobre,
.card--sobre {
  --card-border-top-width: 1px;
  --card-border-top-color: var(--rule);
  --card-shadow: none;
}

.card__text {
  margin-top: 0.75rem;
}

/* ─── Profils ─────────────────────────────────────────────── */

.profiles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space);
  margin-top: 2rem;
}

.profile {
  max-width: var(--measure);
}

/* Basis 22 rem : uniquement en disposition en ligne. En colonne (bandeau,
   liste, éditorial) il s'appliquait à la HAUTEUR (11.17). */
.profiles > .profile {
  flex: 1 1 22rem;
}

/* Gabarit « bandeau » (11.13) : chaque profil prend la pleine largeur de la
   section, sur son fond de surface. La liste filetée réutilise le motif
   éditorial (déclaration unique, plus bas). */
.profiles--bandeau {
  display: flex;
  flex-direction: column;
  gap: var(--space);
}

.profiles--bandeau .profile {
  flex: none;
  max-width: none;
}

/* Le bandeau élargit le BLOC, jamais la colonne de lecture : sans cette borne,
   la biographie dépassait 150 caractères par ligne (recette du 19/08). */
.profiles--bandeau .profile__bio {
  max-width: var(--measure);
}

.profile__role {
  margin-top: 0.25rem;
}

.profile__bio {
  margin-top: 1rem;
}

/* ─── Photos : traitement et cadrage (11.20) ──────────────── */

/* Traitement CSS PUR : le fichier téléversé n'est jamais altéré, et l'og:image
   sert toujours l'original. Périmètre strict : les deux cadres de photo —
   ni logo, ni favicon, ni illustration de contenu. */
body.img-naturel,
.media--naturel {
  --img-filter: none;
  --img-veil-alpha: 0;
}

body.img-noir_blanc,
.media--noir_blanc {
  --img-filter: grayscale(1);
  --img-veil-alpha: 0;
}

body.img-duotone,
.media--duotone {
  --img-filter: grayscale(1);
  --img-veil-alpha: 1;
}

/* isolation: isolate borne le mélange au cadre : sans elle, le voile
   déteindrait sur le fond de section derrière. `.media--cadre` est le même
   cadre, à la main du styleguide : un seul corps de règle pour les deux. */
.hero__media,
.profile__media,
.media--cadre {
  position: relative;
  isolation: isolate;
}

.hero__media img,
.profile__media img,
.media--cadre img {
  filter: var(--img-filter, none);
}

/* Duotone : voile de la primaire en mélange « color » sur une image désaturée
   — la luminance vient de la photo, la teinte de la primaire, quelle qu'elle
   soit. Sous @supports : un navigateur sans mix-blend-mode ne pose pas le
   voile du tout et sert l'image naturelle, jamais un aplat opaque. */
@supports (mix-blend-mode: color) {
  .hero__media::after,
  .profile__media::after,
  .media--cadre::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--color-primary);
    mix-blend-mode: color;
    opacity: var(--img-veil-alpha, 0);
    pointer-events: none;
  }
}

/* Cadrage des portraits : photos de profil seulement. Le cadre épouse l'image
   (inline-block) pour que le voile et la pastille ne débordent jamais. */
body.portrait-libre,
.media--libre {
  --portrait-width: auto;
  --portrait-ratio: auto;
  --portrait-fit: fill;
  --portrait-radius: 0;
}

body.portrait-carre,
.media--carre {
  --portrait-width: 12rem;
  --portrait-ratio: 1 / 1;
  --portrait-fit: cover;
  --portrait-radius: 0;
}

body.portrait-pastille,
.media--pastille {
  --portrait-width: 12rem;
  --portrait-ratio: 1 / 1;
  --portrait-fit: cover;
  --portrait-radius: 50%;
}

.profile__media,
.media--cadre {
  display: inline-block;
  vertical-align: top;
  max-width: 100%;
  border-radius: var(--portrait-radius, 0);
  overflow: hidden;
}

.profile__media img,
.media--cadre img {
  width: var(--portrait-width, auto);
  max-width: 100%;
  /* Les attributs width/height sont des indications de présentation : sans
     height auto, une photo bornée par max-width garde sa hauteur de fichier
     et s'étire. C'est la hauteur qui suit le ratio, jamais l'inverse. */
  height: auto;
  aspect-ratio: var(--portrait-ratio, auto);
  object-fit: var(--portrait-fit, fill);
}

/* Cadre d'échantillon : trois de front dans la rangée du styleguide, sur une
   LIGNE DE BASE commune (recette 11.20) — les trois cadrages n'ont pas le même
   ratio (4:5 libre, 1:1 carré et pastille) et les légendes se décalaient. Le
   cadre occupe une hauteur fixe, la légende est poussée en bas. */
.token-sample--media {
  display: flex;
  flex: 0 1 14rem;
  flex-direction: column;
  align-items: flex-start;
}

.token-sample--media .media--cadre {
  display: flex;
  align-items: center;
  height: var(--specimen-frame-h, 12rem);
}

.token-sample--media .media--cadre img {
  /* C'est la HAUTEUR du cadre qui borne l'échantillon : la largeur repasse en
     auto (le token --portrait-width vaut 12rem en carré et en pastille) pour
     que le ratio — celui du fichier ou celui du token — soit préservé par le
     seul jeu des contraintes, jamais par un étirement. */
  width: auto;
  max-height: 100%;
}

.token-sample--media figcaption {
  margin-top: auto;
  padding-top: 0.5rem;
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* ─── Questions fréquentes ────────────────────────────────── */

.faq {
  border-bottom: 1px solid var(--rule);
}

.faq:first-of-type {
  margin-top: 2rem;
  border-top: 1px solid var(--rule);
}

.faq__question {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-block: 0.75rem;
  font-family: var(--font-heading);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question::marker {
  content: "";
}

.faq__question::before {
  content: "";
  width: 0.45rem;
  height: 0.45rem;
  margin-right: 0.75rem;
  flex-shrink: 0;
  background: var(--faq-marker);
}

.faq__answer {
  max-width: var(--measure);
  padding-bottom: 1.25rem;
}

/* ─── Coordonnées ─────────────────────────────────────────── */

.contact {
  margin-top: 1.75rem;
  font-style: normal;
}

.contact__line {
  margin-top: 0.5rem;
}

/* Canal direct (11.9) : cible de 44 px, glyphe aligné sur la ligne de texte.
   Le glyphe suit la couleur du lien (currentColor) — aucun aplat de marque. */
.contact__channel {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
}

.contact__glyph {
  flex-shrink: 0;
}

/* La mise en garde accompagne le lien : texte secondaire, donc --color-muted
   (garanti ≥ 4,5:1 par le modèle), jamais la couleur principale. */
.contact__mention {
  max-width: var(--measure);
  margin-top: 0.25rem;
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* ─── Boutons ─────────────────────────────────────────────── */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.5rem 1.5rem;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius, 4px);
  background: var(--color-bg);
  text-decoration: none;
}

.button:hover {
  background: var(--color-surface);
  text-decoration: underline;
}

.button--primary,
.token-sample.token-sample--btn-plein .button--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  border: 1px solid var(--color-primary);
}

/* Le bouton principal garde sa couleur au survol : aucune variante assombrie
   ne serait garantie AA avec --color-on-primary. Le repère est le soulignement. */
.button--primary:hover,
.token-sample.token-sample--btn-plein .button--primary:hover {
  background: var(--color-primary);
}

/* Contour : bordure primaire, texte en --color-text (jamais en primaire).
   Une seule déclaration, partagée par le rendu réel et le styleguide. */
body.btn-contour .button--primary,
.token-sample--btn-contour .button--primary {
  background: transparent;
  color: var(--color-text);
  border: 2px solid var(--color-primary);
}

body.btn-contour .button--primary:hover,
.token-sample--btn-contour .button--primary:hover {
  background: transparent;
}

/* ─── Champs de formulaire ────────────────────────────────── */

.form {
  margin-top: 2rem;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  max-width: var(--measure);
  margin-top: 1.25rem;
}

.field__label {
  font-weight: 600;
}

.field__input {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--color-text);
  border-radius: var(--radius, 4px);
  background: var(--color-bg);
}

textarea.field__input {
  min-height: 8rem;
  line-height: 1.6;
}

/* L'erreur ne repose pas sur la couleur : bordure épaissie et message explicite,
   faute d'un token de couleur d'alerte validé AA (tokens v2, Phase 1bis). */
.field--error .field__input {
  border-width: 2px;
}

.field__error {
  font-size: var(--step-small);
  font-weight: 600;
}

.specimen__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2rem;
}

/* Spécimens du styleguide : rebind --space pour qu'un --space-scale
   local (inline) prenne effet, le calcul :root étant déjà résolu. */
.token-sample {
  --space: calc(clamp(1rem, 0.5rem + 2vw, 2rem) * var(--space-scale, 1));
}

/* L'échantillon bandeau pousse des marges négatives hors viewport.
   Clip de la zone : les focusables vivent dans .container (--space
   >> outline 3px + offset 2px), les anneaux ne sont pas coupés. */
#separateurs {
  overflow-x: clip;
}

.token-samples {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space);
  margin-top: 1rem;
}

.token-sample--density {
  flex: 1 1 12rem;
  padding: calc(2.5rem * var(--space-scale, 1));
  border: 1px solid var(--rule);
}

.token-sample--case + .token-sample--case {
  margin-top: 0.75rem;
}

.token-sample--rule-faible,
.token-sample--rule-moyen,
.token-sample--rule-fort {
  padding-top: 0.75rem;
  border-top: 2px solid var(--rule);
}

.token-sample--rule-refuse {
  color: var(--color-muted);
  text-decoration: line-through;
}

/* Échantillon de largeur de page (11.21) : la barre prend la largeur forcée
   par le wrapper, exactement comme `.container`. Aucune mise à l'échelle, aucun
   dessin de règle graduée — ce qu'on voit est ce que rend le token. */
.token-sample--largeur {
  margin-top: 0.5rem;
  padding-block: 0.25rem;
}

.echantillon-largeur {
  max-width: var(--container-max);
  margin-inline: auto;
  padding: 0.5rem var(--space);
  border: 1px solid var(--rule);
  background: var(--color-surface);
  font-size: var(--step-small);
}

/* Échantillon d'ampleur des titres : le bloc pose sur lui-même le clamp du
   profil montré (--hero-title-size, valeur émise par le modèle), le titre
   reste un vrai `.hero__title`. Plus aucune formule n'est partagée avec
   `:root` — c'est le facteur unique qui l'imposait (11.10). */
.echantillon-titres + .echantillon-titres {
  margin-top: 1rem;
}

.echantillon-titres .hero__title {
  margin-top: 0;
}

/* Double fond des échantillons de carte (recette 11.20). La carte a pour fond
   --color-bg : posée sur une section au même fond, ni son bord fin ni son
   ombre ne se lisent, et « ombre » devenait indiscernable de « sobre ». Les
   trois styles sont donc montrés sur les DEUX fonds du thème servi — valeurs
   calculées, jamais une couleur d'appoint (témoin honnête, 11.17) : le bord se
   lit sur le fond de page, l'ombre et le liseré sur le fond de carte. */
.echantillon-fond {
  margin-top: 1rem;
  padding: var(--space);
  border: 1px solid var(--rule);
}

.echantillon-fond--page {
  background: var(--color-bg);
}

.echantillon-fond--surface {
  background: var(--color-surface);
}

.echantillon-fond > .cards {
  margin-top: 0;
}

.echantillon-fond__legende {
  margin-top: 0.75rem;
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* ─── Pied de page ────────────────────────────────────────── */

.footer {
  padding-block: calc(clamp(2rem, 1.5rem + 2vw, 3rem) * var(--space-scale, 1));
  background: var(--color-surface);
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space) 3rem;
  border-top: 1px solid var(--rule);
}

.footer__col {
  flex: 1 1 12rem;
}

.footer__heading {
  margin-bottom: 0.5rem;
  font-family: var(--font-heading);
}

.footer__contact {
  font-style: normal;
}

.footer__hours,
.footer__areas,
.contact__hours {
  list-style: none;
  padding: 0;
  margin-top: 0.75rem;
}

.footer__hours li + li,
.footer__areas li + li,
.contact__hours li + li {
  margin-top: 0.25rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

body.footer-colonnes .footer-links {
  flex-direction: column;
  gap: 0.5rem;
}

.footer__bandeau {
  background: var(--color-surface);
  padding-block: calc(clamp(2rem, 1.5rem + 2vw, 3rem) * var(--space-scale, 1));
}

.footer__bandeau-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space) 3rem;
}

body.footer-bandeau .footer {
  padding-block: 0;
  background: transparent;
  border-top: none;
}

.footer__legal {
  padding-block: 1rem;
  border-top: 1px solid var(--rule);
}

/* ─── Texte long (pages légales) ──────────────────────────── */

.prose h2 {
  margin-top: 2rem;
  font-size: var(--step-2);
}

.prose ul {
  list-style: disc;
  padding-left: 1.25rem;
  margin-top: 1rem;
}

.prose li + li {
  margin-top: 0.35rem;
}

/* ─── Articles signés (11.8) ──────────────────────────────── */

/* Réponse directe : le bloc que citent les moteurs, et la première chose que
   lit un visiteur pressé. Il se détache par le fond de surface du cabinet et
   par un liseré en primaire — la primaire trace, elle ne porte pas de texte.
   Épaisseur en littéral et non `var(--card-border-top-width)` : ce token
   descend à 1 px sous les styles de carte `ombre` et `sobre`, le liseré
   s'évaporerait selon le thème.
   Angles GAUCHES à zéro (recette 11.21) : le rayon du thème rognait le liseré
   à ses deux extrémités, qui perdait sa pleine hauteur et se lisait comme un
   trait flottant. Les angles droits, eux, gardent le rayon — le bloc reste du
   même dialecte que les cartes du cabinet. */
.direct-answer {
  max-width: var(--measure);
  padding: 1.25rem 1.5rem;
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius, 4px);
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  background: var(--color-surface);
}

.direct-answer__text {
  font-size: var(--step-1);
  line-height: 1.5;
}

/* Signature : l'E-E-A-T rendu visible. Le nom reste en texte courant (c'est
   l'information portante), les lignes secondaires passent en texte atténué. */
.article-signature {
  max-width: var(--measure);
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

.article-signature__meta {
  margin-top: 0.35rem;
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* Liste d'articles : filets et non cartes — une suite de titres se lit en
   colonne, et le filet est le séparateur du design system. */
.article-list__item {
  padding-block: 1.5rem;
  border-top: 1px solid var(--rule);
}

.article-list__title {
  font-size: var(--step-2);
}

.article-list__chapo,
.article-list__meta {
  max-width: var(--measure);
  margin-top: 0.5rem;
}

.article-list__meta {
  font-size: var(--step-small);
  color: var(--color-muted);
}

.pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: baseline;
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

.pagination__state {
  font-size: var(--step-small);
  color: var(--color-muted);
}

/* ─── Variante éditorial ──────────────────────────────────── */

/* Filets structurels (seuls séparateurs) : mix éditorial du thème,
   validé ≥ 3:1 au modèle. Le mix classique de :root reste inchangé. */
body.layout-editorial {
  --rule: color-mix(
    in srgb,
    var(--color-text) var(--rule-mix-editorial, 55%),
    var(--color-bg)
  );

  /* Colonne de lecture type revue : même mesure que le texte courant. Une
     seule redéfinition — `.container` et la gouttière du héros la lisent.
     Conséquence assumée (11.21) : le token « Largeur de page » est SANS OBJET
     ici, sur le précédent du rayon et du style de cartes. L'éditorial est une
     colonne de revue par définition ; l'élargir demanderait un rail distinct
     pour les seuls éléments pleine largeur, ce qui est un autre chantier. */
  --container-max: calc(var(--measure) + 4rem);
}

/* Filet pleine largeur sous le titre. La taille reste --step-3 : le h1
   (page-header / héros) doit rester dominant sur tous les thèmes. */
body.layout-editorial .section__title {
  font-size: var(--step-3);
}

body.layout-editorial .section__title::after,
body.layout-editorial .prose h2::after {
  content: "";
  display: block;
  width: 100%;
  height: 1px;
  margin-top: 0.75rem;
  background: var(--rule);
}

body.layout-editorial .section__title:has(+ .cards)::after,
body.layout-editorial .section__title:has(+ .profiles)::after,
body.layout-editorial .section__title:has(+ .faq)::after {
  content: none;
}

body.layout-editorial .hero__title {
  font-weight: 700;
}

/* Surfaces : filet de séparation, plus de fond plein. Le pied de page
   a son propre traitement (11.15) : plus de mur transparent. */
body.layout-editorial .section--surface {
  background: transparent;
}

body.layout-editorial.footer-colonnes .footer {
  background: transparent;
  border-top: none;
}

body.layout-editorial.footer-colonnes .footer__inner {
  border-top: 2px solid var(--rule);
}

body.layout-editorial.footer-bandeau .footer__bandeau {
  background: var(--color-surface);
}

/* Cartes → filets horizontaux. Le rayon (token 11.1) devient sans objet.
   Le gabarit « liste » (11.13) partage ces déclarations : un seul style de
   liste filetée, jamais dupliqué (doctrine 11.2c). */
body.layout-editorial .cards,
.cards--liste {
  display: flex;
  flex-direction: column;
  gap: 0;
}

body.layout-editorial .card,
.cards--liste .card {
  flex: none;
  padding: 1.25rem 0;
  border: none;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  background: transparent;
  /* Ici se joue le no-op de card_style (11.20) : la carte redevient un filet,
     liseré ET ombre compris. Sans cette ligne, `card-ombre` laisserait une
     ombre portée sous un simple trait. */
  box-shadow: none;
}

body.layout-editorial .card:first-of-type,
.cards--liste .card:first-of-type {
  border-top: 1px solid var(--rule);
}

/* Profils empilés, filetés, dans la même colonne de lecture. */
body.layout-editorial .profiles,
.profiles--liste {
  display: flex;
  flex-direction: column;
  gap: 0;
}

body.layout-editorial .profile,
.profiles--liste .profile {
  flex: none;
  max-width: none;
  padding-block: 1.25rem;
  border-bottom: 1px solid var(--rule);
}

body.layout-editorial .profile:first-of-type,
.profiles--liste .profile:first-of-type {
  border-top: 1px solid var(--rule);
}

/* ─── Mouvement (11.12, différencié 11.16) ─────────────────── */

/* Durée et distance par niveau. motion-aucun n'en définit aucune :
   aucune transition ni animation n'est attachée à ce body.
   Le décalage par délai d'animation a été retiré : il est inerte avec view(). */
/* --motion-distance = amplitude des RÉVÉLATIONS (entrée du héros, défilement).
   --motion-lift = élévation au SURVOL, bornée : un bouton qui bondit de 25 px
   sous la souris n'est pas « soutenu », il est cassé. Les deux valeurs
   coïncident aux paliers historiques — leur rendu est inchangé. */
body.motion-discret {
  --motion-duration: 180ms;
  --motion-distance: 0.35rem;
  --motion-lift: 0.35rem;
}

body.motion-marque {
  --motion-duration: 320ms;
  --motion-distance: 0.85rem;
  --motion-lift: 0.85rem;
}

/* Palier au-dessus de « marqué » (11.21). Motif : à 320 ms sur 0,85 rem, le
   mouvement reste sous le seuil de perception consciente — aucun thème ne
   pouvait donner l'impression d'une page vivante. Franchement perceptible,
   donc franchement neutralisé sous prefers-reduced-motion comme les autres. */
body.motion-soutenu {
  --motion-duration: 520ms;
  --motion-distance: 1.6rem;
  --motion-lift: 0.85rem;
}

/* Défilement fluide (héritage 1.3) : réservé aux niveaux qui bougent, coupé en
   motion-aucun et sous prefers-reduced-motion (troubles vestibulaires).
   Le token vit sur body, la propriété sur la racine : d'où :has(). */
html:has(body.motion-discret),
html:has(body.motion-marque),
html:has(body.motion-soutenu) {
  scroll-behavior: smooth;
}

@keyframes hero-enter {
  from {
    opacity: 0;
    transform: translateY(var(--motion-distance));
  }

  to {
    opacity: 1;
    transform: none;
  }
}

body.motion-discret .hero,
body.motion-marque .hero,
body.motion-soutenu .hero {
  animation: hero-enter calc(var(--motion-duration) * 2) ease-out both;
}

body.motion-discret .button,
body.motion-marque .button,
body.motion-soutenu .button {
  transition: transform var(--motion-duration) ease, box-shadow var(--motion-duration) ease;
}

body.motion-discret .button:hover,
body.motion-discret .button:focus-visible,
body.motion-marque .button:hover,
body.motion-marque .button:focus-visible,
body.motion-soutenu .button:hover,
body.motion-soutenu .button:focus-visible {
  transform: translateY(calc(-1 * var(--motion-lift)));
  box-shadow: 0 4px 12px color-mix(in srgb, var(--color-text) 18%, transparent);
}

body.motion-discret a,
body.motion-marque a,
body.motion-soutenu a {
  text-underline-offset: 0.12em;
  transition: text-underline-offset var(--motion-duration) ease,
    text-decoration-thickness var(--motion-duration) ease;
}

body.motion-discret a:hover,
body.motion-marque a:hover,
body.motion-soutenu a:hover {
  text-underline-offset: 0.28em;
  text-decoration-thickness: 0.12em;
}

body.motion-discret .card,
body.motion-marque .card,
body.motion-soutenu .card {
  transition: transform var(--motion-duration) ease, box-shadow var(--motion-duration) ease;
}

body.motion-discret .card:hover,
body.motion-marque .card:hover,
body.motion-soutenu .card:hover {
  transform: translateY(calc(-1 * var(--motion-lift)));
  box-shadow: 0 6px 16px color-mix(in srgb, var(--color-text) 14%, transparent);
}

@supports (animation-timeline: view()) {
  @keyframes motion-reveal {
    from {
      opacity: 0;
      transform: translateY(var(--motion-distance));
    }

    to {
      opacity: 1;
      transform: none;
    }
  }

  body.motion-marque .section,
  body.motion-marque .card,
  body.motion-soutenu .section,
  body.motion-soutenu .card {
    opacity: 0;
    animation: motion-reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 25%;
  }

  /* En « soutenu », le titre de section se révèle aussi : c'est ce qui fait
     lire la page comme animée, là où marqué ne bouge que les blocs. */
  body.motion-soutenu .section__title,
  body.motion-soutenu .page-header {
    opacity: 0;
    animation: motion-reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }

  body.motion-marque .cards .card:nth-child(1),
  body.motion-marque .profiles .profile:nth-child(1),
  body.motion-soutenu .cards .card:nth-child(1),
  body.motion-soutenu .profiles .profile:nth-child(1) {
    animation-range: entry 0% cover 25%;
  }

  body.motion-marque .cards .card:nth-child(2),
  body.motion-marque .profiles .profile:nth-child(2),
  body.motion-soutenu .cards .card:nth-child(2),
  body.motion-soutenu .profiles .profile:nth-child(2) {
    animation-range: entry 8% cover 30%;
  }

  body.motion-marque .cards .card:nth-child(3),
  body.motion-marque .profiles .profile:nth-child(3),
  body.motion-soutenu .cards .card:nth-child(3),
  body.motion-soutenu .profiles .profile:nth-child(3) {
    animation-range: entry 16% cover 35%;
  }

  body.motion-marque .cards .card:nth-child(4),
  body.motion-marque .cards .card:nth-child(n + 5),
  body.motion-marque .profiles .profile:nth-child(4),
  body.motion-marque .profiles .profile:nth-child(n + 5),
  body.motion-soutenu .cards .card:nth-child(4),
  body.motion-soutenu .cards .card:nth-child(n + 5),
  body.motion-soutenu .profiles .profile:nth-child(4),
  body.motion-soutenu .profiles .profile:nth-child(n + 5) {
    animation-range: entry 24% cover 40%;
  }

  /* Neutralisation en MIROIR EXACT des règles ci-dessus : une règle active,
     une règle neutralisée, mêmes sélecteurs dans le même ordre. Fusionner les
     listes serait plus court et ferait perdre la garantie de cascade que
     tests/tenants/test_theme_motion.py vérifie sélecteur par sélecteur. */
  @media (prefers-reduced-motion: reduce) {
    body.motion-discret .hero,
    body.motion-marque .hero,
    body.motion-soutenu .hero {
      animation: none;
      animation-range: normal;
      opacity: 1;
      transform: none;
    }

    body.motion-marque .section,
    body.motion-marque .card,
    body.motion-soutenu .section,
    body.motion-soutenu .card {
      animation: none;
      animation-range: normal;
      opacity: 1;
      transform: none;
    }

    body.motion-soutenu .section__title,
    body.motion-soutenu .page-header {
      animation: none;
      animation-range: normal;
      opacity: 1;
      transform: none;
    }
  }
}

/* Neutralisation du mouvement — DOIT rester le dernier bloc du fichier.
   Le reset du haut de fichier cible `*` en propriétés longues
   (transition-duration, animation-duration) ; les raccourcis `transition:` et
   `animation:` ci-dessus, sous des sélecteurs bien plus spécifiques, les
   reconstruisent et gagnaient donc la cascade. La neutralisation reprend les
   MÊMES sélecteurs, en dernier : elle gagne sans !important. */
@media (prefers-reduced-motion: reduce) {
  html:has(body.motion-discret),
  html:has(body.motion-marque),
  html:has(body.motion-soutenu) {
    scroll-behavior: auto;
  }

  body.motion-discret .hero,
  body.motion-marque .hero,
  body.motion-soutenu .hero {
    animation: none;
  }

  body.motion-discret .button,
  body.motion-marque .button,
  body.motion-soutenu .button {
    transition: none;
  }

  body.motion-discret a,
  body.motion-marque a,
  body.motion-soutenu a {
    transition: none;
  }

  body.motion-discret .card,
  body.motion-marque .card,
  body.motion-soutenu .card {
    transition: none;
  }

  body.motion-marque .section,
  body.motion-marque .card,
  body.motion-soutenu .section,
  body.motion-soutenu .card {
    animation: none;
    animation-range: normal;
    opacity: 1;
    transform: none;
  }

  body.motion-soutenu .section__title,
  body.motion-soutenu .page-header {
    animation: none;
    animation-range: normal;
    opacity: 1;
    transform: none;
  }
}
