/* =========================================================================
   LVMP — Feuille de style
   Consomme uniquement les variables de tokens.css, qui doit être chargé
   avant ce fichier. Aucune couleur, aucune taille, aucune durée n'est
   écrite en dur ici, sauf dans le reset.
   Ordre : reset · typographie · mise en page · marque · les 9 mouvements
   dans l'ordre du CONTRAT §6 · page mentions · hooks d'animation.
   Mobile d'abord. Points de rupture : 640, 900, 1200.
   ========================================================================= */


/* ---- Reset ------------------------------------------------------------- */
/* Seul endroit du fichier où des valeurs littérales sont admises : ce sont
   des corrections de comportement de navigateur, pas des choix de design. */

*,*::before,*::after{box-sizing:border-box}

html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
/* Lenis prend la main sur le défilement animé : le smooth natif ferait
   concurrence au sien sur les clics d'ancre. */
html.lenis{scroll-behavior:auto}

body{
  margin:0;
  min-height:100svh;
  background:var(--void);
  color:var(--ivory);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  font-weight:300;
  line-height:var(--lh-normal);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  /* garantit qu'aucun débordement latéral ne crée de barre horizontale :
     le scroll-expand écarte ses mots au-delà de la fenêtre. */
  overflow-x:hidden;
  overflow-wrap:break-word;
}

img,picture,svg{display:block;max-width:100%}
img{height:auto}
a{color:inherit;text-decoration:none}
ul,ol{list-style:none;margin:0;padding:0}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;-webkit-tap-highlight-color:transparent}
input,textarea{font:inherit;color:inherit}
h1,h2,h3,p,figure,figcaption{margin:0}

::selection{background:var(--navy);color:var(--ivory)}

/* CONTRAT §8 — valeur imposée telle quelle. */
:focus-visible{outline:2px solid var(--champagne);outline-offset:3px}


/* ---- Typographie ------------------------------------------------------- */

h1,h2,h3{
  font-family:var(--font-display);
  font-weight:400;
  line-height:var(--lh-tight);
  color:var(--ivory);
  text-wrap:balance;
}

/* Sur-titre en capitales interlettrées — CONTRAT §4. C'est l'écho au
   monogramme, et la seule graisse de signal du site. */
.eyebrow{
  display:block;
  font-family:var(--font-body);
  font-size:var(--fs-eyebrow);
  font-weight:400;
  line-height:var(--lh-normal);
  letter-spacing:var(--ls-wide);
  text-transform:uppercase;
  color:var(--champagne-text);
}

.section__title{font-size:var(--fs-h2)}
.section__lead{margin-top:var(--sp-3);color:var(--ivory-soft);line-height:var(--lh-loose)}
.section__eyebrow{margin-bottom:var(--sp-5)}

.link{
  color:var(--champagne-text);
  text-decoration:underline;
  text-underline-offset:.25em;
  transition:color var(--dur-s) var(--ease);
}
.link:hover{color:var(--ivory)}


/* ---- Mise en page ------------------------------------------------------ */

