/* Base : reset léger + éléments transverses. */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	/* Sans cette réserve, l'en-tête collant recouvre la cible d'une ancre. */
	scroll-padding-top: var(--header-h);
}

body {
	margin: 0;
	background: var(--c-bg);
	color: var(--c-ink);
	font-family: var(--ff-body);
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	font-weight: 400;
	-webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure {
	margin: 0;
	padding: 0;
}

/* Un <hr> non réinitialisé est centré par le navigateur (margin: .5em auto). */
hr {
	margin: 0;
	border: 0;
}

/* Toute la maquette est en graisse normale ; les composants demandent 500 au besoin. */
h1, h2, h3, h4, h5, h6 { font-weight: 400; }

address { font-style: normal; }

/* Filets de section : dessinés par-dessus le contenu, hors flux, comme dans la
   maquette où ils traversent les photos pleine largeur. */
.rule-t, .rule-b { position: relative; }

.rule-t::before,
.rule-b::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	z-index: 2;
	height: var(--rule);
	background: var(--c-line);
	pointer-events: none;
}

.rule-t::before { top: 0; }
.rule-b::after { bottom: 0; }

ul, ol { list-style: none; }

img, svg, picture, video {
	display: block;
	max-width: 100%;
}

a {
	color: inherit;
	text-decoration: none;
}

button {
	font: inherit;
	color: inherit;
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
}

:where(a, button, [tabindex]):focus-visible {
	outline: 2px solid var(--c-accent);
	outline-offset: 3px;
}

/* La maquette est un canevas fixe de 1728 px : au-delà, on plafonne et on centre
   plutôt que d'étirer les gouttières, sinon la nav et les photos grandissent.
   Cible le <main> lui-même pour que tout nouveau gabarit en hérite. */
.site-header,
#main,
.site-footer {
	max-width: var(--canvas);
	margin-inline: auto;
}

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.skip-link:focus {
	position: fixed;
	top: 8px;
	left: 8px;
	z-index: 1000;
	width: auto;
	height: auto;
	clip-path: none;
	padding: 8px 16px;
	background: var(--c-deep);
	color: var(--c-light);
}

/* Sur-titre NORD 10 px / interlettrage 1 px. */
.eyebrow {
	font-family: var(--ff-display);
	font-weight: 500;
	font-size: var(--fs-eyebrow);
	line-height: 1.1; /* node h = 11 pour 10 px */
	letter-spacing: var(--ls-eyebrow);
	color: var(--c-accent);
}

.section-title {
	font-family: var(--ff-display);
	font-weight: 400;
	font-size: var(--fs-h2);
	line-height: var(--lh-h2);
	color: var(--c-ink);
}

.section-text {
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	color: var(--c-ink);
	opacity: var(--o-text);
	--o-arrivee: var(--o-text);
}

/* ─────────────────────── Révélation au défilement ─────────────────────────
   Les attributs « data-reveal » sont posés par le script : sans JavaScript,
   aucun contenu n'est masqué.

   UN SEUL déclenchement par unité (IntersectionObserver), dès que son haut
   franchit 92 % de la hauteur de fenêtre. L'unité porte « is-in » ; tout ce qui
   s'anime à l'intérieur en dépend. On ne suit PAS le défilement
   (« animation-timeline: view() ») : l'avancement y est asservi à la molette,
   le fondu se dévide à l'envers dès qu'on remonte, et la phase « entry » a une
   longueur égale à la hauteur de l'élément — un intitulé de 11 px s'animait sur
   9 px de défilement, une carte de 675 px sur 526.

   Quatre gestes, et non un fondu unique appliqué à tout :

   1. les titres se découvrent LIGNE PAR LIGNE derrière leur propre masque
      (aucune opacité : c'est le masque qui travaille) ;
   2. les images se posent — léger dézoom sous le balayage du voile ;
   3. les filets de section se tracent de la gauche vers la droite ;
   4. le reste monte en fondu, court.

   Une unité chorégraphiée ne bouge pas elle-même : ses parties portent le
   mouvement, séquencées depuis un seul déclenchement. Un bloc, lui, se déplace
   d'un tenant — une photo ne doit pas pouvoir se révéler sans son texte.

   RÈGLE STRUCTURANTE : la « transition » n'est JAMAIS déclarée sur l'état
   masqué, uniquement sur l'état d'arrivée. Sinon poser l'attribut déclenche
   une transition VERS le masquage, et « is-in » arrivant en cours de route,
   la révélation repart d'un état à demi caché — ou, pour l'image, se faisait
   annuler en durée nulle.

   Seules « opacity » et « transform » sont animées : aucun reflow, CLS nul. */