.container{
  width:100%;
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
/* Colonne de lecture : manifeste, méthode, sélection, formulaire, mentions. */
.container--narrow{max-width:var(--container-narrow)}

.section{padding-block:var(--sp-7)}
@media (min-width:900px){.section{padding-block:var(--sp-8)}}

/* Le palier --void → --navy-deep est le mécanisme de profondeur du site ;
   le filet en arête haute imite un éclairage zénithal (design-system §5). */
.section--alt{background:var(--navy-deep);border-top:1px solid var(--line)}

.section-head{max-width:var(--container-narrow)}

/* Texte réservé aux technologies d'assistance. Les valeurs littérales sont la
   recette de masquage habituelle — un carré d'un pixel que l'on découpe —,
   pas des choix de design : elles n'ont pas de jeton. */
.sr-only{
  position:absolute;
  width:1px;
  height:1px;
  margin:-1px;
  padding:0;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}


/* ---- Voile sur média --------------------------------------------------- */
/* Deux couches de --scrim superposées. L'opacité composée vaut .98 au ras du
   bas au lieu de .86, et la zone où --ivory tient 4,5:1 sur une image blanche
   passe de 23 % à 43 % de la hauteur du dégradé (design-system §2.2). C'est
   ce qui permet de loger un titre de plusieurs lignes sur un média sans
   dépendre de la valeur réelle de la photo livrée. */

.hero__media::after,
.manifeste__panel::after,
.mosaic__tile::after,
.scroll-expand__media::after{
  content:'';
  position:absolute;
  inset:0;
  background:var(--scrim),var(--scrim);
  pointer-events:none;
}

/* Une seule couche sur le média du slogan. Le doublage a été réglé pour des
   photographies dont on ne connaît pas la valeur ; ici le média est un rendu
   calculé sur --void, et la mesure le confirme : sous le titre, la moyenne du
   fond reste à 8/255 (soit --void) à toutes les progressions, donc --ivory y
   tient déjà 17:1 sans voile. En revanche la couche doublée porte l'opacité
   composée à .96 à la hauteur de la rangée basse de points, qu'elle efface.
   Le jeton --scrim n'est pas touché — CONTRAT §3 : il reste posé sous le
   titre, une fois. */
.scroll-expand__media::after{background:var(--scrim)}


/* ---- Marque ------------------------------------------------------------ */
/* Les SVG sont posés en ligne dans le HTML : c'est la seule façon pour
   currentColor d'hériter de la couleur CSS. Source : logos/lvmp-*.svg. */

.brand{display:block;color:var(--ivory)}
.brand__svg{display:block;width:100%;height:auto}


/* ---- Lien d'évitement -------------------------------------------------- */
/* Premier élément focusable de la page — CONTRAT §8. */

.skip-link{
  position:fixed;
  top:var(--sp-2);
  left:var(--sp-2);
  z-index:var(--z-threshold);
  display:inline-flex;
  align-items:center;
  min-height:44px;
  padding-inline:var(--sp-3);
  background:var(--navy);
  color:var(--ivory);
  border:1px solid var(--champagne);
  border-radius:var(--radius-s);
  font-size:var(--fs-small);
  transform:translateY(calc(var(--sp-8) * -1));
  transition:transform var(--dur-s) var(--ease);
}
.skip-link:focus{transform:none}


/* ---- En-tête ----------------------------------------------------------- */
/* Wordmark seul, en haut à gauche, posé sur le média du hero. */

.header{
  position:absolute;
  inset:0 0 auto 0;
  z-index:var(--z-header);
  /* Wordmark à gauche, appel à l'action à droite. Le scrim est en
     position:absolute : il reste hors du flux flex et ne compte pas
     comme troisième colonne.
     La gouttière est portée ici, en padding, et non plus par la marge de
     chaque enfant : avec deux enfants, `margin-inline` en posait quatre pour
     deux utiles et faisait déborder l'en-tête à 375px. */
  padding:var(--sp-3) var(--gutter);
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--sp-2);
}

/* Le --scrim est orienté vers le haut ; retourné, il assombrit le haut de
   page. C'est la seule façon de poser un élément focusable au-dessus d'un
   média sans passer sous le seuil de contraste (design-system §4). */
.header__scrim{
  position:absolute;
  inset:0 0 auto 0;
  height:calc(var(--sp-8) + var(--sp-6));
  background:var(--scrim);
  transform:rotate(180deg);
  pointer-events:none;
  z-index:-1;
}

.header__brand{
  display:flex;
  align-items:center;
  min-height:44px;
  flex:none;
  /* Réduit sur petit écran pour laisser passer l'appel à l'action à côté. */
  width:5rem;
  transition:opacity var(--dur-s) var(--ease);
}
.header__brand:hover{opacity:.7}
@media (min-width:640px){.header__brand{width:calc(var(--sp-6) + var(--sp-4))}}

/* CONTRAT §15 — le seul appel à l'action de la page. Il passe sous le même
   --scrim que le wordmark, donc au-dessus du seuil de contraste.
   Sélecteur à deux classes volontairement : `.btn`, déclaré plus bas dans ce
   fichier, a la même spécificité et gagnerait sur l'ordre source — le rendu
   compact serait ignoré. Compact par défaut pour tenir à côté du wordmark à
   375px, proportions pleines du .btn au-delà de 640px. */
.header .header__cta{
  flex:none;
  padding-inline:var(--sp-2);
  font-size:.75rem;
  letter-spacing:.08em;
}
@media (min-width:640px){
  .header .header__cta{
    padding-inline:var(--sp-4);
    font-size:var(--fs-small);
    letter-spacing:var(--ls-wide);
  }
}

/* Page mentions : pas de média sous l'en-tête, donc pas de voile et une
   position dans le flux. */
.header--solid{
  position:relative;
  background:var(--void);
  border-bottom:1px solid var(--line);
}


/* ==================== 0 · SEUIL ==================== */
/* Sans JS, un premier écran que l'on dépasse en défilant — la ligne reste
   lue. Avec JS, un voile fixe au-dessus de tout, que main.js efface. */

.threshold{
  position:relative;
  min-height:100svh;
  display:grid;
  place-items:center;
  padding:var(--sp-7) var(--gutter);
  background:var(--void);
  text-align:center;
}
.js .threshold{
  position:fixed;
  inset:0;
  z-index:var(--z-threshold);
  transition:opacity var(--dur-l) var(--ease),visibility var(--dur-l) var(--ease);
}
.js .threshold.is-hidden{opacity:0;visibility:hidden;pointer-events:none}

.threshold__inner{display:grid;justify-items:center;gap:var(--sp-5)}

.threshold__mark{width:min(calc(var(--sp-8) + var(--sp-7)),62vw)}

.threshold__line{
  font-family:var(--font-display);
  font-style:italic;
  font-weight:300;
  font-size:var(--fs-h3);
  color:var(--ivory-soft);
}


/* ==================== 1 · HERO ==================== */

.hero{
  position:relative;
  min-height:100svh;
  display:flex;
  flex-direction:column;
  justify-content:flex-end;
  overflow:hidden;
  padding-bottom:var(--sp-6);
}

.hero__media{position:absolute;inset:0;z-index:0}
/* Débord haut et bas réservé à la parallaxe : sans JS le cadrage reste exact. */
.js .hero__media{inset:calc(var(--sp-8) * -1) 0;will-change:transform}
.hero__media picture,.hero__media img{width:100%;height:100%;object-fit:cover}
.hero__canvas{position:absolute;inset:0;display:block;width:100%;height:100%}

.hero__content{
  position:relative;
  z-index:1;
  width:100%;
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

.hero__title{font-size:var(--fs-h1);letter-spacing:var(--ls-tight);max-width:var(--container-narrow)}
.hero__line{
  margin-top:var(--sp-3);
  max-width:var(--container-narrow);
  /* --ivory-soft est interdit sur média (design-system §4) : ici --ivory. */
  color:var(--ivory);
  line-height:var(--lh-loose);
}

.hero__scroll{
  position:relative;
  z-index:1;
  display:flex;
  align-items:center;
  gap:var(--sp-2);
  margin-top:var(--sp-6);
  padding-inline:var(--gutter);
  max-width:var(--container);
  margin-inline:auto;
  width:100%;
  font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-wide);
  text-transform:uppercase;
  color:var(--ivory);
}
.hero__scroll-rule{flex:0 0 var(--sp-6);height:1px;background:var(--champagne)}

/* Sous 640px de large et sur un écran bas, l'indice de défilement mange la
   place du titre : on le retire plutôt que de comprimer le hero. */
@media (max-height:34rem){.hero__scroll{display:none}}


/* ==================== 2 · MANIFESTE ==================== */

.manifeste__panel{position:relative;min-height:100svh;overflow:hidden}
.manifeste__panel picture,.manifeste__panel img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
}