[data-reveal] {
	opacity: 0;
	transform: translate3d(0, 24px, 0);
}

[data-reveal="bloc"] {
	transform: translate3d(0, 28px, 0);
}

/* Le mouvement est porté par les parties : le conteneur reste net. */
[data-reveal].is-choreo {
	opacity: var(--o-arrivee, 1);
	transform: none;
}

/* Opacité d'arrivée : « --o-arrivee » et non 1, sinon ces règles (0,2,0)
   battent « .section-text » (0,1,0) et le paragraphe perd son gris. */
.is-in[data-reveal] {
	opacity: var(--o-arrivee, 1);
	transform: none;
	transition:
		opacity 0.95s var(--ease-reveal) var(--reveal-delay, 0ms),
		transform 0.95s var(--ease-reveal) var(--reveal-delay, 0ms);
}

/* Pas de « will-change » anticipé : Chrome promeut déjà de lui-même un élément
   dont une transition sur « opacity » ou « transform » est en cours, et forcer
   la couche en amont expose à un artefact de compositeur — un aplat noir le
   temps que la couche soit peinte, visible sur certains pilotes. */

/* ── Parties : séquencées depuis le « is-in » de leur unité ────────────────── */

/* Titres : une ligne, un masque. Le masque fait EXACTEMENT la hauteur de la
   ligne : toute marge de compensation fausserait la hauteur du titre, car deux
   marges verticales adjacentes fusionnent au lieu de s'additionner — chaque
   ligne gagnait 5 px et le H1 de l'accueil 20 px. L'interligne des titres
   laisse la place des jambages et des accents (vérifié : aucun débord). */
.reveal-ligne {
	display: block;
	overflow: hidden;
}

[data-reveal-part="ligne"] {
	display: block;
	transform: translate3d(0, 115%, 0);
}

.is-in [data-reveal-part="ligne"] {
	transform: none;
	transition: transform 1.15s var(--ease-reveal) var(--d, 0ms);
}

/* Fondu court, pour les intitulés, les chapôs et les liens. */
[data-reveal-part="doux"] {
	opacity: 0;
	transform: translate3d(0, 14px, 0);
}

.is-in [data-reveal-part="doux"] {
	opacity: var(--o-arrivee, 1);
	transform: none;
	transition:
		opacity 0.85s var(--ease-reveal) var(--d, 0ms),
		transform 0.85s var(--ease-reveal) var(--d, 0ms);
}

/* L'image se pose : elle arrive à peine agrandie et revient à sa taille
   pendant que le voile s'ouvre. Composé par le GPU, aucun repeint. */
[data-reveal-part="assise"] {
	transform: scale(1.06);
}

.is-in [data-reveal-part="assise"] {
	transform: none;
	transition: transform 1.8s var(--ease-reveal) var(--d, 0ms);
}