/* Légende serif italique hors centre, dans le bas du voile. */
.caption{
  position:absolute;
  bottom:var(--sp-6);
  z-index:1;
  max-width:min(var(--container-narrow),78%);
  font-family:var(--font-display);
  font-style:italic;
  font-weight:300;
  font-size:var(--fs-h2);
  line-height:var(--lh-loose);
  color:var(--ivory);
}
.caption--bl{left:var(--gutter);text-align:left}
.caption--br{right:var(--gutter);text-align:right}

/* -- Les trois mouvements — CONTRAT §13.2 -------------------------------- */
/* Rien ici ne déplace sur Y un élément qui occupe le cadre : la vitesse de
   défilement perçue reste celle du reste de la page (§13.1). Les amplitudes
   du mouvement vivent dans main.js — ce sont des constantes de comportement,
   pas des valeurs de design. */

/* 1 · Travelling — main.js écrit translateX + scale sur le <picture>. */
.js .manifeste__panel--travelling picture{will-change:transform}

/* 2 · Recalage — trois tranches horizontales de la même image. Les découpes
   restent en CSS : sans JavaScript les bandes sont alignées et l'image est
   entière. */
.manifeste__bands{position:absolute;inset:0}
.manifeste__band{position:absolute;inset:0}
.manifeste__band--1{clip-path:inset(0 0 66.67% 0)}
.manifeste__band--2{clip-path:inset(33.33% 0 33.33% 0)}
.manifeste__band--3{clip-path:inset(66.67% 0 0 0)}
/* Réserve latérale d'un cran de chaque côté — soit 16px, l'amplitude maximale
   admise par le contrat. Une bande décalée ne peut donc jamais découvrir le
   bord du cadre, à aucune largeur d'écran. Même procédé que la parallaxe du
   hero, et pas un scale : le cadrage reste celui des autres panneaux. */
.js .manifeste__band{inset:0 calc(var(--sp-2) * -1);will-change:transform}

/* 3 · La ligne qui s'écrit — les mots n'existent que sous JavaScript. */
.js .manifeste__word{display:inline-block;will-change:transform,opacity}

/* Filet plein par défaut, donc visible sans JavaScript ; main.js le trace
   depuis la gauche. Il est posé DANS la légende, dont la boîte s'ajuste au
   texte : left:0/right:0 lui donnent exactement la largeur de la phrase, ce
   qu'aucune valeur écrite d'avance ne saurait faire. Un cran en dessous. */
.manifeste__rule{
  position:absolute;
  left:0;
  right:0;
  top:100%;
  margin-top:var(--sp-2);
  height:1px;
  background:var(--champagne);
  transform-origin:left center;
}
.js .manifeste__rule{transform:scaleX(0)}


/* ==================== 3 · SLOGAN ==================== */
/* Sans JS, une section d'un écran avec le média à taille pleine et les deux
   mots lisibles : rien ne reste minuscule, rien ne dépend du défilement. */

.scroll-expand{position:relative;background:var(--void)}
.js .scroll-expand{height:280vh}

.scroll-expand__sticky{
  position:relative;
  height:100svh;
  overflow:hidden;
  display:flex;
  align-items:center;
  justify-content:center;
  background:var(--void);
}
.js .scroll-expand__sticky{position:sticky;top:0;height:100vh}

.scroll-expand__eyebrow{
  position:absolute;
  top:var(--sp-6);
  left:0;right:0;
  z-index:2;
  padding-inline:var(--gutter);
  text-align:center;
}

.scroll-expand__media{
  position:relative;
  overflow:hidden;
  width:min(var(--container),92vw);
  aspect-ratio:16/9;
  max-height:78svh;
}
.js .scroll-expand__media{transform:scale(.26);will-change:transform}
/* Le canvas remplit le cadre ; sa résolution interne est fixée en JS au
   devicePixelRatio (CONTRAT §12). Il ne s'anime jamais en CSS : il se
   redessine. L'`img` ne concerne plus que le repli du <noscript>. */
/* Posé en absolu plutôt qu'en height:100% : le cadre tient sa hauteur d'un
   aspect-ratio borné par max-height, dont un pourcentage ne peut pas hériter
   de façon fiable. inset:0 épouse le cadre quelle que soit la contrainte qui
   l'emporte. */
.scroll-expand__canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
.scroll-expand__media img{width:100%;height:100%;object-fit:cover}

.scroll-expand__title{
  position:absolute;
  z-index:2;
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  justify-content:center;
  gap:var(--sp-3);
  width:100%;
  padding-inline:var(--gutter);
  text-align:center;
}
.scroll-expand__word{
  font-family:var(--font-display);
  font-size:var(--fs-h2);
  font-weight:400;
  letter-spacing:var(--ls-tight);
  color:var(--ivory);
  white-space:nowrap;
}
.js .scroll-expand__word{will-change:transform}

.scroll-expand__line{
  position:absolute;
  bottom:var(--sp-6);
  left:0;right:0;
  z-index:2;
  padding-inline:var(--gutter);
  text-align:center;
  color:var(--ivory);
}

@media (min-width:900px){
  .scroll-expand__word{font-size:var(--fs-h1)}
  .scroll-expand__title{gap:var(--sp-5)}
}


/* ==================== 4 · PILIERS ==================== */

.piliers{display:grid;gap:var(--sp-6)}
@media (min-width:900px){.piliers{grid-template-columns:repeat(3,1fr);gap:var(--sp-5)}}
/* sans ça, un mot long élargit sa colonne au-delà de la grille */
.piliers>*{min-width:0}

.pilier__media{overflow:hidden;aspect-ratio:4/5;background:var(--navy-deep)}
.pilier__media img{width:100%;height:100%;object-fit:cover}

.pilier__title{margin-top:var(--sp-3);font-size:var(--fs-h3)}
/* Chiffre décoratif : --champagne, jamais --champagne-text (design-system §3.1). */
.pilier__num{color:var(--champagne);margin-right:var(--sp-1)}
.pilier__body{margin-top:var(--sp-2);color:var(--ivory-soft);line-height:var(--lh-loose)}


/* ==================== 5 · MÉTHODE ==================== */

.rule-fill{position:relative;padding-left:var(--sp-4)}
/* rail permanent */
.rule-fill::before{
  content:'';position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--line);
}
/* barre qui se remplit ; pleine par défaut, donc lisible sans JS */
.rule-fill__bar{
  position:absolute;left:0;top:0;bottom:0;width:1px;
  background:var(--champagne);
  transform-origin:top center;
}
.js .rule-fill__bar{transform:scaleY(0)}

@media (min-width:640px){.rule-fill{padding-left:var(--sp-5)}}

.methode__steps{display:grid;gap:var(--sp-6)}
.step__title{font-size:var(--fs-h3)}
.step__num{
  color:var(--champagne);
  margin-right:var(--sp-1);
  font-family:var(--font-body);
  font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-wide);
  vertical-align:middle;
}
.step__body{margin-top:var(--sp-2);color:var(--ivory-soft);line-height:var(--lh-loose)}


/* ==================== 6 · PREUVES ==================== */

.mosaic{
  margin-top:var(--sp-6);
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:var(--sp-2);
}
.mosaic>*{min-width:0}

.mosaic__tile{position:relative;overflow:hidden;aspect-ratio:1;background:var(--navy-deep)}
/* le média sort du flux : la tuile peut alors tirer sa hauteur soit de son
   aspect-ratio, soit de la rangée de grille, sans que l'image s'y oppose */
.mosaic__tile picture{position:absolute;inset:0}
.mosaic__tile img{width:100%;height:100%;object-fit:cover}

/* Deux tuiles larges en mobile : 8 demi-colonnes, soit quatre rangées pleines.
   À trois colonnes elles redeviennent carrées et la grille tombe juste. */
.mosaic__tile--wide{grid-column:span 2;aspect-ratio:2}

/* Légende posée bas dans le voile — au-delà, le scrim ne protège plus. */
.mosaic__caption{
  position:absolute;
  left:var(--sp-2);right:var(--sp-2);bottom:var(--sp-2);
  z-index:1;
  color:var(--ivory);
  font-size:var(--fs-small);
  line-height:var(--lh-normal);
}