/* ── Médias : balayage par voile ────────────────────────────────────────────
   Le média ne bouge pas : c'est un aplat posé par-dessus qui se replie. Seul un
   « transform » sur une couleur pleine est animé, donc rien n'est repeint.
   L'origine est à l'OPPOSÉ du sens de découverte.

   Un média peut être une cible à lui seul, ou se trouver DANS une unité qui
   s'anime d'un tenant (portraits de l'accueil, notaire d'une page d'expertise) :
   dans ce second cas le voile part du « is-in » de son unité, avec un retard
   pour que le fondu soit fini quand le balayage commence. */

[data-reveal="media"] {
	position: relative;
	opacity: 1;
	transform: none;
}

/* Fond explicite sous l'image : une couche composée qui n'a pas encore été
   peinte affiche la couleur du site et non le noir du compositeur. Invisible
   en temps normal, l'image couvrant sa boîte. */
[data-reveal-sens] {
	background-color: var(--c-light);
}

.reveal-voile {
	position: absolute;
	inset: 0;
	z-index: 3;
	background: var(--c-light);
	transform-origin: bottom center;
	pointer-events: none;
}

[data-reveal-sens="gauche"] > .reveal-voile { transform-origin: right center; }
[data-reveal-sens="droite"] > .reveal-voile { transform-origin: left center; }

.is-in[data-reveal-sens] > .reveal-voile,
.is-in [data-reveal-sens] > .reveal-voile {
	transform: scaleY(0);
	transition: transform 1.15s var(--ease-reveal) var(--d, 0ms);
}

.is-in[data-reveal-sens="gauche"] > .reveal-voile,
.is-in [data-reveal-sens="gauche"] > .reveal-voile,
.is-in[data-reveal-sens="droite"] > .reveal-voile,
.is-in [data-reveal-sens="droite"] > .reveal-voile { transform: scaleX(0); }

/* ── Filets de section : tracés de gauche à droite ─────────────────────────── */

[data-reveal-rule]::before,
[data-reveal-rule]::after {
	transform: scaleX(0);
	transform-origin: left center;
}

/* Variable PROPRE au filet : « --d » est héritée, et le retard posé sur une
   section se propageait à tout son contenu — le sur-titre du héros attendait
   950 ms au lieu de 250. */
[data-reveal-rule].is-in::before,
[data-reveal-rule].is-in::after {
	transform: scaleX(1);
	transition: transform 1.3s var(--ease-reveal) var(--d-filet, 0ms);
}

/* ── Premier écran : animé à l'arrivée, jamais au défilement ─────────────────
   Le premier bloc porte le LCP : il s'anime au chargement, décalé le temps que
   la page se pose. */

[data-reveal].is-entree.is-in [data-reveal-part],
[data-reveal].is-entree.is-in > .reveal-voile {
	transition-delay: calc(var(--d, 0ms) + var(--d-entree, 250ms));
}

/* ── Déjà franchement visible à l'arrivée : montré net ─────────────────────── */

[data-reveal].is-instant,
[data-reveal].is-instant [data-reveal-part],
[data-reveal-rule].is-instant::before,
[data-reveal-rule].is-instant::after {
	transition: none;
}

@media (prefers-reduced-motion: reduce) {
	[data-reveal] {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.reveal-voile { display: none; }

	[data-reveal-part] {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.reveal-ligne {
		overflow: visible;
		padding-block: 0;
		margin-block: 0;
	}

	[data-reveal-rule]::before,
	[data-reveal-rule]::after {
		transform: none;
		transition: none;
	}

	[data-reveal] { animation: none !important; }

	html { scroll-behavior: auto; }

	/* Portée au THÈME, jamais à « * » : un « !important » sur toute la page
	   atteint aussi l'interface des extensions, et une animation en « forwards »
	   poussée à 0,01 ms saute à son état final — ce qui peut figer un élément
	   tiers dans un état masqué. */
	.site-header *, .site-header *::before, .site-header *::after,
	#main *, #main *::before, #main *::after,
	.site-footer *, .site-footer *::before, .site-footer *::after,
	.nav-overlay *, .nav-overlay *::before, .nav-overlay *::after {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
}