.mosaic__note{margin-top:var(--sp-4);color:var(--ivory-soft);font-size:var(--fs-small)}

@media (min-width:900px){
  .mosaic{grid-template-columns:repeat(3,1fr);gap:var(--sp-3)}
  .mosaic__tile--wide{grid-column:span 1;aspect-ratio:1}
  .mosaic__caption{left:var(--sp-3);right:var(--sp-3);bottom:var(--sp-3)}
}
/* À quatre colonnes, les deux tuiles larges retrouvent leur place : 8 unités
   de colonne pour 6 tuiles, soit exactement deux rangées, sans trou.
   Elles n'ont plus d'aspect-ratio : deux colonnes plus une gouttière ne font
   pas exactement le double d'une colonne, et un ratio figé décalerait leur
   bord bas de la hauteur de la gouttière. Elles prennent la hauteur de la
   rangée, imposée par les tuiles carrées voisines. */
@media (min-width:1200px){
  .mosaic{grid-template-columns:repeat(4,1fr)}
  .mosaic__tile--wide{grid-column:span 2;aspect-ratio:auto}
  .piliers{gap:var(--sp-6)}
}


/* ==================== 7 · SÉLECTION ==================== */

.criteres__amorce{
  margin-top:var(--sp-6);
  font-family:var(--font-display);
  font-style:italic;
  font-weight:300;
  font-size:var(--fs-h3);
  color:var(--ivory);
}
.criteres{margin-top:var(--sp-3);display:grid;gap:var(--sp-3)}
.critere{
  position:relative;
  padding-left:var(--sp-5);
  font-family:var(--font-display);
  font-style:italic;
  font-weight:300;
  font-size:var(--fs-h3);
  line-height:var(--lh-loose);
  color:var(--ivory);
}
.critere::before{
  content:'';
  position:absolute;
  left:0;
  top:.75em;
  width:var(--sp-3);
  height:1px;
  background:var(--champagne);
}


/* ==================== 8 · CANDIDATURE ==================== */

.form{margin-top:var(--sp-6)}

.field{margin-bottom:var(--sp-4)}
.field__label{
  display:block;
  margin-bottom:var(--sp-1);
  font-size:var(--fs-small);
  color:var(--ivory);
}
/* Astérisque décoratif ; l'attribut required porte l'information pour les
   technologies d'assistance. */
.field__required{color:var(--champagne)}

.field__input{
  display:block;
  width:100%;
  min-height:44px;                      /* cible tactile — CONTRAT §8 */
  padding:var(--sp-1) var(--sp-2);
  background:transparent;
  color:var(--ivory);
  border:1px solid var(--line);
  border-radius:var(--radius-s);
  transition:border-color var(--dur-s) var(--ease);
}
.field__input:hover,.field__input:focus{border-color:var(--champagne)}
.field__input--area{min-height:calc(var(--sp-8) - var(--sp-2));resize:vertical}
.field__input[aria-invalid="true"]{border-color:var(--champagne-text)}

.field__hint{margin-top:var(--sp-1);font-size:var(--fs-small);color:var(--ivory-soft)}

/* Conteneur d'erreur vide, rempli par main.js. Vide, il ne prend pas de place. */
.field__error{margin-top:var(--sp-1);font-size:var(--fs-small);color:var(--champagne-text)}
.field__error:empty{display:none}

.btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-height:44px;
  padding-inline:var(--sp-4);
  background:transparent;
  color:var(--ivory);
  border:1px solid var(--champagne);
  border-radius:var(--radius-s);
  font-size:var(--fs-small);
  letter-spacing:var(--ls-wide);
  text-transform:uppercase;
  transition:background-color var(--dur-s) var(--ease),color var(--dur-s) var(--ease);
}
.btn:hover{background:var(--champagne);color:var(--void)}

.form__note{margin-top:var(--sp-3);font-size:var(--fs-small);color:var(--ivory-soft)}
.form__status{margin-top:var(--sp-3);color:var(--champagne-text)}
.form__status:empty{display:none}


/* ==================== 9 · PIED DE PAGE ==================== */

.footer{padding-block:var(--sp-6);background:var(--void);border-top:1px solid var(--line)}
.footer__inner{display:grid;justify-items:start;gap:var(--sp-3)}

.footer__brand{
  display:block;
  width:calc(var(--sp-7) + var(--sp-4));
  transition:opacity var(--dur-s) var(--ease);
}
.footer__brand:hover{opacity:.7}

.footer__line{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--fs-h3);
  color:var(--ivory);
}
.footer__link{
  display:inline-flex;
  align-items:center;
  min-height:44px;
  color:var(--champagne-text);
  text-decoration:underline;
  text-underline-offset:.25em;
  transition:color var(--dur-s) var(--ease);
}
.footer__link:hover{color:var(--ivory)}
.footer__copyright{font-size:var(--fs-small);color:var(--ivory-soft)}


/* ---- Page mentions ----------------------------------------------------- */

.legal__title{font-size:var(--fs-h1);letter-spacing:var(--ls-tight)}
.legal__meta{margin-top:var(--sp-2);font-size:var(--fs-small);color:var(--ivory-soft)}
.legal__heading{margin-top:var(--sp-6);font-size:var(--fs-h2)}
.legal__subheading{margin-top:var(--sp-4);font-size:var(--fs-h3);color:var(--champagne-text)}
.legal p{margin-top:var(--sp-2);color:var(--ivory-soft);line-height:var(--lh-loose)}
.legal__list{margin-top:var(--sp-3);display:grid;gap:var(--sp-1);color:var(--ivory-soft)}

/* Marqueur d'information légale manquante : volontairement criard, il doit
   être impossible de le publier sans le voir. */
.todo{
  background:var(--champagne);
  color:var(--void);
  padding-inline:var(--sp-1);
  border-radius:var(--radius-s);
  font-family:var(--font-body);
  font-weight:500;
}


/* ---- Hooks d'animation ------------------------------------------------- */
/* Tout est visible par défaut. L'état masqué n'existe que si le script inline
   du <head> a posé `js` sur <html> — sans JavaScript, ou si main.js échoue,
   la page reste intégralement lisible. Valeurs imposées par le CONTRAT §6. */

.js .reveal{
  opacity:0;
  transform:translateY(28px);
  transition:opacity var(--dur-l) var(--ease),transform var(--dur-l) var(--ease);
}
.js .reveal.is-visible{opacity:1;transform:none}

/* Le zoom porte sur l'image, clippée par la tuile : sur le bloc, il
   déborderait de la grille et créerait un défilement horizontal. */
.js .reveal-scale{opacity:0;transition:opacity var(--dur-l) var(--ease)}
.js .reveal-scale img{transform:scale(1.06);transition:transform var(--dur-l) var(--ease)}
.js .reveal-scale.is-visible{opacity:1}
.js .reveal-scale.is-visible img{transform:none}


/* ---- Mouvement réduit -------------------------------------------------- */
/* tokens.css ramène déjà les durées à 1ms. Restent les comportements qui ne
   sont pas de simples transitions — CONTRAT §6 : parallaxe, scroll-expand
   rendu statique, barre de méthode pleine, révélations toutes visibles. */

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}

  .js .hero__media{inset:0;transform:none}

  /* Les trois panneaux du manifeste se posent à leur état final — CONTRAT
     §13.4. main.js n'attache aucun écouteur et ne découpe aucune légende :
     l'image est centrée, les bandes alignées, la phrase entière, le filet
     plein. */
  .js .manifeste__panel--travelling picture{transform:none}
  .js .manifeste__band{inset:0;transform:none}
  .js .manifeste__rule{transform:none}

  .js .scroll-expand{height:auto}
  .js .scroll-expand__sticky{position:relative;height:100svh}
  .js .scroll-expand__media{transform:none}
  .js .scroll-expand__word{transform:none}

  .js .rule-fill__bar{transform:none}

  .js .reveal,.js .reveal-scale{opacity:1;transform:none}
  .js .reveal-scale img{transform:none}

  .js .threshold{transition:none}
}
