:root {
  /* ═══════════════════════════════════════════════════════════
     Palette finale — Sable & Bleu (Editorial Warm), 2026-07-25.
     "TROIS COULEURS SUR TOUT LE SITE" : le sable (--bg), l'encre
     (--ink/--text) et le bleu (--accent/--accent-2). Le terracotta était
     la 4e teinte : ses tokens (--secondaire*) sont conservés — une
     vingtaine d'usages en dépendent — mais portent désormais le bleu.
     ═══════════════════════════════════════════════════════════ */
  --bg: #f2ede3;
  --bg-alt: #e9e2d3;
  --surface: #faf7f0;
  --surface-soft: #efe9dd;
  --text: #1d2531;
  --text-rgb: 29, 37, 49;
  --text-muted: #5d6875;
  --text-soft: #8a93a0;
  --mute: #5d6875;
  --ink: #141b26;
  --ink-rgb: 20, 27, 38;
  --ink-2: #0a0f1d;
  --paper: #faf7f0;
  --paper-rgb: 250, 247, 240;
  --accent: #3fa9e0;
  --accent-rgb: 63, 169, 224;
  --accent-2: #156a99;
  --accent-2-rgb: 21, 106, 153; /* 5.06:1 sur sable */
  --accent-soft: #bfe3f5;
  --accent-soft-rgb: 191, 227, 245;
  --brass-2: #156a99;
  --secondaire: #156a99;
  --secondaire-rgb: 21, 106, 153;
  --secondaire-vif: #3fa9e0;
  --secondaire-vif-rgb: 255, 122, 69; /* fonds/badges, jamais en texte seul sur sable */
  --border: rgba(29, 37, 49, 0.16);
  --line: rgba(29, 37, 49, 0.14);
  --ligne: rgba(29, 37, 49, 0.14);
  --bordure: rgba(29, 37, 49, 0.16);
  --glow-warm: rgba(63, 169, 224, 0.22);
  --glow-cool: rgba(233, 226, 211, 0.55);
  --shadow-soft: 0 24px 70px -52px rgba(31, 42, 58, 0.28);
  --shadow-card: 0 34px 90px -62px rgba(31, 42, 58, 0.3);
  --shadow-hover: 0 42px 120px -60px rgba(31, 42, 58, 0.34);
  /* alias hérités de l'ancien bloc dark : filet de sécurité pour toute
     règle orpheline qui les référencerait encore */
  --noir: var(--bg);
  --noir-2: var(--bg-alt);
  --creme: var(--text);
  --ambre: var(--accent);
  --ambre-fonce: var(--accent-2);
  --gris: var(--text-muted);

  /* voies du parcours (timeline) : palette additive, ne remplace rien
     ci-dessus, ne sert qu'aux tampons/jauges/balises */
  --voie-bleu: #4a8fe0;
  --voie-bleu-rgb: 74, 143, 224;
  --voie-vert: #aeb7c9;
  --voie-vert-rgb: 174, 183, 201; /* "argent", garde le nom pour compat */
  --voie-jaune: #9b8fd1;
  --voie-jaune-rgb: 155, 143, 209; /* violet subtil, garde le nom pour compat */
  --voie-corail: #7fe0f5;
  --voie-corail-rgb: 127, 224, 245; /* cyan clair, garde le nom pour compat */

  /* DEUX POLICES SUR TOUT LE SITE (2026-07-25, demande "max 2 polices").
     Syne porte toute la VOIX (titres, libellés, chiffres, citations) :
     dessinée pour un centre d'art, elle colle au fil rouge musée du site et
     evite le cliche "creme + serif haute-contraste" que produisent par defaut
     la plupart des maquettes generees. Schibsted Grotesk porte tout le TEXTE.
     Retirees : DotGothic16, Instrument Serif, Geist. */
  --display: "Syne", sans-serif;
  --serif: "Syne", sans-serif; /* rôle "voix discrète" : eyebrows, chapeaux, citations — même famille, différenciée par la graisse et l'interlettrage */
  --font-display: "Syne", sans-serif; /* était Instrument Serif — consolidation 2 polices */
  --mono:
    "Schibsted Grotesk", Inter, system-ui, sans-serif; /* texte / interface (plus de monospace dédié, cf. demande "uniquement ces deux polices") */

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --e-io: cubic-bezier(0.76, 0, 0.24, 1);
  --marge: clamp(
    1.2rem,
    2.6vw,
    2.6rem
  ); /* gouttière commune : header, hero, meta */
  --max: 1500px; /* largeur max du contenu (élargie sur grands écrans, voir media) */
  --edge: 88px;

  --color-bg: #f7f2ea;
  --color-line: #d8d1c4;
  --color-text: #1a1a1a;
}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
/* overflow-x:clip (et non hidden) : interdit AUSSI le scroll horizontal
   programmatique — sinon les décos qui dépassent peuvent décaler la page.
   PAS de scroll-behavior:smooth ici : il entre en conflit avec Lenis. */
/* base typographique remontée (look éditorial Awwwards) — tout le rem/clamp suit.
   Bornes relevées (2026-07-17, "aggrandir... l'ensemble du site") : +1px en bas et en
   haut, +0.05vw de pente — un cran modéré plutôt qu'un doublement, pour ne pas aller à
   l'encontre de la passe de réduction du 2026-07-10 (qui avait resserré les clamp() MAX
   de composants spécifiques, un réglage différent de ce multiplicateur racine partagé
   par tout le site) tout en se faisant sentir partout où du rem est utilisé. */
html {
  overflow-x: clip;
  font-size: clamp(18px, 1rem + 0.4vw, 22px);
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  margin: 0;
  font-family: var(--font-display);
}

.eyebrow {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text);
  text-decoration: none;
  font-weight: 500;
}

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

li a {
  text-decoration: none;
  color: var(--color-text);
  transition: opacity 0.2s ease;
}

li a:hover {
  opacity: 0.6;
}

/* Dernière ligne avec bordure du bas */
ul li:last-child a {
  border-bottom: 1px solid var(--color-line);
}

.work-row__cat {
  color: #8a8378;
}

h2 {
  margin: 0;
}

/* contenu plus large au-delà d'un écran 14" (~1512px), puis sur très grands écrans */
@media (min-width: 1536px) {
  :root {
    --max: 1760px;
  }
}
@media (min-width: 1920px) {
  :root {
    --max: 1920px;
  }
}

/* ───── CSS requise par Lenis (sinon le scroll se fige) ───── */
html.lenis,
html.lenis body {
  height: auto;
}
.lenis.lenis-smooth {
  scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
  overscroll-behavior: contain;
}
.lenis.lenis-stopped {
  overflow: hidden;
}
.lenis.lenis-smooth iframe {
  pointer-events: none;
}
body {
  background: var(--noir);
  color: var(--creme);
  font-family: var(
    --mono
  ); /* "Schibsted Grotesk" : police de texte/interface par défaut — var(--display) (DotGothic16) reste réservée aux gros titres */
  font-size: 1rem;
  line-height: 1.55;
  overflow-x: clip;
  cursor: none;
  --cine-x: 50%;
  --cine-y: 42%;
  --cine-heat: 0;
}
a,
button {
  color: inherit;
  text-decoration: none;
  cursor: none;
}
::selection {
  background: var(--ambre);
  color: var(--noir);
}

/* ───── Scrollbar native aux couleurs du portfolio + suppression de toute barre horizontale parasite ───── */
html {
  scrollbar-color: var(--accent) var(--bg-alt);
  scrollbar-width: thin;
}
::-webkit-scrollbar {
  width: 10px;
  background: var(--bg-alt);
}
::-webkit-scrollbar:horizontal {
  display: none;
  height: 0;
}
::-webkit-scrollbar-track {
  background: var(--bg-alt);
}
::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 999px;
  border: 2px solid var(--bg-alt);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--accent-2);
  border-color: var(--bg-alt);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner {
  background: var(--bg-alt);
}
a:focus-visible,
button:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 5px;
  border-radius: 2px;
}

/* ───── Loading screen premium : rideaux verticaux + compteur 00→100 + texte fragmenté (intro — retiré du DOM après l'animation) ───── */
html.charge {
  overflow: hidden;
}
html.scroll-verrouille {
  overflow: hidden;
}
.loader {
  position: fixed;
  inset: 0;
  z-index: 99999;
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* la coque du vaisseau : tout ce qui n'est pas la fenêtre elle-même.
     .loader-espace (l'extérieur) n'est visible que dans le cercle qu'ouvre
     .loader-hublot-obturateur — nul besoin d'un calque de coque séparé, ce
     fond EST la paroi. */
  background: #05070c;
}

/* ───── Grain ───── */
/* opacity seule (sans blend-mode) restait quasi invisible sur le fond sombre : le grain
   se voit à peine. overlay le fait réagir aux couleurs sous lui, comme un vrai grain pellicule. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 9998;
  pointer-events: none;
  opacity: 0.16;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ───── Traitement cinéma global : vignette, scanlines, letterbox ───── */
.cine-overlay {
  position: fixed;
  inset: 0;
  z-index: 9995;
  pointer-events: none;
  overflow: hidden;
  mix-blend-mode: normal;
}
/* la tache qui suit le curseur vivait AVANT dans ce background, repositionnée
   via --cine-x/--cine-y : ces variables changent à 60fps pour toujours (cf.
   initCinematicShell, main.js), et un radial-gradient qui bouge est une
   propriété de PEINTURE — ça forçait un repaint plein viewport à chaque
   frame, sur TOUTES les pages, en permanence. Déplacée dans .cine-spot,
   pilotée en transform/opacity (compositing GPU seul, aucun repaint). */
.cine-vignette {
  position: absolute;
  inset: -2px;
  background:
    radial-gradient(
      120% 92% at 50% 44%,
      transparent 40%,
      rgba(0, 0, 0, 0.5) 100%
    ),
    linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.36),
      transparent 14%,
      transparent 86%,
      rgba(0, 0, 0, 0.36)
    );
  opacity: 0.85;
}
.cine-spot {
  position: absolute;
  inset: -50%;
  pointer-events: none;
  will-change: transform, opacity;
  background: radial-gradient(
    26rem 17rem at 50% 50%,
    rgba(0, 123, 255, 0.7),
    transparent 58%
  );
  opacity: 0.06;
}
.cine-scan {
  position: absolute;
  inset: 0;
  opacity: 0.08;
  background:
    linear-gradient(rgba(244, 248, 255, 0.22) 1px, transparent 1px) 0 0/100% 4px,
    linear-gradient(
        90deg,
        transparent,
        rgba(0, 123, 255, 0.16),
        transparent
      ) -35vw
      0/35vw 100% no-repeat;
  animation: cineSweep 7.5s linear infinite;
}
@keyframes cineSweep {
  to {
    background-position:
      0 0,
      135vw 0;
  }
}

/* ───── Barre de progression de lecture ───── */
.progres {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 9997;
  background: var(--ambre);
  transform-origin: 0 50%;
  transform: scaleX(0);
}

/* ───── Curseur ───── */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 10px;
  height: 10px;
  background: var(--ambre);
  border-radius: 50%;
  pointer-events: none;
  z-index: 9999;
  transform: translate(-50%, -50%);
  transition:
    width 0.25s,
    height 0.25s,
    background 0.25s;
  mix-blend-mode: exclusion;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: transform;
}
/* au survol : le curseur grossit en blob blanc qui INVERSE le contenu (mix-blend-mode) */
.cursor.grossi {
  width: 56px;
  height: 56px;
  background: #fff;
  mix-blend-mode: difference;
}
.cursor .etiquette {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--noir);
  opacity: 0;
  transition: opacity 0.2s;
}
.cursor.voir {
  width: 74px;
  height: 74px;
  background: #fff;
  mix-blend-mode: difference;
}
.cursor.voir .etiquette {
  opacity: 1;
}
/* anneau traînant derrière le point */
.cursor-anneau {
  position: fixed;
  top: 0;
  left: 0;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(0, 123, 255, 0.5);
  border-radius: 50%;
  pointer-events: none;
  z-index: 9999;
  transform: translate(-50%, -50%);
  transition:
    width 0.35s var(--ease),
    height 0.35s var(--ease),
    opacity 0.3s;
  will-change: transform;
}
.cursor-anneau.grossi {
  width: 64px;
  height: 64px;
  opacity: 0.35;
}
.cursor-anneau.voir {
  opacity: 0;
}
@media (hover: none) {
  body,
  a,
  button {
    cursor: auto;
  }
  .cursor,
  .cursor-anneau {
    display: none;
  }
}

/* ───── Header ───── */
header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1.2rem var(--marge);
  mix-blend-mode: difference;
  color: #fff;
  transition: transform 0.45s var(--ease);
}
header.cache {
  transform: translateY(-110%);
}
header .logo {
  /* cible tactile ≥44px : ce lien-logo (work.html/étude de cas/404) ne
     mesurait que la hauteur de son texte/SVG (~27px, audit mobile headless) */
  display: inline-flex;
  align-items: center;
  padding: 0.8rem 0;
  font-weight: 820;
  letter-spacing: 0.08em;
  font-size: 1rem;
}
header .logo sup {
  color: var(--ambre);
  font-size: 0.6em;
}
header nav {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  font-size: 0.85rem;
  flex-wrap: wrap;
}
header nav a {
  /* même correctif de cible tactile que header .logo ci-dessus — un peu
     de padding horizontal en plus : "Projets"/"Accueil" restaient <44px de
     large avec le seul padding vertical (mots courts, pas assez de matière) */
  display: inline-flex;
  align-items: center;
  padding: 0.8rem 0.25rem;
  margin: 0 -0.25rem;
  position: relative;
  opacity: 0.85;
}
header nav a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  height: 1px;
  width: 0;
  background: #fff;
  transition: width 0.3s var(--ease);
}
header nav a:hover::after {
  width: 100%;
}

/* ───── Hero — reveal par masque logo (repris de portfolio2) ───── */
.hero-cadre {
  position: relative;
}
.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  z-index: 2;
  background: var(--noir);
}
.hero::before {
  content: "";
  position: absolute;
  inset: clamp(0.8rem, 1.6vw, 1.5rem);
  z-index: 6;
  pointer-events: none;
  border: 1px solid rgba(244, 248, 255, 0.18);
  opacity: 0.52;
  mix-blend-mode: screen;
}
.hero::after {
  content: "";
  position: absolute;
  left: clamp(1.2rem, 2.6vw, 2.6rem);
  right: clamp(1.2rem, 2.6vw, 2.6rem);
  bottom: clamp(1rem, 2.4vh, 1.8rem);
  height: 1px;
  z-index: 6;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(244, 248, 255, 0.42),
    transparent
  );
}

/* atmosphère visible autour / derrière le masque */
.hero-light {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  will-change: transform, opacity;
}
.hero-glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  will-change: transform;
}
.glow-a {
  width: 62vw;
  height: 62vw;
  left: -12vw;
  top: -22vw;
  background: radial-gradient(circle, var(--glow-warm), transparent 64%);
}
.glow-b {
  width: 48vw;
  height: 48vw;
  right: -8vw;
  bottom: -14vw;
  background: radial-gradient(circle, var(--glow-cool), transparent 70%);
}
.hero-beam {
  position: absolute;
  top: -25%;
  left: 34%;
  width: 42%;
  height: 170%;
  background: linear-gradient(
    180deg,
    rgba(var(--accent-rgb), 0.13),
    transparent 68%
  );
  transform: rotate(13deg);
  filter: blur(34px);
  mix-blend-mode: screen;
}

/* fond étoilé du hero : canvas Three.js léger (désactivé mobile/reduced-motion/no-webgl) */
.hero-lune {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.hero-lune-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* image finale plein-cadre, AU-DESSUS du SVG : à la fin du scroll elle
   recouvre tout (zéro masque → aucun bug Chrome). Photo seule, plein écran. */
.hero-fallback {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  will-change: opacity;
  background: var(--noir) url(/assets/img/hero.jpg) center/cover no-repeat;
}
.hero-fallback::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      180deg,
      rgba(5, 5, 5, 0.1),
      transparent 34%,
      rgba(5, 5, 5, 0.54)
    ),
    radial-gradient(80% 70% at 50% 42%, transparent 42%, rgba(5, 5, 5, 0.55));
  pointer-events: none;
}

/* couche SVG : UNE image, révélée par le masque logo qui grandit */
.hero-mask-svg {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  display: block;
}
/* PAS de transform-box:fill-box ici : GSAP calcule svgOrigin dans l'espace du viewBox
   (view-box), fill-box le fait réinterpréter dans la bbox propre de l'élément — c'est ce
   qui envoyait le logo hors-cadre pendant le zoom (bbox groupe ≠ point voulu). */
.hero-mask-logo {
  will-change: transform;
}
.hero-mask-logo image {
  display: block;
}
.hero-mask-logo .hero-logo-mask-img-mobile {
  display: none;
}

/* barre d'accent */
.hero-accent {
  position: absolute;
  top: clamp(5.4rem, 12vh, 8rem);
  left: 50%;
  z-index: 4;
  width: clamp(120px, 18vw, 260px);
  height: 3px;
  border-radius: 2px;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    transparent,
    var(--ambre) 30%,
    var(--brass-2) 70%,
    transparent
  );
  transform: translateX(-50%) scaleX(0);
  transform-origin: 50% 50%;
  opacity: 0;
}

/* kicker (haut, centré) */
.hero-kicker {
  position: absolute;
  top: clamp(6.6rem, 15vh, 9rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  margin: 0;
  will-change: transform, opacity;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
}

/* h1 masqué pour le SEO / l'accessibilité (le SVG porte le visuel) */
.hero-vh {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* contenu de support (bas, centré) */
.hero-content {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(8rem, 20vh, 13rem);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-inline: var(--marge);
  gap: clamp(1.3rem, 2.6vh, 2rem);
  will-change: transform, opacity;
}
.hero-subtitle {
  max-width: 52ch;
  color: var(--mute);
  margin: 0;
  font-family: var(--serif);
  font-style: normal;
  font-size: clamp(1.05rem, 1.6vw, 1.35rem);
  line-height: 1.45;
}
.hero-subtitle em {
  color: var(--ambre);
  font-style: normal;
}
.hero-content .ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}

.hero-cinema-meta {
  position: absolute;
  right: var(--marge);
  bottom: clamp(2rem, 5vh, 3.4rem);
  z-index: 5;
  display: grid;
  grid-template-columns: auto auto;
  gap: 0.35rem 1rem;
  align-items: end;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.74);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  text-align: right;
}
.hero-cinema-meta b {
  font-size: clamp(1.4rem, 2.4vw, 2.3rem);
  line-height: 0.9;
  color: var(--ambre);
  letter-spacing: -0.03em;
}
.hero-cinema-meta span:nth-child(2n + 1) {
  color: var(--gris);
}

/* indicateur de scroll */
.hero-scroll-indicator {
  position: absolute;
  left: 50%;
  bottom: 2.2rem;
  z-index: 4;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  opacity: 1;
  will-change: transform, opacity;
}
.hero-scroll-indicator .track {
  width: 1.5px;
  height: 48px;
  background: var(--ligne);
  position: relative;
  overflow: hidden;
  border-radius: 2px;
}
.hero-scroll-indicator .track::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ambre);
  transform: translateY(-100%);
  animation: scrollpulse 2.4s var(--e-io) infinite;
}
@keyframes scrollpulse {
  0% {
    transform: translateY(-100%);
  }
  60%,
  100% {
    transform: translateY(100%);
  }
}
.hero-scroll-indicator span {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
}

/* Réseaux sociaux révélés sur la photo finale (timeline gère l'apparition via autoAlpha) */
.hero-socials {
  position: absolute;
  left: var(--marge);
  bottom: clamp(2rem, 5vh, 3.4rem);
  z-index: 5;
  opacity: 0;
  visibility: hidden;
}
.hsoc-row {
  display: flex;
  gap: 0.8rem;
}
.hsoc-btn {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  font: inherit;
  width: clamp(54px, 4.2vw, 68px);
  height: clamp(54px, 4.2vw, 68px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--creme);
  border: 1px solid rgba(244, 248, 255, 0.32);
  background: rgba(5, 5, 5, 0.32);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition:
    background 0.35s var(--ease),
    color 0.35s,
    border-color 0.35s,
    box-shadow 0.35s var(--ease);
}
.hsoc-btn:hover,
.hsoc-btn.actif {
  background: var(--ambre);
  color: var(--noir);
  border-color: var(--ambre);
  box-shadow: 0 10px 30px -8px rgba(0, 123, 255, 0.55);
}
.hero-socials svg {
  width: 46%;
  height: 46%;
  display: block;
}
/* carte d'info ouverte au clic, au-dessus des boules */
.hsoc-card {
  position: absolute;
  left: 0;
  bottom: calc(100% + 0.9rem);
  min-width: 240px;
  max-width: 340px;
  padding: 1.1rem 1.3rem;
  border-radius: 16px;
  border: 1px solid var(--ligne);
  transform-origin: bottom left;
  background: rgba(8, 7, 6, 0.72);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 26px 55px -22px rgba(0, 0, 0, 0.85);
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px) scale(0.94);
  transition:
    opacity 0.4s var(--ease),
    transform 0.4s var(--ease),
    visibility 0.4s;
}
.hsoc-card.on {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.hsoc-card-titre {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.25rem;
  color: var(--creme);
}
.hsoc-card-sous {
  display: block;
  color: var(--mute);
  font-size: 0.92rem;
  line-height: 1.45;
  margin: 0.35rem 0 1rem;
}
.hsoc-card-lien {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ambre);
  transition: gap 0.3s var(--ease);
}
.hsoc-card-lien:hover {
  gap: 0.8rem;
}

@media (max-width: 640px) {
  .hero-mask-logo .hero-logo-mask-img-desktop {
    display: none;
  }
  .hero-mask-logo .hero-logo-mask-img-mobile {
    display: block;
  }
  .hero-subtitle {
    max-width: 40ch;
  }
  .hero-content {
    bottom: clamp(6rem, 15vh, 10rem);
  }
  .hero-cinema-meta {
    display: none;
  }
}

/* ───── Marquees ───── */
.marquee {
  border-top: 1px solid var(--ligne);
  border-bottom: 1px solid var(--ligne);
  overflow: hidden;
  white-space: nowrap;
  padding: 0.9rem 0;
  user-select: none;
}
.marquee .defile {
  display: inline-block;
  animation: defile 18s linear infinite;
}
.marquee span {
  font-weight: 800;
  text-transform: uppercase;
  font-variation-settings: "wdth" 120;
  font-size: clamp(1.6rem, 4vw, 3rem);
  padding-right: 3.2rem;
  color: var(--creme);
}
.marquee span::after {
  content: "✶";
  color: var(--ambre);
  margin-left: 3.2rem;
  font-weight: 400;
}
@keyframes defile {
  to {
    transform: translateX(-50%);
  }
}

/* reveal masqué : le texte monte derrière une fenêtre invisible */
.masque {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.masque .int {
  display: inline-block;
  transform: translateY(112%);
  transition: transform 1s var(--ease) var(--rd, 0s);
}
.vu .int {
  transform: translateY(0);
}

section {
  padding: 0 0 7rem;
  position: relative;
}
.contenu {
  padding: 4.5rem 2rem 0;
  max-width: var(--max);
  margin: 0 auto;
  position: relative;
  z-index: 2;
}
.section-num {
  font-family: var(--serif);
  font-style: normal;
  color: var(--brass-2);
  font-size: 1rem;
  margin-bottom: 1.6rem;
  display: block;
  position: relative;
}
.section-num b {
  color: var(--ambre);
  font-weight: 400;
}
.section-num::after {
  content: "";
  display: inline-block;
  width: clamp(46px, 7vw, 110px);
  height: 1px;
  margin-left: 1rem;
  vertical-align: middle;
  background: linear-gradient(90deg, rgba(0, 123, 255, 0.75), transparent);
}

/* étoile décorative en parallaxe */
.deco-etoile {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  font-size: clamp(8rem, 22vw, 20rem);
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px rgba(244, 248, 255, 0.09);
  will-change: transform;
}

/* ───── Témoignage (preuve sociale — entre Services et Projets, volontairement non numérotée) ───── */
.temoin {
  padding: clamp(5rem, 10vw, 9rem) var(--marge);
  text-align: center;
  border-top: 1px solid var(--ligne);
  border-bottom: 1px solid var(--ligne);
  position: relative;
  overflow: hidden;
}
.temoin-inner {
  max-width: 52ch;
  margin: 0 auto;
  position: relative;
}
.temoin-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brass-2);
  margin-bottom: 2rem;
}
.temoin-tag::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brass-2);
  flex-shrink: 0;
}
/* grand guillemet décoratif, en filigrane derrière la citation — même technique que les
   sceaux/monogrammes ghost utilisés ailleurs sur le site (case-signature, page 404) */
.temoin-mark {
  position: absolute;
  top: -3rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: -1;
  font-family: var(--display);
  font-size: clamp(8rem, 18vw, 15rem);
  line-height: 1;
  color: transparent;
  background: linear-gradient(160deg, var(--brass-2), var(--ambre));
  -webkit-background-clip: text;
  background-clip: text;
  opacity: 0.16;
  pointer-events: none;
  user-select: none;
}
.temoin-quote p {
  font-family: var(--serif);
  font-style: normal;
  color: var(--creme);
  font-size: clamp(1.8rem, 3.6vw, 3rem);
  line-height: 1.26;
  position: relative;
  z-index: 1;
}
.temoin-quote strong {
  color: var(--brass-2);
  font-weight: 700;
}
.temoin-auteur {
  margin-top: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
}
.temoin-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  font-family: var(--display);
  font-size: 0.92rem;
  color: var(--noir);
  background: linear-gradient(160deg, var(--brass-2), var(--ambre));
}
.temoin-id {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}
.temoin-nom {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--creme);
}
.temoin-titre {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gris);
}
.temoin-lien {
  display: inline-block;
  margin-top: 1.2rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ambre);
  transition: opacity 0.25s;
}
.temoin-lien:hover {
  opacity: 0.7;
}

/* ───── Projets ───── */
.projets-liste {
  border-top: 1px solid var(--ligne);
}
.projet {
  display: grid;
  grid-template-columns: auto minmax(0, 2fr) minmax(0, 2fr) auto;
  gap: 2rem;
  align-items: baseline;
  padding: 2.4rem 2rem;
  border-bottom: 1px solid var(--ligne);
  position: relative;
  overflow: hidden;
  transition:
    background 0.35s,
    color 0.35s,
    letter-spacing 0.35s var(--ease);
  isolation: isolate;
}
.projet::before {
  content: attr(data-shot);
  position: absolute;
  left: 2rem;
  top: 0.65rem;
  z-index: -1;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.24);
  transform: translateY(-8px);
  opacity: 0;
  transition:
    opacity 0.35s var(--ease),
    transform 0.35s var(--ease);
}
.projet::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  opacity: 0;
  background:
    linear-gradient(
      90deg,
      rgba(5, 5, 5, 0.2),
      transparent 18%,
      transparent 82%,
      rgba(5, 5, 5, 0.24)
    ),
    repeating-linear-gradient(
      90deg,
      rgba(5, 5, 5, 0.12) 0 1px,
      transparent 1px 7px
    );
  transition: opacity 0.35s var(--ease);
}
.projet .index {
  font-family: var(--serif);
  font-style: normal;
  color: var(--gris);
  font-size: 1rem;
  transition: color 0.35s;
}
.projet:hover {
  background: var(--ambre);
  color: var(--noir);
}
.projet:hover::before {
  opacity: 1;
  transform: none;
  color: rgba(5, 5, 5, 0.55);
}
.projet:hover::after {
  opacity: 0.32;
}
.projet:hover .index {
  color: rgba(5, 5, 5, 0.55);
}
.projet h3 {
  font-weight: 900;
  font-variation-settings: "wdth" 120;
  font-size: clamp(1.4rem, 3.2vw, 2.6rem);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  transition:
    transform 0.35s var(--ease),
    letter-spacing 0.35s var(--ease);
}
.projet:hover h3 {
  transform: translateX(10px);
  letter-spacing: 0.015em;
}
.projet .infos .desc {
  font-size: 0.95rem;
  opacity: 0.7;
  max-width: 38ch;
}
.projet .tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.7rem;
}
.projet .tags span {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  border-radius: 99px;
  padding: 0.2rem 0.6rem;
  opacity: 0.55;
  transition: opacity 0.3s;
}
.projet:hover .tags span {
  opacity: 0.85;
}
.projet .annee {
  font-family: var(--serif);
  font-style: normal;
  font-size: 1.1rem;
  transition: opacity 0.3s;
}
.projet .fleche {
  position: absolute;
  right: 2rem;
  top: 50%;
  transform: translateY(-50%) translateX(-12px);
  opacity: 0;
  transition:
    opacity 0.3s,
    transform 0.3s var(--ease);
  font-size: 1.6rem;
}
.projet:hover .fleche {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}
.projet:hover .annee {
  opacity: 0;
}

.apercu {
  position: fixed;
  width: min(360px, 32vw);
  height: min(230px, 20vw);
  pointer-events: none;
  z-index: 50;
  border-radius: 3px;
  overflow: hidden;
  opacity: 0;
  transform: scale(0.82) rotate(-2deg) translateY(12px);
  transition:
    opacity 0.3s,
    transform 0.35s var(--ease);
  box-shadow: 0 25px 60px rgba(0, 0, 0, 0.6);
  will-change: left, top;
  border: 1px solid rgba(244, 248, 255, 0.18);
}
.apercu.visible {
  opacity: 1;
  transform: scale(1) rotate(0) translateY(0);
}
.apercu::before {
  content: "";
  position: absolute;
  inset: 8px;
  z-index: 3;
  border: 1px solid rgba(244, 248, 255, 0.25);
  pointer-events: none;
}
.apercu::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(
      180deg,
      rgba(5, 5, 5, 0.08),
      transparent 45%,
      rgba(5, 5, 5, 0.42)
    ),
    repeating-linear-gradient(
      0deg,
      rgba(255, 255, 255, 0.08) 0 1px,
      transparent 1px 5px
    );
  mix-blend-mode: soft-light;
}
.apercu .visuel {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 900;
  font-size: 3.2rem;
  color: rgba(255, 255, 255, 0.9);
  font-variation-settings: "wdth" 125;
  letter-spacing: -0.02em;
}

.prochain {
  text-align: center;
  padding: 6rem 2rem 2rem;
  position: relative;
  z-index: 2;
}
.prochain h3 {
  font-family: var(--serif);
  font-style: normal;
  font-weight: 400;
  font-size: clamp(2rem, 5vw, 3.8rem);
}
.prochain p {
  color: var(--gris);
  margin-top: 0.8rem;
}

/* ───── Compétences ─────
   ANCIENNE grille .comp-grille/.comp-bloc/.chip (audit 2026-08-01) : supprimée,
   0 occurrence dans le HTML/JS — génération encore antérieure à .comp-px,
   elle-même remplacée par la table à dessin actuelle. */

/* ─── ANCIEN « Compétences : PARALLAX plein écran » (audit 2026-08-01) : bloc
   entier supprimé — remplacé depuis par la table à dessin (planches, cf.
   sections/competences-table.js). #competences.comp-px, .comp-stage,
   .comp-scene, .comp-fam, .comp-ghost, .comp-cloud, .comp-tag, .comp-px-ui,
   .comp-px-kicker, .comp-px-foot, .comp-px-nom, .comp-px-ticks : zéro
   occurrence dans le HTML ou le JS, confirmé avant suppression. ─── */

/* ───── Parcours ───── */
.timeline {
  position: relative;
  padding-left: 2.2rem;
  max-width: 880px;
}
.timeline::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.4rem;
  bottom: 0.4rem;
  width: 1px;
  background: linear-gradient(var(--ambre), var(--ligne));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 1.4s var(--ease) 0.25s;
}
.timeline.vu::before {
  transform: scaleY(1);
}
.etape {
  position: relative;
  padding: 0 0 3rem;
}
.etape:last-child {
  padding-bottom: 0;
}
.etape::before {
  content: "";
  position: absolute;
  left: -2.2rem;
  top: 0.55rem;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--noir);
  border: 1px solid var(--ambre);
  transform: translateX(-4px);
  transition: background 0.3s;
}
.etape:hover::before {
  background: var(--ambre);
}
.etape .periode {
  font-family: var(--serif);
  font-style: normal;
  color: var(--ambre);
  font-size: 1.05rem;
}
.etape h3 {
  font-weight: 900;
  font-variation-settings: "wdth" 118;
  font-size: clamp(1.25rem, 2.6vw, 1.9rem);
  text-transform: uppercase;
  margin: 0.25rem 0 0.15rem;
}
.etape .ou {
  color: var(--gris);
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.etape p {
  margin-top: 0.7rem;
  max-width: 62ch;
  opacity: 0.78;
  font-size: 0.96rem;
}
.etape .mini-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.8rem;
}
.etape .mini-tags span {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gris);
  border: 1px dashed var(--ligne);
  border-radius: 99px;
  padding: 0.18rem 0.6rem;
  transition:
    color 0.25s,
    border-color 0.25s;
}
.etape:hover .mini-tags span {
  color: var(--creme);
  border-color: var(--gris);
}

/* ───── Bouton (base) ───── */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.9rem 1.6rem;
  border: 1px solid var(--ligne);
  border-radius: 99px;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--creme);
  white-space: nowrap;
  transition:
    color 0.4s var(--ease),
    border-color 0.4s var(--ease);
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--ambre);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.45s var(--ease);
}
.btn:hover {
  color: var(--noir);
  border-color: var(--ambre);
}
.btn:hover::before {
  transform: scaleX(1);
}
.btn.plein {
  color: var(--noir);
  border-color: var(--ambre);
}
.btn.plein::before {
  background: var(--ambre);
  transform: scaleX(1);
}
.btn.plein:hover::before {
  background: var(--creme);
}
.btn .fl {
  display: inline-block;
  transition: transform 0.35s var(--ease);
}
.btn:hover .fl {
  transform: translateX(3px);
}

/* ───── Contact : bande ambre ───── */
.contact {
  background: var(--ambre);
  color: var(--noir);
  padding: 6rem 0;
}
.contact .titre-contact {
  text-align: center;
  padding: 0 2rem 1rem;
  font-weight: 900;
  font-variation-settings: "wdth" 125;
  font-size: clamp(2.4rem, 7vw, 6rem);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: -0.02em;
}
.contact .titre-contact em {
  font-family: var(--serif);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}
.contact .intro {
  text-align: center;
  padding: 0 2rem 3rem;
  font-family: var(--serif);
  font-style: normal;
  font-size: clamp(1.2rem, 2.5vw, 1.8rem);
  color: rgba(5, 5, 5, 0.65);
}
.email-marquee {
  overflow: hidden;
  white-space: nowrap;
}
.email-marquee a {
  display: inline-block;
  animation: defile 26s linear infinite;
}
.email-marquee span {
  font-weight: 900;
  text-transform: uppercase;
  font-variation-settings: "wdth" 125;
  font-size: clamp(2.2rem, 7vw, 6.5rem);
  padding-right: 4rem;
  letter-spacing: -0.02em;
}
.email-marquee a:hover span {
  -webkit-text-stroke: 2px var(--noir);
  color: transparent;
}
.contact .pied {
  text-align: center;
  margin-top: 3.5rem;
  font-size: 0.85rem;
  color: rgba(5, 5, 5, 0.6);
}
.contact .pied .btn {
  border-color: var(--noir);
  color: var(--noir);
  margin-bottom: 1.2rem;
}
.contact .pied .btn::before {
  background: var(--noir);
}
.contact .pied .btn:hover {
  color: var(--ambre);
}

/* ───── Contact final : CTA fusionnée dans le bloom de fin de Parcours (.p3-contact) ───── */
.contact-kicker,
.contact-note {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(5, 5, 5, 0.8);
}
.contact-cta {
  font-size: clamp(1rem, 1.7vw, 1.4rem);
  padding: 1.05rem clamp(1.8rem, 3vw, 2.6rem);
  box-shadow: 0 24px 80px rgba(5, 5, 5, 0.14);
}
.contact-alt {
  position: relative;
  font-family: var(--mono);
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: rgba(5, 5, 5, 0.78);
  text-decoration: underline;
  text-decoration-color: rgba(5, 5, 5, 0.4);
  text-decoration-thickness: 1px;
  transition: color 0.25s;
}
/* cible tactile ≥44px : ce lien n'avait AUCUN padding (hauteur = line-height du texte
   seul, ~20px). Le gap du parent .contact-inner est trop court (14.4-24px) pour un
   padding+marge négative sans mordre sur .contact-cta/.contact-note voisins — zone de
   clic invisible en ::before à la place, hors flux donc sans risque pour l'espacement. */
.contact-alt::before {
  content: "";
  position: absolute;
  inset: -12px -6px;
  min-height: 44px;
}
.contact-alt:hover {
  color: var(--noir);
}

/* ───── Footer ───── */
footer {
  background: var(--noir);
  color: var(--creme);
  padding: 4rem 2rem 2rem;
  border-top: 1px solid var(--ligne);
}
.footer-grille {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 3rem;
  max-width: var(--max);
  margin: 0 auto 4rem;
  font-size: 0.9rem;
}
footer .label {
  font-family: var(--serif);
  font-style: normal;
  color: var(--gris);
  margin-bottom: 1rem;
}
footer ul {
  list-style: none;
}
footer li {
  padding: 0;
}
footer a {
  /* cible tactile ≥44px (WCAG/HIG) : un <a> inline ne mesurait que sa hauteur
     de ligne (~19px, confirmé en audit mobile headless) — le padding vertical
     porte maintenant la zone tapable, .25rem de li a été retiré pour ne pas
     doubler l'espace visuel entre les liens (le padding de l'ancre y suffit). */
  display: inline-block;
  padding: 0.8rem 0;
  opacity: 0.8;
  transition:
    opacity 0.2s,
    color 0.2s,
    padding-left 0.25s var(--ease);
}
footer a:hover {
  opacity: 1;
  color: var(--ambre);
  padding-left: 0.3rem;
}
.footer-bas {
  max-width: var(--max);
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.8rem;
  color: var(--gris);
  flex-wrap: wrap;
  gap: 1rem;
}

/* ───── Système de reveal au scroll ───── */
.reveal {
  opacity: 0;
  filter: blur(16px);
  transform: translateY(40px);
  will-change: opacity, transform, filter;
  transition:
    opacity 1.1s var(--ease),
    transform 1.1s var(--ease),
    filter 1.1s var(--ease);
}
.reveal.vu {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
/* Reveal mot à mot (Awwwards) — chaque mot monte d'un masque, avec stagger */
[data-reveal="words"] .rv-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  line-height: inherit;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
[data-reveal="words"] .rv-wi {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
  will-change: transform, opacity;
  transition:
    transform 0.85s var(--ease) var(--d, 0s),
    opacity 0.85s var(--ease) var(--d, 0s);
}
[data-reveal="words"].vu .rv-wi {
  transform: none;
  opacity: 1;
}
/* Révélation lettre par lettre (chaque lettre monte d'un masque, en cascade) */
[data-reveal="letters"] .rv-l {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  line-height: inherit;
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}
[data-reveal="letters"] .rv-li {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
  will-change: transform, opacity;
  transition:
    transform 0.7s var(--ease) var(--d, 0s),
    opacity 0.7s var(--ease) var(--d, 0s);
}
[data-reveal="letters"].vu .rv-li {
  transform: none;
  opacity: 1;
}
/* Highlight au scroll — mots sombres qui s'éclairent un à un (scrub GSAP) */
[data-reveal="highlight"] .rv-w {
  display: inline-block;
  vertical-align: top;
  line-height: inherit;
}
[data-reveal="highlight"] .rv-wi {
  display: inline-block;
  opacity: 0.96;
  will-change: opacity;
}
/* Blur-to-sharp au scroll (réutilisable via [data-blur-reveal], hors hero) */
[data-blur-reveal] {
  opacity: 0;
  filter: blur(16px);
  transform: translateY(40px);
  will-change: opacity, filter, transform;
  transition:
    opacity 1.1s var(--ease),
    filter 1.1s var(--ease),
    transform 1.1s var(--ease);
  transition-delay: calc(var(--i, 0) * 80ms);
}
[data-blur-reveal].vu {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
/* Même effet en cascade pour les groupes (enfants révélés un à un) */
.blur-stagger > * {
  opacity: 0;
  filter: blur(16px);
  transform: translateY(40px);
  will-change: opacity, filter, transform;
  transition:
    opacity 1.1s var(--ease),
    filter 1.1s var(--ease),
    transform 1.1s var(--ease);
  transition-delay: calc(var(--i, 0) * 80ms);
}
.blur-stagger.vu > * {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
.stagger > * {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 0.7s,
    transform 0.7s var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.stagger.vu > * {
  opacity: 1;
  transform: none;
}

/* ═══ PARCOURS — chronologie 3D Three.js (voyage scroll-driven) ═══ */
.parcours-3d {
  padding: 0;
  position: relative;
}
.p3-stage {
  height: 100vh;
  position: relative;
  overflow: hidden;
  /* fondu d'entrée : Compétences finit en --noir quasi uni ; sans ce calque, l'éclat bleu du
     haut de cette section démarre plein pot juste à la jointure — coupure nette et sale */
  background:
    linear-gradient(180deg, var(--noir) 0, transparent 16vh),
    radial-gradient(
      46% 46% at 50% 48%,
      rgba(0, 123, 255, 0.1),
      transparent 62%
    ),
    radial-gradient(
      110% 82% at 72% 10%,
      rgba(0, 123, 255, 0.15),
      transparent 56%
    ),
    radial-gradient(
      88% 78% at 12% 88%,
      rgba(120, 150, 200, 0.08),
      transparent 62%
    ),
    linear-gradient(rgba(244, 248, 255, 0.022) 1px, transparent 1px) 0 0/100%
      6.4rem,
    linear-gradient(90deg, rgba(244, 248, 255, 0.018) 1px, transparent 1px) 0
      0/6.4rem 100%,
    var(--noir);
}
.p3-stage::before {
  content: "";
  position: absolute;
  inset: -10%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.32;
  background: conic-gradient(
    from 90deg at 50% 50%,
    transparent,
    rgba(0, 123, 255, 0.16),
    transparent 22%,
    rgba(244, 248, 255, 0.08),
    transparent 55%,
    rgba(150, 170, 210, 0.12),
    transparent
  );
  filter: blur(20px);
  animation: p3nebula 18s linear infinite;
}
.p3-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    repeating-radial-gradient(
      circle at 50% 48%,
      rgba(244, 248, 255, 0.045) 0 1px,
      transparent 1px 54px
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 32%,
      rgba(5, 5, 5, 0.64) 100%
    );
  opacity: 0.42;
}
@keyframes p3nebula {
  to {
    transform: rotate(1turn);
  }
}
.p3-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
}
.p3-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(
      36% 36% at 50% 48%,
      rgba(0, 123, 255, 0.08),
      transparent 64%
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 42%,
      rgba(5, 5, 5, 0.48) 100%
    );
}
/* Vignette cinéma : se referme au clic sur une sphère (opacité pilotée en JS) */
.p3-cine {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  will-change: opacity;
  background: radial-gradient(
    58% 58% at 50% 48%,
    transparent 20%,
    rgba(5, 5, 5, 0.74) 78%
  );
}
/* Bloom de fin : la caméra plonge dans l'étoile → l'écran vire à l'ambre du contact */
.p3-flash {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  will-change: opacity;
  background:
    radial-gradient(
      120% 90% at 50% 42%,
      rgba(255, 248, 231, 0.97) 0%,
      rgba(245, 208, 152, 0.5) 30%,
      rgba(0, 123, 255, 0) 62%
    ),
    var(--ambre);
}
/* rideau de fin de voyage : referme la scène juste avant que le pin ne se relâche,
   pour un fondu net vers le footer plutôt qu'un cut brut (opacité pilotée en JS) */
.p3-curtain {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  will-change: opacity;
  background:
    radial-gradient(
      60% 50% at 50% 40%,
      rgba(95, 211, 236, 0.1),
      transparent 70%
    ),
    var(--ink);
}
/* display:flex column : .p3-head (haut, hauteur naturelle) et .p3-steps (bas,
   pousse via margin-top:auto) ne peuvent JAMAIS se chevaucher, quelle que soit
   la hauteur réelle du sous-titre — contrairement à l'ancien calage en
   position:absolute + bottom fixe des deux blocs, qui supposait une hauteur
   de .p3-head jamais dépassée (faux sur viewport étroit/court : le sous-titre
   passe sur 2 lignes et vient chevaucher le contenu de l'étape active, cf.
   collision constatée en diagnostic à 1024×700). .p3-meter/.p3-dots/.p3-hint/
   .p3-cue restent en position:absolute (sortis du flux flex), non affectés. */
.p3-ui {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
  padding: clamp(2.4rem, 6vh, 4rem) var(--marge);
  display: flex;
  flex-direction: column;
}
.parcours-3d.p3-ending .p3-veil {
  opacity: var(--p3-veil-opacity, 1);
}
.parcours-3d.p3-ending .p3-ui {
  filter: blur(var(--p3-ui-blur, 0px));
  transform: scale(var(--p3-ui-scale, 1));
}
/* CTA contact : apparaît dans le bloom .p3-flash, pilotée en JS (opacity/transform/pointer-events) */
.p3-contact {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  width: min(920px, 100%);
  margin: 0 auto;
  padding: 0 var(--marge);
  opacity: 0;
  pointer-events: none;
  transform: scale(0.95);
  color: var(--noir);
}
/* Enveloppe recentrée sur le CONTENU (pas le stage plein écran) : cadre le texte au plus
   près pour poser les cartels d'angle ci-dessous, et sert d'ancre position:relative pour eux. */
.contact-inner {
  /* ⚠️ `min-width: 0` : un enfant de grille (ou de flex) vaut `min-width: auto`
     par defaut, c'est-a-dire qu'il REFUSE de descendre sous la largeur de son
     contenu. Sur petit ecran il deborde alors du cadre au lieu de se
     comprimer. Mesure avant/apres : 73px de debordement a 320px */
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.9rem, 2.2vh, 1.5rem);
  padding: clamp(2.6rem, 6vh, 4rem) clamp(2rem, 6vw, 4.5rem);
}
.contact-inner > * {
  will-change: opacity, transform, filter;
}
/* Cartels d'angle façon viseur/HUD : reprend le vocabulaire "mission" du kicker sans texte
   supplémentaire — donne au bloc un cadre net à l'oeil, au lieu de flotter dans le panneau vide. */
.cf-corner {
  position: absolute;
  width: 22px;
  height: 22px;
  border: 0 solid rgba(var(--accent-rgb), 0.4);
  pointer-events: none;
}
.cf-corner.cf-tl {
  top: 0;
  left: 0;
  border-top-width: 1.5px;
  border-left-width: 1.5px;
  border-top-left-radius: 3px;
}
.cf-corner.cf-tr {
  top: 0;
  right: 0;
  border-top-width: 1.5px;
  border-right-width: 1.5px;
  border-top-right-radius: 3px;
}
.cf-corner.cf-bl {
  bottom: 0;
  left: 0;
  border-bottom-width: 1.5px;
  border-left-width: 1.5px;
  border-bottom-left-radius: 3px;
}
.cf-corner.cf-br {
  bottom: 0;
  right: 0;
  border-bottom-width: 1.5px;
  border-right-width: 1.5px;
  border-bottom-right-radius: 3px;
}
/* taille revue à la baisse : DotGothic16 dessine ses lettres fines (i, l) très étroites dans une
   cellule pleine largeur (héritage CJK) — l'écart visuel autour d'elles devient flagrant en très
   grand corps. En réduisant la taille, l'écart en pixels réels rétrécit d'autant. */
.p3-contact h2 {
  font-family: var(--display);
  font-weight: 860;
  font-size: clamp(2.1rem, 4vw, 3.6rem);
  line-height: 0.96;
  letter-spacing: normal;
  color: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* Titre coupé explicitement en 2 lignes fixes (au lieu d'un retour à la ligne organique via
   text-wrap:balance) : sur cette police mono/dot-matrix, "Construisons" (mot unique, 12
   caractères) dépassait déjà la largeur max fixée pour équilibrer les 2 lignes — l'algorithme de
   balance ne pouvait pas le faire rentrer, et le mot débordait du bloc de manière asymétrique
   (mesuré : centre de "Construisons" à 42px du centre réel de la page, "ensemble" correctement
   centré lui). Deux lignes déclarées + centrage flex indépendant par ligne = alignement garanti
   quelle que soit la largeur du mot, sur toutes les tailles d'écran. */
.cc-ligne {
  display: block;
}
.cc-accent {
  color: var(--accent-2);
}
.p3-contact p {
  font-family: var(--serif);
  font-style: normal;
  color: rgba(5, 5, 5, 0.85);
  font-size: clamp(1.1rem, 1.5vw, 1.4rem);
  line-height: 1.3;
  max-width: 34ch;
}
.p3-head {
  max-width: min(54vw, 620px);
  transition: opacity 0.4s var(--ease);
  flex: none;
}
/* DotGothic16 : glyphes très larges (grille dot-matrix) → 30ch d'avant (taillé pour Instrument Serif)
   faisait passer ce titre sur 4 lignes et il débordait sur .p3-steps en dessous. Conteneur élargi,
   taille revue à la baisse, et un line-clamp en filet de sécurité : 2 lignes maximum, jamais plus. */
.p3-titre {
  font-family: var(--display);
  font-weight: 860;
  font-size: clamp(1.9rem, 3.2vw, 2.8rem);
  line-height: 1.08;
  letter-spacing: normal;
  margin: 0.4rem 0 0.3rem;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.p3-sub {
  font-family: var(--serif);
  font-style: normal;
  color: var(--mute);
  font-size: clamp(1.05rem, 1.4vw, 1.3rem);
}
.p3-meter {
  position: absolute;
  top: clamp(2.4rem, 6vh, 4rem);
  right: var(--marge);
  font-family: var(--mono);
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  transition: opacity 0.4s var(--ease);
}
.p3-num {
  font-size: clamp(1.8rem, 3vw, 2.6rem);
  color: var(--ambre);
  font-weight: 700;
  line-height: 1;
  text-shadow: 0 0 24px rgba(0, 123, 255, 0.5);
  font-variant-numeric: tabular-nums;
}
.p3-of {
  font-size: 1rem;
  color: var(--gris);
}
.p3-steps {
  position: relative;
  margin-top: auto;
  margin-bottom: clamp(3rem, 9vh, 6rem);
  width: min(48vw, 560px);
  min-height: clamp(240px, 34vh, 360px);
  flex: none;
}
/* ancré en HAUT (pas bottom:0) : quand le contenu d'une étape (titre + texte +
   tags) dépasse le min-height estimé de .p3-steps, il déborde vers le BAS
   (zone vide) plutôt que vers le HAUT — où il chevaucherait .p3-head. Un
   ancrage bottom:0 avait exactement l'effet inverse (débordement constaté
   vers le haut, collision avec le sous-titre de section sur viewport étroit). */
.p3-step {
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  opacity: 0;
  transition:
    opacity 0.45s var(--ease),
    transform 0.7s var(--ease);
  pointer-events: none;
}
.p3-step.actif {
  opacity: 1;
}
/* 3D : apparition EN CASCADE — index → catégorie → titre → texte → tags, tout
   sur .actif (posé automatiquement au passage de chaque sphère, cf. ticker
   dans initParcours3D/main.js). Texte et tags dépendaient de .focus (clic
   sur la sphère) : en défilement rapide, personne ne s'arrête pour cliquer
   les 6 sphères, donc l'essentiel du contenu restait invisible pour la
   plupart des visiteurs — demande explicite : tout doit se lire au passage,
   sans clic. .focus (qui pose TOUJOURS .actif en même temps, cf. focusStep
   dans main.js) reste le déclencheur du zoom cinématique ci-dessous, mais
   n'est plus nécessaire pour lire le contenu. */
.parcours-3d:not(.p3-flat) .p3-step > * {
  opacity: 0;
  transform: translateY(26px);
  filter: blur(8px);
  transition:
    opacity 0.75s var(--ease),
    transform 0.75s var(--ease),
    filter 0.75s var(--ease);
}
.parcours-3d:not(.p3-flat) .p3-step.actif .chrono-idx {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.06s;
}
.parcours-3d:not(.p3-flat) .p3-step.actif .chrono-cat {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.15s;
}
.parcours-3d:not(.p3-flat) .p3-step.actif h3 {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.26s;
}
.parcours-3d:not(.p3-flat) .p3-step.actif p {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.36s;
}
.parcours-3d:not(.p3-flat) .p3-step.actif .chrono-tags {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.46s;
}
.parcours-3d:not(.p3-flat) .p3-step.focus {
  transform: scale(1.05);
  transform-origin: left top;
}
/* indice : cliquez une sphère */
.p3-cue {
  position: absolute;
  left: var(--marge);
  bottom: clamp(1.3rem, 3.4vh, 2.3rem);
  z-index: 3;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gris);
  transition: opacity 0.4s var(--ease);
}
.p3-cue::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ambre);
  box-shadow: 0 0 0 4px rgba(0, 123, 255, 0.18);
  animation: p3pulse 1.8s var(--ease) infinite;
}
@keyframes p3pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.6);
    opacity: 0.45;
  }
}
.parcours-3d.p3-zoom .p3-cue,
.parcours-3d.p3-zoom .p3-head,
.parcours-3d.p3-zoom .p3-meter {
  opacity: 0;
}
.parcours-3d.p3-flat .p3-cue {
  display: none;
}
.chrono-idx {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--ambre);
  letter-spacing: 0.1em;
}
.chrono-cat {
  display: block;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gris);
  margin: 0.45rem 0 1rem;
}
.p3-step h3 {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.4rem, 1.9vw, 1.9rem);
  line-height: 1.1;
  letter-spacing: normal;
  margin-bottom: 0.9rem;
  text-wrap: balance;
}
.p3-step p {
  color: var(--creme);
  font-size: clamp(1rem, 1.25vw, 1.18rem);
  line-height: 1.55;
  max-width: 46ch;
}
.chrono-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.4rem;
}
.chrono-tags span {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--creme);
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--ligne);
  border-radius: 999px;
  background: rgba(5, 5, 5, 0.52);
}
.p3-hint {
  position: absolute;
  right: var(--marge);
  bottom: clamp(2rem, 5vh, 3rem);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--gris);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.p3-hint::after {
  content: "";
  width: 34px;
  height: 1px;
  background: var(--gris);
  animation: p3hint 1.8s var(--ease) infinite;
}
@keyframes p3hint {
  0%,
  100% {
    transform: scaleX(0.3);
    opacity: 0.4;
    transform-origin: left;
  }
  50% {
    transform: scaleX(1);
    opacity: 1;
    transform-origin: left;
  }
}

/* Fallback à plat (mobile / tactile / pas de WebGL / reduced-motion) : liste verticale lisible */
.parcours-3d.p3-flat .p3-canvas,
.parcours-3d.p3-flat .p3-veil,
.parcours-3d.p3-flat .p3-flash,
.parcours-3d.p3-flat .p3-hint {
  display: none;
}
.parcours-3d.p3-flat {
  padding: 0 0 6rem;
}
.parcours-3d.p3-flat .p3-stage {
  height: auto;
  overflow: visible;
}
.parcours-3d.p3-flat .p3-ui {
  position: relative;
  inset: auto;
}
.parcours-3d.p3-flat .p3-meter {
  position: static;
  margin: 1.4rem 0 0;
}
.parcours-3d.p3-flat .p3-steps {
  position: relative;
  left: auto;
  bottom: auto;
  width: 100%;
  min-height: 0;
  margin-top: clamp(2.5rem, 6vh, 4rem);
  border-left: 1px solid var(--ligne);
  padding-left: 1.8rem;
}
.parcours-3d.p3-flat .p3-step {
  position: relative;
  inset: auto;
  opacity: 1;
  transform: none;
  filter: none;
  padding-bottom: 3rem;
  margin-bottom: 0;
}
.parcours-3d.p3-flat .p3-step::before {
  content: "";
  position: absolute;
  left: calc(-1.8rem - 6px);
  top: 0.4rem;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ambre);
  box-shadow: 0 0 0 5px rgba(0, 123, 255, 0.12);
}
.parcours-3d.p3-flat .p3-contact {
  position: relative;
  inset: auto;
  opacity: 1;
  pointer-events: auto;
  transform: none;
  margin: clamp(3rem, 8vh, 5rem) auto 0;
}
.parcours-3d.p3-flat .p3-contact h2,
.parcours-3d.p3-flat .p3-contact p {
  color: var(--creme);
}
.parcours-3d.p3-flat .p3-contact .contact-kicker,
.parcours-3d.p3-flat .p3-contact .contact-note {
  color: var(--gris);
}
.parcours-3d.p3-flat .p3-contact .contact-alt {
  color: var(--gris);
}
.parcours-3d.p3-flat .p3-contact .contact-alt:hover {
  color: var(--creme);
}

@media (max-width: 820px) {
  .projet {
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    gap: 0.4rem 1rem;
    padding: 1.8rem 1.2rem;
  }
  .projet .infos {
    grid-column: 2;
  }
  .projet .annee {
    grid-column: 2;
  }
  .projet .fleche {
    display: none;
  }
  .apercu {
    display: none;
  }
  .deco-etoile {
    display: none;
  }
  .contenu {
    padding: 3rem 1.2rem 0;
  }
  header {
    padding: 1rem 1.2rem;
  }
  header nav {
    gap: 1rem;
    font-size: 0.78rem;
  }
  .hero-cadre {
    height: auto;
  }
  .timeline {
    padding-left: 1.6rem;
  }
  .etape::before {
    left: -1.6rem;
  }
  .footer-grille {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  .hero-cadre {
    height: auto;
  }
  .hero {
    position: relative;
  }
  .apropos-visuel {
    opacity: 1 !important;
    clip-path: none !important;
  }
  .hero-socials {
    opacity: 1 !important;
    visibility: visible !important;
  }
  .cursor-anneau {
    display: none;
  }
  .masque .int {
    transform: none;
  }
  .reveal,
  .case-reveal,
  .stagger > *,
  [data-reveal="words"] .rv-wi,
  [data-reveal="letters"] .rv-li,
  [data-reveal="highlight"] .rv-wi,
  [data-blur-reveal],
  .blur-stagger > * {
    opacity: 1;
    transform: none;
    filter: none;
  }
  .p3-canvas,
  .p3-veil,
  .p3-hint {
    display: none !important;
  }
  .p3-stage {
    height: auto !important;
    overflow: visible !important;
  }
  .p3-ui {
    position: relative !important;
    inset: auto !important;
  }
  .p3-meter {
    position: static !important;
    margin: 1.4rem 0 0;
  }
  .p3-steps {
    position: relative !important;
    left: auto !important;
    bottom: auto !important;
    width: 100% !important;
    min-height: 0 !important;
    margin-top: 3rem;
    border-left: 1px solid var(--ligne);
    padding-left: 1.8rem;
  }
  .p3-step {
    position: relative !important;
    inset: auto !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    padding-bottom: 3rem;
  }
  .p3-flash {
    display: none !important;
  }
  .p3-contact {
    position: relative !important;
    inset: auto !important;
    opacity: 1 !important;
    pointer-events: auto !important;
    transform: none !important;
    margin: 3rem auto 0;
  }
  .p3-contact h2,
  .p3-contact p {
    color: var(--creme);
  }
  .p3-contact .contact-kicker,
  .p3-contact .contact-note {
    color: var(--gris);
  }
  .p3-contact .contact-alt {
    color: var(--gris);
  }
  .timeline::before {
    transform: scaleY(1);
  }
  .progres {
    display: none;
  }
  .rideau {
    display: none;
  }
  html {
    scroll-behavior: auto;
  }
}

/* ═══════════════════════════════════════════════════════════
   AJOUTS — Stack Awwwards (Barba / pages projet / filet)
   ═══════════════════════════════════════════════════════════ */

/* ───── Rideau de transition Barba ───── */
.rideau {
  position: fixed;
  inset: 0;
  /* Au-dessus du grain, de la progression, du shell cinéma et du curseur :
     aucun doublon de logo ne peut traverser le calque pendant le swap Barba. */
  z-index: 100000;
  background: var(--ambre);
  clip-path: none;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  visibility: hidden;
  overflow: hidden;
  /* La position de repos est appliquee par GSAP avec yPercent. Garder ici une
     matrice neutre evite de cumuler 101% CSS + 101% GSAP (rideau a 202%). */
  transform: translate3d(0, 0, 0);
  will-change: transform;
  contain: paint;
  isolation: isolate;
}
html.barba-busy .rideau {
  pointer-events: auto;
}
.rideau-mot {
  font-weight: 900;
  font-variation-settings: "wdth" 125;
  font-size: clamp(2.2rem, 7vw, 5rem);
  color: var(--noir);
  text-transform: uppercase;
  letter-spacing: -0.02em;
}
.rideau-mot sup {
  font-size: 0.4em;
  vertical-align: super;
}

/* ───── Filet de sécurité : si une lib CDN échoue, on révèle tout ───── */
.secours .reveal,
.secours .stagger > *,
.secours .blur-stagger > *,
.secours [data-blur-reveal],
.secours .masque .int,
.secours .case-reveal,
.secours .hero-kicker,
.secours .hero-content,
.secours .hero-light,
.secours .hero-mask-svg,
.secours .hero-subtitle,
.secours .hero-content .ctas {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
  clip-path: none !important;
}
.secours .timeline::before {
  transform: scaleY(1) !important;
}
.secours .rideau {
  display: none !important;
}
.secours .loader {
  display: none !important;
}
.secours .transition-overlay {
  display: none !important;
}

/* ═══════════════ Pages projet (étude de cas) ═══════════════ */
.case {
  --case-grad: linear-gradient(135deg, var(--accent), var(--accent-soft));
  padding-top: 6.4rem;
  overflow-x: clip;
  position: relative;
}
.case::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.36;
  background:
    radial-gradient(
      60rem 38rem at 74% 8%,
      rgba(var(--accent-soft-rgb), 0.24),
      transparent 60%
    ),
    radial-gradient(
      40rem 34rem at 0 58%,
      rgba(var(--accent-rgb), 0.08),
      transparent 70%
    );
}
.case-hero {
  min-height: calc(100svh - 6.4rem);
  padding: 4.5vh var(--marge) 4rem;
  max-width: var(--max);
  margin: 0 auto;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  isolation: isolate;
}
.case-ambiance {
  position: absolute;
  inset: -8% 0 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.case-ambiance span {
  position: absolute;
  border-radius: 999px;
  filter: blur(22px);
  opacity: 0.32;
  background: var(--case-grad);
}
.case-ambiance span:nth-child(1) {
  width: 42vw;
  height: 42vw;
  right: -10vw;
  top: -16vw;
}
.case-ambiance span:nth-child(2) {
  width: 28vw;
  height: 28vw;
  left: -9vw;
  bottom: 8vh;
  opacity: 0.18;
}
.case-ambiance span:nth-child(3) {
  width: 18vw;
  height: 18vw;
  left: 46%;
  top: 30%;
  opacity: 0.16;
}
.case-navline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  position: relative;
  z-index: 2;
}
.case-retour {
  /* cible tactile ≥44px : ce lien retour (haut de chaque étude de cas) ne
     mesurait que ~18px de haut, audit mobile headless */
  padding: 0.7rem 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gris);
  transition:
    gap 0.3s var(--ease),
    color 0.3s;
}
.case-retour:hover {
  gap: 0.95rem;
  color: var(--ambre);
}
.case-num {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.72);
  border: 1px solid rgba(244, 248, 255, 0.14);
  border-radius: 999px;
  padding: 0.55em 1.05em;
}
.case-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 420px);
  gap: clamp(2rem, 5vw, 6rem);
  align-items: end;
  margin-top: 8vh;
}
.case-kicker {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ambre);
  margin-bottom: 1.2rem;
}
.case-titre {
  font-weight: 880;
  font-size: clamp(2.4rem, 5.5vw, 4.6rem);
  line-height: 0.94;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  text-wrap: balance;
  overflow-wrap: normal;
  word-break: keep-all;
  max-width: 16ch;
}
.case-sous {
  font-family: var(--serif);
  font-style: normal;
  font-size: clamp(1.15rem, 1.8vw, 1.5rem);
  color: var(--creme);
  max-width: 34ch;
  margin-top: 1.8rem;
  line-height: 1.2;
}
.case-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 2rem;
  max-width: 54rem;
}
.case-tags span {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.76);
  border: 1px solid rgba(244, 248, 255, 0.16);
  border-radius: 999px;
  padding: 0.52rem 0.72rem;
  background: rgba(5, 5, 5, 0.28);
}
.case-panel {
  min-height: 420px;
  border: 1px solid rgba(244, 248, 255, 0.14);
  border-radius: 8px;
  padding: 1.4rem;
  background: linear-gradient(
    180deg,
    rgba(244, 248, 255, 0.055),
    rgba(244, 248, 255, 0.018)
  );
  backdrop-filter: blur(12px);
  position: relative;
  overflow: hidden;
}
.case-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    120% 80% at 100% 0,
    rgba(0, 123, 255, 0.16),
    transparent 60%
  );
}
.case-panel-index {
  font-weight: 880;
  font-size: clamp(4rem, 9vw, 7.5rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(244, 248, 255, 0.2);
}
.case-meta {
  list-style: none;
  margin-top: clamp(3rem, 8vh, 6rem);
  position: relative;
  z-index: 1;
}
.case-meta li {
  border-top: 1px solid rgba(244, 248, 255, 0.12);
  padding: 1rem 0;
}
.case-meta .lab {
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.62rem;
  color: var(--gris);
  margin-bottom: 0.42rem;
}
.case-meta .val {
  font-weight: 650;
  font-size: 1rem;
  color: var(--creme);
  line-height: 1.25;
}
.case-meta .val a {
  color: var(--ambre);
  background: linear-gradient(var(--ambre), var(--ambre)) 0 100%/100% 1px
    no-repeat;
}

/* grand visuel signature — affiche une direction artistique même sans screenshot */
.case-visuel {
  max-width: calc(var(--max) - var(--marge) * 2);
  margin: 0 auto;
  height: clamp(280px, 42vh, 460px);
  border-radius: 8px;
  overflow: hidden;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: var(--case-grad);
  border: 1px solid rgba(244, 248, 255, 0.14);
  box-shadow: 0 80px 140px -75px rgba(0, 0, 0, 0.95);
  padding: clamp(1.6rem, 3vw, 2.8rem);
  transform: translateZ(0);
}
.case-visuel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.case-visuel::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: soft-light;
  z-index: 2;
  background:
    radial-gradient(
      130% 110% at 20% 8%,
      rgba(255, 255, 255, 0.34),
      transparent 52%
    ),
    linear-gradient(
      90deg,
      rgba(5, 5, 5, 0.7),
      transparent 34%,
      transparent 66%,
      rgba(5, 5, 5, 0.64)
    ),
    repeating-linear-gradient(
      90deg,
      rgba(255, 255, 255, 0.08) 0 1px,
      transparent 1px 7vw
    );
}
.case-visuel::after {
  content: "";
  position: absolute;
  inset: clamp(0.7rem, 1.5vw, 1.2rem);
  border-radius: 5px;
  pointer-events: none;
  z-index: 4;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.12),
    inset 0 60px 160px -60px rgba(0, 0, 0, 0.72);
}
/* fiche technique : la place vide devient une vraie carte d'info (catégorie, pitch, stack, année) */
.case-plaque-top {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.case-plaque-cat {
  font-family: var(--mono);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: clamp(0.7rem, 1.1vw, 0.84rem);
  color: rgba(255, 255, 255, 0.76);
}
.case-plaque-init {
  font-family: var(--display);
  font-weight: 880;
  font-size: clamp(1.3rem, 2.2vw, 1.8rem);
  letter-spacing: normal;
  color: rgba(255, 255, 255, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.32);
  border-radius: 999px;
  padding: 0.22rem 0.95rem;
  line-height: 1.4;
}
.case-plaque-pitch {
  position: relative;
  z-index: 3;
  font-family: var(--serif);
  font-style: normal;
  color: #fff;
  font-size: clamp(1.15rem, 1.9vw, 1.6rem);
  line-height: 1.3;
  max-width: 36ch;
  text-shadow: 0 4px 30px rgba(0, 0, 0, 0.4);
}
.case-plaque-bas {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-top: clamp(1rem, 2vw, 1.6rem);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}
.case-plaque-bas .mono {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.92);
}
.case-plaque-stack {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}

/* ═══ Project Overview : bloc gauche sticky + 5 sections à droite révélées au scroll ═══ */
.case-overview {
  max-width: var(--max);
  margin: clamp(5rem, 9vw, 9rem) auto 0;
  padding: 0 var(--marge);
  display: grid;
  grid-template-columns: minmax(240px, 0.62fr) minmax(0, 1.55fr);
  gap: clamp(2.5rem, 6vw, 6rem);
}
.case-sticky {
  position: sticky;
  top: 6.5rem;
  align-self: start;
  height: fit-content;
}
.case-sticky-eyebrow {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brass-2);
  margin-bottom: 1rem;
}
.case-sticky-nom {
  font-family: var(--display);
  font-weight: 860;
  color: var(--creme);
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  line-height: 1.1;
  letter-spacing: normal;
  margin-bottom: 1.8rem;
}
.case-sticky-meta {
  list-style: none;
}
.case-sticky-meta li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--ligne);
  padding: 0.75rem 0;
  font-size: 0.92rem;
}
.case-sticky-meta li:first-child {
  border-top: 0;
}
.case-sticky-meta span {
  color: var(--gris);
}
.case-sticky-meta b {
  color: var(--creme);
  font-weight: 600;
  text-align: right;
}
.case-sticky-jauge {
  display: flex;
  align-items: baseline;
  gap: 0.3em;
  margin-top: 2.2rem;
  font-family: var(--mono);
  font-weight: 700;
  color: var(--brass-2);
  font-size: 1.6rem;
  font-variant-numeric: tabular-nums;
}
.case-sticky-tot {
  font-size: 0.6em;
  color: var(--gris);
  font-weight: 500;
}
.case-sticky-label {
  display: block;
  margin-top: 0.4rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--creme);
}

.case-tracks {
  display: flex;
  flex-direction: column;
}
.case-track {
  position: relative;
  padding: clamp(3.5rem, 7vw, 6.5rem) 0;
  border-top: 1px solid var(--ligne);
}
.case-track:first-child {
  border-top: 0;
  padding-top: 0;
}
.case-track-idx {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  color: var(--gris);
  margin-bottom: 1.1rem;
}
.case-track h3 {
  font-family: var(--display);
  font-weight: 850;
  font-size: clamp(1.7rem, 2.6vw, 2.3rem);
  line-height: 1.02;
  letter-spacing: normal;
  text-transform: uppercase;
  color: var(--creme);
  text-wrap: balance;
  max-width: 18ch;
  margin-bottom: 1.4rem;
}
.case-track-txt {
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  line-height: 1.62;
  color: var(--creme);
  max-width: 62ch;
}
.case-track-txt p + p {
  margin-top: 1.1rem;
}
.case-track-txt strong {
  color: var(--brass-2);
  font-weight: 760;
}
.case-track-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 1.5rem;
}
.case-track-tags span {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.76);
  border: 1px solid rgba(244, 248, 255, 0.16);
  border-radius: 999px;
  padding: 0.5rem 0.8rem;
  background: rgba(244, 248, 255, 0.04);
}
/* sceau du projet en filigrane, comme la citation finale — clip-path-friendly, transform/opacity only */
.case-track-visuel {
  position: absolute;
  right: 0;
  bottom: clamp(1rem, 3vw, 2.5rem);
  z-index: -1;
  pointer-events: none;
  font-family: var(--display);
  font-weight: 880;
  font-size: clamp(3.5rem, 7vw, 6rem);
  letter-spacing: normal;
  background: var(--mark-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0.16;
  -webkit-text-stroke: 1px rgba(244, 248, 255, 0.18);
}

/* ═══ Détails visuels : grille asymétrique (pas de fausses images, palette réelle du projet) ═══ */
.case-visuals {
  max-width: var(--max);
  margin: clamp(5rem, 9vw, 8rem) auto 0;
  padding: 0 var(--marge);
}
.case-visuals-kicker {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brass-2);
  margin-bottom: 1.6rem;
}
.case-visuals-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  grid-template-rows: auto auto;
  gap: 1.2rem;
}
.case-visuals-card {
  border: 1px solid rgba(244, 248, 255, 0.14);
  border-radius: 8px;
  padding: clamp(1.6rem, 2.6vw, 2.4rem);
  background: var(--noir-2);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 220px;
}
.case-visuals-cover {
  grid-row: 1 / 3;
  background: var(--mark-grad);
  border-color: rgba(255, 255, 255, 0.18);
  min-height: 300px;
  position: relative;
  overflow: hidden;
}
.case-visuals-cat {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
}
.case-visuals-init {
  align-self: flex-end;
  font-family: var(--display);
  font-weight: 880;
  color: rgba(255, 255, 255, 0.94);
  font-size: clamp(2.2rem, 4.5vw, 3.6rem);
  letter-spacing: normal;
  line-height: 1;
}
.case-visuals-lab {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gris);
}
.case-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.2rem;
}
.case-swatch {
  width: 64px;
  height: 64px;
  border-radius: 6px;
  display: flex;
  align-items: flex-end;
  padding: 0.4rem;
  border: 1px solid rgba(244, 248, 255, 0.14);
}
.case-swatch b {
  font-family: var(--mono);
  font-size: 0.56rem;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.case-visuals-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.2rem;
}
.case-visuals-chips span {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(244, 248, 255, 0.7);
  border: 1px solid rgba(244, 248, 255, 0.14);
  border-radius: 999px;
  padding: 0.4rem 0.7rem;
}
@media (max-width: 900px) {
  .case-visuals-grid {
    grid-template-columns: 1fr;
  }
  .case-visuals-cover {
    grid-row: auto;
  }
}

.case-signature {
  position: relative;
  overflow: hidden;
  max-width: var(--max);
  margin: clamp(3rem, 7vw, 6rem) auto 0;
  padding: clamp(3.5rem, 8vw, 7.5rem) var(--marge);
  border-top: 1px solid rgba(244, 248, 255, 0.12);
  border-bottom: 1px solid rgba(244, 248, 255, 0.12);
  background: radial-gradient(
    64% 140% at 50% 100%,
    rgba(var(--accent-rgb), 0.12),
    transparent 72%
  );
}
.case-next .lab {
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gris);
  font-size: 0.72rem;
}
/* étiquette + voyant qui pulse doucement — lexique "terminal", cohérent avec la police dot-matrix */
.case-signature-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gris);
  font-size: 0.72rem;
}
.case-signature-tag i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brass-2);
  flex-shrink: 0;
  animation: caseSignaturePulse 2.4s ease-out infinite;
}
@keyframes caseSignaturePulse {
  0% {
    box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.45);
  }
  70% {
    box-shadow: 0 0 0 8px rgba(var(--accent-rgb), 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0);
  }
}
/* citation : taille et interlignage pensés pour une police dot-matrix (pas d'italique réelle,
   donc pas de font-style:italic — on distingue plutôt par la couleur des mots du data-reveal) */
.case-signature p {
  font-family: var(--serif);
  font-size: clamp(1.25rem, 1.9vw, 1.7rem);
  line-height: 1.5;
  max-width: 30ch;
  margin-top: 1.4rem;
  color: var(--creme);
  letter-spacing: normal;
}
/* rangée de clôture : sceau du projet (badge contenu, pas un ghost-letter géant — trop bruyant
   en dot-matrix) + retour, traité comme un vrai bouton plutôt qu'un lien nu */
.case-signature-bas {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.6rem;
  margin-top: clamp(2.4rem, 5vw, 4rem);
}
.case-signature-stamp {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: clamp(54px, 6vw, 72px);
  height: clamp(54px, 6vw, 72px);
  border-radius: 14px;
  background: var(--mark-grad);
  font-family: var(--display);
  font-size: 1.2rem;
  letter-spacing: normal;
  color: rgba(255, 255, 255, 0.94);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  box-shadow:
    0 12px 32px -10px rgba(0, 0, 0, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}
.case-signature-retour {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--creme);
  border: 1px solid rgba(244, 248, 255, 0.22);
  border-radius: 999px;
  padding: 0.85rem 1.4rem;
  transition:
    color 0.3s,
    border-color 0.3s,
    background-color 0.3s,
    gap 0.3s;
}
.case-signature-retour:hover {
  color: var(--noir);
  background-color: var(--brass-2);
  border-color: var(--brass-2);
  gap: 0.85em;
}

/* lien projet suivant — plus proche d'une fin de film que d'un lien texte */
.case-next {
  display: block;
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(4rem, 9vw, 8rem) var(--marge);
  border-top: 0;
  text-align: left;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.case-next::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.36;
  transition: opacity 0.6s;
  background: var(--case-grad);
}
.case-next::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(180deg, var(--noir), rgba(5, 5, 5, 0.72), var(--noir)),
    radial-gradient(
      80% 120% at 50% 110%,
      rgba(0, 123, 255, 0.22),
      transparent 62%
    );
}
.case-next:hover::before {
  opacity: 0.65;
}
.case-next-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
.case-next-idx {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  color: var(--gris);
}
.case-next .nom {
  display: block;
  font-weight: 880;
  font-size: clamp(2.2rem, 5vw, 4rem);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 0.94;
  margin-top: 1rem;
  transition:
    transform 0.55s var(--ease),
    color 0.45s;
}
.case-next:hover .nom {
  transform: translateY(-8px);
  color: var(--ambre);
}
.case-next-desc {
  font-family: var(--serif);
  font-style: normal;
  color: var(--mute);
  font-size: clamp(1.05rem, 1.8vw, 1.4rem);
  line-height: 1.4;
  max-width: 48ch;
  margin-top: 1.1rem;
}
.case-next-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 1.5rem;
}
.case-next-tags span {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  color: var(--gris);
  border: 1px solid rgba(244, 248, 255, 0.18);
  border-radius: 999px;
  padding: 0.32rem 0.75rem;
  transition:
    color 0.3s,
    border-color 0.3s;
}
.case-next:hover .case-next-tags span {
  color: var(--creme);
  border-color: rgba(0, 123, 255, 0.4);
}

/* reveal léger propre aux pages projet */
.case-reveal {
  opacity: 0;
  filter: blur(16px);
  transform: translateY(40px);
  will-change: opacity, transform, filter;
  transition:
    opacity 1.1s var(--ease),
    transform 1.1s var(--ease),
    filter 1.1s var(--ease);
}
.case-reveal.vu {
  opacity: 1;
  filter: blur(0);
  transform: none;
}

@media (max-width: 820px) {
  .case {
    padding-top: 5.5rem;
  }
  .case-hero {
    min-height: auto;
    padding-top: 2rem;
  }
  .case-navline {
    align-items: flex-start;
  }
  .case-hero-grid {
    grid-template-columns: 1fr;
    gap: 2rem;
    margin-top: 4rem;
  }
  .case-titre {
    font-size: clamp(2.2rem, 9vw, 3rem);
    max-width: 13ch;
  }
  .case-sous {
    font-size: clamp(1.1rem, 5vw, 1.5rem);
  }
  .case-panel {
    min-height: auto;
    padding: 1.1rem;
  }
  .case-panel-index {
    font-size: 4.5rem;
  }
  .case-meta {
    margin-top: 2rem;
  }
  .case-visuel {
    height: auto;
    min-height: clamp(260px, 46vh, 400px);
    border-radius: 0;
    margin-inline: 0;
    max-width: none;
    border-left: 0;
    border-right: 0;
  }
  .case-plaque-pitch {
    font-size: clamp(1.1rem, 4vw, 1.4rem);
  }
  .case-overview {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }
  .case-sticky {
    position: static;
    top: auto;
  }
  .case-track h3 {
    font-size: clamp(1.7rem, 8vw, 2.3rem);
    max-width: none;
  }
  .case-track-visuel {
    display: none;
  }
  .case-visuals-grid {
    grid-template-columns: 1fr;
  }
  .case-visuals-cover {
    grid-row: auto;
  }
  .case-signature p {
    font-size: clamp(1.2rem, 5vw, 1.5rem);
    max-width: none;
  }
  .case-signature-bas {
    gap: 1.2rem;
  }
}

/* ───── Page 404 ───── */
.notfound {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: var(--max);
  margin: 0 auto;
  padding: 8rem var(--marge) 4rem;
  position: relative;
  overflow: hidden;
}
.nf-ghost {
  position: absolute;
  right: -2vw;
  top: 50%;
  transform: translateY(-50%);
  z-index: 0;
  pointer-events: none;
  font-family: var(--display);
  font-weight: 880;
  font-size: clamp(8rem, 22vw, 17rem);
  line-height: 0.86;
  letter-spacing: normal;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(244, 248, 255, 0.07);
}
.notfound > *:not(.nf-ghost) {
  position: relative;
  z-index: 1;
}
.nf-kicker {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ambre);
}
.nf-titre {
  font-family: var(--display);
  font-weight: 860;
  font-size: clamp(2.2rem, 4.5vw, 3.6rem);
  line-height: 1;
  letter-spacing: normal;
  margin: 1.2rem 0;
  text-wrap: balance;
}
.nf-sous {
  font-family: var(--serif);
  font-style: normal;
  color: var(--mute);
  font-size: clamp(1.05rem, 1.4vw, 1.3rem);
  max-width: 42ch;
  line-height: 1.45;
}
.nf-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2.4rem;
}

/* ───── Skip-link (accessibilité) ───── */
.skip {
  position: fixed;
  top: -120px;
  left: 1rem;
  z-index: 100000;
  background: var(--ambre);
  color: var(--noir);
  padding: 0.7rem 1.2rem;
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 700;
  text-decoration: none;
  transition: top 0.25s var(--ease);
}
.skip:focus {
  top: 1rem;
  outline: 2px solid var(--creme);
  outline-offset: 3px;
}

/* ───── Toggle son (audio cinématique) ───── */
.son-toggle {
  position: fixed;
  right: clamp(1rem, 2.4vw, 2rem);
  bottom: clamp(1rem, 2.4vw, 2rem);
  z-index: 9000;
  display: inline-flex;
  align-items: center;
  /* cible tactile ≥44px, position:fixed indépendante : aucun voisin à préserver */
  min-height: 44px;
  gap: 0.55rem;
  padding: 0.6rem 0.9rem;
  background: rgba(13, 12, 10, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid var(--ligne);
  border-radius: 999px;
  color: var(--mute);
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  line-height: 1;
  transition:
    border-color 0.3s var(--ease),
    color 0.3s var(--ease),
    transform 0.3s var(--ease);
}
.son-toggle:hover {
  border-color: var(--ambre);
  color: var(--creme);
  transform: translateY(-2px);
}
.son-toggle:focus-visible {
  outline: 2px solid var(--ambre);
  outline-offset: 3px;
}
.son-bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 13px;
}
.son-bars i {
  width: 2px;
  height: 4px;
  background: currentColor;
  border-radius: 1px;
  transition: height 0.2s var(--ease);
}
.son-toggle.actif {
  border-color: var(--ambre);
  color: var(--ambre);
}
.son-toggle.actif .son-bars i {
  animation: eqbar 0.9s var(--ease) infinite;
}
.son-toggle.actif .son-bars i:nth-child(2) {
  animation-delay: 0.18s;
}
.son-toggle.actif .son-bars i:nth-child(3) {
  animation-delay: 0.36s;
}
.son-toggle.actif .son-bars i:nth-child(4) {
  animation-delay: 0.12s;
}
@keyframes eqbar {
  0%,
  100% {
    height: 4px;
  }
  50% {
    height: 13px;
  }
}
@media (max-width: 640px) {
  .son-txt {
    display: none;
  }
  .son-toggle {
    /* devient icône seule (texte caché) : largeur/hauteur fixes en px pour
       garantir la cible tactile mini 44px, indépendamment du root font-size */
    width: 44px;
    height: 44px;
    padding: 0;
    justify-content: center;
  }
}
@media (prefers-reduced-motion: reduce) {
  .son-toggle.actif .son-bars i {
    animation: none;
    height: 9px;
  }
}

/* ═══════════════════════════════════════════════════════════
   Fallback historique sans variables CSS.
   Les navigateurs modernes utilisent la DA finale Editorial Warm ci-dessous.
   ═══════════════════════════════════════════════════════════ */
@supports not (color: var(--bg)) {
  :root {
    --noir: #070a14;
    --noir-2: #efe7d8;
    --creme: #10131a;
    --ambre: #2563eb;
    --ambre-fonce: #173b8f;
    --gris: #5f6470;
    --ligne: rgba(16, 19, 26, 0.15);
    --brass-2: #00b8d9;
    --mute: #5f6470;
    --surface: #ffffff;
    --bordure: #ded6c8;
    --accent-orange: #ff8a3d;
    --accent-green: #7cb342;
    --ink-soft: #2a2f3a;
    --shadow-soft: 0 24px 70px -48px rgba(16, 19, 26, 0.42);
    --shadow-card: 0 28px 80px -56px rgba(16, 19, 26, 0.46);
    --glow-warm: rgba(255, 138, 61, 0.2);
    --glow-cool: rgba(0, 184, 217, 0.18);
  }

  html {
    scrollbar-color: #2563eb #efe7d8;
  }
  ::-webkit-scrollbar {
    background: #efe7d8;
  }
  ::-webkit-scrollbar-track {
    background: #efe7d8;
  }
  ::-webkit-scrollbar-thumb {
    background: #2563eb;
    border-color: #efe7d8;
  }
  ::-webkit-scrollbar-thumb:hover {
    background: #00b8d9;
    border-color: #efe7d8;
  }
  ::-webkit-scrollbar-corner {
    background: #efe7d8;
  }

  body {
    background:
      radial-gradient(
        58rem 34rem at 8% -8%,
        rgba(0, 184, 217, 0.12),
        transparent 62%
      ),
      radial-gradient(
        50rem 34rem at 92% 2%,
        rgba(255, 138, 61, 0.12),
        transparent 60%
      ),
      linear-gradient(180deg, #070a14 0%, #efe7d8 100%);
    color: #10131a;
  }
  ::selection {
    background: #10131a;
    color: #070a14;
  }

  .grain {
    opacity: 0.08;
    mix-blend-mode: multiply;
  }
  .cine-overlay {
    opacity: 0.24;
    mix-blend-mode: multiply;
  }
  .cine-vignette {
    background:
      radial-gradient(
        52rem 34rem at var(--cine-x) var(--cine-y),
        rgba(0, 184, 217, calc(0.07 + var(--cine-heat) * 0.04)),
        transparent 58%
      ),
      radial-gradient(
        120% 92% at 50% 44%,
        transparent 54%,
        rgba(16, 19, 26, 0.1) 100%
      ),
      linear-gradient(
        90deg,
        rgba(16, 19, 26, 0.05),
        transparent 14%,
        transparent 86%,
        rgba(16, 19, 26, 0.05)
      );
    opacity: 1;
  }
  .cine-scan {
    opacity: 0.035;
  }
  .progres {
    background: linear-gradient(90deg, #2563eb, #00b8d9, #ff8a3d);
  }
  .cursor {
    background: #2563eb;
  }
  .cursor .etiquette {
    color: #10131a;
  }
  .cursor-anneau {
    border-color: rgba(37, 99, 235, 0.45);
  }

  .hero {
    background: #070a14;
  }
  .hero::before {
    border-color: rgba(16, 19, 26, 0.18);
    mix-blend-mode: multiply;
  }
  .hero::after {
    background: linear-gradient(
      90deg,
      transparent,
      rgba(16, 19, 26, 0.28),
      transparent
    );
  }
  .hero-beam {
    background: linear-gradient(
      180deg,
      rgba(255, 138, 61, 0.18),
      transparent 68%
    );
  }
  .hero-fallback {
    background-color: #10131a;
  }
  .hero.hero-final .hero-fallback::after {
    background:
      linear-gradient(
        180deg,
        rgba(16, 19, 26, 0.18),
        transparent 28%,
        rgba(16, 19, 26, 0.78)
      ),
      radial-gradient(
        80% 70% at 50% 42%,
        transparent 34%,
        rgba(16, 19, 26, 0.72)
      );
  }
  .hero.hero-final .hero-kicker {
    color: rgba(247, 243, 234, 0.74);
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.42);
  }
  .hero.hero-final .hero-subtitle {
    color: rgba(247, 243, 234, 0.82);
    text-shadow: 0 2px 22px rgba(0, 0, 0, 0.55);
  }
  .hero-subtitle em {
    color: #2563eb;
  }
  .hero.hero-final .hero-subtitle em {
    color: #00b8d9;
  }
  .hero.hero-final .hero-content .btn:not(.plein) {
    color: #070a14;
    border-color: rgba(247, 243, 234, 0.55);
    background: rgba(16, 19, 26, 0.24);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
  }
  .hero.hero-final .hero-content .btn:not(.plein)::before {
    background: #070a14;
  }
  .hero.hero-final .hero-content .btn:not(.plein):hover {
    color: #10131a;
    border-color: #070a14;
  }
  .hero-cinema-meta {
    color: rgba(247, 243, 234, 0.76);
  }
  .hero-cinema-meta b {
    color: #00b8d9;
  }
  .hero-cinema-meta span:nth-child(2n + 1) {
    color: rgba(247, 243, 234, 0.62);
  }
  .hsoc-btn {
    color: #070a14;
    border-color: rgba(247, 243, 234, 0.36);
    background: rgba(16, 19, 26, 0.38);
  }
  .hsoc-btn:hover,
  .hsoc-btn.actif {
    background: #070a14;
    color: #10131a;
    border-color: #070a14;
    box-shadow: 0 18px 45px -24px rgba(16, 19, 26, 0.72);
  }
  .hsoc-card {
    background: rgba(255, 255, 255, 0.86);
    color: #10131a;
    border-color: rgba(222, 214, 200, 0.82);
    box-shadow: var(--shadow-card);
  }

  .marquee {
    background: rgba(255, 255, 255, 0.34);
    border-color: var(--bordure);
  }
  .marquee span {
    color: #10131a;
  }
  .marquee span::after {
    color: #ff8a3d;
  }
  .section-num {
    color: #00b8d9;
  }
  .section-num::after {
    background: linear-gradient(
      90deg,
      rgba(37, 99, 235, 0.72),
      rgba(0, 184, 217, 0.28),
      transparent
    );
  }
  .deco-etoile {
    --creme: #10131a;
    color: transparent;
    -webkit-text-stroke-color: rgba(16, 19, 26, 0.08);
  }

  .temoin-quote p,
  .temoin-nom {
    color: #10131a;
  }
  .temoin {
    border-color: var(--bordure);
  }
  .chrono-tags span {
    background: rgba(255, 255, 255, 0.86);
    color: #10131a;
    border-color: var(--bordure);
    box-shadow: 0 18px 44px -34px rgba(16, 19, 26, 0.42);
  }

  .temoin {
    background: rgba(255, 255, 255, 0.3);
  }
  .temoin-mark {
    background: linear-gradient(160deg, #00b8d9, #2563eb, #ff8a3d);
    -webkit-background-clip: text;
    background-clip: text;
  }
  .temoin-avatar {
    color: #ffffff;
    background: linear-gradient(160deg, #2563eb, #00b8d9);
  }

  .projets-liste {
    border-color: var(--bordure);
  }
  .projet {
    border-color: var(--bordure);
    background: rgba(255, 255, 255, 0.2);
  }
  .projet::before {
    color: rgba(16, 19, 26, 0.18);
  }
  .projet::after {
    background:
      linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.32),
        transparent 20%,
        transparent 80%,
        rgba(255, 255, 255, 0.42)
      ),
      repeating-linear-gradient(
        90deg,
        rgba(16, 19, 26, 0.055) 0 1px,
        transparent 1px 7px
      );
  }
  .projet:hover {
    background: #2563eb;
    color: #070a14;
  }
  .projet:hover::before,
  .projet:hover .index {
    color: rgba(247, 243, 234, 0.62);
  }
  .apercu {
    border-color: rgba(255, 255, 255, 0.72);
    box-shadow: 0 28px 80px -38px rgba(16, 19, 26, 0.7);
  }
  .apercu::before {
    border-color: rgba(255, 255, 255, 0.45);
  }

  .parcours-3d {
    background: #10131a;
    color: #070a14;
  }
  .p3-stage {
    --noir: #10131a;
    --noir-2: #171b24;
    --creme: #070a14;
    --gris: rgba(247, 243, 234, 0.68);
    --mute: rgba(247, 243, 234, 0.62);
    --ligne: rgba(247, 243, 234, 0.16);
    background:
      radial-gradient(
        70% 70% at 55% 42%,
        rgba(0, 184, 217, 0.11),
        transparent 58%
      ),
      radial-gradient(
        64% 58% at 16% 88%,
        rgba(255, 138, 61, 0.1),
        transparent 62%
      ),
      linear-gradient(rgba(247, 243, 234, 0.028) 1px, transparent 1px) 0 0/100%
        6.4rem,
      linear-gradient(90deg, rgba(247, 243, 234, 0.024) 1px, transparent 1px) 0
        0/6.4rem 100%,
      #10131a;
  }
  .p3-of,
  .chrono-cat,
  .p3-cue,
  .p3-hint {
    color: rgba(247, 243, 234, 0.62);
  }
  .p3-titre,
  .p3-step h3,
  .p3-step p {
    color: #070a14;
  }
  .p3-stage::before {
    background: conic-gradient(
      from 90deg at 50% 50%,
      transparent,
      rgba(0, 184, 217, 0.15),
      transparent 22%,
      rgba(255, 138, 61, 0.08),
      transparent 55%,
      rgba(37, 99, 235, 0.14),
      transparent
    );
  }
  .p3-stage::after {
    background:
      repeating-radial-gradient(
        circle at 50% 48%,
        rgba(247, 243, 234, 0.045) 0 1px,
        transparent 1px 54px
      ),
      radial-gradient(
        120% 100% at 50% 50%,
        transparent 32%,
        rgba(16, 19, 26, 0.7) 100%
      );
  }
  .p3-veil {
    background:
      radial-gradient(
        36% 36% at 50% 48%,
        rgba(0, 184, 217, 0.09),
        transparent 64%
      ),
      radial-gradient(
        120% 100% at 50% 50%,
        transparent 42%,
        rgba(16, 19, 26, 0.52) 100%
      );
  }
  .p3-cine {
    background: radial-gradient(
      58% 58% at 50% 48%,
      transparent 20%,
      rgba(16, 19, 26, 0.78) 78%
    );
  }
  .p3-flash {
    background:
      radial-gradient(
        120% 90% at 50% 42%,
        rgba(255, 255, 255, 0.97) 0%,
        rgba(255, 138, 61, 0.36) 30%,
        rgba(0, 184, 217, 0) 62%
      ),
      linear-gradient(135deg, #2563eb, #00b8d9);
  }
  .p3-contact {
    color: #ffffff;
  }
  .p3-contact p,
  .contact-kicker,
  .contact-note,
  .contact-alt {
    color: rgba(255, 255, 255, 0.82);
  }
  .p3-contact .btn.plein {
    background: #ffffff;
    color: #10131a;
    border-color: #ffffff;
  }
  .p3-contact .btn.plein::before {
    background: #ffffff;
  }

  .btn {
    background: rgba(255, 255, 255, 0.34);
    border-color: var(--bordure);
    color: #10131a;
  }
  .btn::before {
    background: #2563eb;
  }
  .btn:hover {
    color: #070a14;
    border-color: #2563eb;
  }
  .btn.plein {
    color: #070a14;
    border-color: #2563eb;
    background: #2563eb;
    box-shadow: 0 22px 58px -34px rgba(37, 99, 235, 0.72);
  }
  .btn.plein::before {
    background: #2563eb;
  }
  .btn.plein:hover {
    color: #070a14;
    border-color: #10131a;
  }
  .btn.plein:hover::before {
    background: #10131a;
  }

  .contact {
    background:
      radial-gradient(
        52rem 38rem at 12% 0,
        rgba(255, 138, 61, 0.3),
        transparent 60%
      ),
      linear-gradient(135deg, #2563eb 0%, #00b8d9 100%);
    color: #ffffff;
  }
  .contact .intro,
  .contact .pied,
  .contact-kicker,
  .contact-note {
    color: rgba(255, 255, 255, 0.76);
  }
  .contact .pied .btn {
    border-color: rgba(255, 255, 255, 0.75);
    color: #ffffff;
    background: transparent;
  }
  .contact .pied .btn::before {
    background: #ffffff;
  }
  .contact .pied .btn:hover {
    color: #10131a;
  }
  .email-marquee a:hover span {
    -webkit-text-stroke-color: #ffffff;
  }
  .contact-alt {
    color: rgba(255, 255, 255, 0.84);
    text-decoration-color: rgba(255, 255, 255, 0.46);
  }
  .contact-alt:hover {
    color: #ffffff;
  }

  footer {
    --creme: #070a14;
    --gris: rgba(247, 243, 234, 0.62);
    --ligne: rgba(247, 243, 234, 0.14);
    background: #10131a;
    color: #070a14;
    border-color: rgba(247, 243, 234, 0.14);
  }
  footer a:hover {
    color: #00b8d9;
  }

  .rideau {
    background: linear-gradient(135deg, #2563eb, #00b8d9);
  }
  .rideau-mot {
    color: #ffffff;
  }
  .skip {
    background: #10131a;
    color: #070a14;
  }
  .skip:focus {
    outline-color: #2563eb;
  }
  .son-toggle {
    background: rgba(255, 255, 255, 0.82);
    border-color: var(--bordure);
    color: #5f6470;
    box-shadow: 0 20px 54px -38px rgba(16, 19, 26, 0.44);
  }
  .son-toggle:hover {
    border-color: #2563eb;
    color: #10131a;
  }
  .son-toggle.actif {
    border-color: #2563eb;
    color: #2563eb;
  }

  .case {
    --case-grad: linear-gradient(135deg, #2563eb, #00b8d9);
    background:
      radial-gradient(
        54rem 34rem at 76% 4%,
        rgba(0, 184, 217, 0.12),
        transparent 62%
      ),
      radial-gradient(
        40rem 30rem at 0 62%,
        rgba(255, 138, 61, 0.1),
        transparent 70%
      );
  }
  .case::before {
    opacity: 0.72;
    background:
      radial-gradient(
        60rem 38rem at 74% 8%,
        rgba(0, 184, 217, 0.12),
        transparent 60%
      ),
      radial-gradient(
        40rem 34rem at 0 58%,
        rgba(255, 138, 61, 0.08),
        transparent 70%
      );
  }
  .case-retour {
    color: #5f6470;
  }
  .case-num {
    color: #5f6470;
    border-color: var(--bordure);
    background: rgba(255, 255, 255, 0.58);
  }
  .case-kicker,
  .case-sticky-eyebrow,
  .case-visuals-kicker,
  .case-signature-tag {
    color: #2563eb;
  }
  .case-sous,
  .case-sticky-nom,
  .case-track h3,
  .case-track-txt,
  .case-signature p {
    color: #10131a;
  }
  .case-tags span,
  .case-track-tags span,
  .case-next-tags span {
    color: #5f6470;
    border-color: var(--bordure);
    background: rgba(255, 255, 255, 0.54);
  }
  .case-panel {
    border-color: var(--bordure);
    background: rgba(255, 255, 255, 0.72);
    box-shadow: var(--shadow-card);
  }
  .case-panel::before {
    background: radial-gradient(
      120% 80% at 100% 0,
      rgba(0, 184, 217, 0.14),
      transparent 60%
    );
  }
  .case-panel-index {
    -webkit-text-stroke-color: rgba(16, 19, 26, 0.17);
  }
  .case-meta li {
    border-color: var(--bordure);
  }
  .case-meta .val,
  .case-sticky-meta b,
  .case-sticky-label {
    color: #10131a;
  }
  .case-meta .val a {
    color: #2563eb;
    background-image: linear-gradient(#2563eb, #2563eb);
  }
  .case-overview,
  .case-track,
  .case-sticky-meta li,
  .case-signature {
    border-color: var(--bordure);
  }
  .case-track-txt strong {
    color: #2563eb;
  }
  .case-track-visuel {
    -webkit-text-stroke-color: rgba(16, 19, 26, 0.16);
    opacity: 0.13;
  }
  .case-visuals-card {
    border-color: var(--bordure);
    background: #ffffff;
    box-shadow: var(--shadow-card);
  }
  .case-visuals-cover {
    background: var(--mark-grad);
    border-color: rgba(255, 255, 255, 0.36);
  }
  .case-visuals-chips span {
    color: #5f6470;
    border-color: var(--bordure);
    background: rgba(247, 243, 234, 0.58);
  }
  .case-swatch {
    border-color: rgba(255, 255, 255, 0.44);
  }
  .case-signature {
    background:
      radial-gradient(
        60% 130% at 50% 100%,
        rgba(0, 184, 217, 0.12),
        transparent 72%
      ),
      rgba(255, 255, 255, 0.3);
  }
  .case-signature-stamp {
    box-shadow:
      0 18px 45px -24px rgba(16, 19, 26, 0.56),
      inset 0 0 0 1px rgba(255, 255, 255, 0.2);
  }
  .case-signature-retour {
    color: #10131a;
    border-color: var(--bordure);
    background: rgba(255, 255, 255, 0.54);
  }
  .case-signature-retour:hover {
    color: #ffffff;
    background-color: #2563eb;
    border-color: #2563eb;
  }
  .case-next {
    background: #10131a;
    color: #070a14;
  }
  .case-next::before {
    opacity: 0.34;
  }
  .case-next::after {
    background:
      linear-gradient(
        180deg,
        rgba(16, 19, 26, 0.96),
        rgba(16, 19, 26, 0.78),
        rgba(16, 19, 26, 0.96)
      ),
      radial-gradient(
        80% 120% at 50% 110%,
        rgba(0, 184, 217, 0.2),
        transparent 62%
      );
  }
  .case-next .lab,
  .case-next-idx,
  .case-next-tags span {
    color: rgba(247, 243, 234, 0.62);
  }
  .case-next-desc {
    color: rgba(247, 243, 234, 0.72);
  }
  .case-next:hover .nom {
    color: #00b8d9;
  }
  .case-next:hover .case-next-tags span {
    color: #070a14;
    border-color: rgba(0, 184, 217, 0.42);
  }

  .notfound {
    color: #10131a;
  }
  .nf-ghost {
    -webkit-text-stroke-color: rgba(16, 19, 26, 0.08);
  }
}

/* ═══════════════════════════════════════════════════════════
   DA finale — Light Editorial Warm / Awwwards
   Palette retenue : Editorial Warm, plus cohérente avec le portrait,
   la typo éditoriale et le positionnement premium du portfolio.
   ═══════════════════════════════════════════════════════════ */

html {
  scrollbar-color: var(--accent) var(--bg-alt);
}
::-webkit-scrollbar {
  background: var(--bg-alt);
}
::-webkit-scrollbar-track {
  background: var(--bg-alt);
}
::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-color: var(--bg-alt);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--accent-2);
  border-color: var(--bg-alt);
}
::-webkit-scrollbar-corner {
  background: var(--bg-alt);
}

body {
  background:
    radial-gradient(
      62rem 36rem at 9% -7%,
      rgba(95, 211, 236, 0.12),
      transparent 64%
    ),
    radial-gradient(
      58rem 34rem at 95% 4%,
      rgba(191, 231, 242, 0.34),
      transparent 62%
    ),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
  color: var(--text);
}
::selection {
  background: var(--text);
  color: var(--paper);
}
.grain {
  opacity: 0.065;
  mix-blend-mode: multiply;
}
.cine-overlay {
  opacity: 0.18;
  mix-blend-mode: multiply;
}
.cine-vignette {
  background:
    radial-gradient(
      52rem 34rem at var(--cine-x) var(--cine-y),
      rgba(95, 211, 236, calc(0.05 + var(--cine-heat) * 0.035)),
      transparent 60%
    ),
    radial-gradient(
      120% 92% at 50% 44%,
      transparent 58%,
      rgba(23, 23, 23, 0.09) 100%
    );
  opacity: 1;
}
.cine-scan {
  opacity: 0.025;
}
.progres {
  background: linear-gradient(
    90deg,
    var(--accent),
    var(--accent-soft),
    var(--accent-2)
  );
}

.cursor {
  background: var(--accent);
}
.cursor .etiquette {
  color: var(--text);
}
.cursor-anneau {
  border-color: rgba(95, 211, 236, 0.48);
}

.hero {
  background: var(--bg);
}
.hero::before {
  border-color: rgba(23, 23, 23, 0.14);
  mix-blend-mode: multiply;
}
.hero::after {
  background: linear-gradient(
    90deg,
    transparent,
    rgba(23, 23, 23, 0.2),
    transparent
  );
}
.hero-beam {
  background: linear-gradient(
    180deg,
    rgba(95, 211, 236, 0.14),
    transparent 68%
  );
}
.hero-fallback {
  background-color: var(--ink);
}
.hero.hero-final .hero-fallback::after {
  background:
    linear-gradient(
      180deg,
      rgba(27, 24, 21, 0.1),
      transparent 26%,
      rgba(27, 24, 21, 0.82)
    ),
    radial-gradient(78% 70% at 50% 42%, transparent 34%, rgba(27, 24, 21, 0.72));
}
.hero-kicker,
.hero-scroll-indicator span {
  color: var(--text-muted);
}
.hero.hero-final .hero-kicker,
.hero.hero-final .hero-subtitle {
  color: rgba(237, 239, 246, 0.84);
  text-shadow: 0 2px 22px rgba(0, 0, 0, 0.56);
}
.hero-subtitle {
  color: var(--text-muted);
}
.hero-subtitle em {
  color: var(--accent-2);
}
.hero.hero-final .hero-subtitle em {
  color: var(--accent-2);
}
.hero-content .ctas {
  gap: 1.05rem;
}
.hero.hero-final .hero-content .btn:not(.plein) {
  color: var(--paper);
  border-color: rgba(237, 239, 246, 0.48);
  background: rgba(27, 24, 21, 0.24);
}
.hero.hero-final .hero-content .btn:not(.plein)::before {
  background: var(--paper);
}
.hero.hero-final .hero-content .btn:not(.plein):hover {
  color: var(--text);
  border-color: var(--paper);
}
.hero-cinema-meta {
  color: rgba(237, 239, 246, 0.74);
}
.hero-cinema-meta b {
  color: var(--accent-soft);
}
.hero-cinema-meta span:nth-child(2n + 1) {
  color: rgba(237, 239, 246, 0.58);
}
.hsoc-btn {
  color: var(--paper);
  border-color: rgba(237, 239, 246, 0.34);
  background: rgba(27, 24, 21, 0.38);
}
.hsoc-btn:hover,
.hsoc-btn.actif {
  background: var(--paper);
  color: var(--text);
  border-color: var(--paper);
  box-shadow: 0 18px 45px -24px rgba(0, 0, 0, 0.72);
}
.hsoc-card {
  background: rgba(237, 239, 246, 0.9);
  color: var(--text);
  border-color: rgba(222, 214, 202, 0.88);
  box-shadow: var(--shadow-card);
}
.hsoc-card-titre {
  color: var(--text);
}
.hsoc-card-sous {
  color: var(--text-muted);
}
.hsoc-card-lien {
  color: var(--accent-2);
}

.marquee {
  background: rgba(237, 239, 246, 0.34);
  border-color: var(--border);
}
.marquee span {
  color: var(--text);
}
.marquee span::after {
  color: var(--accent);
}
/* Les eyebrows ouvrent CHAQUE section : en bleu, la couleur revenait six
   fois par page sur du texte purement indicatif. En encre atténuée elles
   redeviennent de la ponctuation, et la couleur reste disponible pour ce
   qui la mérite (un état actif, un CTA). --text-muted et pas --text-soft :
   ~5,5:1 sur le sable, donc lisible en petit corps. */
.section-num {
  color: var(--accent-2);
}
.section-num::after {
  background: linear-gradient(
    90deg,
    rgba(95, 211, 236, 0.68),
    rgba(191, 231, 242, 0.52),
    transparent
  );
}
.deco-etoile {
  color: transparent;
  -webkit-text-stroke-color: rgba(23, 23, 23, 0.075);
}

.temoin-quote p,
.temoin-nom {
  color: var(--text);
}
.temoin {
  border-color: var(--border);
}
.temoin-titre {
  color: var(--text-muted);
}
.chrono-tags span {
  background: rgba(237, 239, 246, 0.88);
  color: var(--text);
  border-color: var(--border);
  box-shadow: 0 20px 48px -38px rgba(23, 23, 23, 0.34);
}

.temoin {
  background: rgba(237, 239, 246, 0.32);
}
.temoin-tag {
  color: var(--accent);
}
.temoin-quote strong {
  color: var(--accent-2);
}
.temoin-tag::before {
  background: var(--accent);
}
.temoin-mark {
  background: linear-gradient(160deg, var(--accent-soft), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
}
.temoin-avatar {
  color: var(--paper);
  background: linear-gradient(160deg, var(--accent), var(--accent-2));
}
.temoin-lien {
  color: var(--accent);
}

.projets-liste {
  border-color: var(--border);
}
.projet {
  border-color: var(--border);
  background: rgba(237, 239, 246, 0.18);
}
.projet::before {
  color: rgba(23, 23, 23, 0.18);
}
.projet::after {
  background:
    linear-gradient(
      90deg,
      rgba(237, 239, 246, 0.34),
      transparent 20%,
      transparent 80%,
      rgba(237, 239, 246, 0.44)
    ),
    repeating-linear-gradient(
      90deg,
      rgba(23, 23, 23, 0.05) 0 1px,
      transparent 1px 7px
    );
}
.projet:hover {
  background: var(--accent);
  color: var(--paper);
}
.projet:hover::before,
.projet:hover .index {
  color: rgba(237, 239, 246, 0.64);
}
.projet .infos .desc {
  color: var(--text-muted);
  opacity: 1;
}
.projet:hover .infos .desc {
  color: rgba(237, 239, 246, 0.78);
}
.apercu {
  border-color: rgba(237, 239, 246, 0.74);
  box-shadow: 0 30px 80px -42px rgba(23, 23, 23, 0.72);
}
.apercu::before {
  border-color: rgba(237, 239, 246, 0.45);
}

.parcours-3d {
  background: var(--ink);
  color: var(--paper);
}
.p3-stage {
  --noir: var(--ink);
  --noir-2: var(--ink-2);
  --creme: var(--paper);
  --gris: rgba(237, 239, 246, 0.66);
  --mute: rgba(237, 239, 246, 0.62);
  --ligne: rgba(237, 239, 246, 0.14);
  --ambre: var(--accent-soft);
  --brass-2: var(--accent);
  background:
    radial-gradient(
      70% 70% at 55% 42%,
      rgba(95, 211, 236, 0.13),
      transparent 58%
    ),
    radial-gradient(
      64% 58% at 16% 88%,
      rgba(191, 231, 242, 0.08),
      transparent 62%
    ),
    linear-gradient(rgba(237, 239, 246, 0.026) 1px, transparent 1px) 0 0/100%
      6.4rem,
    linear-gradient(90deg, rgba(237, 239, 246, 0.022) 1px, transparent 1px) 0
      0/6.4rem 100%,
    var(--ink);
}
.p3-of,
.chrono-cat,
.p3-cue,
.p3-hint {
  color: rgba(237, 239, 246, 0.62);
}
.p3-titre,
.p3-step h3,
.p3-step p {
  color: var(--paper);
}
.p3-stage::before {
  background: conic-gradient(
    from 90deg at 50% 50%,
    transparent,
    rgba(95, 211, 236, 0.15),
    transparent 22%,
    rgba(191, 231, 242, 0.08),
    transparent 55%,
    rgba(74, 143, 224, 0.13),
    transparent
  );
}
.p3-stage::after {
  background:
    repeating-radial-gradient(
      circle at 50% 48%,
      rgba(237, 239, 246, 0.04) 0 1px,
      transparent 1px 54px
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 34%,
      rgba(27, 24, 21, 0.7) 100%
    );
}
.p3-veil {
  background:
    radial-gradient(
      36% 36% at 50% 48%,
      rgba(95, 211, 236, 0.1),
      transparent 64%
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 42%,
      rgba(27, 24, 21, 0.52) 100%
    );
}
.p3-cine {
  background: radial-gradient(
    58% 58% at 50% 48%,
    transparent 20%,
    rgba(27, 24, 21, 0.78) 78%
  );
}
/* arrivée de fin de voyage : clairière de lumière lunaire, jamais un blanc plein écran
   (l'ancien flash "blanc chaud" plein cadre cassait tout le contraste sur fond sombre) */
.p3-flash {
  background: radial-gradient(
    70% 60% at 50% 42%,
    rgba(95, 211, 236, 0.34) 0%,
    rgba(13, 20, 36, 0.88) 55%,
    var(--bg) 78%
  );
}
.p3-contact {
  color: var(--text);
}
.p3-contact p,
.contact-kicker,
.contact-note,
.contact-alt {
  color: var(--text-muted);
}
.p3-contact .btn.plein {
  background: var(--accent);
  color: var(--ink);
  border-color: var(--accent);
}
.p3-contact .btn.plein::before {
  background: var(--accent);
}
.chrono-tags span {
  background: rgba(237, 239, 246, 0.12);
  color: var(--paper);
  border-color: rgba(237, 239, 246, 0.18);
}

.btn {
  background: rgba(237, 239, 246, 0.42);
  border-color: var(--border);
  color: var(--text);
  box-shadow: none;
}
.btn::before {
  background: var(--accent);
}
.btn:hover {
  color: var(--paper);
  border-color: var(--accent);
}
.btn.plein {
  color: var(--paper);
  border-color: var(--accent);
  background: var(--accent);
  box-shadow: 0 24px 58px -38px rgba(95, 211, 236, 0.78);
}
.btn.plein::before {
  background: var(--accent);
}
.btn.plein:hover {
  color: var(--paper);
  border-color: var(--text);
}
.btn.plein:hover::before {
  background: var(--text);
}

.contact {
  background:
    radial-gradient(
      52rem 38rem at 12% 0,
      rgba(237, 239, 246, 0.18),
      transparent 60%
    ),
    linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
  color: var(--paper);
}
.contact .intro,
.contact .pied {
  color: rgba(237, 239, 246, 0.76);
}
.contact .pied .btn {
  border-color: rgba(237, 239, 246, 0.68);
  color: var(--paper);
  background: transparent;
}
.contact .pied .btn::before {
  background: var(--paper);
}
.contact .pied .btn:hover {
  color: var(--text);
}
.email-marquee a:hover span {
  -webkit-text-stroke-color: var(--paper);
}
.contact-alt {
  color: rgba(237, 239, 246, 0.84);
  text-decoration-color: rgba(237, 239, 246, 0.46);
}
.contact-alt:hover {
  color: var(--paper);
}

footer {
  --creme: var(--paper);
  --gris: rgba(237, 239, 246, 0.62);
  --ligne: rgba(237, 239, 246, 0.14);
  background: var(--ink);
  color: var(--paper);
  border-color: rgba(237, 239, 246, 0.14);
}
footer a:hover {
  color: var(--accent-soft);
}

.temoin .temoin-auteur {
  --creme: var(--text);
  --gris: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  max-width: min(100%, 720px);
  margin: 2.7rem auto 0;
  padding: 0.85rem 1.05rem;
  gap: 0.9rem;
  background: rgba(237, 239, 246, 0.78);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 24px 68px -52px rgba(23, 23, 23, 0.38);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.temoin .temoin-nom {
  color: var(--text);
}
.temoin .temoin-titre {
  color: var(--text-muted);
}

.temoin {
  min-height: clamp(620px, 78svh, 760px);
  display: flex;
  align-items: center;
  padding: clamp(4.2rem, 7vw, 7rem) var(--marge);
  text-align: left;
  background:
    radial-gradient(
      48rem 32rem at 16% 12%,
      rgba(95, 211, 236, 0.12),
      transparent 62%
    ),
    radial-gradient(
      46rem 30rem at 84% 86%,
      rgba(141, 134, 201, 0.16),
      transparent 66%
    ),
    var(--bg-alt);
}
.temoin-inner {
  width: 100%;
  max-width: min(var(--max), 1480px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 410px);
  grid-template-rows: auto 1fr auto;
  column-gap: clamp(3rem, 8vw, 8rem);
  row-gap: clamp(1rem, 2vw, 1.8rem);
  align-items: center;
}
.temoin-tag {
  grid-column: 1;
  grid-row: 1;
  justify-self: start;
  margin: 0 0 clamp(0.5rem, 1.4vw, 1rem);
}
.temoin-mark {
  top: clamp(-2.2rem, -3vw, -1rem);
  left: clamp(15rem, 34vw, 34rem);
  transform: none;
  z-index: 0;
  opacity: 0.1;
  font-size: clamp(8rem, 17vw, 17rem);
}
.temoin-quote {
  grid-column: 1;
  grid-row: 2 / span 2;
  position: relative;
  z-index: 1;
  max-width: min(100%, 820px);
}
.temoin-quote p {
  font-size: clamp(1.5rem, 2.4vw, 2.1rem);
  line-height: 1.25;
  letter-spacing: 0;
  text-align: left;
}
.temoin .temoin-auteur {
  grid-column: 2;
  grid-row: 2;
  position: relative;
  justify-self: stretch;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  justify-content: start;
  margin: 0;
  padding: clamp(1.25rem, 2vw, 1.65rem);
  border-radius: 8px;
  background:
    linear-gradient(
      180deg,
      rgba(237, 239, 246, 0.07),
      rgba(237, 239, 246, 0.02)
    ),
    var(--surface-soft);
  box-shadow: 0 36px 88px -62px rgba(0, 3, 10, 0.6);
}
.temoin .temoin-auteur::before {
  content: "Lettre de recommandation";
  grid-column: 1 / -1;
  margin-bottom: clamp(1.3rem, 2vw, 1.8rem);
  padding-bottom: clamp(1rem, 1.8vw, 1.4rem);
  border-bottom: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-2);
}
.temoin .temoin-avatar {
  width: 48px;
  height: 48px;
  box-shadow: 0 18px 34px -22px rgba(74, 143, 224, 0.78);
}
.temoin-lien {
  grid-column: 2;
  grid-row: 3;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.2rem;
  padding: 0.78rem 0;
  border-bottom: 1px solid rgba(95, 211, 236, 0.32);
}

@media (max-width: 980px) {
  .temoin {
    min-height: auto;
    padding: clamp(4.5rem, 12vw, 6.5rem) var(--marge);
    text-align: left;
  }
  .temoin-inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1.5rem;
  }
  .temoin-mark {
    top: 1.4rem;
    right: 0;
    left: auto;
    font-size: clamp(7rem, 34vw, 12rem);
  }
  .temoin-quote {
    max-width: 100%;
  }
  .temoin-quote p {
    font-size: clamp(1.5rem, 6vw, 1.9rem);
    line-height: 1.2;
  }
  .temoin .temoin-auteur {
    width: 100%;
    margin-top: 0.6rem;
  }
  .temoin-lien {
    margin-top: -0.5rem;
  }
}

.rideau {
  background: linear-gradient(135deg, var(--accent), var(--accent-soft));
}
.rideau-mot {
  color: var(--text);
}
.skip {
  background: var(--text);
  color: var(--paper);
}
.skip:focus {
  outline-color: var(--accent);
}
.son-toggle {
  background: rgba(237, 239, 246, 0.86);
  border-color: var(--border);
  color: var(--text-muted);
  box-shadow: 0 20px 54px -40px rgba(23, 23, 23, 0.4);
}
.son-toggle:hover {
  border-color: var(--accent);
  color: var(--text);
}
.son-toggle.actif {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.case {
  --case-grad: linear-gradient(135deg, var(--accent), var(--accent-soft));
  background:
    radial-gradient(
      54rem 34rem at 76% 4%,
      rgba(191, 231, 242, 0.32),
      transparent 62%
    ),
    radial-gradient(
      40rem 30rem at 0 62%,
      rgba(95, 211, 236, 0.1),
      transparent 70%
    );
}
.case::before {
  opacity: 0.78;
  background:
    radial-gradient(
      60rem 38rem at 74% 8%,
      rgba(191, 231, 242, 0.34),
      transparent 60%
    ),
    radial-gradient(
      40rem 34rem at 0 58%,
      rgba(95, 211, 236, 0.08),
      transparent 70%
    );
}
.case-retour,
.case-num {
  color: var(--text-muted);
}
.case-num {
  border-color: var(--border);
  background: rgba(13, 20, 36, 0.7);
}
.case-kicker,
.case-sticky-eyebrow,
.case-visuals-kicker,
.case-signature-tag {
  color: var(--accent);
}
.case-sous,
.case-sticky-nom,
.case-track h3,
.case-track-txt,
.case-signature p {
  color: var(--text);
}
/* pastilles (tags hero/étape/projet suivant) : verre de nuit, texte clair —
   l'ancien fond quasi-blanc + texte gris rendait un gris-sur-gris illisible */
.case-tags span,
.case-track-tags span,
.case-next-tags span {
  color: var(--text-muted);
  border-color: rgba(237, 239, 246, 0.15);
  background: rgba(13, 20, 36, 0.7);
}
.case-panel {
  border-color: var(--border);
  background: rgba(237, 239, 246, 0.76);
  box-shadow: var(--shadow-card);
}
.case-panel::before {
  background: radial-gradient(
    120% 80% at 100% 0,
    rgba(95, 211, 236, 0.12),
    transparent 60%
  );
}
.case-panel-index {
  -webkit-text-stroke-color: rgba(23, 23, 23, 0.15);
}
.case-meta li,
.case-overview,
.case-track,
.case-sticky-meta li,
.case-signature {
  border-color: var(--border);
}
.case-meta .val,
.case-sticky-meta b,
.case-sticky-label {
  color: var(--text);
}
.case-meta .val a {
  color: var(--accent-2);
  background-image: linear-gradient(var(--accent), var(--accent));
}
.case-track-txt strong {
  color: var(--accent-2);
}
.case-track-visuel {
  -webkit-text-stroke-color: rgba(23, 23, 23, 0.14);
  opacity: 0.12;
}
.case-visuals-card {
  border-color: var(--border);
  background: var(--surface-soft);
  box-shadow: var(--shadow-card);
}
.case-visuals-cover {
  background: var(--mark-grad);
  border-color: rgba(237, 239, 246, 0.36);
}
.case-visuals-chips span {
  color: var(--text-muted);
  border-color: rgba(237, 239, 246, 0.15);
  background: rgba(13, 20, 36, 0.7);
}
/* lecture finale : citation de clôture — même bug que .temoin/.p3-flash avant elles (un
   fond quasi-blanc plein cadre sous du texte clair) ; glow sombre cohérent avec le reste */
.case-signature {
  background:
    radial-gradient(
      60% 130% at 50% 100%,
      rgba(95, 211, 236, 0.14),
      transparent 72%
    ),
    var(--bg-alt);
}
.case-signature-retour {
  color: var(--paper);
  border-color: rgba(237, 239, 246, 0.16);
  background: rgba(13, 20, 36, 0.7);
}
.case-signature-retour:hover {
  color: var(--ink);
  background-color: var(--accent);
  border-color: var(--accent);
}
.case-next {
  background: var(--ink);
  color: var(--paper);
}
.case-next::after {
  background:
    linear-gradient(
      180deg,
      rgba(27, 24, 21, 0.96),
      rgba(27, 24, 21, 0.78),
      rgba(27, 24, 21, 0.96)
    ),
    radial-gradient(
      80% 120% at 50% 110%,
      rgba(95, 211, 236, 0.2),
      transparent 62%
    );
}
.case-next .lab,
.case-next-idx,
.case-next-tags span {
  color: rgba(237, 239, 246, 0.62);
}
.case-next-desc {
  color: rgba(237, 239, 246, 0.72);
}
.case-next:hover .nom {
  color: var(--accent-soft);
}
.case-next:hover .case-next-tags span {
  color: var(--paper);
  border-color: rgba(191, 231, 242, 0.42);
}

/* ───── Selected works : chaque scroll révèle une couche du projet — chapitres cinématiques ─────
   BASE (mobile-first, aussi servie à reduced-motion et sans JS) : pile verticale lisible.
   Le mode chapitres (.works.sc-on, posé par le JS ≥1024px + hover) : un seul viewport sticky,
   scènes empilées en absolu, master timeline scrubée — une scène visible à la fois. */
.works {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding: clamp(4.5rem, 10vh, 7rem) 0 0;
  background: var(--bg);
  color: var(--text);
}
.works::after {
  /* mot fantôme éditorial */
  content: "WORKS";
  position: absolute;
  left: -0.03em;
  bottom: -0.06em;
  z-index: 0;
  pointer-events: none;
  font-family: var(--display);
  font-size: clamp(7rem, 20vw, 22rem);
  font-weight: 900;
  line-height: 0.72;
  color: transparent;
  -webkit-text-stroke: 1px rgba(var(--text-rgb), 0.05);
  letter-spacing: 0;
}

/* Frontières de sections de la home : le calque est injecté par
   initHomeSectionTransitions. Il ne modifie jamais les conteneurs sticky/pinned
   et conserve donc intactes les mesures du hero, des compétences et du parcours. */
main[data-barba-namespace="home"] .home-boundary {
  position: relative;
}
main[data-barba-namespace="home"] .home-boundary-layer {
  --bt-rgb: var(--accent-rgb);
  --bt-line: 0;
  --bt-pos: 0%;
  --bt-x: 0px;
  --bt-alpha: 0;
  --bt-glow: 0;
  --bt-y: 14px;
  --bt-drift: 0px;
  --bt-spark-scale: 1;
  --bt-bloom: 20px;
  --bt-spread: 5px;
  position: absolute;
  z-index: 9;
  top: 0;
  left: 0;
  right: 0;
  display: block;
  height: 7.5rem;
  height: clamp(4.5rem, 12svh, 7.5rem);
  overflow: hidden;
  pointer-events: none;
  contain: paint;
}
main[data-barba-namespace="home"] .home-boundary-layer::before {
  content: "";
  position: absolute;
  inset: -45% 0 0;
  background: radial-gradient(
    ellipse at 50% 0,
    rgba(var(--bt-rgb), 0.14),
    transparent 70%
  );
  opacity: var(--bt-glow);
  transform: translate3d(
    calc(var(--bt-pos) - 50% + var(--bt-drift)),
    var(--bt-y),
    0
  );
}
main[data-barba-namespace="home"] .home-boundary-layer.tone-deep {
  --bt-rgb: var(--accent-2-rgb);
}
main[data-barba-namespace="home"] .home-boundary-layer.tone-warm {
  --bt-rgb: var(--secondaire-rgb);
}
main[data-barba-namespace="home"] .home-boundary-layer.tone-dark {
  --bt-rgb: var(--accent-soft-rgb);
}
/* Méthode passe en nuit : le cyan pâle reste perceptible sans tracer une
   séparation horizontale franche sur le fondu d'entrée. */
main[data-barba-namespace="home"] .home-boundary-layer.tone-method {
  --bt-rgb: var(--accent-soft-rgb);
}
main[data-barba-namespace="home"] .home-boundary-track {
  position: absolute;
  top: 4px;
  left: var(--marge);
  right: var(--marge);
  height: 1px;
}
main[data-barba-namespace="home"] .home-boundary-beam {
  position: absolute;
  inset: 0;
  display: block;
  background: linear-gradient(
    90deg,
    rgba(var(--bt-rgb), 0.72),
    rgba(var(--bt-rgb), 0.28) 68%,
    transparent
  );
  opacity: var(--bt-alpha);
  transform: scaleX(var(--bt-line));
  transform-origin: left center;
  box-shadow: 0 0 18px rgba(var(--bt-rgb), 0.12);
}
main[data-barba-namespace="home"]
  .home-boundary-layer.is-reverse
  .home-boundary-beam {
  background: linear-gradient(
    270deg,
    rgba(var(--bt-rgb), 0.96),
    rgba(var(--bt-rgb), 0.42) 72%,
    transparent
  );
  transform-origin: right center;
}
main[data-barba-namespace="home"] .home-boundary-spark {
  position: absolute;
  top: 50%;
  left: 0;
  display: block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgb(var(--bt-rgb));
  opacity: var(--bt-alpha);
  transform: translate3d(var(--bt-x), -50%, 0) translateX(-50%)
    scale(var(--bt-spark-scale));
  box-shadow:
    0 0 0 4px rgba(var(--bt-rgb), 0.08),
    0 0 var(--bt-bloom) var(--bt-spread) rgba(var(--bt-rgb), 0.3);
}
main[data-barba-namespace="home"]
  .home-boundary-layer.tone-method
  .home-boundary-track {
  display: none;
}
main[data-barba-namespace="home"]
  .home-boundary-layer.is-boundary-active::before,
main[data-barba-namespace="home"]
  .home-boundary-layer.is-boundary-active
  .home-boundary-beam,
main[data-barba-namespace="home"]
  .home-boundary-layer.is-boundary-active
  .home-boundary-spark {
  will-change: opacity, transform;
}
@media (max-width: 767px) {
  main[data-barba-namespace="home"] .home-boundary-layer {
    height: 4.5rem;
  }
  main[data-barba-namespace="home"] .home-boundary-layer::before {
    display: none;
  }
  main[data-barba-namespace="home"] .home-boundary-spark {
    box-shadow:
      0 0 0 3px rgba(var(--bt-rgb), 0.08),
      0 0 12px 3px rgba(var(--bt-rgb), 0.24);
  }
}
@media (prefers-reduced-motion: reduce) {
  main[data-barba-namespace="home"] .home-boundary-layer {
    display: none !important;
  }
}

/* CTA générique de la section (souligné qui se déploie au hover) */
.works-cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding-bottom: 0.4rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 760;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.works-cta::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0.3);
  transform-origin: left center;
  transition: transform 0.5s var(--ease);
}
.works-cta span {
  transition: transform 0.35s var(--ease);
}
.works-cta:hover::after {
  transform: scaleX(1);
  background: var(--secondaire);
}
.works-cta:hover span {
  transform: translateX(5px);
}

/* ═══════════════ Direction "expédition" : tokens additifs + primitives partagées ═══════════════
   Vient se greffer sur la palette crème/encre existante (elle lit déjà comme un parchemin) sans la
   remplacer : ces teintes ne servent qu'aux nouveaux motifs (tampons, jauges, balises). */

/* Tampon : badge de classification façon cachet, contour pointillé */
.tampon {
  --t-rgb: var(--accent-rgb);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  padding: 0.3rem 0.68rem 0.3rem 0.56rem;
  border-radius: 999px;
  border: 1px dashed rgba(var(--t-rgb), 0.5);
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 740;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgb(var(--t-rgb));
  background: rgba(var(--t-rgb), 0.08);
}
.tampon i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* Puces : jauge à points (scope projet, niveau de compétence) */
.puces {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
}
.puces i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(var(--text-rgb), 0.16);
  flex: none;
  transform: scale(0.7);
  transition:
    transform 0.5s var(--ease),
    background 0.5s var(--ease),
    box-shadow 0.5s var(--ease);
  transition-delay: calc(var(--i, 0) * 0.07s);
}
.puces i.on {
  background: rgb(var(--p-rgb, var(--t-rgb, var(--accent-rgb))));
  transform: scale(1);
  box-shadow: 0 0 0 3px
    rgba(var(--p-rgb, var(--t-rgb, var(--accent-rgb))), 0.16);
}

/* Balise : petit repère "étape franchie" */
.balise {
  position: relative;
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: rgba(var(--text-rgb), 0.2);
  transition:
    background 0.5s var(--ease),
    box-shadow 0.5s var(--ease),
    transform 0.5s var(--ease);
}
.balise.on {
  background: var(--voie-vert);
  box-shadow: 0 0 0 4px rgba(var(--voie-vert-rgb), 0.22);
  transform: scale(1.2);
}

/* Motes : particules flottantes discrètes, décor pur CSS (négatif : toujours derrière le contenu) */
.motes {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.motes i {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(0.5px);
  opacity: 0.55;
  animation: moteDerive 15s ease-in-out infinite;
}
.motes i:nth-child(1) {
  left: 9%;
  top: 22%;
  width: 5px;
  height: 5px;
  background: var(--voie-jaune);
  animation-duration: 12s;
}
.motes i:nth-child(2) {
  left: 84%;
  top: 64%;
  width: 6px;
  height: 6px;
  background: var(--voie-bleu);
  animation-duration: 17s;
  animation-delay: -4s;
}
.motes i:nth-child(3) {
  left: 58%;
  top: 14%;
  width: 4px;
  height: 4px;
  background: var(--voie-vert);
  animation-duration: 20s;
  animation-delay: -9s;
}
.motes i:nth-child(4) {
  left: 32%;
  top: 78%;
  width: 5px;
  height: 5px;
  background: var(--accent);
  animation-duration: 14s;
  animation-delay: -6s;
}
@keyframes moteDerive {
  0%,
  100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(16px, -24px);
  }
}

/* ───── Compétences → jauge de maîtrise à puces ───── */
.comp-niveau {
  margin-top: 0.65rem;
}

/* ───── Parcours → pastilles d'étapes (desktop) + balise renforcée (frise mobile) ───── */
/* piste en orbite : un trait relie les repères, écho direct de la scène 3D juste à
   côté (la comète reliée à la sphère par un fil lumineux) — les points ne sont plus
   un simple indicateur de progression abstrait, ils rejouent le motif qu'on regarde. */
.p3-dots {
  position: absolute;
  top: calc(clamp(2.4rem, 6vh, 4rem) + 4.6rem);
  right: var(--marge);
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.p3-dots::before {
  content: "";
  position: absolute;
  left: 3px;
  right: 3px;
  top: 50%;
  height: 1px;
  background: rgba(var(--paper-rgb), 0.14);
  z-index: -1;
}
.p3-dots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(237, 239, 246, 0.22);
  transition:
    background 0.5s var(--ease),
    box-shadow 0.5s var(--ease),
    transform 0.5s var(--ease);
}
.p3-dots i.on {
  /* couleur/ombre réellement affichées : posées plus bas sur le sélecteur scopé
     main[data-barba-namespace="home"] #parcours .p3-dots i.on, plus spécifique, qui
     l'emporte quel que soit l'ordre des règles — ne garder ici que ce qu'il ne couvre
     pas (l'agrandissement), pour ne pas laisser une couleur jamais réellement visible. */
  transform: scale(1.35);
}
.parcours-3d.p3-flat .p3-dots {
  display: none;
}
.parcours-3d.p3-flat .p3-step::before {
  box-shadow: 0 0 0 5px rgba(var(--voie-vert-rgb), 0.16);
}

/* ───── Contact final : sceau de fin de voyage ───── */
.contact-kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

/* ───── Témoignage : anneau discret, sans toucher au texte du client ───── */
.temoin-avatar {
  outline: 2px solid rgba(var(--voie-vert-rgb), 0.32);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .motes i {
    animation: none;
  }
  .puces i,
  .balise,
  .p3-dots i {
    transition: none;
  }
}

/* ═══════════════ Direction "néo-lunaire cinématique" : palette finale ═══════════════
   Dernier :root du fichier → gagne le cascade sur les 3 blocs précédents (historique du
   fichier). Les tokens texte/surface/ligne restaient sur l'ancienne valeur claire après le
   remplacement mécanique des cuivres/crèmes (on évitait volontairement #171717/#FFFFFF, aussi
   utilisés ailleurs pour des effets mix-blend-mode indépendants du thème) : on les corrige ici. */

/* corps de page : halo lunaire discret au lieu de la lumière chaude précédente */
body {
  background:
    radial-gradient(
      70rem 42rem at 15% -10%,
      rgba(95, 211, 236, 0.07),
      transparent 62%
    ),
    radial-gradient(
      60rem 40rem at 88% 6%,
      rgba(141, 134, 201, 0.08),
      transparent 64%
    ),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
}
::selection {
  background: var(--accent);
  color: var(--ink);
}

/* même famille de pastille (services, méthode, repli grille compétences) : fond clair translucide
   + texte --text-muted ne passait plus (contraste ~1.6:1 sur fond sombre) — pastille plus opaque,
   texte sombre ; au survol, fond cyan clair + texte clair souffrait du même souci (~1.5:1) */
.notfound {
  color: var(--text);
}
.nf-ghost {
  -webkit-text-stroke-color: rgba(23, 23, 23, 0.08);
}

@media (max-width: 760px) {
  #flux,
  .contenu,
  .parcours-3d,
  .parcours-3d.p3-flat .p3-stage {
    max-width: 100vw;
    overflow-x: clip;
  }
}

/* ═══════════════════════════════════════════════════════════
   NOXARI STUDIO — marque (nav, loader, rideau Barba) + finitions
   ═══════════════════════════════════════════════════════════ */

/* lockup de navigation : monogramme + wordmark. L'étoile garde l'or du logo
   d'origine — seule touche chaude du site, réservée à la marque. */
header .logo {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
}
.logo-mark {
  width: 24px;
  height: auto;
  flex-shrink: 0;
}
.nx-m {
  fill: var(--text);
}
.nx-e {
  fill: #cf9d3b;
}
.logo-nom {
  letter-spacing: 0.1em;
}
.logo-studio {
  margin-left: 0.55em;
  font-weight: 400;
  letter-spacing: 0.24em;
  color: var(--text-muted);
  font-size: 0.78em;
}

/* ═══════ Loader « hublot » (2026-08-01, remplace le rideau de scène) ═══════
   Une fenêtre de vaisseau, fermée au repos. Trois calques concentriques :
   .loader-espace (l'extérieur — étoiles, lueur, le mark) est en PERMANENCE
   derrière tout le reste, visible seulement à travers le disque que découpe
   son propre clip-path circulaire ; .loader-hublot-obturateur est un disque
   plein, de la même teinte que la coque (.loader, ci-dessus), qui REJOINT
   exactement ce cercle au repos et le couvre — la fenêtre paraît alors fermée,
   sans qu'aucune forme n'ait besoin de changer de taille pour ça. Ouvrir la
   fenêtre ne fait qu'UNE chose : rétrécir l'obturateur (scale 1 → 0). Le cadre
   (.loader-hublot-cadre) ne bouge jamais — c'est la vitre qu'on traverse, pas
   la fenêtre elle-même. Aucun WebGL : tout ici est transform/opacity/filter,
   le budget GPU reste bas sur mobile.
   Chorégraphie : transitions/loader.js (initLoader). */

/* rayon de l'ouverture, en vmin : partagé par l'espace, l'obturateur et le
   cadre pour qu'ils s'alignent au pixel près sans dupliquer la valeur. */
:root {
  --hublot-r: 40vmin;
}

.loader-espace {
  position: absolute;
  inset: -20%;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  clip-path: circle(var(--hublot-r) at 50% 50%);
  /* la scène est surdimensionnée (inset -20%) : le zoom d'approche (piloté en
     JS) grandit ce calque, jamais le cercle qui le découpe — sans cette marge,
     le zoom découvrirait un bord carré au-delà du cercle dès les premiers %. */
  background:
    radial-gradient(60% 55% at 50% 46%, rgba(207, 157, 59, 0.16), transparent 68%),
    radial-gradient(90% 80% at 26% 92%, rgba(95, 211, 236, 0.05), transparent 70%),
    radial-gradient(140% 120% at 50% 50%, #0d1626 0%, #070a14 55%, #04060c 100%);
  /* fermé : flou et sombre, comme un hublot dont l'œil ne s'est pas encore
     adapté à ce qu'il y a dehors. JS anime ces deux propriétés vers 0/1 au
     même rythme que le compteur — « le contenu devient de plus en plus clair »
     n'est pas un effet à part, c'est ce calque qui se révèle. */
  filter: blur(15px) brightness(0.45) saturate(0.75);
  will-change: transform, filter;
}
.loader-etoiles {
  position: absolute;
  inset: -30%;
  background-image:
    radial-gradient(1.6px 1.6px at 12% 22%, rgba(250, 247, 240, 0.9), transparent),
    radial-gradient(1.2px 1.2px at 28% 68%, rgba(250, 247, 240, 0.7), transparent),
    radial-gradient(1.8px 1.8px at 46% 14%, rgba(250, 247, 240, 0.85), transparent),
    radial-gradient(1.1px 1.1px at 64% 78%, rgba(250, 247, 240, 0.6), transparent),
    radial-gradient(1.5px 1.5px at 78% 34%, rgba(250, 247, 240, 0.8), transparent),
    radial-gradient(1.2px 1.2px at 85% 62%, rgba(250, 247, 240, 0.65), transparent),
    radial-gradient(1.4px 1.4px at 18% 88%, rgba(250, 247, 240, 0.75), transparent),
    radial-gradient(1.1px 1.1px at 55% 92%, rgba(250, 247, 240, 0.55), transparent),
    radial-gradient(1.6px 1.6px at 92% 12%, rgba(250, 247, 240, 0.8), transparent),
    radial-gradient(1.3px 1.3px at 36% 42%, rgba(250, 247, 240, 0.7), transparent);
  background-repeat: no-repeat;
  opacity: 0.8;
}
.loader-lueur {
  position: absolute;
  inset: 0;
  background: radial-gradient(28% 24% at 50% 46%, rgba(255, 224, 168, 0.5), transparent 72%);
  mix-blend-mode: screen;
}
/* le mark est ce vers quoi on avance : au centre de l'espace, il grandit et
   s'éclaire avec lui, puis flambe au passage (cf. .loader.etoile-allumee ci-dessous). */
.loader-mark {
  position: relative;
  z-index: 1;
  display: block;
  width: clamp(64px, 11vmin, 150px);
  aspect-ratio: 681 / 633;
}
.loader-mark svg { display: block; width: 100%; height: 100%; }
.loader-mark .nx-m,
.loader-mark .nx-e {
  fill: none;
  stroke: rgba(250, 247, 240, 0.82);
  stroke-width: 2.4px;
  vector-effect: non-scaling-stroke;
}
.loader-mark .nx-e { transition: fill 0.4s ease; }
.loader.etoile-allumee .loader-mark .nx-e {
  fill: #cf9d3b;
  filter: drop-shadow(0 0 14px rgba(207, 157, 59, 0.65));
}

.loader-hublot {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
/* l'obturateur : même teinte que la coque (.loader), posé pile sur le cercle
   de .loader-espace + une légère marge (41 contre 40vmin) pour ne jamais
   laisser un liseré sombre entre le disque et le bord du cercle pendant le
   rétrécissement — un sous-pixel suffirait à trahir que ce sont deux formes
   distinctes. */
.loader-hublot-obturateur {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--hublot-r) * 2 + 2vmin);
  height: calc(var(--hublot-r) * 2 + 2vmin);
  transform: translate(-50%, -50%) scale(1);
  transform-origin: 50% 50%;
  border-radius: 50%;
  background: #05070c;
  /* seams d'iris : purement décoratif, ne s'anime jamais — c'est le
     rétrécissement du disque qui fait le travail, ce dégradé ne fait que
     dire "mécanisme" plutôt que "cache plat" pendant qu'il est encore visible. */
  background-image: conic-gradient(
    from 0deg,
    rgba(0, 0, 0, 0.3) 0deg 2deg,
    transparent 2deg 28deg,
    rgba(0, 0, 0, 0.25) 28deg 30deg,
    transparent 30deg 58deg,
    rgba(0, 0, 0, 0.3) 58deg 60deg,
    transparent 60deg 88deg,
    rgba(0, 0, 0, 0.25) 88deg 90deg,
    transparent 90deg 118deg,
    rgba(0, 0, 0, 0.3) 118deg 120deg,
    transparent 120deg 148deg,
    rgba(0, 0, 0, 0.25) 148deg 150deg,
    transparent 150deg 178deg,
    rgba(0, 0, 0, 0.3) 178deg 180deg,
    transparent 180deg 208deg,
    rgba(0, 0, 0, 0.25) 208deg 210deg,
    transparent 210deg 238deg,
    rgba(0, 0, 0, 0.3) 238deg 240deg,
    transparent 240deg 268deg,
    rgba(0, 0, 0, 0.25) 268deg 270deg,
    transparent 270deg 298deg,
    rgba(0, 0, 0, 0.3) 298deg 300deg,
    transparent 300deg 328deg,
    rgba(0, 0, 0, 0.25) 328deg 330deg,
    transparent 330deg 360deg
  );
  will-change: transform;
}
/* biseau : ombre interne + filet clair, juste à l'intérieur du cadre —
   donne de l'épaisseur à l'ouverture sans ajouter d'élément DOM. */
.loader-hublot-biseau {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--hublot-r) * 2);
  height: calc(var(--hublot-r) * 2);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-shadow:
    inset 0 0 1.4vmin rgba(0, 0, 0, 0.7),
    inset 0 0.3vmin 0.5vmin rgba(255, 246, 228, 0.14);
  pointer-events: none;
}
/* le cadre : anneau métallique épais, lumière tombant du haut (plus clair en
   haut, plus sombre en bas) — la seule vraie source dans cette scène avant
   que le hublot ne s'ouvre est l'étoile en son centre, mais un cadre qui
   ignorait toute lumière se lisait plat, en aplat de couleur pure. */
.loader-hublot-cadre {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--hublot-r) * 2 + 8vmin);
  height: calc(var(--hublot-r) * 2 + 8vmin);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 4vmin solid #171d29;
  border-top-color: #232c3d;
  border-bottom-color: #0c0f16;
  box-shadow:
    0 0 0 0.3vmin #04060a,
    0 2vmin 5vmin -1vmin rgba(0, 0, 0, 0.75),
    inset 0 0 0 0.25vmin rgba(0, 0, 0, 0.4);
}
/* reflet : arc clair diagonal, comme une vitre qui accroche une lumière
   qu'on ne voit pas — signe court de "verre", pas de mouvement nécessaire. */
.loader-hublot-reflet {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--hublot-r) * 2);
  height: calc(var(--hublot-r) * 2);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: conic-gradient(
    from 200deg at 50% 50%,
    transparent 0deg,
    rgba(255, 255, 255, 0.1) 18deg,
    transparent 42deg,
    transparent 360deg
  );
  mix-blend-mode: screen;
  pointer-events: none;
}

.loader-console {
  position: absolute;
  left: 50%;
  bottom: clamp(1.6rem, 6vh, 4rem);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
}
.loader-console-trait {
  width: 2.4em;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(207, 157, 59, 0.6), transparent);
}
/* même lissage que le rideau (majCompteur, loader.js) — seule la mise en page
   change : un readout de cockpit centré sous la fenêtre plutôt qu'un cartel. */
.loader-compteur-num {
  display: block;
  font-family: var(--mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.6rem, 4vw, 2.4rem);
  line-height: 1;
  letter-spacing: 0.14em;
  color: rgba(250, 247, 240, 0.88);
}

/* flash du passage : plein cadre, au-dessus de tout. Reste à opacity 0 tant
   que le hublot n'est pas franchi (loader.js: traverse()) — c'est lui qui
   masque la coupure entre la disparition du loader et l'entrée du hero. */
.loader-flash {
  position: fixed;
  inset: 0;
  z-index: 4;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(
    65% 65% at 50% 50%,
    rgba(255, 241, 214, 0.98),
    rgba(255, 224, 168, 0.7) 55%,
    rgba(207, 157, 59, 0.15) 100%
  );
}

@media (max-width: 620px) {
  :root { --hublot-r: 34vmin; }
  .loader-compteur-num { font-size: clamp(1.3rem, 5.2vw, 1.9rem); }
}

@media (prefers-reduced-motion: reduce) {
  .loader-espace, .loader-hublot-obturateur { transition: none !important; }
}

/* Rideau Barba (2026-08-01, remplace la vague en cercle du 2026-07-19) : même
   étoffe et même geste que le rideau du chargement initial — un pan plein
   cadre qui TOMBE pour couvrir la page sortante (rideau.js: couvrir), puis
   MONTE pour révéler la suivante, déjà échangée par Barba derrière (ouvrir).
   Pas d'origine de clic à respecter : contrairement à l'ancienne vague qui
   naissait du lien cliqué, un rideau de théâtre ne se soucie pas d'où vient
   le regard — un seul geste, toujours le même.
   Repos hors-champ en HAUT via `transform` (et non plus clip-path) : même
   technique que loader.js (yPercent/scaleY), pour que les deux rideaux du
   site partagent une seule grammaire de mouvement.
   Fond et grain repris À L'IDENTIQUE de .loader-porte-laque (même recette,
   dupliquée à dessein plutôt que factorisée : ce fichier hardcode déjà les
   couleurs du loader plutôt que des tokens, pour ne jamais hériter d'une
   couche de thème imprévue plus loin dans la cascade — cf. commentaire
   Task 3 plus haut). Seuls les libellés gardent leurs couleurs locales
   verrouillées (papier) : hériter --text, redéfini en encre sombre par le
   thème clair plus bas dans le fichier, produisait un contraste de 1.12:1
   sur la nuit et rendait logo + wordmark invisibles.
   Relecture design (2026-08-01) : même correctif que .loader-porte-laque —
   le bleu-indigo #212f48 était une teinte inventée, pas celle du site
   (--bg:#070a14 / --bg-alt:#0b0f1e), et le spot de plafonnier ignorait que
   le mark affiché ICI MÊME est une étoile censée éclairer la scène. Même
   lueur ambrée centrée sur le mark. Un contrepoint froid a été essayé sur
   .loader-porte-laque puis retiré (coût de boot mesuré sans bénéfice visible
   à 3 % d'opacité, cf. son commentaire) : jamais ajouté ici pour la même
   raison. */
.rideau {
  /* Ancré en HAUT (comme .loader-seuil--monte .loader-porte) : sans ce
     transform-origin, le scaleY par défaut se comprime depuis le CENTRE, pas
     depuis les cintres — le rideau semblerait se rétracter vers son milieu au
     lieu de se ramasser vers le haut. */
  transform-origin: 50% 0%;
  background:
    radial-gradient(56% 60% at 50% 46%, rgba(207, 157, 59, 0.1), transparent 68%),
    radial-gradient(120% 100% at 50% 50%, transparent 52%, rgba(3, 5, 11, 0.4) 100%),
    linear-gradient(175deg, #131b2c 0%, #0b0f1e 52%, #070a14 100%);
  /* PAS de transform ici : le commentaire plus haut sur .rideau (bloc de
     base) prévient déjà contre le cumul CSS+GSAP (rideau à 202%). Défaut
     réel commis puis trouvé ici même : un `translateY(-104%)` posé en CSS
     s'additionnait au `yPercent:-104` de rideau.js — GSAP ne REMPLACE le
     transform existant qu'à partir de son PREMIER appel, il ne le lit pas
     comme base à modifier. Tracé sur une navigation réelle : top à -1872px
     au lieu des -936px attendus, exactement 2x. `visibility: hidden` (bloc
     de base) suffit déjà à interdire tout flash avant que GSAP n'agisse. */
}
/* Grain : même signature de bruit que .loader-porte-laque::after — voir son
   commentaire pour le pourquoi (le .grain global du site vit sous ce calque). */
.rideau::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.05;
  mix-blend-mode: overlay;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Ourlet : le bord qui balaie l'écran — toujours celui du BAS, que le rideau
   tombe pour couvrir ou remonte pour révéler (c'est le même bord physique du
   même pan dans les deux cas). Même liseré chaud que le loader. */
.rideau::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 28px;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent,
    rgba(0, 0, 0, 0.4) 62%,
    rgba(0, 0, 0, 0.6) 90%,
    rgba(207, 157, 59, 0.42) 100%
  );
}
.rideau-mot {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Valeurs en px : la home change volontairement la taille du rem racine.
     Le lockup persistant ne doit donc jamais heriter de cette variation. */
  gap: 18px;
  font-family: var(--display);
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: 1;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  white-space: nowrap;
  color: #faf7f0;
  text-shadow: 0 8px 22px rgba(2, 4, 10, 0.48);
  backface-visibility: hidden;
}
.rideau-mark {
  width: clamp(56px, 4.5vw, 72px);
  height: auto;
  aspect-ratio: 681 / 633;
  flex: none;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  filter: drop-shadow(0 0 16px rgba(63, 169, 224, 0.2));
}
.rideau .rideau-mark .nx-m {
  fill: #faf7f0;
}
.rideau .rideau-mark .nx-e {
  fill: #cf9d3b;
}

/* rideau de fin de voyage (Méthode → Contact) : panneau OPAQUE plein écran qui
   monte du bas au scroll (transform piloté en JS) et porte le contact — le
   contact se lit sur un fond maîtrisé, jamais par-dessus la scène 3D.
   DA claire : panneau SABLE en dur (pas var(--bg), qui a déjà changé de camp
   une fois et rendu le rideau illisible), texte encre par-dessus (cf. bloc
   "Méthode — version light" en fin de fichier), filet bleu profond en tête. */
.p3-flash {
  opacity: 1;
  transform: translate3d(0, 103.5%, 0);
  will-change: transform;
  background:
    radial-gradient(
      120% 58% at 50% 0%,
      rgba(63, 169, 224, 0.13),
      transparent 58%
    ),
    linear-gradient(180deg, #ede6d6 0%, #f2ede3 42%, #faf7f0 100%);
  box-shadow:
    0 -1px 0 rgba(21, 106, 153, 0.32),
    0 -34px 90px -34px rgba(31, 42, 58, 0.2);
}
.parcours-3d.p3-flat .p3-flash {
  display: none;
}

/* header mobile : le lockup complet déborde sur les liens — on garde monogramme + NOXARI */
@media (max-width: 760px) {
  .logo-studio {
    display: none;
  }
  .logo-mark {
    width: 19px;
  }
  header .logo {
    gap: 0.45em;
  }
}

/* fiche latérale des études de cas : même famille de bug que les pastilles —
   panneau clair hérité + texte clair = illisible. Verre de nuit, index fantôme clair. */
.case-panel {
  background: rgba(13, 20, 36, 0.78);
  border-color: rgba(237, 239, 246, 0.12);
}
.case-panel-index {
  -webkit-text-stroke-color: rgba(237, 239, 246, 0.14);
}
.case-meta li {
  border-color: rgba(237, 239, 246, 0.1);
}
.case-meta .lab {
  color: var(--text-muted);
}

/* Hero clarity refresh : proposition de valeur et CTA visibles dès le premier écran.
   Une seule colonne, une seule ligne de marque (l'ancien kicker doublonnait l'eyebrow
   ET le lockup du header : trois lignes de marque en 150px, supprimé). */
.hero-content {
  left: var(--marge);
  right: auto;
  top: 0;
  bottom: 0;
  justify-content: center; /* centrage vertical par flex : GSAP anime y sans conflit de transform */
  width: min(700px, 48vw);
  align-items: flex-start;
  text-align: left;
  padding-inline: 0;
  gap: clamp(1rem, 2.2vh, 1.5rem);
  transform: none;
  color: var(--paper);
}

/* grille lunaire : trame technique très discrète, fondue radialement, qui dérive
   lentement vers le bas (les lignes verticales sont invariantes → seul un glissement
   subtil se perçoit). Donne la profondeur "blueprint" sans aucun coût JS. */
.hero-grille {
  --gpas: clamp(64px, 7.5vw, 104px);
  position: absolute;
  inset: 0;
  background:
    linear-gradient(rgba(237, 239, 246, 0.05) 1px, transparent 1px) 0 0/100%
      var(--gpas),
    linear-gradient(90deg, rgba(237, 239, 246, 0.05) 1px, transparent 1px) 0 0 /
      var(--gpas) 100%;
  -webkit-mask-image: radial-gradient(
    120% 95% at 50% 40%,
    rgba(0, 0, 0, 0.85),
    transparent 80%
  );
  mask-image: radial-gradient(
    120% 95% at 50% 40%,
    rgba(0, 0, 0, 0.85),
    transparent 80%
  );
  animation: heroGrille 30s linear infinite;
}
@keyframes heroGrille {
  to {
    background-position:
      0 var(--gpas),
      0 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-grille {
    animation: none;
  }
}

/* halo doux derrière le logo-masque : ancre la "lune" dans une lueur, sans rivaliser */
.hero-halo {
  position: absolute;
  right: 2vw;
  top: 8vh;
  width: 46vw;
  height: 46vw;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(var(--accent-rgb), 0.09),
    rgba(141, 134, 201, 0.05) 46%,
    transparent 68%
  );
  filter: blur(30px);
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 820;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-soft);
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.5);
}
.hero-eyebrow::before {
  content: "";
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow:
    0 0 0 5px rgba(var(--accent-rgb), 0.16),
    0 0 22px rgba(var(--accent-rgb), 0.5);
}
.hero-title {
  max-width: 16.5ch;
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(3.1rem, 5.4vw, 5.6rem);
  line-height: 0.92;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--paper);
  text-wrap: balance;
  text-shadow: 0 8px 34px rgba(0, 0, 0, 0.62);
}
/* micro-signature : le point final porte l'accent — confiance, pas de décor */
.hero-title .pt {
  color: var(--accent);
}
.hero-subtitle,
.hero.hero-final .hero-subtitle {
  max-width: 48ch;
  color: rgba(237, 239, 246, 0.82);
  font-family: var(--mono);
  font-size: clamp(1rem, 1.18vw, 1.2rem);
  line-height: 1.55;
  text-shadow: 0 4px 22px rgba(0, 0, 0, 0.56);
}
/* services : ligne éditoriale, pas des pastilles boîtées — trois mots, des séparateurs
   fins, l'œil reste sur le titre et les CTA */
.hero-points {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0;
  margin: 0.15rem 0 0.2rem;
  padding: 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.62);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.5);
}
.hero-points li + li::before {
  content: "/";
  margin: 0 0.95em;
  color: rgba(var(--accent-rgb), 0.55);
  font-weight: 400;
}
.hero-content .ctas {
  justify-content: flex-start;
  gap: 0.85rem;
  margin-top: 0.3rem;
}

@media (max-width: 1023px) {
  .hero-content {
    width: min(620px, calc(100vw - var(--marge) * 2));
  }
  .hero-title {
    font-size: clamp(2.8rem, 9vw, 5rem);
  }
}

@media (max-width: 640px) {
  .hero-content {
    top: clamp(7.6rem, 16vh, 9.5rem);
    bottom: auto;
    justify-content: flex-start;
    transform: none;
    gap: 0.8rem;
  }
  .hero-eyebrow {
    font-size: 0.58rem;
    letter-spacing: 0.14em;
  }
  .hero-title {
    max-width: 11.8ch;
    font-size: clamp(2.35rem, 11.6vw, 3.7rem);
  }
  .hero-subtitle,
  .hero.hero-final .hero-subtitle {
    max-width: 34ch;
    font-size: 0.92rem;
    line-height: 1.48;
  }
  .hero-points {
    font-size: 0.6rem;
    letter-spacing: 0.12em;
  }
  .hero-points li + li::before {
    margin: 0 0.7em;
  }
  .hero-content .ctas {
    width: 100%;
    gap: 0.6rem;
  }
  .hero-content .btn {
    flex: 1 1 auto;
    justify-content: center;
    min-width: min(11rem, 100%);
    padding: 0.78rem 0.9rem;
    font-size: 0.62rem;
  }
  .hero-halo {
    right: -24vw;
    top: auto;
    bottom: -8vh;
    width: 95vw;
    height: 95vw;
  }
}

/* filet no-JS/erreur : les enfants du hero-content sont cachés un à un par le loader,
   ils doivent réapparaître avec lui si GSAP tombe */
.secours .hero-content > * {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
}

/* ═══════════════════════════════════════════════════════════
   PAGE WORK — galerie-couloir 3D (canvas fixe + espaceur) ; repli : liste
   ═══════════════════════════════════════════════════════════ */
.work {
  position: relative;
  min-height: 100svh;
  padding-top: 6.4rem;
  /* le sélecteur générique `section` (plus haut dans ce fichier) pose 7rem de
     padding-bottom partout — resté invisible tant que .work-liste finissait
     aussi en noir plein, mais ce padding est rempli par le #050711 de .work
     lui-même : une fois .work-liste passée en fond clair (2026-07-18), ça
     laissait un pavé noir vide entre la liste et le footer, en plus du
     padding-bottom déjà prévu sur .work-liste. Neutralisé ici, le raccord
     clair → sombre se joue entièrement dans le dégradé de .work-liste. */
  padding-bottom: 0;
  background: #050711;
  color: var(--text);
  overflow: clip;
}

/* intro : statique en mode liste ; en mode 3D elle passe en overlay fixe et
   se fond au premier scroll (tween dans la timeline scrubée) */
.work-intro {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(2rem, 6vh, 4rem) var(--marge) 2rem;
  position: relative;
  z-index: 3;
}
.work-3d .work-intro {
  position: fixed;
  top: 50%;
  left: var(--marge);
  right: var(--marge);
  z-index: 3;
  pointer-events: none;
  max-width: none;
  margin: 0;
  padding: 0;
  text-align: center;
  display: grid;
  justify-items: center;
  transform: translateY(-50%);
  will-change: opacity, transform, filter;
}
.work-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
/* ── L'intro attend la police. Sans ca, au chargement de la galerie :
   le titre s'affiche d'abord dans la police de repli, large de 1300px dans une
   fenetre de 1371 (quasi bord a bord), puis saute a 1047px quand Syne arrive —
   253px de reflux visible. Et son opacite faisait 1 → 0 → 1, l'etat final
   etant peint avant que l'animation de reveal soit posee : le titre
   clignotait. La classe est POSEE ET RETIREE EN JS (init-page.js) avec un
   garde-fou de 2,5s : si le JS ne tourne pas du tout, rien n'est cache. ── */
.work-intro {
  transition: opacity 0.5s var(--ease);
}
.work-intro.attente-police {
  opacity: 0;
}
.work-titre {
  margin: 0.6rem 0 0;
  font-family: var(--display);
  font-weight: 900;
  /* le MINIMUM du clamp (2,4rem = 38,4px) debordait a 320px : « Galerie »
     mesurait 319px pour 277px disponibles, mots insecables. */
  font-size: clamp(2.1rem, 5.5vw, 4.6rem);
  line-height: 0.96;
  text-transform: uppercase;
  color: var(--text);
  text-shadow: 0 8px 34px rgba(0, 0, 0, 0.6);
}

/* scène : cachée en mode liste, plein écran fixe en mode 3D */
.work-stage {
  display: none;
}
.work-3d .work-stage {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 1;
  background: #070a14;
}
.work-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  background: #070a14;
}
.work-3d .work-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(
      72% 62% at 50% 42%,
      rgba(201, 222, 240, 0),
      rgba(7, 10, 20, 0.1) 56%,
      rgba(2, 4, 10, 0.46) 100%
    ),
    linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.18),
      rgba(0, 0, 0, 0) 21%,
      rgba(0, 0, 0, 0) 79%,
      rgba(0, 0, 0, 0.2)
    );
  box-shadow: inset 0 0 96px rgba(0, 0, 0, 0.42);
}
.work-espace {
  height: 0;
}

/* HUD : compteur + consigne, purement informatif (aucun clic à capter) */
.work-hud {
  position: absolute;
  left: var(--marge);
  right: var(--marge);
  bottom: clamp(1.6rem, 4vh, 2.6rem);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  pointer-events: none;
  z-index: 2;
}
/* progression par ticks (remplace le compteur "01/04" 2026-07-12 : le HUD garde le
   wayfinding — une salle allumée par œuvre — sans numérotation apparente) */
.work-hud-ticks {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding-bottom: 0.55rem;
}
.work-hud-ticks i {
  width: 26px;
  height: 2px;
  background: rgba(var(--paper-rgb, 237, 239, 246), 0.24);
  transition:
    background 0.45s var(--ease),
    transform 0.45s var(--ease);
}
.work-hud-ticks i.on {
  background: var(--accent);
  transform: scaleY(2.2);
  box-shadow: 0 0 12px rgba(var(--accent-rgb), 0.55);
}
.work-hud-title {
  position: absolute;
  left: 50%;
  bottom: 0.15rem;
  transform: translateX(-50%);
  max-width: min(44vw, 560px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--display);
  font-weight: 880;
  font-size: clamp(1.1rem, 2.1vw, 2rem);
  line-height: 1;
  text-transform: uppercase;
  color: #f5f2ea;
  letter-spacing: 0;
  text-shadow: 0 10px 34px rgba(0, 0, 0, 0.8);
  opacity: 0.9;
}
.work-hud-title.cadre-gauche {
  left: 39%;
}
.work-hud-title.cadre-droite {
  left: 41%;
}
.work-hud-hint {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding-right: 7.5rem; /* réserve le coin du bouton SON */
}

/* panneau de détail au survol d'un tableau : verre de nuit, jamais au-dessus du pointeur */
.work-panneau {
  position: absolute;
  left: var(--marge);
  bottom: clamp(5.4rem, 12vh, 8rem);
  z-index: 2;
  /* 360px donnaient 315px de contenu, or « RELANCEWORK » est un mot
     INSECABLE de 433px : il ne pouvait ni tenir ni passer a la ligne, il
     debordait de 118px et se faisait rogner (« RELANCEWO… »). */
  width: min(400px, 42vw);
  padding: 1.3rem 1.4rem;
  background: rgba(13, 20, 36, 0.82);
  border: 1px solid rgba(237, 239, 246, 0.14);
  border-radius: 8px;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 34px 90px -40px rgba(0, 3, 10, 0.9);
  opacity: 0;
  visibility: hidden;
  transform: translateX(-14px);
  pointer-events: none;
}
.work-panneau-auto {
  bottom: clamp(6rem, 13vh, 8.8rem);
  width: min(400px, 40vw);
  background: rgba(10, 14, 22, 0.76);
  border-color: rgba(245, 232, 205, 0.18);
  box-shadow:
    0 36px 100px -48px rgba(0, 0, 0, 0.95),
    inset 0 1px 0 rgba(255, 245, 225, 0.08);
}
.work-panneau-droite {
  left: auto;
  right: var(--marge);
}
.work-panneau-num {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  color: var(--accent);
}
.work-panneau-titre {
  margin: 0.45rem 0 0;
  font-family: var(--display);
  font-weight: 880;
  /* calibre sur le titre le plus long d'un seul mot (RELANCEWORK) */
  font-size: clamp(1.1rem, 1.5vw, 1.35rem);
  line-height: 1.04;
  text-transform: uppercase;
  /* garde-fou : quel que soit le titre ajoute plus tard, il ne pourra plus
     deborder en silence — il cassera plutot que de se faire rogner */
  overflow-wrap: break-word;
  color: var(--text);
}
.work-panneau-desc {
  margin: 0.7rem 0 0;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--text-muted);
}
.work-panneau-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.85rem;
}
.work-panneau-tags span {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid rgba(237, 239, 246, 0.16);
  border-radius: 999px;
  padding: 0.32rem 0.55rem;
  background: rgba(7, 10, 20, 0.5);
}
.work-panneau-cta {
  display: block;
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid rgba(237, 239, 246, 0.12);
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
.work-transition-veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(
      70% 56% at 50% 45%,
      rgba(9, 6, 4, 0),
      rgba(4, 3, 2, 0.5) 58%,
      rgba(2, 2, 2, 0.86) 100%
    ),
    linear-gradient(180deg, rgba(2, 2, 2, 0.12), rgba(2, 2, 2, 0.88));
  -webkit-backdrop-filter: blur(1px);
  backdrop-filter: blur(1px);
  will-change: opacity, -webkit-mask-image, mask-image;
}
/* fin de l'exposition : titre posé dans la pièce, révélé par la timeline scrubée */
.work-fin {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.1rem;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
}
.work-fin-titre {
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(2.4rem, 5.5vw, 5rem);
  text-transform: uppercase;
  color: var(--text);
  text-shadow: 0 10px 44px rgba(0, 0, 0, 0.75);
}
.work-fin-sous {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.work-fin-diamant {
  width: 11px;
  height: 11px;
  border: 1.5px solid rgba(237, 239, 246, 0.8);
  transform: rotate(45deg);
  margin-top: 0.35rem;
}

/* section 2 — l'index : en-tête montré uniquement en mode 3D (en mode liste, l'intro suffit).
   En mode 3D, l'index passe en pleine largeur avec fond opaque : c'est LUI qui recouvre
   la galerie pendant que la scène s'éteint, puis dégrade vers le footer (section 3). */
.work-index-tete {
  display: none;
}
.work-3d .work-index-tete,
.work-flat .work-index-tete {
  display: block;
  position: relative;
  z-index: 2;
  padding: clamp(4.5rem, 10vh, 7rem)
    max(var(--marge), calc((100% - var(--max)) / 2)) 1.4rem;
  background: linear-gradient(
    180deg,
    rgba(4, 6, 12, 0),
    rgba(4, 6, 12, 0.9) 16%,
    var(--bg-alt) 58%
  );
}
.work-index-eyebrow {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
.work-index-titre {
  margin: 0.55rem 0 0;
  font-family: var(--display);
  font-weight: 900;
  /* mots insecables (inline-blocks de `titres-lignes`) : « projets »
     mesurait 317px pour 277 disponibles a 320px de large */
  font-size: clamp(1.8rem, 6.4vw, 3.4rem);
  line-height: 0.95;
  text-transform: uppercase;
  color: var(--text);
}

/* repli / contenu sémantique : index catalogue clair (fond blanc, lignes
   numérotées) — même famille visuelle que .collection-ligne de l'accueil,
   volontairement réutilisée telle quelle (2026-07-18 : "reprend l'exemple
   [de la liste claire]") : le couloir nocturne débouche sur une page-catalogue
   nette, contraste assumé plutôt que prolonger le noir jusqu'au bout.
   Masquée tant que .work-flat/.work-3d ne sont pas posées (même logique que
   .work-index-tete juste au-dessus) : sans ce display:none, les lignes — remplies dès
   que projets.json répond — restent visibles nues pendant toute la fenêtre asynchrone
   du mode 3D (chargeThree + chargeReflector CDN + l'attente anti-gel du rideau,
   cf. initWork3D) ; le rideau Barba s'ouvre sur son propre minuteur, indépendant de
   cette fenêtre, donc cette liste nue reste bien visible à l'écran (signalé 2026-07-13 :
   "j'atterris sur cette page [liste] avant de tomber sur la galerie"). */
.work-liste {
  max-width: var(--max);
  margin: 0 auto;
  padding: 1rem var(--marge) clamp(4rem, 9vw, 7rem);
  display: none;
  list-style: none;
  position: relative;
  z-index: 2;
  border-bottom: 1px solid var(--line);
}
.work-3d .work-liste,
.work-flat .work-liste {
  display: block;
}
.work-3d .work-liste {
  max-width: none;
  margin: 0;
  padding: 1.6rem max(var(--marge), calc((100% - var(--max)) / 2))
    clamp(5rem, 10vw, 8rem);
  background: var(--paper);
}
/* .work-carte* retiré 2026-07-18 : la liste cartes-sombres est remplacée par
   .collection-ligne (index clair, cf. .work-liste plus haut) — voir mémoire
   projet pour l'historique de cette section. */

@media (max-width: 640px) {
  .work {
    padding-top: 5.4rem;
  }
  .work-panneau {
    width: calc(100vw - var(--marge) * 2);
  }
}

/* ═══════════════════════════════════════════════════════════
   DA "SABLE & BLEU" — site clair premium, chapitres 3D sombres assumés
   (le bloc final gagne la cascade : tout ce qui est en var() bascule ici)
   ═══════════════════════════════════════════════════════════ */
body {
  background:
    radial-gradient(
      70rem 42rem at 15% -10%,
      rgba(143, 190, 223, 0.14),
      transparent 62%
    ),
    radial-gradient(
      60rem 40rem at 88% 6%,
      rgba(233, 226, 211, 0.6),
      transparent 64%
    ),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
}
::selection {
  background: var(--accent);
  color: var(--ink);
}

/* chapitres immersifs : ils restent des pièces sombres DANS le site clair
   (c'est le modèle de la référence : galerie noire, éditorial sable) */
.parcours-3d,
.p3-stage {
  background: #070a14;
}

/* footer : ancre encre — fonctionne après le contact (rideau sombre) comme après les pages claires */
footer {
  background: linear-gradient(180deg, var(--ink), #0b1119);
  color: #e9e5db;
  border-top: 0;
}
footer .label {
  color: rgba(233, 229, 219, 0.55);
}
footer a {
  color: #e9e5db;
}
footer a:hover {
  color: var(--accent);
}
.footer-bas {
  color: rgba(233, 229, 219, 0.6);
}

/* eyebrows / kickers : le bleu doux ne tient pas sur sable en petit corps → bleu profond */
.case-kicker,
.case-sticky-eyebrow,
.case-visuals-kicker,
.case-signature-tag {
  color: var(--accent-2);
}
.hero-eyebrow,
.work-eyebrow,
.work-index-eyebrow {
  color: var(--accent-2);
}
.hero-eyebrow::before {
  background: var(--accent-2);
  box-shadow:
    0 0 0 5px rgba(78, 127, 166, 0.14),
    0 0 18px rgba(78, 127, 166, 0.4);
}

/* boutons : bleu doux + encre (le texte papier ne contrastait plus) */
.btn.plein {
  color: var(--ink);
  border-color: var(--accent);
  background: var(--accent);
  box-shadow: 0 24px 58px -38px rgba(78, 127, 166, 0.55);
}
.btn.plein::before {
  background: var(--accent-2);
}
.btn.plein:hover {
  color: var(--paper);
}
.btn:not(.plein) {
  color: var(--text);
  border-color: rgba(29, 37, 49, 0.32);
  background: rgba(250, 247, 240, 0.5);
}
.btn:not(.plein)::before {
  background: var(--text);
}
.btn:not(.plein):hover {
  color: var(--paper);
}

/* ── HERO clair : logo centré sur sable, contenu centré en bas ── */
.hero-lune {
  display: none;
}
.hero-grille {
  background:
    linear-gradient(rgba(29, 37, 49, 0.055) 1px, transparent 1px) 0 0/100%
      var(--gpas),
    linear-gradient(90deg, rgba(29, 37, 49, 0.055) 1px, transparent 1px) 0 0 /
      var(--gpas) 100%;
}
.hero-halo {
  right: auto;
  top: 27%;
  left: 50%;
  width: 48vw;
  height: 48vw;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    circle,
    rgba(143, 190, 223, 0.22),
    rgba(233, 226, 211, 0.35) 46%,
    transparent 70%
  );
  filter: blur(26px);
}
.hero-beam {
  display: none;
}
.hero-content {
  left: 0;
  right: 0;
  top: auto;
  bottom: clamp(4.5rem, 10vh, 7.5rem);
  justify-content: flex-end;
  width: min(760px, 92vw);
  margin-inline: auto;
  align-items: center;
  text-align: center;
  color: var(--text);
}
.hero-title {
  color: var(--text);
  text-shadow: none;
  max-width: 18ch;
}
.hero-title .pt {
  color: var(--accent-2);
}
.hero-subtitle,
.hero.hero-final .hero-subtitle {
  color: var(--text-muted);
  text-shadow: none;
}
.hero-eyebrow {
  text-shadow: none;
}
.hero-points {
  color: var(--text-muted);
  text-shadow: none;
  justify-content: center;
}
.hero-points li + li::before {
  color: rgba(78, 127, 166, 0.6);
}
.hero-content .ctas {
  justify-content: center;
}
.hero-kicker,
.hero-scroll-indicator span {
  color: var(--text-muted);
}
.hero-scroll-indicator .track {
  background: rgba(29, 37, 49, 0.18);
}
.hero-scroll-indicator .track::after {
  background: var(--accent-2);
}
.hero-accent {
  background: linear-gradient(
    90deg,
    transparent,
    var(--accent-2) 30%,
    var(--accent) 70%,
    transparent
  );
}

/* ── PAGE WORK : la pièce reste noire → ses overlays restent clairs (hors tokens) ; l'index passe sable ── */
.work-3d .work-titre {
  color: #f5f2ea;
  text-shadow: 0 8px 34px rgba(0, 0, 0, 0.6);
}
.work-3d .work-eyebrow {
  color: #a8cbe8;
}
.work-3d .work-hud-num {
  color: rgba(233, 229, 219, 0.6);
}
.work-3d .work-hud-num b {
  color: #8fbedf;
}
.work-3d .work-hud-hint {
  color: rgba(233, 229, 219, 0.55);
}
.work-panneau {
  background: rgba(13, 20, 30, 0.85);
  border-color: rgba(245, 242, 234, 0.14);
}
.work-panneau-titre {
  color: #f5f2ea;
}
.work-panneau-desc {
  color: rgba(233, 229, 219, 0.75);
}
.work-panneau-num,
.work-panneau-cta {
  color: #a8cbe8;
}
.work-panneau-tags span {
  color: rgba(233, 229, 219, 0.7);
  border-color: rgba(245, 242, 234, 0.16);
  background: rgba(7, 10, 20, 0.5);
}
.work-fin-titre {
  color: #f5f2ea;
}
.work-fin-sous {
  color: rgba(233, 229, 219, 0.6);
}
.work-fin-diamant {
  border-color: rgba(245, 242, 234, 0.8);
}
.work-3d .work-index-tete,
.work-flat .work-index-tete {
  overflow: hidden;
  background:
    radial-gradient(
      46rem 24rem at 50% 0%,
      rgba(146, 200, 234, 0.12),
      transparent 62%
    ),
    linear-gradient(
      180deg,
      rgba(2, 2, 2, 0),
      #050711 18%,
      var(--paper) 96%
    );
}
/* le couloir nocturne débouche sur l'index : catalogue clair, contraste assumé
   (2026-07-18). Le fondu final vers --ink (repère fixe calc(100% - 140px), pour
   ne pas dépendre de la hauteur totale de la liste) a d'abord réglé un pavé noir
   vide entre la liste et le footer (padding de section en trop) — mais une fois
   ce bug corrigé, ce bandeau sombre est resté visible en bas de liste et se lit
   comme un second footer plaqué là, pas une transition ("enlève le footer ici,
   pas ouff" — 2026-07-18). Retiré : la liste reste claire jusqu'à son bord, le
   footer (déjà sombre, avec son propre border-top) fait la coupure lui-même. */
.work-3d .work-liste,
.work-flat .work-liste {
  background:
    radial-gradient(
      54rem 34rem at 50% -12%,
      rgba(var(--accent-soft-rgb), 0.07),
      transparent 62%
    ),
    var(--paper);
}

/* ── Hero : contenu révélé À LA FIN du zoom, PAR-DESSUS la photo plein cadre ──
   .hero-final est déjà posé par poseHeroFinal() (progress >= 0.68) ; .hero-fallback::after
   assombrit déjà la photo (dégradé haut/bas + radial) — on bascule juste le texte en clair
   et on muscle le CTA secondaire en verre sombre pour qu'il reste lisible sur la peau claire
   du portrait comme sur les zones sombres. */
.hero.hero-final .hero-eyebrow {
  color: var(--paper);
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.55);
}
.hero.hero-final .hero-eyebrow::before {
  background: var(--accent);
  box-shadow:
    0 0 0 5px rgba(143, 190, 223, 0.22),
    0 0 18px rgba(143, 190, 223, 0.6);
}
.hero.hero-final .hero-title {
  color: var(--paper);
  text-shadow: 0 8px 34px rgba(0, 0, 0, 0.7);
}
.hero.hero-final .hero-subtitle {
  color: rgba(250, 247, 240, 0.86);
  text-shadow: 0 3px 18px rgba(0, 0, 0, 0.6);
}
.hero.hero-final .hero-points {
  color: rgba(250, 247, 240, 0.8);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.55);
}
.hero.hero-final .hero-points li + li::before {
  color: rgba(143, 190, 223, 0.75);
}
.hero.hero-final .hero-content .btn:not(.plein) {
  color: var(--paper);
  border-color: rgba(250, 247, 240, 0.4);
  background: rgba(20, 27, 38, 0.42);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.hero.hero-final .hero-content .btn:not(.plein)::before {
  background: var(--paper);
}
.hero.hero-final .hero-content .btn:not(.plein):hover {
  color: var(--text);
  border-color: var(--paper);
}

/* focus clavier du CTA plein : currentColor (encre) suffit sur fond sable partout ailleurs,
   mais le même bouton reste visible sur la photo assombrie du hero-final où l'encre se fond
   dans le noir — anneau à deux tons (clair+sombre) garanti visible sur N'IMPORTE quel fond */
.btn.plein:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 3px var(--paper),
    0 0 0 5px var(--ink);
}

/* Cinematic video hero + dark Noxari direction */

html {
  scrollbar-color: var(--accent) var(--bg-alt);
}
::-webkit-scrollbar {
  background: var(--bg-alt);
}
::-webkit-scrollbar-track {
  background: var(--bg-alt);
}
::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-color: var(--bg-alt);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--accent-soft);
  border-color: var(--bg-alt);
}
::-webkit-scrollbar-corner {
  background: var(--bg-alt);
}

body {
  background:
    radial-gradient(
      58rem 36rem at 12% -12%,
      rgba(94, 144, 186, 0.2),
      transparent 66%
    ),
    radial-gradient(
      54rem 36rem at 88% 2%,
      rgba(244, 239, 228, 0.08),
      transparent 64%
    ),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 52%, #050711 100%);
  color: var(--text);
}
::selection {
  background: var(--accent);
  color: var(--ink);
}
.grain {
  /* ⚠️ Le fond des pages sombres (work.html, 404) est quasi noir : `multiply`
     y serait invisible (il assombrit), tout comme `overlay` et `soft-light`
     qui laissent la base INCHANGEE sur du gris moyen — or un feTurbulence est
     centre sur le gris moyen. Sur fond sombre c'est `screen` qui marche : il
     eclaircit, et le grain se lit comme des grains de lumiere.
     L'accueil, lui, est clair et garde `multiply` (regle scopee plus bas). */
  opacity: 0.16;
  mix-blend-mode: screen;
}
.cine-overlay {
  opacity: 0.3;
  mix-blend-mode: normal;
}
.cine-vignette {
  background:
    radial-gradient(
      118% 94% at 50% 44%,
      transparent 44%,
      rgba(0, 0, 0, 0.48) 100%
    ),
    linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.34),
      transparent 18%,
      transparent 82%,
      rgba(0, 0, 0, 0.34)
    );
}
.cine-spot {
  background: radial-gradient(
    26rem 17rem at 50% 50%,
    rgba(146, 200, 234, 0.7),
    transparent 58%
  );
}
.cine-scan {
  opacity: 0.045;
}
.progres {
  background: linear-gradient(
    90deg,
    var(--accent),
    var(--accent-soft),
    var(--accent-2)
  );
}

header {
  color: var(--paper);
  mix-blend-mode: normal;
  /* background: linear-gradient(
    180deg,
    rgba(2, 4, 10, 0.78),
    rgba(2, 4, 10, 0.18) 72%,
    transparent
  );
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px); */
}
header nav a {
  /* 0.78 ramenait le contraste REEL de 5,30 a 3,39 — sous le seuil AA de 4,5.
     Le calcul de contraste porte sur la couleur COMPOSEE (couleur x opacite
     sur le fond), pas sur la couleur declaree : une opacite de repos suffit a
     faire echouer une couleur pourtant conforme. L'etat de repos doit rester
     lisible ; c'est le filet `::after` au survol qui distingue. */
  opacity: 0.95;
}
header nav a:hover {
  opacity: 1;
}

.btn {
  color: var(--text);
  border-color: rgba(244, 239, 228, 0.26);
  background: rgba(244, 239, 228, 0.06);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.btn::before {
  background: var(--paper);
}
.btn:hover {
  color: var(--ink);
  border-color: var(--paper);
}
.btn.plein {
  color: var(--ink);
  border-color: var(--accent);
  background: var(--accent);
  box-shadow: 0 24px 58px -34px rgba(146, 200, 234, 0.68);
}
.btn.plein::before {
  background: linear-gradient(135deg, var(--accent), var(--accent-soft));
  transform: scaleX(1);
}
/* le bleu principal (repos) bascule sur la couleur secondaire à l'interaction —
   même logique que "convertir" dans le hero : le secondaire signe l'action. */
.btn.plein:hover {
  color: var(--paper);
  border-color: var(--secondaire);
}
.btn.plein:hover::before {
  background: var(--secondaire);
}

.hero {
  height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(5.5rem, 12vh, 8rem) var(--marge) clamp(4rem, 8vh, 6rem);
  overflow: hidden;
  isolation: isolate;
  background: var(--ink);
}
.hero::before {
  inset: clamp(0.75rem, 1.4vw, 1.35rem);
  z-index: 6;
  border-color: rgba(244, 239, 228, 0.18);
  mix-blend-mode: normal;
}
.hero::after {
  z-index: 6;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(244, 239, 228, 0.34),
    transparent
  );
}
.hero-video-wrap {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--ink) url(/assets/img/hero.jpg) center/cover no-repeat;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}
.hero-video-wrap::before,
.hero-video-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
.hero-video-wrap::before {
  background:
    radial-gradient(
      44rem 28rem at 50% 42%,
      rgba(146, 200, 234, 0.16),
      transparent 62%
    ),
    radial-gradient(
      90% 82% at 50% 50%,
      transparent 38%,
      rgba(2, 4, 10, 0.72) 100%
    );
  mix-blend-mode: screen;
  opacity: 0.8;
}
.hero-video-wrap::after {
  background:
    linear-gradient(
      180deg,
      rgba(2, 4, 10, 0.76),
      rgba(2, 4, 10, 0.36) 36%,
      rgba(2, 4, 10, 0.82)
    ),
    linear-gradient(
      90deg,
      rgba(2, 4, 10, 0.78),
      rgba(2, 4, 10, 0.2) 34%,
      rgba(2, 4, 10, 0.24) 66%,
      rgba(2, 4, 10, 0.78)
    );
}
.hero-video {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center center;
  filter: saturate(0.86) contrast(1.08) brightness(0.72);
  transform: scale(1.01);
  transform-origin: 50% 50%;
  will-change: transform;
}
.hero-mask-svg,
.hero-fallback,
.hero-accent,
.hero-lune,
.hero-socials,
.hero-cinema-meta {
  display: none !important;
}
.hero-light {
  z-index: 1;
  opacity: 0.72;
  mix-blend-mode: screen;
}
.hero-grille {
  --gpas: 96px;
  background:
    linear-gradient(rgba(244, 239, 228, 0.045) 1px, transparent 1px) 0 0/100%
      var(--gpas),
    linear-gradient(90deg, rgba(244, 239, 228, 0.04) 1px, transparent 1px) 0 0 /
      var(--gpas) 100%;
  -webkit-mask-image: radial-gradient(
    110% 85% at 50% 46%,
    rgba(0, 0, 0, 0.82),
    transparent 82%
  );
  mask-image: radial-gradient(
    110% 85% at 50% 46%,
    rgba(0, 0, 0, 0.82),
    transparent 82%
  );
}
.glow-a {
  background: radial-gradient(
    circle,
    rgba(146, 200, 234, 0.16),
    transparent 64%
  );
}
.glow-b {
  background: radial-gradient(
    circle,
    rgba(244, 239, 228, 0.12),
    transparent 70%
  );
}
.hero-halo {
  left: 50%;
  top: 50%;
  right: auto;
  width: min(680px, 62vw);
  height: min(680px, 62vw);
  transform: translate(-50%, -50%);
  background: radial-gradient(
    circle,
    rgba(146, 200, 234, 0.11),
    rgba(244, 239, 228, 0.055) 48%,
    transparent 70%
  );
  filter: blur(28px);
}
.hero-beam {
  display: block;
  left: 50%;
  top: -36%;
  width: 32%;
  height: 172%;
  background: linear-gradient(
    180deg,
    rgba(146, 200, 234, 0.14),
    transparent 70%
  );
  transform: translateX(-50%) rotate(11deg);
}
.hero-content {
  position: relative;
  inset: auto;
  z-index: 3;
  width: min(980px, calc(100vw - var(--marge) * 2));
  margin: 0 auto;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.15rem;
  text-align: center;
  color: var(--paper);
  transform: none;
}
.hero-title,
.hero.hero-final .hero-title {
  max-width: 12.5ch;
  margin: 0;
  font-family: var(--mono);
  font-size: 5.8rem;
  font-weight: 820;
  line-height: 0.95;
  letter-spacing: 0;
  text-transform: none;
  color: var(--paper);
  text-wrap: balance;
  text-shadow: 0 18px 54px rgba(0, 0, 0, 0.82);
}
.hero-subtitle,
.hero.hero-final .hero-subtitle {
  max-width: 48rem;
  margin: 0.15rem auto 0;
  color: rgba(244, 239, 228, 0.84);
  font-family: var(--mono);
  font-size: 1.12rem;
  line-height: 1.58;
  text-shadow: 0 8px 30px rgba(0, 0, 0, 0.7);
}
.hero-content .ctas {
  justify-content: center;
  gap: 0.85rem;
  margin-top: 0.75rem;
}
.hero .btn {
  min-height: 3.2rem;
  padding: 0.9rem 1.45rem;
  font-size: 0.78rem;
  font-weight: 780;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.hero.hero-final .hero-content .btn:not(.plein) {
  color: var(--paper);
  border-color: rgba(244, 239, 228, 0.42);
  background: rgba(2, 4, 10, 0.34);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}
.hero.hero-final .hero-content .btn:not(.plein)::before {
  background: var(--paper);
}
.hero.hero-final .hero-content .btn:not(.plein):hover {
  color: var(--ink);
  border-color: var(--paper);
}
.hero-scroll-indicator {
  z-index: 4;
  bottom: clamp(1.6rem, 4vh, 2.4rem);
}
.hero-scroll-indicator .track {
  background: rgba(244, 239, 228, 0.2);
}
.hero-scroll-indicator .track::after {
  background: var(--accent);
}
.hero-scroll-indicator span {
  color: rgba(244, 239, 228, 0.66);
}

.marquee {
  background: rgba(7, 12, 26, 0.66);
  border-color: rgba(244, 239, 228, 0.11);
}
.marquee span {
  color: rgba(244, 239, 228, 0.92);
}
.marquee span::after {
  color: var(--accent);
}
.section-num,
.hero-eyebrow,
.work-eyebrow,
.work-index-eyebrow,
.case-kicker,
.case-sticky-eyebrow,
.case-visuals-kicker,
.case-signature-tag {
  color: var(--accent-2);
}
.section-num::after {
  background: linear-gradient(
    90deg,
    rgba(146, 200, 234, 0.7),
    rgba(244, 239, 228, 0.18),
    transparent
  );
}

.temoin-quote p,
.temoin-nom,
.p3-titre,
.p3-step h3,
.p3-step p {
  color: var(--text);
}
.temoin-titre {
  color: var(--text-muted);
}
.chrono-tags span {
  color: rgba(244, 239, 228, 0.72);
  border-color: rgba(244, 239, 228, 0.14);
  background: rgba(244, 239, 228, 0.06);
}
.temoin {
  background:
    radial-gradient(
      44rem 28rem at 50% 0,
      rgba(146, 200, 234, 0.1),
      transparent 62%
    ),
    rgba(7, 12, 26, 0.56);
  border-color: rgba(244, 239, 228, 0.12);
}
.temoin-tag,
.temoin-lien {
  color: var(--accent-soft);
}
.temoin-tag::before {
  background: var(--accent);
}
.temoin-mark {
  background: linear-gradient(160deg, var(--accent-soft), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
}
.temoin-avatar {
  color: var(--ink);
  background: linear-gradient(160deg, var(--accent), var(--accent-soft));
}

.p3-stage,
.parcours-3d {
  background:
    radial-gradient(
      70% 70% at 55% 42%,
      rgba(146, 200, 234, 0.12),
      transparent 58%
    ),
    linear-gradient(180deg, #070a14, #050711);
}
footer {
  background: linear-gradient(180deg, #07101d, #02040a);
  color: var(--paper);
}
footer .label,
.footer-bas {
  color: rgba(244, 239, 228, 0.58);
}
footer a {
  color: var(--paper);
}
footer a:hover {
  color: var(--accent-soft);
}

@media (max-width: 1100px) {
  .hero-title,
  .hero.hero-final .hero-title {
    font-size: 4.6rem;
  }
  .hero-subtitle,
  .hero.hero-final .hero-subtitle {
    font-size: 1.04rem;
    max-width: 42rem;
  }
}

@media (max-width: 700px) {
  header {
    padding: 0.9rem var(--marge);
    background: linear-gradient(
      180deg,
      rgba(2, 4, 10, 0.86),
      rgba(2, 4, 10, 0.46)
    );
  }
  header nav {
    gap: 0.8rem;
    font-size: 0.72rem;
  }
  .hero {
    padding-top: 5.8rem;
    padding-bottom: 4.8rem;
  }
  .hero-video {
    object-position: 50% 50%;
  }
  .hero-content {
    width: min(100%, calc(100vw - var(--marge) * 2));
    gap: 0.95rem;
  }
  .hero-title,
  .hero.hero-final .hero-title {
    font-size: 3.2rem;
    max-width: 11.5ch;
  }
  .hero-subtitle,
  .hero.hero-final .hero-subtitle {
    font-size: 0.98rem;
    line-height: 1.5;
    max-width: 30rem;
  }
  .hero-content .ctas {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.65rem;
  }
  .hero .btn {
    width: 100%;
    justify-content: center;
    min-height: 3rem;
    padding: 0.85rem 1rem;
    white-space: normal;
  }
  .hero-scroll-indicator {
    display: none;
  }
}

@media (max-width: 420px) {
  .hero-title,
  .hero.hero-final .hero-title {
    font-size: 2.65rem;
  }
  .hero-subtitle,
  .hero.hero-final .hero-subtitle {
    font-size: 0.92rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-video,
  .hero-video-wrap {
    transform: none !important;
  }
  .hero-grille {
    animation: none;
  }
}

/* Méthode : espacement et lisibilité du bloc gauche */
.parcours-3d:not(.p3-flat) .p3-ui {
  padding-top: clamp(3.1rem, 7vh, 5rem);
  padding-bottom: clamp(2.8rem, 6vh, 4.4rem);
}
.parcours-3d:not(.p3-flat) .p3-head {
  max-width: min(45vw, 620px);
}
.parcours-3d:not(.p3-flat) .p3-titre {
  /* "Geist" était une voix isolée, sans lien avec le reste du site — le seul endroit
     à parler une police différente de tous les autres titres de section (DotGothic16,
     cf. "Galerie Musée" sur work.html, mêmes eyebrows "· ma méthode" à côté). Restaure
     var(--display), cohérent avec le registre technique du contenu (Brief/Audit/SEO). */
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 3.4vw, 3rem);
  line-height: 1.08;
  margin: 0.85rem 0 0.9rem;
  letter-spacing: 0;
}
.parcours-3d:not(.p3-flat) .p3-sub {
  font-family: var(--mono);
  font-size: clamp(1rem, 1.25vw, 1.22rem);
  line-height: 1.46;
  max-width: 34ch;
  margin: 0;
}
.parcours-3d:not(.p3-flat) .p3-steps {
  bottom: clamp(4.8rem, 10vh, 7rem);
  width: min(42vw, 540px);
  min-height: clamp(250px, 32vh, 350px);
}
.parcours-3d:not(.p3-flat) .chrono-cat {
  margin: 0.7rem 0 1.25rem;
  line-height: 1.2;
}
.parcours-3d:not(.p3-flat) .p3-step h3 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.5rem, 2.1vw, 2.05rem);
  line-height: 1.16;
  letter-spacing: 0;
  max-width: 22ch;
  margin: 0 0 1.05rem;
}
.parcours-3d:not(.p3-flat) .p3-step p {
  font-family: var(--mono);
  max-width: 40ch;
}

/* Apparition des éléments pilotée par Lenis */
.lenis-reveal {
  transition:
    opacity 0.95s var(--ease) var(--lr-delay, 0ms),
    filter 0.95s var(--ease) var(--lr-delay, 0ms),
    transform 1.05s var(--ease) var(--lr-delay, 0ms);
  will-change: opacity, filter, transform;
}
.lenis-reveal:not(.is-inview) {
  opacity: 0;
  filter: blur(14px);
  transform: translate3d(0, 34px, 0) scale(0.985);
}
.lenis-reveal.is-inview {
  opacity: 1;
  filter: blur(0);
}
/* Un parcours clavier ne doit jamais atterrir dans un bloc encore masqué par
   son reveal. Le focus rend immédiatement visible le groupe concerné. */
main[data-barba-namespace="home"] .lenis-reveal:focus-within {
  opacity: 1;
  filter: none;
  transform: none;
  transition: none;
}
.lenis-letters .lr-word {
  display: inline-block;
  white-space: nowrap;
}
.lenis-letters .lr-char {
  display: inline-block;
  opacity: 0;
  filter: blur(8px);
  transform: translate3d(0, 1.05em, 0) rotateX(18deg);
  transform-origin: 50% 100%;
  transition:
    opacity 0.72s var(--ease)
      calc(var(--lr-delay, 0ms) + (var(--lr-char-i, 0) * 18ms)),
    filter 0.72s var(--ease)
      calc(var(--lr-delay, 0ms) + (var(--lr-char-i, 0) * 18ms)),
    transform 0.78s var(--ease)
      calc(var(--lr-delay, 0ms) + (var(--lr-char-i, 0) * 18ms));
  will-change: opacity, filter, transform;
}
.lenis-reveal.is-inview .lr-char {
  opacity: 1;
  filter: blur(0);
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .lenis-reveal,
  .lenis-reveal:not(.is-inview),
  .lenis-letters .lr-char {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }
}

/* ==========================================================================
   DA claire — home page (remplace le bloc "Cinematic video hero + dark
   Noxari direction" ci-dessus pour tout ce qui n'est pas volontairement
   sombre : work-3d / parcours-3d / scenes-viewport / footer
   restent protégés en dur, cf. commentaires plus haut dans le fichier).
   Reprend telle quelle la palette "Sable & Bleu" déjà éprouvée et
   contrast-vérifiée (bloc dormant ~3116), plutôt que d'en inventer une.

   Couleur principale/secondaire (demande : "bleu ciel en principale + une
   autre couleur en secondaire, comme les sites awwards") : le bleu ciel est
   repris plus vif (--accent/--accent-2, --accent-2 étant la nuance profonde
   utilisée partout où c'est du TEXTE, vérifiée 5.06:1 sur sable) — il
   cascade déjà à travers tout le site via les alias --ambre/--brass-2 posés
   plus bas. Une seconde couleur chaude (--secondaire, terracotta, 5.04:1 sur
   sable) est ajoutée et posée par touches ciblées plutôt que diluée partout
   (cf. .works-titre em, dernier mot en italique du hero) — c'est ce dosage
   "une couleur dominante + un accent rare" qui lit comme un site primé,
   pas une troisième couleur appliquée en boucle partout où l'accent existait.
   ========================================================================== */
/* une nappe "chaleur" avait été ajoutée ici en fond ambiant sur tout le site
   (2026-07-18), retirée le lendemain ("trop de couleur présente sur
   l'ensemble du site") : posée sur TOUTES les pages en plus du bleu déjà
   présent + du terracotta + de l'or du loader, elle faisait déborder le
   dosage "une couleur dominante + un accent rare" que ce bloc lui-même
   documentait plus haut. Le brun-miel reste contenu à la cabine du hero
   (couleurs propres à sa scène 3D, cf. sections/hero.js), pas de token
   partagé ici. */
body {
  background:
    radial-gradient(
      70rem 42rem at 15% -10%,
      rgba(143, 190, 223, 0.14),
      transparent 62%
    ),
    radial-gradient(
      60rem 40rem at 88% 6%,
      rgba(233, 226, 211, 0.6),
      transparent 64%
    ),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
}

/* ==========================================================================
   Hero scroll-vidéo — porté depuis hero-scroll-demo.css (sandbox, jamais
   modifiée) en remplaçant chaque couleur codée en dur par les tokens
   ci-dessus. La barre de nav et la vidéo restent volontairement sombres
   (elles flottent sur une vidéo, pas sur le fond de page) ; le panneau
   verre et le texte de page basculent clairs.
   ========================================================================== */
/* display:grid;place-items:center;padding:… (règle de base ~L3365) datait de
   l'ancien hero à contenu centré unique — avec ce nouveau hero (stage sticky
   + panneau, tous censés remplir 100% de la largeur), le grid gardait ses
   enfants auto-dimensionnés à leur contenu au lieu de les étirer, ce qui les
   faisait déborder (constaté : .hero-sticky-stage mesurait 1250px de large
   sur un viewport de 390px). display:block + padding:0 neutralisent ça. */
/* Dernier mot sur le fond de .hero : un bloc plus ancien (L3365, mécanisme
   de reveal-logo désormais mort) redéclare background:var(--ink) après le
   bloc clair L1839 et gagne le cascade — sans ce override, la zone autour
   du cadre reste sombre malgré la DA claire. */
.hero {
  height: 350vh;
  overflow: visible;
  display: block;
  padding: 0;
  background: var(--bg);
}

/* Stage sticky partagé : .hero fait 350vh (scroll-scrub), mais la page continue
   après (services, works, etc.) — si vidéo et titre étaient CHACUN sticky/fixed
   indépendamment, soit ils s'empilent en flux normal (deux sticky = deux blocs
   successifs, ne se superposent pas), soit fixed reste épinglé par-dessus TOUT
   le reste de la page une fois .hero dépassé. Un seul stage sticky (occupe
   100vh de flux, se libère naturellement à la fin de .hero) + ses deux enfants
   en absolute (superposés, transforms indépendants l'un de l'autre). */
/* overflow:hidden : la coque géante en fin de zoom (cf. bouclerScrub, main.js)
   doit sortir de champ comme une caméra qui avance, pas déborder en scrollbars. */
.hero-sticky-stage {
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  z-index: 0;
  overflow: hidden;
  /* DA "musée contemporain" (2026-07-19) : bleu-noir profond, même famille
     que le couloir 3D de work.html — sert de REPLI complet (mobile/reduced/
     sans WebGL, où la salle 3D n'existe pas) ET de fond de couture derrière
     le canvas (même nuit que son clear-color). Le sable reprend aux sections
     suivantes : le hero est le premier chapitre sombre du site, en immersion
     avec la galerie, pas un troisième univers chromatique isolé (la cabine
     brun-doré du 2026-07-18 est retirée). */
  background: radial-gradient(
    58% 50% at 50% 45%,
    #274a5e 0%,
    #16232e 40%,
    #0d1420 70%,
    #05070c 100%
  );
}

/* Salle 3D (initHeroSalle) : premier enfant du stage, sous .hero-scene et
   .hero-cine-fond par ordre DOM (tous à z-index 0). L'opacité est pilotée en
   JS (fondu de sortie quand le voile cinéma prend le relais au scroll) ; le
   clear-color du renderer est le même bleu-noir que le fond du stage, la
   disparition est donc invisible en couture. */
.hero-salle {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.hero-salle canvas {
  display: block;
  width: 100%;
  height: 100%;
}
/* vignette photographique : assombrit doucement les bords du cadre — la
   signature "objectif" du rendu cinématique, en CSS pour zéro coût GPU 3D.
   Dans .hero-salle : elle suit la même opacité que la pièce, donc disparaît
   avec elle au scroll et n'assombrit jamais l'acte vidéo. */
.hero-salle::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* vignette appuyée : les coins plongent dans le bleu-noir de l'immersion */
  background: radial-gradient(
    112% 88% at 50% 44%,
    transparent 48%,
    rgba(4, 6, 10, 0.46) 100%
  );
}
/* la salle allumée remplace les artifices DOM du fond plat : l'ellipse d'ombre
   écran de .hero-scene::after ignorait la perspective du sol 3D ("cette ombre
   est moche" — 2026-07-18, remplacée par une ombre de contact dans la scène),
   et le halo de socle de ::before double la nappe cuite au mur — atténué. */
.hero-scene::after,
.hero-scene::before {
  transition: opacity 0.8s var(--ease);
}
.hero-salle-on .hero-scene::after {
  opacity: 0;
}
.hero-salle-on .hero-scene::before {
  opacity: 0.45;
}
/* mise sous tension : bref flash de rétroéclairage de l'écran quand les
   lumières de la salle montent (classe posée par initHeroSalle une seule fois,
   jamais en reduced-motion — la salle n'y est pas montée). filter uniquement :
   le transform de .hero-mockup-box appartient au scrub GSAP, on n'y touche pas. */
@keyframes heroBacklight {
  0% {
    filter: brightness(0.82);
  }
  38% {
    filter: brightness(1.3);
  }
  100% {
    filter: brightness(1);
  }
}
.hero-salle-flash .hero-mockup-screen {
  animation: heroBacklight 0.55s ease-out 1;
}

/* Salle de cinéma : calque nuit DERRIÈRE le mockup, opacité pilotée en JS
   (bouclerScrub) — la pièce s'éteint pendant le zoom vers l'écran ("entrer
   dans l'expérience"), reste sombre pendant toute la lecture scrubée, se
   rallume en remontant. Dans le STAGE et pas dans .scroll-video-wrapper :
   le wrapper bouge en parallaxe souris (±30px), un fond embarqué laisserait
   voir ses bords. Le fond crème passe du wrapper au stage pour la même raison. */
.hero-cine-fond {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  background:
    radial-gradient(
      52% 42% at 50% 46%,
      rgba(63, 169, 224, 0.1),
      transparent 62%
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 40%,
      rgba(2, 4, 10, 0.5) 100%
    ),
    linear-gradient(180deg, #0b1322 0%, #050a14 100%);
}

/* ── Scène d'exposition au repos : le laptop est une œuvre dans la première
   salle du musée (écho direct de la Galerie Musée de work.html). Marque en
   flancs glissée SOUS la coque (profondeur), cartel d'œuvre, halo de socle.
   Tout le calque vit sous .hero-cine-fond : l'extinction de la salle au scroll
   le recouvre naturellement, aucun pilotage JS supplémentaire. ── */
.hero-scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.hero-scene::before {
  /* lumière de socle derrière l'œuvre, + un cône descendant depuis "le plafond" : la
     pièce vide au-dessus du laptop manquait de justification visuelle — un aplomb de
     lumière la transforme en salle éclairée plutôt qu'en vide (2026-07-12). */
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: min(72vw, 1180px);
  height: 100%;
  transform: translateX(-50%);
  background:
    radial-gradient(
      46% 30% at 50% 50%,
      rgba(var(--accent-rgb), 0.22),
      rgba(var(--secondaire-rgb), 0.06) 55%,
      transparent 72%
    ),
    linear-gradient(180deg, rgba(var(--ink-rgb), 0.05), transparent 34%);
  filter: blur(6px);
}
.hero-scene::after {
  /* ombre portée au sol : l'œuvre est POSÉE, pas flottante dans le vide */
  content: "";
  position: absolute;
  left: 50%;
  top: calc(50% + min(19.5vw, 320px));
  width: min(50vw, 820px);
  height: 44px;
  transform: translateX(-50%);
  background: radial-gradient(
    50% 50% at 50% 50%,
    rgba(var(--ink-rgb), 0.22),
    transparent 70%
  );
  filter: blur(6px);
}
.hero-scene-kicker {
  position: absolute;
  /* dégagé sous le header (2026-07-12) : ce top supposait encore le header masqué au
     repos sur desktop (cf. commentaire plus bas, devenu faux depuis l'ajout du CTA
     "Réserver un appel" dans le header — resté volontairement visible en permanence
     désormais) — l'ancienne valeur (clamp 1.6rem→2.6rem, ~25-42px) chevauchait le nav
     (~79px de haut), texte illisible superposé au repos et au retour sur le hero. */
  top: clamp(5.5rem, 10vh, 6.5rem);
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.64);
  white-space: nowrap;
}
/* phrase-manifeste scindée autour de l'œuvre (composition "hublot", 2026-07-18) :
   « Des sites comme des œuvres » haut-gauche, « exposés avec soin » bas-droite —
   une seule phrase qui enveloppe l'appareil, les extrémités intérieures passent
   SOUS la coque (profondeur, comme avant avec les flancs de marque). La marque,
   elle, flotte DANS l'écran (.hero-scroll-cue-title). */
.hero-scene-mot {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.5rem, 4.6vw, 4.5rem);
  line-height: 1.04;
  color: var(--paper);
  letter-spacing: -0.01em;
  /* halo nuit : porte le crème là où la phrase croise la nappe de lumière
     (zones claires du mur) — invisible sur le bleu-noir profond */
  text-shadow:
    0 2px 18px rgba(4, 6, 10, 0.6),
    0 0 44px rgba(4, 6, 10, 0.38);
}
.hero-scene-mot em {
  font-style: italic;
  /* le doré reste réservé au sceau du loader (DA 2026-07-19, "un doré très
     rare comme sceau de collection") — ici, l'accent glacé de l'immersion */
  color: var(--accent-soft);
}
.hero-scene-mot--g {
  left: clamp(1.6rem, 4vw, 4.5rem);
  top: clamp(6.5rem, 15vh, 9.5rem);
}
.hero-scene-mot--d {
  right: clamp(1.6rem, 4vw, 4.5rem);
  bottom: clamp(7.5rem, 18vh, 11rem);
  text-align: right;
}
/* cartel en texte nu (DA cabine : la référence pose son bloc copie directement
   sur la paroi, sans carte) — la carte sable vitrée n'a plus de sens sur brun */
.hero-cartel {
  position: absolute;
  left: clamp(1.6rem, 4vw, 4.5rem);
  bottom: clamp(1.8rem, 5vh, 3.4rem);
  max-width: 250px;
}
.hero-cartel-num {
  display: block;
  font-family: var(--mono);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-soft);
  margin-bottom: 0.45rem;
}
.hero-cartel b {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-style: italic;
  font-size: 1.15rem;
  line-height: 1.1;
  color: var(--paper);
}
.hero-cartel-meta {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  color: rgba(var(--paper-rgb), 0.55);
}
.hero-cartel-desc {
  display: block;
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid rgba(var(--paper-rgb), 0.2);
  font-size: 0.78rem;
  line-height: 1.5;
  color: rgba(var(--paper-rgb), 0.66);
}
/* invitation à défiler, bas-droite (miroir du cartel) : filet + libellé —
   reprend le rôle que tenait l'ancien cue dans l'écran */
.hero-invite {
  position: absolute;
  right: clamp(1.6rem, 4vw, 4.5rem);
  /* au-dessus du bouton Son (fixed, bas-droite) — collision constatée sinon */
  bottom: clamp(4.6rem, 9vh, 6.2rem);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
  text-align: right;
}
.hero-invite-ligne {
  width: clamp(120px, 14vw, 220px);
  height: 1px;
  background: rgba(var(--paper-rgb), 0.28);
}
.hero-invite-txt {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--paper);
}
.hero-invite-fin {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.52);
}
/* CTA d'entrée, bas-centre (pastille pleine — le seul bouton de la scène) */
.hero-entrer {
  position: absolute;
  left: 50%;
  bottom: clamp(1.7rem, 4.6vh, 3.2rem);
  transform: translateX(-50%);
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.85rem 1.5rem;
  border: 1px solid rgba(var(--paper-rgb), 0.2);
  border-radius: 999px;
  /* pilule pleine claire sur la cabine sombre — le pop de la référence */
  background: var(--paper);
  box-shadow: 0 24px 52px -26px rgba(0, 0, 0, 0.85);
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
  transition:
    background 0.3s var(--ease),
    border-color 0.3s var(--ease),
    box-shadow 0.3s var(--ease);
}
.hero-entrer:hover {
  background: #fff;
  border-color: rgba(var(--accent-soft-rgb), 0.6);
  box-shadow: 0 28px 60px -26px rgba(0, 0, 0, 0.95);
}
.hero-entrer:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 3px;
}
.hero-entrer-ic {
  display: inline-block;
  transition: transform 0.35s var(--ease);
}
.hero-entrer:hover .hero-entrer-ic {
  transform: translateX(4px);
}
/* entrée à la fin du loader (body.is-loaded) — 2026-07-20, troisième passe
   ("tu peux mieux faire ! utilise lenis js pour ça") : pilotée en GSAP
   (entreeHero, sections/hero.js), plus en transition CSS déclarative. Deux
   raisons concrètes, pas juste "GSAP c'est mieux" : (1) gsap.ticker pilote
   lenis.raf à chaque frame (cf. lib/lenis.js) — tout le reste du site (scrub
   du hero, ScrollTrigger, Lenis) tourne déjà sur cette même horloge unique ;
   des transitions CSS tournent sur l'horloge native du navigateur, une
   seconde horloge séparée qui peut légèrement dériver sous charge (constaté :
   décrochages visibles pendant cette même fenêtre au tour précédent). (2) une
   transition CSS démarre au moment où la CLASSE est posée (body.is-loaded,
   qui arrive PENDANT le balayage de sortie du loader, ~0.79s avant qu'il ne
   soit vraiment retiré) — il fallait deviner et retarder chaque delay pour
   compenser. GSAP, lui, se déclenche quand le JS s'exécute réellement (après
   la résolution de la promesse du loader, donc après sa disparition
   complète) : plus de calcul de compensation, l'entrée démarre exactement
   quand l'écran est libre. Ces règles ne posent donc plus que l'état de
   REPOS (transform-origin, décalage initial) — cf. entreeHero() pour
   l'animation, opacity/transform restent les seules propriétés touchées
   (pas de filter, cf. raison au tour précédent). */
.hero-scene-kicker,
.hero-scene-mot,
.hero-cartel,
.hero-invite,
.hero-entrer {
  opacity: 0;
  transform-origin: 50% 100%;
}
.hero-scene-kicker {
  transform: translateX(-50%) scale(0.96);
}
.hero-scene-mot--g {
  transform: translateY(20px) translateX(-26px) scale(0.96);
}
.hero-scene-mot--d {
  transform: translateY(20px) translateX(26px) scale(0.96);
}
.hero-cartel,
.hero-invite {
  transform: translateY(18px) scale(0.97);
}
.hero-entrer {
  transform: translateX(-50%) translateY(18px) scale(0.96);
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.secours .hero-scene-kicker,
.secours .hero-scene-mot,
.secours .hero-cartel,
.secours .hero-invite {
  opacity: 1 !important;
  transform: none !important;
}
.secours .hero-entrer {
  opacity: 1 !important;
  transform: translateX(-50%) !important;
}

/* flottement lent de l'œuvre — wrapper dédié (GSAP écrit les transforms du
   wrapper parallaxe et du mockup zoomé, une animation CSS y serait écrasée).
   Suspendu dès que le scrub commence (.hero-scrub, posé dans majCine).
   Entrée (2026-07-20, troisième passe — "utilise lenis js pour ça") : plus de
   transition CSS ici du tout, GSAP possède désormais entièrement opacity ET
   transform (cf. entreeHero(), sections/hero.js) — laisser une transition CSS
   sur une propriété que GSAP anime aussi ferait cumuler deux lissages (chaque
   valeur posée frame par frame par GSAP se ferait re-lisser par la transition
   CSS par-dessus), un classique de latence perçue. entreeHero() met
   heroFlotte (le flottement idle ci-dessous) en pause le temps de l'entrée
   PUIS la relâche, pour que les deux ne se disputent jamais translateY en
   même temps. Repli mobile/reduced-motion : opacity posée explicitement en
   JS (cf. le bloc `state.reduit || esMobile` plus haut, AVANT le retour qui
   empêche entreeHero() de jamais s'exécuter sur ces replis). */
.hero-mockup-float {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  animation: heroFlotte 7s ease-in-out infinite;
  will-change: transform;
}
@keyframes heroFlotte {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-9px);
  }
}
.hero.hero-scrub .hero-mockup-float {
  animation-play-state: paused;
}
@media (prefers-reduced-motion: reduce) {
  .hero-mockup-float {
    animation: none;
  }
}
/* repli sans JS/GSAP (cf. .secours dans loader.js) : body.is-loaded n'est jamais
   posé dans ce cas précis, entreeHero() ne peut évidemment pas tourner non
   plus — sans ce filet, le laptop resterait invisible pour toujours (même
   filet que .hero-scene-kicker etc juste au-dessus). */
.secours .hero-mockup-float {
  opacity: 1 !important;
}

/* écrans étroits : plus de place pour les flancs ni le cartel — on garde le
   kicker seul (la scène redevient sobre, le mockup mobile est déjà petit) */
@media (max-width: 1099px) {
  .hero-scene-mot,
  .hero-cartel,
  .hero-invite,
  /* le hero mobile est statique (état final déjà affiché, nom révélé) : le CTA
     d'entrée dans la traversée n'y a pas de fonction et chevauchait le nom */
  .hero-entrer {
    display: none;
  }
}
/* mobile : le header est visible au repos (le desktop aussi, désormais — cf. le
   commentaire sur .hero-scene-kicker plus haut) — sur petit écran il n'y a pas la
   place de descendre le kicker sous le nav sans déborder, donc on le masque
   entièrement plutôt que de le pousser plus bas comme sur desktop.
   La scène mobile = laptop + halo + ombre, l'écran (vidéo visible) porte la vie. */
@media (max-width: 820px) {
  .hero-scene-kicker {
    display: none;
  }
}

.scroll-video-wrapper {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  transform: scale(1);
  will-change: transform;
}

/* Mockup.png (coque + écran RGBA tous deux transparents, mesuré au pixel :
   écran ≈ 10.9%→89.1% en x, 6.6%→87.1% en y) : posé centré et modéré au repos,
   .hero-mockup-box est ensuite zoomé par le scroll avec transform-origin calé
   sur le centre RÉEL de l'écran (50% 46.85%, pas 50% 50%) — la caméra avance
   vers l'écran précisément, pas vers le centre visuel de tout l'ordinateur. */
.hero-mockup-box {
  position: relative;
  width: min(64vw, 1040px);
  aspect-ratio: 1059/636;
  will-change: transform;
  transform-origin: 50% 46.85%;
}
.hero-mockup-screen {
  position: absolute;
  left: 10.9%;
  top: 6.6%;
  width: 78.2%;
  height: 80.5%;
  overflow: hidden;
  background: var(--ink);
  border-radius: 2px;
  isolation: isolate;
}
/* scrim aligné sur l'écran lui-même (pas le viewport entier) : reste correct
   quelle que soit la taille du mockup, avant/pendant/après le zoom. */
.hero-mockup-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(
      180deg,
      rgba(2, 4, 10, 0.28),
      rgba(2, 4, 10, 0.1) 38%,
      rgba(2, 4, 10, 0.6)
    ),
    radial-gradient(80% 68% at 50% 42%, transparent 38%, rgba(2, 4, 10, 0.3));
}
#hero-scroll-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
}
.hero-mockup-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}
.video-loading {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  background: var(--ink);
  color: rgba(var(--paper-rgb), 0.82);
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition:
    opacity 0.5s ease,
    visibility 0.5s ease;
}
.video-loading.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
#hero-loading-progress {
  font-variant-numeric: tabular-nums;
}
.hero-scroll-cue {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: min(72%, 520px);
  padding: 0 1rem;
  text-align: center;
  pointer-events: none;
  transform: translate(-50%, -50%);
  color: var(--paper);
  text-shadow: 0 10px 34px rgba(2, 4, 10, 0.82);
  transition:
    opacity 0.35s ease,
    transform 0.35s ease,
    visibility 0.35s ease;
}
.hero-scroll-cue-kicker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.9rem;
  font-family: var(--mono);
  font-size: clamp(0.58rem, 0.92vw, 0.78rem);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
.hero-scroll-cue-kicker::before,
.hero-scroll-cue-kicker::after {
  content: "";
  width: clamp(1.6rem, 4vw, 3.6rem);
  height: 1px;
  margin: 0 0.8rem;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(var(--accent-rgb), 0.72)
  );
}
.hero-scroll-cue-kicker::after {
  background: linear-gradient(
    90deg,
    rgba(var(--accent-rgb), 0.72),
    transparent
  );
}
.hero-scroll-cue-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.45rem, 3.6vw, 3.7rem);
  line-height: 0.98;
  letter-spacing: 0;
  color: rgba(var(--paper-rgb), 0.94);
}
.hero-scroll-cue-line {
  position: relative;
  width: 1px;
  height: clamp(2.2rem, 6vh, 4.8rem);
  margin-top: clamp(1rem, 2.8vh, 2rem);
  overflow: hidden;
  background: rgba(var(--paper-rgb), 0.16);
}
.hero-scroll-cue-line::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 45%;
  background: linear-gradient(180deg, transparent, var(--accent), transparent);
  animation: heroCueLine 1.7s cubic-bezier(0.76, 0, 0.24, 1) infinite;
}
.hero-scroll-cue.is-hidden {
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -58%);
}
@keyframes heroCueLine {
  0% {
    transform: translateY(-120%);
  }
  70%,
  100% {
    transform: translateY(260%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-scroll-cue-line::after {
    animation: none;
  }
}

/* Navigation plate pleine largeur, SANS fond — logo+nom à gauche, liens texte
   simples à droite (repris d'une référence utilisateur). Sans repli de fond,
   le texte doit changer de couleur selon ce qu'il y a DERRIÈRE lui : encre
   sombre sur le reste du site (clair), clair sur la vidéo du hero une fois
   plein écran (sombre) — bouclerScrub (main.js) pose .sur-video sur <header>
   exactement pendant cette fenêtre (vidéo visible ET hero encore à l'écran),
   jamais ailleurs. Contraste vérifié dans les deux états. */
.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 27px;
  padding: 19px clamp(22px, 3vw, 46px);
}
.nav-brand {
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  align-items: center;
  gap: 10.8px;
  background: none;
  border: none;
  /* cible tactile ≥44px×44px : padding:0 laissait le bouton se réduire à son
     contenu (19×19px sur mobile, où .nav-brand-txt est caché et il ne reste
     que l'icône 20px — audit mobile headless). Padding horizontal ET vertical
     : la marge négative compense l'impact sur le flex du header (justify:
     space-between), la cible tactile déborde sans pousser le lockup. */
  padding: 14.4px;
  margin: -14.4px;
  cursor: pointer;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 15.5px;
  letter-spacing: 0.03em;
  color: var(--ink);
  transition: color 0.3s var(--ease);
}
.nav-brand .logo-mark {
  flex: none;
}
.nav-brand .nx-m {
  fill: var(--ink);
  transition: fill 0.3s var(--ease);
}
.nav-brand-txt {
  white-space: nowrap;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(25px, 2.6vw, 43px);
}
.nav-link {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  background: none;
  border: none;
  /* cible tactile ≥44px : padding agrandi, marge négative égale et opposée pour que
     .nav-links (flex + gap) ne bouge pas — seule la zone de clic invisible grandit.
     Réel sur tablette (768-1024px) : le menu hamburger ne se déclenche qu'à 767px,
     ces liens texte du nav desktop restent donc la cible tactile jusque-là. */
  padding: 14px 0;
  margin: -8.6px 0;
  cursor: pointer;
  font-family: var(--mono);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-shadow: 0 1px 12px rgba(var(--paper-rgb), 0);
  transition:
    color 0.3s var(--ease),
    text-shadow 0.3s var(--ease);
}
.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition:
    transform 0.35s var(--ease),
    background 0.3s var(--ease);
}
.nav-link:hover,
.nav-link:focus-visible {
  color: var(--ink);
}
.nav-link:hover::after,
.nav-link:focus-visible::after,
.nav-link[aria-current="page"]::after {
  transform: scaleX(1);
}
.nav-link[aria-current="page"] {
  color: var(--ink);
}

/* CTA du header : seul élément à fond plein de la nav — se détache
   volontairement des liens texte pour porter l'action prioritaire du site.
   ⚠️ Depuis le passage du bleu à l'encre (2026-07-24, passe de sobriété
   chromatique), ce bouton a BESOIN d'une variante header.sur-video : l'ancien
   bleu se détachait de n'importe quel fond, l'encre non — sur le panneau
   sombre de #parcours le contour tombait à ~1,1:1 (mesuré). La variante plus
   bas inverse en crème sur encre. Si la couleur de ce bouton rechange un
   jour, revérifier les DEUX contextes (sable et #parcours). */
.nav-cta {
  display: inline-flex;
  align-items: center;
  /* cible tactile ≥44px : inline-flex + align-items:center recentre le contenu tout
     seul, min-height suffit (pas besoin de padding/marge compensés comme .nav-link) */
  min-height: 44px;
  gap: 0.5rem;
  padding: 9.9px 20.7px;
  border-radius: 999px;
  /* Encre plutôt que bleu saturé : ce bouton est en position fixe, donc
     présent sur CHAQUE écran du site — c'était de loin l'élément le plus
     coloré, et il tirait tout le reste vers le bruit. En encre il reste le
     point de contraste le plus fort de la page (donc toujours le CTA
     évident) sans rien colorer. */
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.01em;
  white-space: nowrap;
  box-shadow: 0 10px 26px -14px rgba(20, 27, 38, 0.55);
  transition:
    transform 0.3s var(--ease),
    box-shadow 0.3s var(--ease),
    background 0.3s var(--ease);
}
.nav-cta:hover,
.nav-cta:focus-visible {
  transform: translateY(-1px);
  background: var(--text);
  box-shadow: 0 14px 34px -14px rgba(20, 27, 38, 0.7);
}
.nav-cta:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 3px;
}
/* Contexte sombre (#parcours, via .sur-video posé par majFondNav dans
   sections/hero.js) : on inverse crème/encre. Le texte gardait déjà 16:1
   dans les deux cas, c'est la PASTILLE qui disparaissait sur le fond nuit. */
header.sur-video .nav-cta {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 10px 26px -14px rgba(0, 0, 0, 0.55);
}
header.sur-video .nav-cta:hover,
header.sur-video .nav-cta:focus-visible {
  background: #fff;
  box-shadow: 0 14px 34px -14px rgba(0, 0, 0, 0.7);
}

@media (max-width: 767px) {
  .nav-cta {
    display: none;
  }
}

/* Sur la vidéo plein écran du hero : encre → clair, avec une ombre portée
   discrète pour rester lisible quel que soit le photogramme (clair ou sombre). */
header.sur-video .nav-brand,
header.sur-video .nav-link {
  color: var(--paper);
  text-shadow: 0 1px 12px rgba(2, 4, 10, 0.5);
}
header.sur-video .nav-brand .nx-m {
  fill: var(--paper);
}
header.sur-video .nav-link::after {
  background: var(--paper);
}
header.sur-video .nav-link:hover,
header.sur-video .nav-link:focus-visible,
header.sur-video .nav-link[aria-current="page"] {
  color: var(--paper);
  opacity: 0.8;
}
header.sur-video .mobile-menu-btn {
  border-color: rgba(var(--paper-rgb), 0.4);
}
header.sur-video .mobile-menu-btn span {
  background: var(--paper);
}

.mobile-menu-btn {
  display: none;
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 50%;
  background: none;
  border: 1px solid rgba(var(--ink-rgb), 0.22);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
.mobile-menu-btn span {
  display: block;
  width: 16px;
  height: 1.5px;
  background: var(--ink);
  border-radius: 2px;
  transition:
    transform 0.3s ease,
    opacity 0.3s ease;
}
.mobile-menu-btn.is-open span:first-child {
  transform: translateY(3.25px) rotate(45deg);
}
.mobile-menu-btn.is-open span:last-child {
  transform: translateY(-3.25px) rotate(-45deg);
}

.mobile-menu {
  display: none;
  position: fixed;
  top: 64px;
  right: clamp(1.2rem, 3vw, 2.6rem);
  z-index: 99;
  flex-direction: column;
  gap: 2px;
  min-width: 190px;
  padding: 10px;
  border-radius: 16px;
  background: rgba(var(--paper-rgb), 0.97);
  border: 1px solid rgba(var(--ink-rgb), 0.12);
  box-shadow: var(--shadow-card);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  opacity: 0;
  visibility: hidden;
  translate: 0 -8px;
  transition:
    opacity 0.3s ease,
    translate 0.3s ease,
    visibility 0.3s ease;
}
.mobile-menu.is-open {
  opacity: 1;
  visibility: visible;
  translate: 0 0;
}
.mobile-menu-link {
  /* min-height 44px (cible tactile) : 13px de padding ne suffisaient pas à
     couvrir la hauteur mini WCAG/HIG une fois le texte (13px, line-height ~1.4)
     compté — min-height + flex centre le texte sans dépendre du line-height */
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 14px;
  border-radius: 10px;
  text-align: left;
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 13px;
  background: none;
  border: none;
}
.mobile-menu-link:hover,
.mobile-menu-link:focus-visible {
  background: rgba(var(--ink-rgb), 0.06);
}
/* CTA du menu mobile : même geste que .nav-cta (fond plein, seule action
   remplie du menu) — posée en premier, margin-bottom la détache des liens
   de navigation qui suivent (pas de filet superposé, plus simple). */
.mobile-menu-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 44px;
  margin-bottom: 8px;
  border-radius: 10px;
  background: var(--accent);
  color: #02040a;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 13px;
}
.mobile-menu-cta:hover,
.mobile-menu-cta:focus-visible {
  background: var(--accent-2);
  color: var(--paper);
}

/* Titre flottant — clair : le titre apparaît en fondu (cf. main.js → tlTitre)
   seulement une fois le cadre plein écran (fin de la phase 1), donc TOUJOURS
   sur la vidéo, jamais sur le fond clair de la page — texte clair + ombre
   sombre pour rester lisible quel que soit le photogramme affiché.
   absolute (dans .hero-sticky-stage), pas fixed : indépendant du transform de
   .scroll-video-wrapper (parallax souris), mais se libère avec le stage à la fin de .hero. */
.hero-text {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 1rem;
  pointer-events: none;
  z-index: 10;
}
@media (min-width: 768px) {
  .hero-text {
    padding: 2rem;
  }
}
#scroll-title {
  font-family: var(--font-display);
  font-size: clamp(3.2rem, 8vw, 8rem);
  line-height: 0.9;
  letter-spacing: 0;
  color: var(--paper);
  text-transform: none;
  font-weight: 400;
  text-shadow:
    0 4px 28px rgba(2, 4, 10, 0.72),
    0 18px 70px rgba(2, 4, 10, 0.62);
}
#scroll-title .word {
  display: inline-block;
}
#scroll-title .char {
  display: inline-block;
  transform-origin: 50% 0%;
}

/* Panneau éditorial de fin de hero */
.glass-panel-section {
  position: static;
  width: 100%;
}
.glass-panel-frame {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 2rem);
  max-width: 1250px;
  margin-bottom: 2rem;
  perspective: 1000px;
  z-index: 20;
}
.glass-panel {
  position: relative;
  height: 900px;
  max-height: 85vh;
  background-color: rgba(var(--paper-rgb), 0.6);
  /* 160px de rayon sur un calque ~900px de haut, posé sur la vidéo scrubée du
     hero qui se repeint à chaque frame de scroll : un des postes de coût les
     plus lourds du site (le flou doit se recalculer en continu). 40px donne
     un effet "verre" quasi identique (le fond à .6 d'opacité fait déjà
     l'essentiel du travail visuel) pour une fraction du coût de rendu. */
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(40px);
  border: 1px solid rgba(var(--ink-rgb), 0.14);
  border-radius: 1.5rem;
  will-change: transform;
  overflow: hidden;
  color: var(--ink);
  box-shadow: var(--shadow-card);
}
.glass-panel-tilt {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  transform-style: preserve-3d;
  will-change: transform;
}
.glass-panel-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  padding: 2rem;
  text-align: center;
}
.glass-panel h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.8rem, 3.6vw, 3.4rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  max-width: 1000px;
}
.glass-panel h2 .italic {
  font-style: italic;
  color: var(--accent-2);
}
/* le dernier mot (l'action : "convertir") porte la couleur secondaire — les
   deux premiers (qualités : "artistique"/"propre") restent en bleu principal. */
.glass-panel h2 .italic:last-of-type {
  color: var(--secondaire);
}
@media (min-width: 1024px) {
  .glass-panel h2 {
    line-height: 92.6px;
  }
}
.brand-marquee {
  border-top: 1px solid rgba(var(--ink-rgb), 0.14);
  overflow: hidden;
  padding: 1rem 0;
  opacity: 0.55;
  transition: opacity 0.3s ease;
}
.brand-marquee:hover {
  opacity: 1;
}
.marquee-track {
  display: flex;
  gap: 4rem;
  white-space: nowrap;
  width: max-content;
  animation: marquee 20s linear infinite;
  text-transform: uppercase;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.15em;
  font-family: var(--mono);
  color: var(--text-muted);
}
@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* Repli mobile / mouvement réduit / erreur GSAP : pas de scroll-scrub, tout
   en flux normal. Le hero garde son contenu, juste sans le mécanisme.
   scroll-video-wrapper/hero-text RESTENT absolute (superposés dans le stage,
   cf. règle de base) — seul le stage lui-même se libère de sticky ; les
   repasser en relative les empilerait l'un sous l'autre au lieu de les
   superposer (bug constaté : le titre se retrouvait poussé hors écran). */
@media (max-width: 820px), (prefers-reduced-motion: reduce) {
  .hero {
    height: auto;
  }
  .hero-sticky-stage {
    position: relative;
    height: 100vh;
  }
  /* left:50%+translateX(-50%) et width:calc(100% - 2rem) du bloc de base sont
     pensés pour position:absolute (référence = .hero, 390px) ; en relative,
     ces mêmes valeurs se recalculent contre le contexte de ligne normal et
     gonflaient la largeur bien au-delà du viewport (constaté : 1250px sur un
     écran de 390px) — tout réinitialiser plutôt que d'hériter partiellement. */
  .glass-panel-frame {
    position: relative;
    bottom: auto;
    left: auto;
    top: auto;
    transform: none;
    width: calc(100% - 2rem);
    max-width: 1250px;
    margin: 0 auto 2rem;
  }
}
html.secours .hero {
  height: auto;
}
html.secours .hero-sticky-stage {
  position: relative;
  height: 100vh;
}
html.secours .glass-panel-frame {
  position: relative;
  bottom: auto;
  left: auto;
  top: auto;
  transform: none;
  width: calc(100% - 2rem);
  max-width: 1250px;
  margin: 0 auto 2rem;
}

@media (max-width: 767px) {
  .nav-links {
    display: none;
  }
  .nav-brand-txt {
    display: none;
  }
  .mobile-menu-btn {
    display: flex;
  }
  .mobile-menu {
    display: flex;
  }
  .glass-panel-frame {
    width: calc(100% - 1rem);
  }
  .glass-panel {
    height: 700px;
  }
  .glass-panel h2 {
    font-size: clamp(1.7rem, 6vw, 2.2rem);
  }
}

/* ==========================================================================
   Conversions cosmétiques restantes vers les tokens clairs — cartes services
   et témoignage (le reste de ces sections est déjà token-driven).
   ========================================================================== */
.chrono-tags span {
  color: rgba(var(--ink-rgb), 0.72);
  border-color: rgba(var(--ink-rgb), 0.14);
  background: rgba(var(--ink-rgb), 0.05);
}

.temoin {
  background:
    radial-gradient(
      44rem 28rem at 50% 0,
      rgba(var(--accent-rgb), 0.14),
      transparent 62%
    ),
    rgba(var(--paper-rgb), 0.7);
  border-color: rgba(var(--ink-rgb), 0.12);
}

/* Corrections contraste home : le theme final est clair, donc les accents
   cyan tres pales ne doivent pas porter de texte sur les sections sable. */
#competences:not(.comp-px) .section-num,
.temoin-tag,
.temoin-lien {
  color: var(--accent-2);
}
/* seule touche de la couleur secondaire dans un titre de section : le duo-tone
   encre + terracotta sur "WORKS" fait la démonstration de la 2e couleur sans
   la diluer ailleurs (cf. commentaire sur le token, plus haut dans le fichier). */
#competences:not(.comp-px) .section-num b {
  color: var(--accent-2);
}
.temoin-tag::before {
  background: var(--accent-2);
}

/* Scrim déplacé sur .hero-mockup-screen (cf. plus haut) : appliqué au
   viewport entier ici, il assombrissait aussi le fond clair autour du petit
   mockup au repos — remplacé, cette règle ne doit plus matcher nulle part. */

/* Panneau a propos : fond plus opaque et textes moins delaves. */
.glass-panel {
  background-color: rgba(var(--paper-rgb), 0.84);
  border-color: rgba(var(--ink-rgb), 0.2);
}
.glass-panel h2 {
  color: var(--ink);
}
.brand-marquee {
  /* .marquee-track est en --text-muted : à .88 le texte composité tombe à
     4.12:1 (calcul WCAG réel), sous le seuil AA 4.5:1 pour du texte normal
     (14px, pas "large text") — .94 remonte à 4.67:1 tout en gardant l'effet
     "discret au repos". */
  opacity: 0.94;
  border-top-color: rgba(var(--ink-rgb), 0.18);
  /* fondu latéral : les noms qui défilent entrent/sortent en dégradé au lieu
     d'être tranchés net par les coins arrondis de la carte (le masque fond
     aussi les extrémités du filet border-top, même douceur des deux côtés) */
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0,
    #000 9%,
    #000 91%,
    transparent 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0,
    #000 9%,
    #000 91%,
    transparent 100%
  );
}
.brand-marquee:hover {
  opacity: 1;
}
.brand-marquee .marquee-track {
  color: rgba(var(--ink-rgb), 0.76);
}

/* Les tags de la timeline vivent sur une section sombre, contrairement aux
   chips des cartes claires corrigees juste au-dessus. */
.parcours-3d .chrono-tags span {
  color: rgba(var(--paper-rgb), 0.88);
  border-color: rgba(var(--paper-rgb), 0.2);
  background: rgba(var(--paper-rgb), 0.1);
}
.parcours-3d:not(.p3-flat) .section-num,
.parcours-3d:not(.p3-flat) .p3-titre,
.parcours-3d:not(.p3-flat) .p3-step h3,
.parcours-3d:not(.p3-flat) .p3-step p {
  color: var(--paper);
}
/* .p3-contact (scène finale, sphère "contact") manquait à ce ratissage : ses
   textes passaient par --creme/--gris, désormais réaliasés sur --text/--text-muted
   (encre sombre) pour la DA claire — invisibles sur la pièce sombre du voyage 3D.
   Seul .contact-alt avait une couleur en dur (L2054) et restait donc correct ;
   ses frères (kicker/h2/p/note) ont besoin du même traitement clair explicite. */
.parcours-3d:not(.p3-flat) .p3-contact h2,
.parcours-3d:not(.p3-flat) .p3-contact p,
.parcours-3d:not(.p3-flat) .contact-kicker,
.parcours-3d:not(.p3-flat) .contact-note {
  color: var(--paper);
}
.parcours-3d:not(.p3-flat) .p3-sub,
.parcours-3d:not(.p3-flat) .chrono-cat,
.parcours-3d:not(.p3-flat) .p3-of,
.parcours-3d:not(.p3-flat) .p3-cue,
.parcours-3d:not(.p3-flat) .p3-hint {
  color: rgba(var(--paper-rgb), 0.72);
}
.parcours-3d:not(.p3-flat) .section-num b,
.parcours-3d:not(.p3-flat) .chrono-idx,
.parcours-3d:not(.p3-flat) .p3-num {
  color: var(--accent-soft);
}
.parcours-3d.p3-flat .chrono-tags span {
  color: var(--text);
  border-color: rgba(var(--ink-rgb), 0.14);
  background: rgba(var(--paper-rgb), 0.72);
}

/* ==========================================================================
   Selected works — explorateur de fichiers (rendreWorksListe/initWorksExplorer,
   main.js). Remplace l'ancien système de chapitres cinématiques scrollés
   (classes .scenes- et .scene- , laissées intactes plus haut mais inertes —
   plus aucun sélecteur ne matche depuis que #projets ne rend plus ces classes).
   Métaphore : la liste = arborescence de fichiers (écho au VS Code du hero),
   chaque ligne un fichier-projet ; au survol/focus (pointeur fin desktop
   seulement), un panneau d'aperçu ANCRÉ (pas une carte qui suit la souris)
   affiche le wireframe + la citation du projet.
   ========================================================================== */
.works-tete {
  position: relative;
}
.works-all {
  position: absolute;
  top: 0.3rem;
  right: 0;
}
@media (max-width: 640px) {
  .works-all {
    position: static;
    display: inline-flex;
    margin-top: 1rem;
  }
}

/* ───── WORK CARDS — Cartes éditoriales premium AWWWARDS (homepage) ───── */
.works-liste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  perspective: 1200px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.work-item {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: 20px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--card-grad);
  box-shadow:
    0 8px 40px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(237, 239, 246, 0.06);
  transition:
    transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.6s ease,
    border-color 0.5s ease;
  min-height: 420px;
}

.work-media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.work-media-img,
.work-media-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.02);
  opacity: 0;
  transition:
    transform 1.2s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.8s ease 0.1s;
}

.work-item:hover .work-media-img,
.work-item:hover .work-media-video {
  transform: scale(1);
  opacity: 0.35;
}

.work-media-fallback {
  position: absolute;
  inset: 0;
  background: var(--card-grad);
  z-index: -1;
}

.work-media-fallback::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse at 50% 0%,
      rgba(255, 255, 255, 0.06),
      transparent 60%
    ),
    linear-gradient(180deg, rgba(3, 5, 11, 0.1), rgba(3, 5, 11, 0.7));
}

@keyframes mediaSweep {
  0%,
  100% {
    background-position: 200% 50%;
    opacity: 0.3;
  }
  50% {
    background-position: -50% 50%;
    opacity: 0.6;
  }
}

.work-media-fallback::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    105deg,
    transparent 35%,
    rgba(255, 255, 255, 0.04) 45%,
    transparent 55%
  );
  background-size: 200% 100%;
  animation: mediaSweep 8s ease-in-out infinite;
}

.work-content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: clamp(2rem, 4vw, 3rem);
}

.work-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.5);
  border: 1px solid rgba(237, 239, 246, 0.1);
  border-radius: 999px;
  padding: 0.35rem 0.85rem;
  background: rgba(7, 10, 20, 0.5);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  width: fit-content;
  margin-bottom: clamp(1rem, 2vw, 1.5rem);
  transition: all 0.4s ease;
}

.work-item:hover .work-badge {
  border-color: rgba(237, 239, 246, 0.3);
  color: rgba(237, 239, 246, 0.85);
  background: rgba(var(--accent-rgb), 0.1);
}

.work-title {
  margin: 0 0 clamp(0.5rem, 1.2vw, 1rem) 0;
  font-family: var(--display);
  font-weight: 900;
  text-transform: uppercase;
  line-height: 0.98;
  color: var(--paper);
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
  font-size: clamp(1.8rem, 4vw, 2.8rem);
  max-width: 90%;
}

.work-subtitle {
  margin: 0 0 clamp(1.5rem, 3vw, 2rem) 0;
  font-size: clamp(0.95rem, 1.8vw, 1.15rem);
  line-height: 1.55;
  color: rgba(237, 239, 246, 0.7);
  max-width: 60ch;
  font-weight: 400;
  transition: color 0.4s ease;
}

.work-item:hover .work-subtitle {
  color: rgba(237, 239, 246, 0.9);
}

.work-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1rem, 2vw, 1.5rem);
  margin-top: auto;
  padding-top: clamp(1rem, 2vw, 1.5rem);
  border-top: 1px solid rgba(237, 239, 246, 0.06);
}

.work-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.work-tag {
  font-family: var(--mono);
  font-size: 0.5rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.45);
  border: 1px solid rgba(237, 239, 246, 0.08);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  background: rgba(7, 10, 20, 0.4);
  transition: all 0.3s ease;
}

.work-item:hover .work-tag {
  border-color: rgba(237, 239, 246, 0.2);
  color: rgba(237, 239, 246, 0.7);
}

.work-role {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.55rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.4);
}

.work-role-label {
  color: rgba(var(--accent-rgb), 0.55);
}

.work-role-text {
  color: rgba(237, 239, 246, 0.5);
}

.work-year {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.35);
  margin-left: auto;
}

.work-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--paper);
  background: rgba(237, 239, 246, 0.04);
  border: 1px solid rgba(237, 239, 246, 0.08);
  border-radius: 8px;
  padding: 0.6rem 1.2rem;
  opacity: 0;
  transform: translateY(10px) scale(0.96);
  transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  margin-top: 0.75rem;
  width: fit-content;
}

.work-cta-arrow {
  display: inline-block;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.work-item:hover .work-cta {
  opacity: 1;
  transform: translateY(0) scale(1);
  background: rgba(var(--accent-rgb), 0.12);
  border-color: rgba(var(--accent-rgb), 0.3);
  color: var(--accent);
}

.work-item:hover .work-cta-arrow {
  transform: translateX(8px);
}

.work-item:hover {
  transform: translateY(-8px);
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.45),
    0 0 50px rgba(var(--accent-rgb), 0.06),
    0 0 0 1px rgba(var(--accent-rgb), 0.18);
  border-color: rgba(var(--accent-rgb), 0.2);
}

.work-item:hover .work-title {
  transform: translateX(6px);
}

.work-item:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
}

.work-item:active {
  transform: translateY(-2px) scale(0.99);
}

@media (max-width: 900px) {
  .works-liste {
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  }
}

@media (max-width: 640px) {
  .works-liste {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }
  .work-item {
    min-height: 380px;
  }
  .work-content {
    padding: clamp(1.5rem, 4vw, 2.5rem);
  }
  .work-cta {
    opacity: 1;
    transform: none;
    border-color: rgba(var(--accent-rgb), 0.2);
    background: rgba(var(--accent-rgb), 0.06);
  }
  .work-item:hover {
    transform: translateY(-4px);
    box-shadow:
      0 12px 40px rgba(0, 0, 0, 0.4),
      0 0 40px rgba(var(--accent-rgb), 0.06);
  }
  .work-item:hover .work-title {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .work-item,
  .work-media-img,
  .work-media-video,
  .work-media-fallback,
  .work-media-fallback::before,
  .work-title,
  .work-subtitle,
  .work-badge,
  .work-tag,
  .work-cta,
  .work-cta-arrow {
    transition: none !important;
    animation: none !important;
  }
  .work-media-img,
  .work-media-video {
    transform: scale(1);
    opacity: 1;
  }
  .work-media-fallback {
    opacity: 0;
  }
  .work-item:hover {
    transform: none;
    box-shadow:
      0 8px 40px rgba(0, 0, 0, 0.35),
      0 0 0 1px rgba(237, 239, 246, 0.06);
  }
  .work-item:hover .work-title {
    transform: none;
  }
  .work-item:hover .work-cta {
    opacity: 1;
    transform: none;
  }
}

/* Light theme adjustments */
@media (prefers-color-scheme: light) {
  .work-item {
    box-shadow:
      0 4px 24px rgba(0, 0, 0, 0.1),
      0 0 0 1px rgba(0, 0, 0, 0.06);
  }
  .work-badge,
  .work-tag {
    background: rgba(255, 255, 255, 0.8);
    border-color: rgba(0, 0, 0, 0.08);
    color: rgba(0, 0, 0, 0.5);
  }
  .work-item:hover .work-badge,
  .work-item:hover .work-tag {
    border-color: rgba(var(--accent-rgb), 0.3);
    color: var(--accent);
  }
  .work-subtitle {
    color: rgba(0, 0, 0, 0.6);
  }
  .work-item:hover .work-subtitle {
    color: rgba(0, 0, 0, 0.85);
  }
  .work-role,
  .work-year {
    color: rgba(0, 0, 0, 0.4);
  }
  .work-cta {
    background: rgba(0, 0, 0, 0.04);
    border-color: rgba(0, 0, 0, 0.08);
    color: var(--ink);
  }
  .work-item:hover .work-cta {
    background: rgba(var(--accent-rgb), 0.12);
    border-color: rgba(var(--accent-rgb), 0.3);
    color: var(--accent);
  }
  .work-item:hover {
    box-shadow:
      0 20px 60px rgba(0, 0, 0, 0.15),
      0 0 50px rgba(var(--accent-rgb), 0.08),
      0 0 0 1px rgba(var(--accent-rgb), 0.18);
  }
}

/* Grain global de la home : texture fine sur tout le viewport, assez visible pour
   donner de la matiere au fond sans casser le contraste des textes. */
body:has(main[data-barba-namespace="home"]) .grain {
  /* ⚠️ PAS soft-light ni overlay. Ces deux modes laissent la base INCHANGÉE
     quand la source est du gris moyen — et un feTurbulence est précisément
     centré sur le gris moyen. Le grain était donc mathématiquement invisible
     sur le sable, quelle que soit son opacité. `multiply` assombrit : le
     grain se lit comme des grains sombres dans le papier, ce qui est le bon
     modèle physique pour un fond clair. */
  opacity: 0.2;
  mix-blend-mode: multiply;
  overflow: hidden;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' seed='7'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.74'/%3E%3C/svg%3E"),
    radial-gradient(
      circle at 28% 18%,
      rgba(255, 255, 255, 0.18) 0 1px,
      transparent 1px 3px
    );
  background-size:
    180px 180px,
    5px 5px;
  background-blend-mode: overlay, normal;
  filter: contrast(130%) brightness(112%);
}
body:has(main[data-barba-namespace="home"]) .grain::before,
body:has(main[data-barba-namespace="home"]) .grain::after {
  content: "";
  position: absolute;
  inset: -18%;
  pointer-events: none;
}
body:has(main[data-barba-namespace="home"]) .grain::before {
  opacity: 0.48;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.12' numOctaves='2' seed='14'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.62'/%3E%3C/svg%3E");
  background-size: 220px 220px;
  animation: homeGrainShift 7s steps(5, end) infinite;
}
body:has(main[data-barba-namespace="home"]) .grain::after {
  opacity: 0.18;
  background:
    repeating-linear-gradient(
      0deg,
      rgba(255, 255, 255, 0.18) 0 1px,
      transparent 1px 4px
    ),
    repeating-linear-gradient(
      90deg,
      rgba(2, 4, 10, 0.22) 0 1px,
      transparent 1px 6px
    );
  mix-blend-mode: overlay;
}
@keyframes homeGrainShift {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  20% {
    transform: translate3d(-2%, 1%, 0);
  }
  40% {
    transform: translate3d(1%, -2%, 0);
  }
  60% {
    transform: translate3d(3%, 2%, 0);
  }
  80% {
    transform: translate3d(-1%, 3%, 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  body:has(main[data-barba-namespace="home"]) .grain::before {
    animation: none;
  }
}

/* Échelle typographique de la home : la racine réduit tous les corps en rem
   sans toucher aux pages Work/Projet. Les grands displays en vw reçoivent une
   correction dédiée pour que la baisse reste perceptible sur desktop. */
html:has(main[data-barba-namespace="home"]) {
  font-size: clamp(17px, 0.95rem + 0.2vw, 18px);
}
/* Nom en grand sur la vidéo : corps relevé (moment cinéma, fond sombre). Le
   conteneur reste FIXE (ni scale ni transform) — c'est chaque lettre (.char)
   qui se construit au scroll, cf. tlTitre dans main.js. */
main[data-barba-namespace="home"] #scroll-title {
  font-size: clamp(3.4rem, 9vw, 9rem);
}
main[data-barba-namespace="home"] #scroll-title .char {
  will-change: transform, opacity;
}
/* Épilogue du hero : panneau éditorial sombre inspiré du cadre de référence.
   Il tranche avec le sable sans devenir un nouveau plein écran. */
main[data-barba-namespace="home"] .glass-panel-frame {
  width: min(92vw, 1440px);
  margin-bottom: clamp(1.5rem, 3.5vh, 3rem);
}
main[data-barba-namespace="home"] .glass-panel {
  height: clamp(500px, 68vh, 680px);
  height: clamp(500px, 68svh, 680px);
  max-height: none;
  border-radius: clamp(1.4rem, 2.4vw, 2.25rem);
  border-color: rgba(var(--paper-rgb), 0.13);
  /* légèrement transparent (demande explicite) : voile principal détendu
     ~0.78→0.8 vers ~0.6→0.64 pour laisser plus de la vidéo scrubée derrière
     transparaître à travers le flou — le texte reste en --paper (quasi blanc)
     sur un fond qui demeure assez sombre (vidéo + voile combinés) pour garder
     un contraste confortable. */
  background:
    radial-gradient(
      65% 110% at 10% 38%,
      rgba(42, 82, 103, 0.26),
      transparent 66%
    ),
    radial-gradient(
      58% 110% at 82% 28%,
      rgba(94, 56, 43, 0.15),
      transparent 70%
    ),
    linear-gradient(
      118deg,
      rgba(8, 19, 27, 0.6) 0%,
      rgba(28, 29, 32, 0.64) 48%,
      rgba(35, 30, 31, 0.62) 100%
    );
  -webkit-backdrop-filter: blur(18px) saturate(0.85) brightness(0.72);
  backdrop-filter: blur(18px) saturate(0.85) brightness(0.72);
  color: var(--paper);
  box-shadow:
    0 46px 120px -58px rgba(2, 4, 10, 0.9),
    inset 0 1px 0 rgba(var(--paper-rgb), 0.08);
}
main[data-barba-namespace="home"] .glass-panel::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(115deg, rgba(255, 255, 255, 0.025), transparent 28%),
    radial-gradient(
      circle at 50% 28%,
      rgba(var(--paper-rgb), 0.035),
      transparent 46%
    );
}
main[data-barba-namespace="home"] .glass-panel::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(var(--paper-rgb), 0.08);
  pointer-events: none;
}
main[data-barba-namespace="home"] .glass-panel-tilt {
  position: relative;
  z-index: 1;
}
main[data-barba-namespace="home"] .glass-panel-content {
  position: relative;
  gap: clamp(1rem, 2.4vh, 1.5rem);
  /* padding-top était plus grand que padding-bottom pour laisser la place
     au kicker ".about-label" ("À propos", retiré) au-dessus du h2 — rééquilibré
     pour un contenu centré maintenant que ce kicker a disparu. */
  padding: clamp(3.4rem, 7vh, 5rem);
}
main[data-barba-namespace="home"] .glass-panel h2 {
  max-width: 25ch;
  font-size: clamp(2.15rem, 4.65vw, 5rem);
  line-height: 0.98;
  letter-spacing: -0.025em;
  color: var(--paper);
  text-wrap: balance;
}
main[data-barba-namespace="home"] .glass-panel h2 .italic {
  display: inline-block;
  color: var(--paper);
  will-change: transform, opacity;
}
main[data-barba-namespace="home"] .glass-panel h2 .italic:last-of-type {
  color: var(--paper);
}
main[data-barba-namespace="home"] .about-panel-rule {
  position: absolute;
  left: clamp(1.4rem, 2.7vw, 2.6rem);
  right: clamp(1.4rem, 2.7vw, 2.6rem);
  bottom: clamp(1.15rem, 2.5vh, 1.8rem);
  height: 1px;
  background: rgba(var(--paper-rgb), 0.09);
}
@media (max-width: 820px) {
  main[data-barba-namespace="home"] .glass-panel-frame {
    width: calc(100% - 1.25rem);
    margin: 0 auto 1.25rem;
  }
  main[data-barba-namespace="home"] .glass-panel {
    height: clamp(460px, 68svh, 580px);
    min-height: 0;
    border-radius: 1.35rem;
  }
  main[data-barba-namespace="home"] .glass-panel-content {
    padding: 3.5rem clamp(1.35rem, 7vw, 2.4rem);
  }
  main[data-barba-namespace="home"] .glass-panel h2 {
    max-width: 18ch;
    font-size: clamp(2rem, 9.3vw, 3.25rem);
    line-height: 1.02;
  }
}
@media (prefers-reduced-motion: reduce) {
  main[data-barba-namespace="home"] .glass-panel,
  main[data-barba-namespace="home"] .glass-panel h2,
  main[data-barba-namespace="home"] .glass-panel h2 .italic {
    will-change: auto;
  }
}
main[data-barba-namespace="home"] .marquee span {
  font-size: clamp(1.45rem, 3.55vw, 2.7rem);
}
/* ── Tailles de titres de la home (l'ancien override de --display n'a plus
   lieu d'être : depuis la consolidation à deux polices, --display vaut Syne
   partout, et Syne est desormais chargee sur TOUTES les pages). Ce bloc ne
   garde que l'echelle de tailles, propre a la home. ── */
main[data-barba-namespace="home"] .temoin-quote p {
  font-size: clamp(1.4rem, 2.15vw, 1.9rem);
}

main[data-barba-namespace="home"] .parcours-3d:not(.p3-flat) .p3-titre {
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  letter-spacing: -0.02em;
}
main[data-barba-namespace="home"] .parcours-3d:not(.p3-flat) .p3-step h3 {
  font-size: clamp(1.5rem, 2.1vw, 2rem);
  letter-spacing: -0.015em;
}
main[data-barba-namespace="home"] .p3-contact h2 {
  /* « Construisons » est un mot de 12 caracteres qu'on ne peut pas couper :
     il mesurait 398px pour 251px disponibles a 360px de large. Le minimum du
     clamp est calibre dessus. */
  font-size: clamp(1.45rem, 6.4vw, 4.2rem);
  letter-spacing: -0.02em;
}


/* ═══════════════════════════════════════════════════════════
   Méthode (parcours 3D) — chambre nuit, profonde mais intégrée à la DA
   sable. La rampe au-dessus de #parcours absorbe la fin de Compétences sans
   toucher aux dimensions du pin. Les couleurs WebGL jumelles vivent dans
   main.js et doivent rester synchronisées avec ce fond.
   ═══════════════════════════════════════════════════════════ */

/* Bandeaux SERVICES / COMPÉTENCES : l'ancien verre de nuit (rgba(7,12,26,.66))
   posait une barre charbon en pleine page sable — la couture la plus brutale
   du site. Verre de papier + filets d'encre : la ponctuation reste (grands
   mots, ✶, tilt au scroll), la page ne change plus de monde. */
.marquee {
  background: rgba(var(--paper-rgb), 0.55);
  border-color: rgba(var(--ink-rgb), 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.marquee span {
  color: var(--text);
}
.marquee span::after {
  color: var(--accent-2);
}

/* Le pseudo s'étend dans la section précédente : il crée une pénombre
   progressive avant l'arrivée du stage, puis sort du viewport quand le pin
   commence. Aucun transform, filtre ou changement de hauteur sur le pin. */
main[data-barba-namespace="home"] #parcours {
  --p3-blend: clamp(8rem, 24svh, 15rem);
  background: linear-gradient(180deg, #080d18 0%, #050711 56%, #02040a 100%);
  color: var(--paper);
}
main[data-barba-namespace="home"] #parcours::before {
  content: "";
  position: absolute;
  z-index: 8;
  left: 0;
  right: 0;
  top: calc(var(--p3-blend) * -1);
  height: var(--p3-blend);
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(8, 13, 24, 0.08) 24%,
    rgba(8, 13, 24, 0.58) 72%,
    #080d18 100%
  );
}
main[data-barba-namespace="home"] #parcours .p3-stage {
  --noir: #050711;
  --noir-2: #080d18;
  --creme: var(--paper);
  --text: var(--paper);
  --text-rgb: var(--paper-rgb);
  --text-muted: rgba(var(--paper-rgb), 0.64);
  --gris: rgba(var(--paper-rgb), 0.64);
  --mute: rgba(var(--paper-rgb), 0.64);
  --ligne: rgba(var(--paper-rgb), 0.12);
  --ambre: var(--accent);
  --brass-2: var(--accent-soft);
  background:
    radial-gradient(
      54% 52% at 50% 48%,
      rgba(var(--accent-rgb), 0.11),
      transparent 64%
    ),
    radial-gradient(
      108% 82% at 76% 6%,
      rgba(var(--accent-2-rgb), 0.1),
      transparent 58%
    ),
    radial-gradient(
      82% 74% at 10% 92%,
      rgba(var(--secondaire-rgb), 0.035),
      transparent 64%
    ),
    linear-gradient(rgba(var(--paper-rgb), 0.018) 1px, transparent 1px) 0 0/100%
      6.4rem,
    linear-gradient(90deg, rgba(var(--paper-rgb), 0.014) 1px, transparent 1px) 0
      0/6.4rem 100%,
    linear-gradient(180deg, #080d18 0%, #050711 54%, #02040a 100%);
}
main[data-barba-namespace="home"] #parcours .p3-stage::before {
  opacity: 0.28;
  background: conic-gradient(
    from 90deg at 50% 50%,
    transparent,
    rgba(var(--accent-rgb), 0.13),
    transparent 22%,
    rgba(var(--accent-soft-rgb), 0.055),
    transparent 55%,
    rgba(var(--accent-2-rgb), 0.12),
    transparent
  );
}
main[data-barba-namespace="home"] #parcours .p3-stage::after {
  opacity: 0.74;
  background:
    repeating-radial-gradient(
      circle at 50% 48%,
      rgba(var(--paper-rgb), 0.035) 0 1px,
      transparent 1px 54px
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 38%,
      rgba(2, 4, 10, 0.76) 100%
    );
}
main[data-barba-namespace="home"] #parcours .p3-veil {
  background:
    radial-gradient(
      36% 36% at 50% 48%,
      rgba(var(--accent-rgb), 0.08),
      transparent 64%
    ),
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 42%,
      rgba(2, 4, 10, 0.58) 100%
    );
}
main[data-barba-namespace="home"] #parcours .p3-cine {
  background: radial-gradient(
    58% 58% at 50% 48%,
    transparent 22%,
    rgba(2, 4, 10, 0.88) 80%
  );
}

/* Textes et repères : papier lunaire sur nuit, avec un seul accent cyan. */
main[data-barba-namespace="home"] #parcours .section-num {
  color: rgba(var(--paper-rgb), 0.62);
}
main[data-barba-namespace="home"] #parcours .section-num b,
main[data-barba-namespace="home"] #parcours .chrono-idx {
  color: var(--accent-soft);
}
main[data-barba-namespace="home"] #parcours .p3-titre,
main[data-barba-namespace="home"] #parcours .p3-step h3,
main[data-barba-namespace="home"] #parcours .p3-step p {
  color: var(--paper);
}
main[data-barba-namespace="home"] #parcours .p3-sub,
main[data-barba-namespace="home"] #parcours .chrono-cat,
main[data-barba-namespace="home"] #parcours .p3-of,
main[data-barba-namespace="home"] #parcours .p3-cue,
main[data-barba-namespace="home"] #parcours .p3-hint {
  color: rgba(var(--paper-rgb), 0.64);
}
main[data-barba-namespace="home"] #parcours .p3-num {
  color: var(--accent);
  text-shadow: 0 0 26px rgba(var(--accent-rgb), 0.34);
}
main[data-barba-namespace="home"] #parcours .chrono-tags span {
  color: rgba(var(--paper-rgb), 0.82);
  border-color: rgba(var(--paper-rgb), 0.14);
  background: rgba(var(--paper-rgb), 0.065);
}
main[data-barba-namespace="home"] #parcours .p3-dots i {
  background: rgba(var(--paper-rgb), 0.18);
}
main[data-barba-namespace="home"] #parcours .p3-dots i.on {
  /* accent-soft (le bleu pâle, déjà celui de la numérotation/l'index de cette section)
     plutôt que le accent plein — un point "allumé" gagne à être plus clair que profond,
     et ça le rapproche de la comète lumineuse qu'il rejoue juste à côté. */
  background: var(--accent-soft);
  box-shadow:
    0 0 0 3px rgba(var(--accent-soft-rgb), 0.28),
    0 0 10px rgba(var(--accent-soft-rgb), 0.5);
}
main[data-barba-namespace="home"] #parcours .p3-cue::before {
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18);
}
main[data-barba-namespace="home"] #parcours .p3-hint::after {
  background: rgba(var(--paper-rgb), 0.58);
}

/* Rideau final nuit : il finit exactement dans la famille du footer.
   Un second halo, bas et plus sourd, sert de "lumière d'arrivée" derrière le
   bouton — sans lui, le panneau tombait à plat après le voyage stellaire qui
   précède (tunnel/étoiles du parcours 3D) : un aplat uni juste après tant de
   profondeur se lisait comme une coupure plutôt qu'un atterrissage. */
main[data-barba-namespace="home"] #parcours .p3-flash {
  background:
    radial-gradient(
      90% 62% at 50% 0%,
      rgba(var(--accent-rgb), 0.16),
      transparent 62%
    ),
    radial-gradient(
      52% 40% at 50% 74%,
      rgba(var(--accent-rgb), 0.09),
      transparent 68%
    ),
    linear-gradient(180deg, #080d18 0%, #050711 48%, #07101d 100%);
  box-shadow:
    0 -1px 0 rgba(var(--accent-rgb), 0.18),
    0 -40px 100px -44px rgba(2, 4, 10, 0.74);
}
main[data-barba-namespace="home"] #parcours .p3-contact,
main[data-barba-namespace="home"] #parcours .p3-contact h2,
main[data-barba-namespace="home"] #parcours .p3-contact p {
  color: var(--paper);
}
main[data-barba-namespace="home"] #parcours .p3-contact .contact-kicker,
main[data-barba-namespace="home"] #parcours .p3-contact .contact-note,
main[data-barba-namespace="home"] #parcours .p3-contact .contact-alt {
  color: rgba(var(--paper-rgb), 0.68);
}
main[data-barba-namespace="home"] #parcours .p3-contact .contact-alt {
  text-decoration-color: rgba(var(--paper-rgb), 0.36);
}
main[data-barba-namespace="home"] #parcours .p3-contact .contact-alt:hover {
  color: var(--paper);
}
main[data-barba-namespace="home"] #parcours .p3-contact .balise.on {
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.2);
  /* pulsation douce : "Nouvelle mission" se lit comme un statut vivant,
     pas une pastille figée — seule animation ajoutée, en boucle discrète */
  animation: p3ContactPulse 2.6s ease-in-out infinite;
}
@keyframes p3ContactPulse {
  0%,
  100% {
    box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.2);
  }
  50% {
    box-shadow: 0 0 0 7px rgba(var(--accent-rgb), 0.08);
  }
}
@media (prefers-reduced-motion: reduce) {
  main[data-barba-namespace="home"] #parcours .p3-contact .balise.on {
    animation: none;
  }
}
main[data-barba-namespace="home"] #parcours .p3-contact .btn.plein {
  background: var(--accent);
  color: #02040a;
  border-color: var(--accent);
  /* le box-shadow de base (.contact-cta, noir 0.14) est invisible sur ce
     panneau quasi noir — remplacé par une lueur bleue qui se voit vraiment,
     cohérente avec l'accent du bouton et la lumière d'arrivée du rideau */
  box-shadow: 0 20px 60px -20px rgba(var(--accent-rgb), 0.55);
  transition:
    color 0.4s var(--ease),
    border-color 0.4s var(--ease),
    box-shadow 0.4s var(--ease);
}
/* Halo qui respire doucement en continu : seule invitation "ambiante" du panneau avec la
   pastille du kicker (2 sources de mouvement max dans cette vue, pas plus) — sur ::after,
   jamais ::before (déjà pris par le sweep de remplissage de .btn), et jamais transform sur le
   bouton lui-même : rendu() (main.js) écrit .style.transform sur .contact-cta à chaque frame
   tant que la section est visible, un transform CSS non-!important y perdrait systématiquement. */
main[data-barba-namespace="home"] #parcours .p3-contact .btn.plein::after {
  content: "";
  position: absolute;
  inset: -7px;
  z-index: -2;
  border-radius: inherit;
  background: radial-gradient(
    circle,
    rgba(var(--accent-rgb), 0.6),
    transparent 72%
  );
  opacity: 0.4;
  animation: p3CtaRespire 3.6s ease-in-out infinite;
}
@keyframes p3CtaRespire {
  0%,
  100% {
    opacity: 0.32;
    transform: scale(0.94);
  }
  50% {
    opacity: 0.62;
    transform: scale(1.06);
  }
}
main[data-barba-namespace="home"] #parcours .p3-contact .btn.plein:hover {
  box-shadow: 0 24px 70px -18px rgba(var(--accent-rgb), 0.75);
}
main[data-barba-namespace="home"] #parcours .p3-contact .btn.plein:active {
  box-shadow: 0 10px 32px -16px rgba(var(--accent-rgb), 0.6);
}
/* outline: 2px solid currentColor (règle globale) résoudrait en #02040a (texte du bouton,
   quasi noir) — invisible sur ce panneau lui-même quasi noir. Reciblé sur l'accent clair. */
main[data-barba-namespace="home"]
  #parcours
  .p3-contact
  .btn.plein:focus-visible {
  outline-color: var(--accent-soft);
}
@media (prefers-reduced-motion: reduce) {
  main[data-barba-namespace="home"] #parcours .p3-contact .btn.plein::after {
    animation: none;
    opacity: 0.42;
  }
}

/* ==========================================================================
   Pages projet — format recruteur commun
   Un seul template, contenu issu de projets.json, lecture complète en moins
   de 30 secondes. Les anciens composants case-* restent disponibles pour les
   archives mais ne participent plus au rendu des pages projet actuelles.
   ========================================================================== */
.case.case-brief-page {
  padding: 0;
  overflow: clip;
  color: var(--text);
  background:
    radial-gradient(
      52rem 34rem at 82% 2%,
      rgba(var(--accent-rgb), 0.13),
      transparent 66%
    ),
    radial-gradient(
      42rem 30rem at 4% 38%,
      rgba(var(--secondaire-rgb), 0.045),
      transparent 70%
    ),
    var(--bg);
}
/* Les pages projet sont claires : le header global blanc utilisé au-dessus
   des univers sombres manquerait de contraste ici. */
main[data-barba-namespace="projet"] > header {
  color: var(--ink);
  background: linear-gradient(
    180deg,
    rgba(var(--paper-rgb), 0.96) 0%,
    rgba(var(--paper-rgb), 0.78) 62%,
    rgba(var(--paper-rgb), 0) 100%
  );
}
main[data-barba-namespace="projet"] > header .logo,
main[data-barba-namespace="projet"] > header nav a {
  color: var(--ink);
}
main[data-barba-namespace="projet"] > header .logo-studio {
  color: var(--text-muted);
}
main[data-barba-namespace="projet"] > header .nx-m {
  fill: var(--ink);
}
main[data-barba-namespace="projet"] > header nav a::after {
  background: var(--ink);
}
/* ═══════════════ Page projet (étude de cas) — v2 "carte d'encre" ═══════════════
   Deux panneaux d'encre teintés par le dégradé propre au projet (--case-grad,
   posé sur l'article) ouvrent et closent la page : le hero et la carte "projet
   suivant". Entre les deux, le fil du récit reste sur sable. La nav fixe passe
   en pilule de verre (body[data-cas], plus bas) pour rester lisible quand la
   carte sombre défile sous elle. */
.project-page-shell {
  padding: clamp(1.2rem, 2.5vh, 2rem) var(--marge) 0;
}
.project-topline,
.project-content,
.project-pager {
  width: min(1180px, 100%);
  margin-inline: auto;
}
.project-topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: clamp(1.2rem, 2.5vh, 1.8rem);
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.project-topline a {
  /* cible tactile ≥44px (même geste qu'avant) */
  display: inline-block;
  padding: 0.8rem 0;
  margin: -0.8rem 0;
  color: var(--accent-2);
  transition: color 0.25s var(--ease);
}
.project-topline a:hover {
  color: var(--text);
}

/* ── Hero : la carte d'encre teintée par le dégradé du projet ── */
.project-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: min(1180px, 100%);
  margin: 0 auto;
  padding: clamp(3rem, 7vw, 5.4rem) clamp(1.5rem, 4.5vw, 4.4rem)
    clamp(1.6rem, 3vw, 2.6rem);
  border: 1px solid rgba(var(--paper-rgb), 0.09);
  border-radius: clamp(1.2rem, 2.4vw, 1.9rem);
  background:
    linear-gradient(158deg, rgba(4, 7, 15, 0.5) 0%, rgba(4, 7, 15, 0.92) 74%),
    var(--case-grad) #04070f;
  box-shadow: 0 70px 150px -90px rgba(4, 7, 15, 0.85);
}
.project-hero-init {
  position: absolute;
  z-index: 0;
  right: clamp(0.4rem, 2vw, 2rem);
  bottom: -0.14em;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(9rem, 24vw, 19rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  color: rgba(var(--paper-rgb), 0.05);
  pointer-events: none;
  user-select: none;
}
.project-hero-main {
  position: relative;
  z-index: 1;
}
.project-kicker {
  display: block;
  margin: 0 0 1.15rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.66);
}
.project-kicker::before {
  content: "✳";
  margin-right: 0.6em;
  color: #d4a94e; /* or de l'étoile — seule touche chaude, comme le loader */
}
.project-hero h1 {
  max-width: 14ch;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(2.5rem, 5.6vw, 4.5rem);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: 0;
  text-wrap: balance;
  color: var(--paper);
}
.project-lead {
  max-width: 52ch;
  margin: clamp(1.2rem, 2.8vh, 1.9rem) 0 0;
  font-size: clamp(1.06rem, 1.5vw, 1.3rem);
  line-height: 1.52;
  color: rgba(var(--paper-rgb), 0.78);
}
.project-hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  margin-top: clamp(1.6rem, 3.5vh, 2.4rem);
}
.project-live {
  margin: 0;
}
.project-hero-defile {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: 0.62rem 1.2rem;
  border: 1px solid rgba(var(--paper-rgb), 0.3);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.85);
  transition:
    background 0.25s var(--ease),
    color 0.25s var(--ease),
    border-color 0.25s var(--ease);
}
.project-hero-defile:hover {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}
/* faits : rangée en bas de carte, séparée par une hairline */
.project-facts {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.2rem, 3vw, 2.6rem);
  margin: clamp(2.6rem, 6vh, 4.2rem) 0 0;
  padding-top: clamp(1.4rem, 2.8vh, 2.1rem);
  border-top: 1px solid rgba(var(--paper-rgb), 0.16);
}
.project-facts dt {
  margin-bottom: 0.45rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.55);
}
.project-facts dd {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--paper);
}
.project-tech {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.project-tech span {
  padding: 0.28rem 0.58rem;
  border-radius: 999px;
  background: rgba(var(--paper-rgb), 0.08);
  border: 1px solid rgba(var(--paper-rgb), 0.2);
  font-family: var(--mono);
  font-size: 0.66rem;
  color: rgba(var(--paper-rgb), 0.85);
}

/* ── fil du récit (sur sable) ── */
.project-content {
  padding-bottom: clamp(3rem, 6vw, 5rem);
  scroll-margin-top: 6.5rem;
}
.project-section {
  display: grid;
  grid-template-columns: minmax(190px, 0.62fr) minmax(0, 1.38fr);
  gap: clamp(2.2rem, 6vw, 6rem);
  padding: clamp(3.4rem, 7vw, 5.6rem) 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.15);
}
/* Variante pleine largeur : titre au-dessus, contenu sur toute la laize. Pour
   les blocs dont le contenu a besoin de place (médias, triptyque de cartes) —
   dans la colonne de droite, trois cartes tombaient à ~150px de texte utile,
   soit deux ou trois mots par ligne. */
.project-section-pleine {
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.6rem, 3.5vw, 2.6rem);
}
.project-section-pleine .project-section-heading h2 {
  max-width: 28ch;
}
/* Le titre suit la lecture des sections longues (chapitres, leçons) : on sait
   toujours dans quelle partie du récit on se trouve. */
@media (min-width: 901px) {
  .project-section:not(.project-section-pleine) > .project-section-heading {
    position: sticky;
    top: 7.5rem;
    align-self: start;
  }
}
.project-section-heading > span {
  display: block;
  margin-bottom: 0.8rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  color: var(--accent-2);
}
.project-section-heading h2 {
  max-width: 19ch;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.65rem, 2.6vw, 2.35rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: 0;
  text-wrap: balance;
  color: var(--text);
}
.project-copy {
  max-width: 66ch;
}
.project-copy p {
  margin: 0;
  font-size: clamp(1rem, 1.18vw, 1.13rem);
  line-height: 1.68;
  color: var(--text);
}
.project-copy p + p {
  margin-top: 0.85rem;
}

/* chapitres : le récit problème → résultat, numéroté */
.project-chapter-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.project-chapter {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr);
  gap: clamp(1rem, 2.4vw, 2rem);
  padding: clamp(1.6rem, 3.4vw, 2.4rem) 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.13);
}
.project-chapter:first-child {
  border-top: 0;
  padding-top: 0.4rem;
}
.project-chapter-num {
  font-family: var(--display);
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  color: var(--accent-2);
}
.project-chapter-body {
  min-width: 0;
}
.project-chapter-body h3 {
  margin: 0 0 0.7rem;
  font-family: var(--display);
  font-size: clamp(1.2rem, 1.7vw, 1.5rem);
  font-weight: 700;
  line-height: 1.12;
  color: var(--text);
}
.project-chapter-text p {
  margin: 0;
  max-width: 62ch;
  font-size: clamp(1rem, 1.15vw, 1.1rem);
  line-height: 1.66;
  color: var(--text);
}
.project-chapter-text p + p {
  margin-top: 0.85rem;
}
.project-chapter-text strong {
  font-weight: 700;
}
.project-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.42rem;
  margin: 1.1rem 0 0;
  padding: 0;
  list-style: none;
}
.project-chips li {
  padding: 0.3rem 0.62rem;
  border-radius: 999px;
  background: rgba(var(--accent-2-rgb), 0.07);
  border: 1px solid rgba(var(--accent-2-rgb), 0.16);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent-2);
}

/* points (leçons / apports) : deux formes distinctes, pas deux fois le même
   triptyque de cartes. Les leçons se lisent en rangées filetées (registre
   réflexif) ; les apports gardent les cartes, sur le panneau d'encre. */
.project-point-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2.2vw, 1.6rem);
}
.project-value {
  min-width: 0;
  padding: clamp(1.3rem, 2.4vw, 1.8rem);
  border: 1px solid rgba(var(--ink-rgb), 0.12);
  border-radius: 1rem;
  background: var(--surface);
  box-shadow: 0 24px 60px -52px rgba(var(--ink-rgb), 0.4);
}
/* Repère de carte : un filet, pas un « 01 » — les apports ne forment pas une
   séquence, seuls les chapitres en sont une. */
.project-point-filet {
  display: block;
  width: 2rem;
  height: 2px;
  margin-bottom: clamp(1.1rem, 2.6vh, 1.7rem);
  border-radius: 2px;
  background: var(--accent-2);
}
/* leçons : rangées filetées, titre à gauche, explication à droite */
.project-lessons .project-point-grid {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}
.project-lesson {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(0.9rem, 2.4vw, 2.2rem);
  align-items: start;
  min-width: 0;
  padding: clamp(1.3rem, 2.6vw, 1.9rem) 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.13);
}
.project-lesson:first-child {
  border-top: 0;
  padding-top: 0.2rem;
}
.project-lesson .project-point-filet {
  display: none;
}
/* deux classes : doit passer devant `.project-point-grid p`, écrit plus bas */
.project-lessons .project-lesson p {
  font-size: 0.97rem;
  line-height: 1.62;
}
.project-point-grid h3 {
  margin: 0 0 0.7rem;
  font-family: var(--display);
  font-size: clamp(1.02rem, 1.25vw, 1.18rem);
  font-weight: 700;
  line-height: 1.22;
  color: var(--text);
}
.project-point-grid p {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.58;
  color: var(--text-muted);
}

/* company : second panneau d'encre — l'écho du hero en fin de récit */
.project-company {
  margin-block: clamp(0.6rem, 2vw, 1.4rem);
  padding: clamp(2.2rem, 5vw, 4.2rem);
  border: 1px solid rgba(var(--paper-rgb), 0.09);
  border-radius: clamp(1.2rem, 2.4vw, 1.9rem);
  background:
    radial-gradient(
      52rem 30rem at 10% 0%,
      rgba(var(--accent-rgb), 0.16),
      transparent 65%
    ),
    linear-gradient(145deg, #101a30, #050711 62%, #02040a);
  box-shadow: 0 60px 130px -80px rgba(2, 4, 10, 0.85);
}
.project-company .project-section-heading {
  max-width: 40rem;
}
.project-company .project-section-heading > span {
  color: var(--accent-soft);
}
.project-company .project-section-heading h2,
.project-company .project-point-grid h3 {
  color: var(--paper);
}
.project-company .project-value {
  background: rgba(var(--paper-rgb), 0.045);
  border-color: rgba(var(--paper-rgb), 0.14);
  box-shadow: none;
}
.project-company .project-point-filet {
  background: var(--accent-soft);
}
.project-company .project-point-grid p {
  color: rgba(var(--paper-rgb), 0.72);
}

/* visuals : un seul média occupe toute la laize (avant, il restait coincé sur
   une demi-colonne), deux ou plus se rangent côte à côte. */
.project-visual-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr));
  gap: 1rem;
}
.project-visual {
  margin: 0;
}
.project-visual img,
.project-visual video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.8rem;
  border: 1px solid rgba(var(--ink-rgb), 0.13);
  background: var(--surface-soft);
}
.project-visual figcaption {
  margin-top: 0.65rem;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* signature : la citation éditoriale, précédée de l'étoile or */
.project-signature {
  margin: clamp(3.4rem, 8vw, 6.4rem) auto;
  max-width: 40rem;
  text-align: center;
}
.project-signature-star {
  width: 26px;
  height: auto;
  margin-bottom: 1.2rem;
  fill: #d4a94e;
  filter: drop-shadow(0 0 10px rgba(212, 169, 78, 0.45));
}
.project-signature blockquote {
  margin: 0;
}
.project-signature p {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.3rem, 2.5vw, 1.9rem);
  font-weight: 700;
  line-height: 1.32;
  text-wrap: balance;
  color: var(--text);
}

/* clôture : la conclusion, puis la sortie. L'étude de cas ne débouchait que
   sur « projet suivant » — un visiteur convaincu n'avait rien à faire. */
.project-summary {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
  margin-top: clamp(2.4rem, 6vw, 4.5rem);
  padding: clamp(2rem, 4vw, 3.5rem) 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.18);
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.18);
}
.project-summary > h2 {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent-2);
}
.project-summary-phrase {
  max-width: 46ch;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.45rem, 2.45vw, 2.15rem);
  font-weight: 700;
  line-height: 1.2;
  color: var(--text);
}
.project-summary-suite {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem 1.5rem;
  margin-top: clamp(1.5rem, 3.2vh, 2.2rem);
}
.project-summary-lien {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--accent-2);
  box-shadow: inset 0 -1px 0 rgba(var(--accent-2-rgb), 0.45);
  transition:
    color 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}
.project-summary-lien:hover {
  color: var(--text);
  box-shadow: inset 0 -1px 0 rgba(var(--ink-rgb), 0.5);
}

/* ── clôture : la carte "projet suivant", teintée par SON dégradé ── */
.project-pager {
  padding-bottom: clamp(4rem, 8vw, 6.5rem);
}
.project-next {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: clamp(2.4rem, 5.5vw, 4.4rem) clamp(1.5rem, 4.5vw, 4.4rem);
  border: 1px solid rgba(var(--paper-rgb), 0.09);
  border-radius: clamp(1.2rem, 2.4vw, 1.9rem);
  background:
    linear-gradient(158deg, rgba(4, 7, 15, 0.45) 0%, rgba(4, 7, 15, 0.9) 72%),
    var(--next-grad, var(--case-grad)) #04070f;
  transition:
    transform 0.35s var(--ease),
    box-shadow 0.35s var(--ease);
}
.project-next:hover {
  transform: translateY(-3px);
  box-shadow: 0 70px 140px -70px rgba(4, 7, 15, 0.8);
}
.project-next-init {
  position: absolute;
  z-index: 0;
  right: clamp(0.6rem, 2.5vw, 2.4rem);
  bottom: -0.16em;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(6rem, 15vw, 11rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  color: rgba(var(--paper-rgb), 0.05);
  pointer-events: none;
  user-select: none;
}
.project-next-lab {
  position: relative;
  z-index: 1;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(var(--paper-rgb), 0.6);
}
.project-next-titre {
  position: relative;
  z-index: 1;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.9rem, 4.2vw, 3.3rem);
  line-height: 1;
  color: var(--paper);
}
.project-next-meta {
  position: relative;
  z-index: 1;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: rgba(var(--paper-rgb), 0.72);
}
.project-next-fleche {
  display: inline-block;
  transition: transform 0.3s var(--ease);
}
.project-next:hover .project-next-fleche {
  transform: translateX(6px);
}
.project-pager-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.1rem;
}
.project-pager-prev {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--accent-2);
  transition: color 0.25s var(--ease);
}
.project-pager-prev:hover {
  color: var(--text);
}
.project-pager-back {
  flex: none;
  padding: 0.72rem 1rem;
  border: 1px solid rgba(var(--ink-rgb), 0.2);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  transition:
    background 0.25s var(--ease),
    color 0.25s var(--ease),
    border-color 0.25s var(--ease);
}
.project-pager-back:hover {
  color: var(--paper);
  border-color: var(--ink);
  background: var(--ink);
}
.project-not-found {
  min-height: 70vh;
  display: grid;
  place-items: center;
  padding: 8rem var(--marge);
}

/* Nav des études de cas : pilule de verre — la nav fixe reste lisible quand la
   carte d'encre du hero (ou celle du projet suivant) défile sous elle. Ces
   pages n'ont pas de bascule header.sur-video (réservée à la home). */
body[data-cas] .site-nav {
  top: 10px;
  left: clamp(10px, 1.8vw, 22px);
  right: clamp(10px, 1.8vw, 22px);
  padding: 10px clamp(14px, 2vw, 26px);
  border: 1px solid rgba(var(--ink-rgb), 0.09);
  border-radius: 999px;
  background: rgba(var(--paper-rgb), 0.78);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow: 0 18px 50px -30px rgba(var(--ink-rgb), 0.35);
}
/* Verrouillage contraste : le texte du CTA reprenait l'encre du fond (mesuré
   1:1 en headless, pastille visuellement vide) — on impose la paire ici. */
body[data-cas] .nav-cta {
  background: var(--ink);
  color: var(--paper);
}
body[data-cas] .nav-cta:hover,
body[data-cas] .nav-cta:focus-visible {
  background: var(--text);
  color: var(--paper);
}

/* Reveal projet : le bloc entre dans le viewport encore flou, puis sa mise au
   point accompagne sa montée. Le contenu final reste parfaitement net. */
.case-brief-page .case-reveal {
  opacity: 0;
  filter: blur(18px);
  transform: translate3d(0, 28px, 0) scale(0.985);
  transform-origin: 50% 50%;
  will-change: opacity, transform, filter;
  transition:
    opacity 0.72s var(--ease),
    transform 0.92s var(--ease),
    filter 0.95s var(--ease);
}
.case-brief-page .case-reveal.vu {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
.case-brief-page .project-point-grid .case-reveal:nth-child(2),
.case-brief-page .project-visual-grid .case-reveal:nth-child(2) {
  transition-delay: 0.08s;
}
.case-brief-page .project-point-grid .case-reveal:nth-child(3),
.case-brief-page .project-visual-grid .case-reveal:nth-child(3) {
  transition-delay: 0.16s;
}

@media (max-width: 900px) {
  .project-section {
    grid-template-columns: 1fr;
    gap: 2.2rem;
  }
  .project-section-heading h2 {
    max-width: 24ch;
  }
  .project-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .project-point-grid {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .project-lessons .project-point-grid {
    gap: 0;
  }
  .project-lesson {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  .project-point-filet {
    margin-bottom: 1rem;
  }
  .project-company {
    padding: clamp(1.6rem, 6vw, 2.8rem);
  }
}
@media (max-width: 620px) {
  .project-topline {
    align-items: flex-start;
  }
  .project-hero {
    padding-top: 2.6rem;
  }
  .project-hero h1 {
    /* Calibré sur le mot le plus long (« RelanceWork ») : `titres-lignes`
       enferme chaque mot dans un inline-block insécable (cf. note historique). */
    font-size: clamp(1.5rem, 7.6vw, 3.35rem);
  }
  .project-hero-init {
    font-size: 7rem;
  }
  .project-chapter {
    grid-template-columns: 1fr;
    gap: 0.35rem;
  }
  .project-visual-grid {
    grid-template-columns: 1fr;
  }
  .project-summary {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .project-summary-suite .btn {
    justify-content: center;
    width: 100%;
  }
  .project-next-init {
    font-size: 5rem;
  }
  .project-pager-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0.8rem;
  }
  .project-pager-prev {
    order: 2;
    text-align: center;
  }
  .project-pager-back {
    order: 1;
    text-align: center;
  }
}
@media (prefers-reduced-motion: reduce) {
  .case-brief-page .case-reveal,
  .case-brief-page .case-reveal.vu {
    opacity: 1;
    filter: none;
    transform: none;
    transition: none;
  }
}


/* ==========================================================================
   Curseur global Noxari — étoile active + orbite cinétique
   ========================================================================== */
@media (hover: hover) and (pointer: fine) {
  .cursor,
  .cursor-anneau {
    opacity: 0;
  }
  .cursor.is-visible,
  .cursor-anneau.is-visible {
    opacity: 1;
  }
  .cursor {
    width: 16px;
    height: 16px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    mix-blend-mode: difference;
    overflow: visible;
    transition:
      width 0.38s var(--ease),
      height 0.38s var(--ease),
      opacity 0.22s ease,
      border-color 0.3s ease,
      background-color 0.3s ease,
      box-shadow 0.38s var(--ease);
  }
  .cursor::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 13px;
    height: 13px;
    transform: translate(-50%, -50%);
    clip-path: polygon(
      50% 0,
      62% 37%,
      100% 50%,
      62% 63%,
      50% 100%,
      38% 63%,
      0 50%,
      38% 37%
    );
    background: linear-gradient(
      135deg,
      #fff 0%,
      var(--accent-soft) 58%,
      #fff 100%
    );
    box-shadow: 0 0 18px rgba(255, 255, 255, 0.56);
    transition:
      left 0.38s var(--ease),
      width 0.28s var(--ease),
      height 0.28s var(--ease),
      transform 0.18s var(--ease);
  }
  .cursor::after {
    content: "";
    position: absolute;
    inset: -7px;
    z-index: -1;
    border-radius: 50%;
    background: radial-gradient(
      circle,
      rgba(255, 255, 255, 0.2),
      transparent 66%
    );
    transform: scale(0.75);
    transition:
      transform 0.35s var(--ease),
      opacity 0.3s ease;
  }
  .cursor.grossi {
    width: 28px;
    height: 28px;
    border: 1px solid rgba(255, 255, 255, 0.68);
    background: rgba(255, 255, 255, 0.11);
    box-shadow: 0 0 24px rgba(255, 255, 255, 0.12);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    mix-blend-mode: difference;
  }
  .cursor.grossi::after {
    transform: scale(1.18);
  }
  .cursor.voir {
    width: 94px;
    height: 38px;
    border-color: rgba(255, 255, 255, 0.7);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.12);
    box-shadow:
      0 0 30px rgba(255, 255, 255, 0.1),
      inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .cursor.voir::before {
    left: 17px;
    width: 8px;
    height: 8px;
  }
  .cursor .etiquette {
    position: relative;
    z-index: 1;
    padding-left: 14px;
    font-family: var(--mono);
    font-size: 0.55rem;
    font-weight: 750;
    letter-spacing: 0.11em;
    line-height: 1;
    color: #fff;
    opacity: 0;
    transform: translateY(4px);
    transition:
      opacity 0.22s ease 0.05s,
      transform 0.35s var(--ease) 0.04s;
  }
  .cursor.voir .etiquette {
    opacity: 1;
    transform: translateY(0);
  }
  .cursor.pressed::before {
    transform: translate(-50%, -50%) scale(0.55) rotate(45deg);
  }

  .cursor-anneau {
    width: 44px;
    height: 44px;
    border: 1px solid rgba(255, 255, 255, 0.56);
    border-radius: 50%;
    background: transparent;
    box-shadow:
      inset 0 0 18px rgba(255, 255, 255, 0.04),
      0 0 22px rgba(255, 255, 255, 0.07);
    mix-blend-mode: difference;
    transition:
      width 0.5s var(--ease),
      height 0.5s var(--ease),
      border-radius 0.45s var(--ease),
      border-color 0.3s ease,
      opacity 0.22s ease;
  }
  .cursor-anneau::before {
    content: "";
    position: absolute;
    left: 50%;
    top: -3px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    transform: translateX(-50%);
    background: #fff;
    box-shadow: 0 0 12px rgba(255, 255, 255, 0.72);
  }
  .cursor-anneau::after {
    content: "";
    position: absolute;
    inset: 5px;
    border-radius: inherit;
    border-top: 1px solid rgba(255, 255, 255, 0.54);
    border-right: 1px solid transparent;
    transform: rotate(34deg);
  }
  .cursor-anneau.grossi {
    width: 66px;
    height: 66px;
    opacity: 0.72;
    border-color: rgba(255, 255, 255, 0.42);
  }
  .cursor-anneau.voir {
    width: 112px;
    height: 54px;
    border-radius: 999px;
    opacity: 0.38;
  }
  .cursor-anneau.pressed {
    border-color: rgba(255, 255, 255, 0.9);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cursor-anneau {
    display: none !important;
  }
  .cursor,
  .cursor::before,
  .cursor::after,
  .cursor .etiquette {
    transition: none !important;
  }
}

/* ==========================================================================
   HERO — parcours cinématique unifié
   Exposition → approche → immersion → signature → manifeste.
   Les états animés sont posés par initHeroScrollVideo ; ces règles ne font
   qu'isoler les couches et dessiner le HUD, sans deuxième moteur d'animation.
   ========================================================================== */
#scroll-title {
  /* Évite le flash du titre brut entre la sortie du loader et sa découpe en
     lettres. La classe est posée dans la même tâche JS que cette découpe. */
  visibility: hidden;
}
.hero-experience-ready #scroll-title {
  visibility: visible;
}
.hero-experience-ready .hero-mockup-float {
  animation: none;
}
.hero-experience-ready .hero-scroll-cue {
  transition: none;
}
.hero-experience-scroll .scroll-video-wrapper {
  perspective: 1600px;
}
.hero-experience-scroll .hero-mockup-box {
  transform-style: preserve-3d;
}
.hero-experience-scroll .hero-mockup-img,
.hero-experience-scroll #hero-scroll-video {
  backface-visibility: hidden;
}
.hero-video-fallback .video-loading {
  opacity: 0;
  visibility: hidden;
}

/* Repère de chapitres : volontairement discret, comme une légende de musée
   devenue interface de lecture lorsque la caméra traverse l'écran. */
.hero-journey {
  position: absolute;
  z-index: 14;
  right: clamp(1.25rem, 2.6vw, 2.75rem);
  /* ancré en bas plutôt que centré verticalement : au centre, ce repère de chapitre passait
     PILE sous le flanc "Studio" (grand corps italique) — texte illisible, superposé
     (constaté 2026-07-12). Pas tout en bas comme .hero-cartel (bas-gauche, en miroir) : le
     bouton "Son" (.son-toggle, position:fixed bas-droite) occupe déjà ce coin-là. */
  bottom: clamp(6rem, 12vh, 7.5rem);
  display: grid;
  grid-template-rows: auto 112px auto;
  justify-items: center;
  gap: 0.65rem;
  width: 24px;
  color: var(--ink);
  opacity: 0;
  pointer-events: none;
  transition: color 0.45s ease;
}
.hero-journey-index,
.hero-journey-percent {
  font-family: var(--mono);
  font-size: 0.58rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
}
.hero-journey-copy {
  position: absolute;
  right: 31px;
  top: 50%;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  white-space: nowrap;
  transform: translateY(-50%);
  text-align: right;
}
.hero-journey-copy small {
  font-family: var(--mono);
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 0.52;
}
.hero-journey-copy strong {
  font-family: var(--font-display);
  font-size: clamp(1rem, 1.3vw, 1.3rem);
  font-style: italic;
  font-weight: 400;
  line-height: 1;
}
.hero-journey-track {
  position: relative;
  display: block;
  width: 1px;
  height: 112px;
  overflow: hidden;
  background: rgba(var(--ink-rgb), 0.18);
  transition: background-color 0.45s ease;
}
.hero-journey-track i {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--accent-2);
  transform: scaleY(0);
  transform-origin: 50% 0%;
  will-change: transform;
}
.hero.is-immersed .hero-journey {
  color: var(--paper);
  text-shadow: 0 4px 18px rgba(2, 4, 10, 0.62);
}
.hero.is-immersed .hero-journey-track {
  background: rgba(var(--paper-rgb), 0.2);
}
/* le HUD chapitre n'existe que PENDANT la traversée (opacity:0 de base) : au
   repos, ce coin appartient à la phrase-manifeste « exposés avec soin »
   (composition "hublot" 2026-07-18 — collision constatée sinon). La visibilité
   n'est plus posée en inline par le JS, ce fondu CSS fait foi. */
.hero.hero-scrub .hero-journey {
  opacity: 1;
  transition:
    opacity 0.4s ease,
    color 0.45s ease;
}

/* Barres de cadrage très fines : elles n'apparaissent qu'au franchissement de
   l'écran et se retirent avant le manifeste, pour matérialiser le changement
   de chapitre sans réduire durablement la surface de la vidéo. */
.hero-letterbox {
  position: absolute;
  inset: 0;
  z-index: 9;
  overflow: hidden;
  pointer-events: none;
}
.hero-letterbox i {
  position: absolute;
  left: 0;
  width: 100%;
  height: clamp(12px, 2.4vh, 26px);
  display: block;
  background: #02040a;
  transform: scaleY(0);
  will-change: transform;
}
.hero-letterbox i:first-child {
  top: 0;
  box-shadow: 0 12px 34px rgba(2, 4, 10, 0.2);
}
.hero-letterbox i:last-child {
  bottom: 0;
  box-shadow: 0 -12px 34px rgba(2, 4, 10, 0.2);
}

@media (max-width: 1099px) {
  .hero-journey {
    display: none;
  }
}
@media (max-width: 820px), (prefers-reduced-motion: reduce) {
  .hero-journey,
  .hero-letterbox {
    display: none;
  }
  .hero-experience-static #scroll-title {
    visibility: visible;
  }
}

.hero-journey-index,
.hero-journey-percent,
.p3-of {
  display: none;
}

/* ───── WORK CARDS — Cartes éditoriales premium (homepage) ───── */
.works-liste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  perspective: 1200px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.work-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: 20px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transform-style: preserve-3d;
  transition:
    transform 0.6s cubic-bezier(0.33, 1, 0.68, 1),
    box-shadow 0.6s ease,
    border-color 0.5s ease;
  box-shadow:
    0 8px 40px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(237, 239, 246, 0.06);
}

.work-card-bg {
  position: absolute;
  inset: 0;
  background: var(--card-grad);
  z-index: 0;
  pointer-events: none;
}

.work-card-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse at 50% 0%,
      rgba(255, 255, 255, 0.06),
      transparent 60%
    ),
    linear-gradient(180deg, rgba(3, 5, 11, 0.1), rgba(3, 5, 11, 0.7));
  pointer-events: none;
  z-index: 1;
}

@keyframes cardSweep {
  0%,
  100% {
    background-position: 200% 50%;
    opacity: 0.3;
  }
  50% {
    background-position: -50% 50%;
    opacity: 0.6;
  }
}

.work-card-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    105deg,
    transparent 35%,
    rgba(255, 255, 255, 0.04) 45%,
    transparent 55%
  );
  background-size: 200% 100%;
  animation: cardSweep 8s ease-in-out infinite;
  pointer-events: none;
  z-index: 1;
}

.work-card-content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: clamp(1.8rem, 3.5vw, 2.6rem);
}

.work-card-cat {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.45);
  margin-bottom: 0.5rem;
  display: block;
}

.work-card-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 880;
  text-transform: uppercase;
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  line-height: 1.05;
  color: var(--paper);
  transition: transform 0.5s ease;
}

.work-card-desc {
  margin: 0.7rem 0 0 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: rgba(237, 239, 246, 0.72);
  max-width: 50ch;
  flex: 1;
}

.work-card-meta {
  margin-top: 1.5rem;
  padding-top: 1.2rem;
  border-top: 1px solid rgba(237, 239, 246, 0.08);
}

.work-card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin-bottom: 0.75rem;
}

.work-card-tags span {
  font-family: var(--mono);
  font-size: 0.55rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.55);
  border: 1px solid rgba(237, 239, 246, 0.12);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  background: rgba(7, 10, 20, 0.4);
  transition: all 0.3s ease;
}

.work-card:hover .work-card-tags span {
  border-color: rgba(237, 239, 246, 0.25);
  color: rgba(237, 239, 246, 0.75);
}

.work-card-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(237, 239, 246, 0.45);
}

.work-card-role-label {
  color: rgba(var(--accent-rgb), 0.55);
}

.work-card-role {
  color: rgba(237, 239, 246, 0.5);
}

.work-card-year {
  color: rgba(237, 239, 246, 0.35);
  font-weight: 600;
}

.work-card-cta {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.2rem;
  padding: 0.6rem 1.3rem;
  width: fit-content;
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--paper);
  background: rgba(237, 239, 246, 0.05);
  border: 1px solid rgba(237, 239, 246, 0.1);
  transition: all 0.4s var(--ease);
  opacity: 0;
  transform: translateY(8px);
}

.work-card-cta-arrow {
  display: inline-block;
  transition: transform 0.4s var(--ease);
}

.work-card:hover {
  transform: rotateX(-4deg) rotateY(3deg) translateY(-12px);
  box-shadow:
    0 24px 80px rgba(0, 0, 0, 0.5),
    0 0 60px rgba(var(--accent-rgb), 0.08),
    0 0 0 1px rgba(var(--accent-rgb), 0.2);
  border-color: rgba(var(--accent-rgb), 0.25);
}

.work-card:hover .work-card-title {
  transform: translateX(4px);
}

.work-card:hover .work-card-cta {
  opacity: 1;
  transform: translateY(0);
  background: rgba(var(--accent-rgb), 0.12);
  border-color: rgba(var(--accent-rgb), 0.35);
  color: var(--accent);
}

.work-card:hover .work-card-cta-arrow {
  transform: translateX(6px);
}

.work-card:active {
  transform: rotateX(-2deg) rotateY(1.5deg) translateY(-4px) scale(0.99);
}

.work-card:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
}

@media (max-width: 900px) {
  .works-liste {
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  }
}

@media (max-width: 640px) {
  .works-liste {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }
  .work-card-content {
    padding: clamp(1.5rem, 4vw, 2rem);
  }
  .work-card-cta {
    opacity: 1;
    transform: none;
    border-color: rgba(var(--accent-rgb), 0.2);
    background: rgba(var(--accent-rgb), 0.06);
  }
  .work-card:hover {
    transform: translateY(-4px);
    box-shadow:
      0 12px 40px rgba(0, 0, 0, 0.4),
      0 0 40px rgba(var(--accent-rgb), 0.06);
  }
  .work-card:hover .work-card-title {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .work-card,
  .work-card-bg::before,
  .work-card-title,
  .work-card-cta,
  .work-card-cta-arrow,
  .work-card-tags span {
    transition: none;
    animation: none;
  }
  .work-card:hover {
    transform: none;
    box-shadow:
      0 8px 40px rgba(0, 0, 0, 0.35),
      0 0 0 1px rgba(237, 239, 246, 0.06);
  }
  .work-card:hover .work-card-title {
    transform: none;
  }
  .work-card:hover .work-card-cta {
    opacity: 1;
    transform: none;
  }
}

/* ═══════════════════════════════════════════════════════════
   COLLECTION — index de l'exposition (accueil, remplace le
   gabarit "Selected Work" importé : faux projets, styles inline).
   Cartels de musée en lignes ; au survol, un tableau encadré
   (.apercu, composant existant remis en service) suit le curseur
   avec l'aperçu de la pièce — gradient ou vidéo produit réelle.
   ═══════════════════════════════════════════════════════════ */
.collection {
  position: relative;
  z-index: 1;
  padding: clamp(6rem, 13vh, 10.5rem) var(--marge);
  background: var(--bg);
}
.collection-tete,
.collection-liste {
  max-width: var(--max);
  margin-inline: auto;
}
.collection-tete {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1.5rem;
  margin-bottom: clamp(2.2rem, 5vh, 3.6rem);
}
.collection-titre {
  margin: 0.55rem 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.9rem, 5.3vw, 4.9rem);
  line-height: 1.02;
  color: var(--text);
}
.collection-lien {
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: none;
  /* cible tactile ≥44px sans bouger la ligne de base (padding compensé) */
  padding: 0.9rem 0.2rem;
  margin: -0.9rem -0.2rem;
  transition: color 0.3s var(--ease);
}
.collection-lien .fl {
  display: inline-block;
  transition: transform 0.35s var(--ease);
}
.collection-lien:hover {
  color: var(--accent-2);
}
.collection-lien:hover .fl {
  transform: translateX(5px);
}
.collection-liste {
  list-style: none;
  padding: 0;
  margin: 0;
  border-bottom: 1px solid var(--line);
}
.collection-ligne {
  position: relative;
  display: grid;
  grid-template-columns: 4.8rem minmax(0, 1fr) auto 3.8rem 1.5rem;
  align-items: center;
  gap: clamp(1.1rem, 2.6vw, 2.2rem);
  /* rembourrage latéral réel : le remplissage d'encre du survol s'appuie
     dessus (inset:0), sans lui la bande viendrait au ras du texte.
     Vertical volontairement généreux : ces quatre pièces sont le contenu
     le PLUS important d'un portfolio et n'occupaient qu'1,1 écran quand
     Services en prenait 7,5 — chaque ligne doit se lire comme une pièce
     accrochée, pas comme une rangée de tableau. */
  padding: clamp(2.1rem, 4.4vh, 3.4rem) clamp(0.9rem, 1.6vw, 1.6rem);
  border-top: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
}
/* ── Survol : la ligne se REMPLIT d'encre depuis la gauche et son texte
   s'inverse en crème. Repris du geste de index2.html (la ligne devient un
   bloc plein, pas un filet de 1px) : à cette échelle typographique, un
   soulignement de 1px ne se voyait tout simplement pas. Reste sans couleur
   — encre + crème — conformément à la passe de sobriété.
   ⚠️ Le remplissage ET l'inversion du texte sont gatés ENSEMBLE sur
   (hover:hover) : si seule la couleur passait sur un écran tactile, on
   obtiendrait du crème sur du sable, donc du texte invisible. ── */
.collection-ligne::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--ink);
  /* Le remplissage DESCEND (haut -> bas) au lieu de balayer de gauche a
     droite : la rangee se remplit comme un volet qui tombe. Sur des
     rangees larges et basses, le balayage horizontal partait trop loin du
     curseur ; la descente touche toute la largeur d'un coup. */
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.55s var(--ease);
}
/* les cellules repassent au-dessus du remplissage */
.cp-num,
.cp-titre,
.cp-cat,
.cp-annee,
.cp-fleche {
  position: relative;
  z-index: 1;
}
/* Neutralise la règle générique de boilerplate `li a:hover{opacity:.6}`
   (~L103, écrite avec des tokens --color-* qui n'existent plus dans la DA) :
   ces lignes sont des <a> dans des <li>, elles héritaient donc d'un
   estompage — le survol DÉ-mettait en avant l'élément pointé. */
.collection-ligne:hover,
.collection-ligne:focus-visible {
  opacity: 1;
}
.collection-ligne:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
  border-radius: 4px;
}
.cp-num {
  font-family: var(--mono);
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: var(--text-soft);
  transition:
    color 0.4s var(--ease),
    transform 0.55s var(--ease);
}
.cp-corps {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
  transition: transform 0.55s var(--ease);
}
.cp-titre {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.2rem, 4.4vw, 4.2rem);
  line-height: 1;
  color: var(--text);
  transition: color 0.4s var(--ease);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cp-desc {
  display: block;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 52ch;
  transition: color 0.4s var(--ease);
  /* La plus longue accroche fait 128 caractères (2 lignes), les autres une
     seule : on borne à 2 lignes ET on réserve toujours cette hauteur, sinon
     les rangées sont inégales de 25px (mesuré) et la liste lit de travers. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 3em;
}
.cp-cat,
.cp-annee {
  font-family: var(--mono);
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: right;
  transition: color 0.4s var(--ease);
}
.cp-fleche {
  font-size: 1.15rem;
  line-height: 1;
  color: var(--text-soft);
  opacity: 0;
  transform: translateX(-10px) rotate(-45deg);
  transition:
    opacity 0.35s var(--ease),
    transform 0.55s var(--ease),
    color 0.4s var(--ease);
}

/* état actif — décrit une fois, appliqué au survol (pointeur fin) et au
   focus clavier. Le décalage se fait en transform et non en padding :
   même lecture de "poussée" que la référence, sans reflow à chaque frame. */
.collection-ligne:focus-visible::before {
  transform: scaleY(1);
}
.collection-ligne:focus-visible :is(.cp-titre, .cp-desc, .cp-num, .cp-cat, .cp-annee) {
  color: var(--paper);
}
.collection-ligne:focus-visible :is(.cp-num, .cp-corps) {
  transform: translateX(clamp(10px, 1.4vw, 22px));
}
.collection-ligne:focus-visible .cp-fleche {
  opacity: 1;
  color: var(--paper);
  transform: translateX(0) rotate(0deg);
}
@media (hover: hover) {
  .collection-ligne:hover::before {
    transform: scaleY(1);
  }
  .collection-ligne:hover :is(.cp-titre, .cp-desc, .cp-num, .cp-cat, .cp-annee) {
    color: var(--paper);
  }
  .collection-ligne:hover :is(.cp-num, .cp-corps) {
    transform: translateX(clamp(10px, 1.4vw, 22px));
  }
  .collection-ligne:hover .cp-fleche {
    opacity: 1;
    color: var(--paper);
    transform: translateX(0) rotate(0deg);
  }
}

/* ── Le tableau flottant : .apercu remis en service sur fond clair ──
   Le composant date de la DA sombre (ombre noire lourde, liseré blanc) ;
   on l'assoit sur le sable et on cale son cartouche sur la DA actuelle.
   La transition transform d'origine est retirée : le suivi du curseur
   écrit le transform via GSAP à chaque frame, une transition CSS
   par-dessus ferait un double lissage pâteux. */
.apercu {
  left: 0;
  top: 0;
  transition: opacity 0.3s;
  border-color: rgba(var(--ink-rgb), 0.22);
  box-shadow: 0 30px 80px -28px rgba(31, 42, 58, 0.45);
}
.apercu .visuel {
  flex-direction: column;
  gap: 0.55rem;
  font-family: var(--display);
  font-size: clamp(1.5rem, 2vw, 2.1rem);
  font-weight: 400;
  letter-spacing: 0.04em;
  font-variation-settings: normal;
  text-align: center;
  padding: 1rem;
}
.apercu .visuel small {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.68);
}
/* même losange que .work-fin-diamant (écran de fin de la galerie 3D) : le
   tableau flottant porte la même marque que le "fin de l'exposition" qu'on
   vient de croiser, qu'on le rencontre sur l'accueil ou dans l'index. */
.visuel-diamant {
  position: absolute;
  left: 1rem;
  bottom: 1rem;
  width: 10px;
  height: 10px;
  border: 1.5px solid rgba(255, 255, 255, 0.7);
  transform: rotate(45deg);
  pointer-events: none;
}
.apercu video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 720px) {
  .collection-tete {
    flex-direction: column;
    align-items: flex-start;
  }
  .collection-ligne {
    grid-template-columns: 3.4rem minmax(0, 1fr) 1.4rem;
    grid-template-areas:
      "num titre fleche"
      "num meta fleche";
    row-gap: 0.35rem;
  }
  .cp-num { grid-area: num; }
  .cp-corps { grid-area: titre; }
  .cp-titre {
    white-space: normal;
  }
  .cp-cat {
    grid-area: meta;
    text-align: left;
  }
  .cp-annee { display: none; }
  .cp-fleche {
    grid-area: fleche;
    opacity: 1;
    transform: none;
    align-self: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* ::before depuis la refonte du survol (c'était ::after du temps du
     soulignement de 1px) */
  .collection-ligne::before,
  .cp-num,
  .cp-titre,
  .cp-fleche,
  .collection-lien .fl {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════
   HERO — « la traversée de l'écran » (2026-07-17) : le zoom scrubé
   existait mais lisait plat (la pièce s'estompait sans reculer, le
   cue rapetissait au lieu de venir vers soi, aucun seuil marqué).
   Trois ajouts : dolly-parallaxe des textes muraux qui GLISSENT hors
   champ, cue qui grandit vers la caméra, éclat de verre au moment où
   on franchit l'écran. Chorégraphie dans initHeroScrollVideo (hero.js).
   ═══════════════════════════════════════════════════════════ */
/* pendant le scrub, GSAP écrit x/y/opacity sur les textes muraux à chaque frame :
   les transitions 1s de leur cascade d'entrée (body.is-loaded, cf. plus haut)
   lisseraient chaque écriture en bouillie — coupées net dès que .hero-scrub est posé
   (même famille de garde que la pause du flottement .hero-mockup-float). */
.hero.hero-scrub .hero-scene-kicker,
.hero.hero-scrub .hero-scene-mot,
.hero.hero-scrub .hero-cartel,
.hero.hero-scrub .hero-invite,
.hero.hero-scrub .hero-entrer {
  transition: none;
}
/* éclat de traversée : bande diagonale 115° (le même axe signature que le loader
   et les vitres de la galerie), balayée en travers du viewport par la timeline */
.hero-verre {
  position: absolute;
  inset: -12%;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    115deg,
    transparent 34%,
    rgba(255, 255, 255, 0.34) 46%,
    rgba(191, 231, 242, 0.48) 50%,
    rgba(255, 255, 255, 0.26) 54%,
    transparent 66%
  );
  will-change: transform, opacity;
}
.hero-experience-static .hero-verre {
  display: none;
}

/* ═══════════════════════════════════════════════════════════
   SERVICES — "Plan des salles" (2026-07-24) : remplace la section
   .svc3 (accordéon + immeuble isométrique Three.js), retirée d'ici
   ET de assets/js/sections/. Les services deviennent les salles d'une
   exposition — même langage que le hero (cartel) et la collection
   (œuvres, pièces). Dessin au trait en SVG, AUCUN WebGL : c'est ce
   qui supprime d'un coup toute la classe de bugs 3D (occlusion,
   fog, contextes qui fuient) et rend la section utilisable partout.
   Le pin n'épingle que .svcp-stage — le CTA vit hors de la boîte
   épinglée, dans le flux (cf. leçon du relâchement de pin).
   Tokens = les vrais du site ; var(--display) rend en Syne ici
   (home uniquement, cf. bloc "Typographie Home" plus haut).
   ═══════════════════════════════════════════════════════════ */
.svcp {
  position: relative;
  /* aucun padding HAUT ici : c'est .svcp-stage qui est épinglé et qui fait
     100svh — un padding sur la section décalait la scène vers le bas et
     poussait sa fin hors du viewport (mesuré : 43px de décalage). La réserve
     pour le header fixe vit dans .svcp-stage, à l'intérieur des 100svh. */
  padding: 0 clamp(1.6rem, 4vw, 3rem) clamp(2rem, 5vh, 3.5rem);
  background: linear-gradient(
    168deg,
    var(--paper) 0%,
    var(--bg) 58%,
    var(--bg-alt) 100%
  );
}
.svcp-stage {
  position: relative;
  min-height: 100svh;
  box-sizing: border-box;
  /* le header du site est en position:fixed et la scène est épinglée à
     'top top' : sans cette réserve, l'eyebrow passe DERRIÈRE le logo
     (constaté en capture sur un écran court). */
  padding-top: clamp(3.6rem, 8vh, 5.2rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* ── titre ── */
/* Grille à deux colonnes plutôt que flex-wrap : en flex, le sous-titre
   passait sous le titre dès qu'il manquait quelques pixels tout en gardant
   son text-align:right, ce qui donnait un bloc aligné à droite posé à
   gauche (bord gauche en dents de scie, constaté en capture). En grille,
   la disposition est déterministe — deux colonnes, ou une seule sous 900px
   avec l'alignement qui suit. */
.svcp-tete {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto clamp(1.1rem, 2.4vh, 2rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 34ch);
  align-items: end;
  gap: 1.4rem 2.4rem;
}
.svcp-titre {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(2.1rem, 4.3vw, 3.4rem);
  line-height: 1;
  letter-spacing: -0.02em;
  margin-top: 0.5rem;
}
/* Titre entièrement en encre, comme "Pièces choisies" plus bas : le
   terracotta est désormais réservé à l'ÉTAT (la salle en cours et son
   cartel). Un accent qui colore aussi le décor statique n'est plus un
   accent, c'est une couleur de plus. */
.svcp-titre em {
  font-style: normal;
  color: inherit;
}
.svcp-sub {
  color: var(--text-muted);
  font-size: 0.92rem;
  line-height: 1.6;
  max-width: 40ch;
  text-align: right;
}

/* ── le plan + le cartel ──
   minmax(0,…) sur les deux colonnes : un item de grille a min-width:auto
   par défaut, ce qui laisse son contenu élargir la piste au-delà de la
   place réelle (bug d'overflow déjà rencontré sur ce site). */
.svcp-corps {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: clamp(1.6rem, 3.4vw, 3.2rem);
  align-items: center;
}

/* ── plan au trait ── */
.svcp-plan {
  margin: 0;
  min-width: 0;
}
.svcp-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}
.svcp-murs path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: square;
}
.svcp-murs .svcp-mur-ext {
  stroke-width: 3.5;
}
.svcp-lavis rect {
  fill: transparent;
  transition: fill 0.55s var(--ease);
}
.svcp-lavis rect.actif {
  /* 0.1 de bleu sur du sable se lisait gris : la salle en cours ne se
     distinguait pas des autres */
  fill: rgba(21, 106, 153, 0.16);
}

/* salles cliquables : le <rect> sert de zone de clic pleine salle */
.svcp-salle {
  cursor: pointer;
}
.svcp-salle > rect {
  fill: transparent;
  transition: fill 0.35s var(--ease);
}
.svcp-salle:hover > rect {
  fill: rgba(29, 37, 49, 0.045);
}
/* le contour par défaut du navigateur se déclenche sur :focus (donc aussi
   au clic souris) et se superposait à la salle active — on le neutralise
   sur :focus tout court, et on ne dessine notre propre repère que sur
   :focus-visible, c'est-à-dire au clavier. */
.svcp-salle:focus {
  outline: none;
}
.svcp-salle:focus-visible > rect {
  fill: rgba(63, 169, 224, 0.16);
  stroke: var(--accent-2);
  stroke-width: 2;
}
.svcp-num {
  font-family: var(--display);
  font-weight: 800;
  /* Le numero n'est qu'un INDEX de salle ; c'est le nom qui porte
     l'information. A 27px face a un nom de 10,5px (rapport 2,57) il ecrasait
     le libelle qu'on vient reellement lire. Rapport ramene a 1,9.
     ⚠️ Ne PAS toucher a la regle mobile (<620px) : elle masque le nom et
     grossit volontairement le numero, le nom etant relu dans le cartel. */
  font-size: 23px;
  fill: var(--text-soft);
  text-anchor: middle;
  transition: fill 0.45s var(--ease);
}
.svcp-nom {
  font-family: var(--mono);
  /* 10,5px avec 0.18em d'approche, c'etait illisible : a cette taille une
     approche aussi ouverte disloque le mot */
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.18em;
  fill: var(--text-soft);
  text-anchor: middle;
  transition: fill 0.45s var(--ease);
}
.svcp-salle.actif .svcp-num,
.svcp-salle.actif .svcp-nom {
  fill: var(--secondaire);
}

.svcp-entree path {
  fill: none;
  stroke: var(--text-soft);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.svcp-entree text {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  fill: var(--text-soft);
  text-anchor: middle;
}

/* parcours : pointillé complet en fond, trait plein qui se dessine dessus */
.svcp-trace-fond {
  fill: none;
  stroke: var(--text-soft);
  stroke-width: 1.6;
  stroke-dasharray: 2 7;
  stroke-linecap: round;
  opacity: 0.7;
}
.svcp-trace {
  fill: none;
  stroke: var(--secondaire);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.svcp-ici-pt {
  fill: var(--secondaire);
}
.svcp-ici-halo {
  fill: rgba(21, 106, 153, 0.18);
  transform-origin: center;
  animation: svcpPouls 2.4s ease-in-out infinite;
}
@keyframes svcpPouls {
  0%,
  100% {
    opacity: 0.75;
  }
  50% {
    opacity: 0.2;
  }
}

.svcp-legende {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  margin-top: 1rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.svcp-legende i {
  display: inline-block;
  width: 18px;
  vertical-align: middle;
  margin-right: 0.55em;
}
.svcp-cle-trace {
  border-top: 2px dashed currentColor;
}
.svcp-cle-salle {
  height: 9px;
  background: rgba(21, 106, 153, 0.14);
  border: 1px solid var(--secondaire);
}

/* ── cartel de musée : toutes les fiches dans la même cellule de grille,
   donc la boîte prend la hauteur de la plus grande — pas de position
   absolue ni de min-height deviné. ── */
.svcp-cartel {
  display: grid;
  min-width: 0;
}
.svcp-fiche {
  /* ⚠️ `min-width: 0` : un enfant de grille (ou de flex) vaut `min-width: auto`
     par defaut, c'est-a-dire qu'il REFUSE de descendre sous la largeur de son
     contenu. Sur petit ecran il deborde alors du cadre au lieu de se
     comprimer. Mesure avant/apres : 32px de debordement a 320px */
  min-width: 0;
  grid-area: 1 / 1;
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 3px solid var(--secondaire);
  padding: clamp(1.3rem, 2.4vw, 1.9rem);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition:
    opacity 0.5s var(--ease),
    transform 0.5s var(--ease),
    visibility 0.5s var(--ease);
}
.svcp-fiche.actif {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.svcp-fiche-salle {
  display: block;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--secondaire);
  margin-bottom: 0.7rem;
}
.svcp-fiche-nom {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.35rem, 2.3vw, 1.85rem);
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--text);
  margin: 0 0 0.7rem;
}
.svcp-fiche-desc {
  color: var(--text-muted);
  font-size: 0.9rem;
  line-height: 1.6;
  margin: 0;
}
.svcp-fiche-meta {
  margin: 1.1rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0.75rem;
}
.svcp-fiche-meta dt {
  font-family: var(--mono);
  font-size: 0.57rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin-bottom: 0.28rem;
}
.svcp-fiche-meta dd {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text);
}

/* ── CTA de clôture (hors du stage épinglé) ── */
.svcp-cta {
  position: relative;
  z-index: 2;
  max-width: 1440px;
  margin: clamp(2rem, 5vh, 3.5rem) auto 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.5rem;
  padding: clamp(1.6rem, 3.4vw, 2.4rem);
  border: 1px solid var(--line);
  border-radius: 4px;
  background: rgba(250, 247, 240, 0.5);
}
.svcp-cta-txt h3 {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.4rem, 2.4vw, 1.9rem);
  line-height: 1.05;
  margin-bottom: 0.4rem;
}
.svcp-cta-txt p {
  color: var(--text-muted);
  font-size: 0.88rem;
  max-width: 46ch;
}

/* Écrans courts (portables 13-14" : ~700-800px de haut, le cas de la
   capture qui a motivé cette passe). La scène est épinglée donc TOUT doit
   tenir dans la hauteur — on reprend la place sur le titre et les
   rembourrages, pas sur le plan ni sur le texte du cartel qui sont le
   contenu réel. */
@media (min-width: 901px) and (max-height: 860px) {
  .svcp-titre {
    font-size: clamp(1.7rem, 3vw, 2.5rem);
  }
  .svcp-sub {
    font-size: 0.85rem;
  }
  .svcp-tete {
    margin-bottom: clamp(0.7rem, 1.6vh, 1.2rem);
  }
  .svcp-corps {
    gap: clamp(1.2rem, 2.6vw, 2.4rem);
  }
  .svcp-fiche {
    padding: clamp(1rem, 1.9vw, 1.5rem);
  }
  .svcp-fiche-nom {
    font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  }
  .svcp-fiche-meta {
    margin-top: 0.85rem;
    padding-top: 0.8rem;
    gap: 0.55rem;
  }
  .svcp-legende {
    margin-top: 0.7rem;
  }
}

@media (max-width: 900px) {
  .svcp-stage {
    min-height: 0;
    padding-top: 0;
  }
  .svcp-tete {
    grid-template-columns: 1fr;
  }
  .svcp-corps {
    grid-template-columns: 1fr;
  }
  .svcp-tete {
    flex-direction: column;
    align-items: flex-start;
  }
  .svcp-sub {
    text-align: left;
  }
  .svcp-cta {
    flex-direction: column;
    align-items: flex-start;
  }
}
/* sous ~620px les noms de salle deviennent illisibles dans le plan
   (le SVG est mis à l'échelle) : on ne garde que les numéros, le nom
   complet reste lu dans le cartel juste en dessous. */
@media (max-width: 620px) {
  .svcp-nom {
    display: none;
  }
  .svcp-num {
    font-size: 34px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .svcp-ici-halo {
    animation: none;
  }
}
@media (max-width: 820px) {
  .hero-verre {
    display: none;
  }
}
/* viewports larges et courts (ratio ≥ 1.8) : le laptop (64vw de large) monte
   jusqu'à ~90px du haut — exactement sous le header. Aucune place pour le kicker
   entre les deux (constaté en capture : texte à cheval sur la coque, coupé par
   l'encoche). Même logique de renoncement que le masquage mobile : pas la place,
   on retire, la scène reste lisible sans lui. */
@media (min-aspect-ratio: 9/5) {
  .hero-scene-kicker {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════════
   SOBRIÉTÉ CHROMATIQUE — home (2026-07-24, "il y a beaucoup trop de
   couleur sur l'ensemble du site").
   Les eyebrows (.section-num) sont repeints en --accent-soft par un bloc
   tardif hérité du thème NUIT d'origine (cf. ~L9414, voisin d'un .marquee
   au fond rgba(7,12,26,…) : ce bloc a été écrit pour un fond sombre).
   Sur le sable actuel, ce bleu pâle #bfe3f5 tombe à ~1,4:1 de contraste —
   donc à la fois trop coloré ET quasi illisible. On le neutralise ici
   seulement sur la home et hors #parcours (dont le panneau sombre garde
   légitimement des libellés clairs, et dont les règles sont plus
   spécifiques que celles-ci, donc intactes).
   Même doctrine que le commentaire de la DA v3 plus haut dans ce fichier :
   une couleur dominante + un accent RARE. La couleur est désormais
   réservée à ce qui porte un état (salle en cours, focus clavier), pas au
   décor qui se répète à chaque section.
   ═══════════════════════════════════════════════════════════ */
main[data-barba-namespace="home"] .section-num {
  color: var(--text-muted);
}
main[data-barba-namespace="home"] .section-num::after {
  background: linear-gradient(90deg, var(--line), transparent);
}

/* ═══════════════════════════════════════════════════════════
   COMPÉTENCES — carrousel de PLANCHES TECHNIQUES (2026-07-25).
   Remplace la liste de rangées (elle-même successeur du nuage épinglé) :
   deux listes à remplissage d'encre qui se suivaient — Projets puis
   Compétences — se ressemblaient trop. Et surtout le contenu s'adressait
   à un recruteur (noms de technos + pastilles de niveau auto-évaluées),
   pas à un client.
   Chaque planche DESSINE ce que la compétence produit — fenêtre découpée
   en composants, maquette cotée, parcours d'achat, schéma de base,
   workflow — dans le MÊME langage au trait que le plan des salles. Le
   site a donc deux dessins techniques (ce que je vends / ce avec quoi je
   le fais) et des listes typographiques pour les œuvres.
   Défilement horizontal LIBRE (glisser + molette + aimantation), sans
   pin : un pin ici réintroduirait exactement la fragilité qui avait fait
   disparaître cette section (cf. refreshPriority plus haut).
   ═══════════════════════════════════════════════════════════ */
.comp-titre {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(2rem, 4.1vw, 3.2rem);
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 0.6rem 0 clamp(1.8rem, 4vh, 2.8rem);
}
.comp-titre em {
  font-style: normal;
  color: inherit;
}
/* ═══════════════════════════════════════════════════════════
   LA TABLE À DESSIN — un plan de travail fixe sur lequel les feuilles se
   balayent l'une par-dessus l'autre (2026-07-26).

   Le cadre ne bouge jamais : c'est le contenu qui est remplacé, comme un
   calque qu'on fait glisser sur une table. Le clip-path ouvre le
   CONTENANT, le stroke-dashoffset remplit le CONTENU — jamais les deux en
   même temps (bloc « Chaque planche SE DESSINE » plus bas).

   ⚠️ AUCUN ScrollTrigger, aucun pin. Le `position: sticky` du plateau fait
   tout le positionnement ; le JS ne fait que LIRE une progression et poser
   --p. C'est un pin qui avait fait disparaître cette section.
   ═══════════════════════════════════════════════════════════ */
.tab {
  /* Segment d'une planche = sa hauteur (le balayage, au pixel près) + une
     pose fixe (la lecture). La hauteur totale de .tab est posée EN JS à
     partir des hauteurs MESURÉES : les feuilles sont dimensionnées par leur
     contenu, et un calc() en vh se désynchroniserait en silence dès qu'un
     cartel dépasse son min-height.
     La durée de pose vit dans competences-table.js (POSE) et nulle part
     ailleurs — le CSS ne sait pas résoudre un vh de custom property en px,
     deux sources dériveraient. */
  /* UNE seule taille de feuille pour les cinq. Des hauteurs différentes
     faisaient dépasser les feuilles du fond par le haut ET par le bas du
     cadre de celle de devant : deux bandes parasites en permanence. Un jeu de
     planches, c'est un format unique — c'est ce qui en fait un jeu. */
  --feuille: clamp(400px, 66vh, 620px);
  position: relative;
}
.tab-plateau {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  place-items: center;
  /* ni fond ni bordure : c'est la FEUILLE qui porte le papier. Le sable de
     la section reste visible tout autour — on doit voir une feuille posée
     sur une table, pas un bloc collé aux bords de l'écran.
     Le rembourrage HAUT dégage le header fixe : sans lui, le plateau colle à
     top:0 et les feuilles passent dessous. */
  padding: clamp(4.5rem, 10vh, 6.5rem) 0 clamp(1.6rem, 4vh, 3rem);
  /* la feuille qui arrive vient de SOUS le cadre : la table la borne. Le
     plateau fait exactement la fenêtre, donc ce recadrage est invisible —
     il ne sert qu'à cacher la feuille avant qu'elle monte. */
  overflow: hidden;
}
.tab-feuilles {
  position: relative;
  width: 100%;
  display: grid;
  place-items: center;
  /* la profondeur est réelle : les feuilles déjà lues reculent en Z */
  /* une focale plus courte creuse la profondeur : à 1500px le recul se
     lisait à peine, la pile paraissait plate */
  perspective: 1050px;
  perspective-origin: 50% 46%;
}

.planche {
  /* toutes les feuilles dans la MÊME cellule de grille : elles se
     superposent sans position absolue ni décalage à compenser */
  grid-area: 1 / 1;
  z-index: var(--i, 0);
  position: relative;
  width: 100%;
  /* format de feuille COMMUN aux cinq planches (voir le bloc formats) */
  max-width: 1180px;
  margin: 0;
  display: grid;
  align-items: center;
  gap: clamp(1.2rem, 2.6vw, 2.6rem);
  padding: clamp(1.5rem, 3vw, 2.6rem);
  border: 1px solid var(--line);
  /* coins nets : une feuille de calque n'a pas de rayon */
  background: var(--paper);
  /* ── ARRIVÉE EN TRANSFORM PUR, PAS EN CLIP-PATH ────────────────────
     Essayé et abandonné : révéler la feuille par `clip-path: inset()` avec
     une arête bleue posée sur la limite. Vu à l'écran, ça ne marche pas —
     les deux feuilles ont la MÊME couleur de papier, donc le masque est
     invisible et la seule chose qu'on perçoit est du TEXTE COUPÉ EN DEUX à
     la limite (« PLANCHE 03 » tranché à mi-hauteur, un cadre de dessin vide
     posé sur le contenu d'en dessous). Ça lit comme un bug d'affichage, pas
     comme un calque qu'on pose. Le défaut est dans la méthode, pas dans son
     réglage : masquer une feuille par une autre de couleur identique est
     invisible par construction.
     Ici la feuille arrive ENTIÈRE — elle glisse sur la table depuis sous le
     cadre en se redressant. Rien n'est jamais tronqué, et `transform` est
     composé par le GPU : pas de repeint du texte à chaque image, donc pas de
     scintillement des bords pendant le scrub.
       --p     : 0 = sous le cadre, 1 = posée
       --recul : nombre de feuilles posées par-dessus (la profondeur) */
  transform:
    /* 100vh et PAS un % de la feuille : le plateau est plus haut qu'une
       feuille, donc un décalage relatif à la feuille laissait celles qui n'ont
       pas encore monté dépasser par le bas du cadre — et comme leur z-index
       est supérieur, elles s'affichaient PAR-DESSUS la feuille courante. */
    translateY(calc((1 - var(--p, 1)) * 100vh + var(--recul, 0) * 7px))
    translateZ(calc((1 - var(--p, 1)) * -60px + var(--recul, 0) * -40px))
    /* elle se RABAT sur la table : encore inclinée en entrant dans le cadre,
       à plat quand elle se pose */
    rotateX(calc((1 - var(--p, 1)) * 13deg));
  box-shadow:
    0 1px 0 rgba(20, 27, 38, 0.05),
    0 3px 12px -8px rgba(20, 27, 38, 0.22),
    0 36px 64px -52px rgba(20, 27, 38, 0.6);
  will-change: transform;
}

/* ── LE RÉGLET : le té qu'on pose sur la feuille. Il se tire d'un bord à
   l'autre du bord haut à mesure que la feuille monte, et reste en place une
   fois posée. C'est le seul endroit de la page où le bleu devient un trait
   plein et continu — c'est ce qui lui donne son poids.
   ⚠️ `scaleX` et pas `width` : une largeur animée relance le layout à chaque
   image. Et contrairement à l'arête de clip-path qu'il remplace, il ne
   traverse JAMAIS le contenu — il ne peut donc rien trancher. ── */
.planche::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -1px;
  height: 1px;
  background: var(--secondaire);
  transform: scaleX(var(--p, 1));
  transform-origin: left center;
  pointer-events: none;
  /* UNIQUEMENT sur la feuille de devant. Les feuilles reculées gardaient
     chacune son réglet : quatre traits bleus empilés au-dessus de la feuille
     courante, ça lit comme des doublons mal calés, pas comme de la
     profondeur. Le calc s'annule dès le premier cran de recul. */
  opacity: calc(1 - var(--recul, 0));
}

/* ── LE VOILE : les feuilles du fond s'assombrissent d'un cran à chaque
   feuille posée par-dessus. Avec un format unique, le recul en Z ne suffit
   plus à faire lire la profondeur (toutes les feuilles ont la même taille) ;
   la lumière, si. Encre pure à très faible alpha — aucune teinte fabriquée,
   et surtout aucun mix-blend-mode.
   `position: absolute` : sans ça le pseudo-élément deviendrait une CELLULE de
   la grille de la feuille et casserait la composition. ── */
.planche::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(20, 27, 38, 1);
  opacity: calc(var(--recul, 0) * 0.055);
  pointer-events: none;
  z-index: 2;
}

/* ── UN SEUL GABARIT ────────────────────────────────────────────────
   Même largeur ET même hauteur pour les cinq. `height` et pas `min-height` :
   avec un plancher, le contenu reprenait la main et les hauteurs redevenaient
   inégales (549 / 519 / 436 / 519 / 436), donc les feuilles du fond
   dépassaient du cadre de celle de devant.
   La seule variation de composition : le dessin change de côté d'une planche
   à l'autre. À la fin de chaque montée la composition se réorganise, ce qui
   marque le changement de feuille sans rien ajouter à l'écran. Le rythme, lui,
   vient de la durée de pose, qui suit la longueur du cartel
   (competences-table.js). ── */
.planche[data-format] {
  height: var(--feuille);
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
}
.planche:nth-child(odd) .planche-cartel { order: 1; }
.planche:nth-child(odd) .planche-dessin { order: 2; }
.planche:nth-child(even) .planche-dessin { order: 1; }
.planche:nth-child(even) .planche-cartel { order: 2; }

.planche-dessin {
  position: relative;
  width: 100%;
  border: 1px solid var(--line);
  background: var(--bg);
  aspect-ratio: 420 / 300;
  overflow: hidden;
  min-width: 0;
}
.planche-dessin svg {
  display: block;
  width: 100%;
  height: 100%;
}
.planche-cartel {
  min-width: 0;
}

/* ── vocabulaire du trait, commun aux cinq planches ── */
.pl-cadre {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
}
.pl-trait {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.2;
}
.pl-boite {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
}
.pl-fill {
  fill: rgba(29, 37, 49, 0.07);
}
.pl-accent-fill {
  fill: rgba(21, 106, 153, 0.13);
}
.pl-pt {
  fill: rgba(29, 37, 49, 0.28);
}
.pl-guide {
  stroke: rgba(29, 37, 49, 0.13);
  stroke-width: 1;
  stroke-dasharray: 3 5;
}
.pl-cote {
  stroke: var(--text-soft);
  stroke-width: 1;
}
.pl-pointille {
  fill: none;
  stroke: var(--text-soft);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
}
.pl-fleche {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pl-lab {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  fill: var(--text-muted);
}
.pl-lab-accent {
  fill: var(--accent-2);
}
.pl-champ {
  font-family: var(--mono);
  font-size: 12px;
  fill: var(--text);
}
.pl-cle {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  fill: var(--text-soft);
}

/* ── cartel sous la planche : le bénéfice CLIENT d'abord, les technos
   en note de bas de cartel pour le prospect technique ── */
.planche-cartel {
  padding-top: 1.05rem;
}
.planche-num {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-bottom: 0.5rem;
}
.planche-titre {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.6rem, 3.4vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  margin: 0 0 0.5rem;
}
.planche-quoi {
  font-size: clamp(0.95rem, 1.4vw, 1.2rem);
  line-height: 1.6;
  max-width: 42ch;
  color: var(--text);
  margin: 0 0 0.7rem;
}
.planche-outils {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--text-soft);
  margin: 0;
}


/* ── Lettres du titre de pièce : .rv-l/.rv-li viennent de decoupeLettres()
   (lib/text-split.js), dont le CSS d'origine est scopé à
   [data-reveal="letters"] — il faut donc leur redonner un display ici,
   sinon les spans restent inline et aucun transform ne s'applique.
   L'overflow:hidden sur .rv-l masque le bas de la lettre pendant le saut,
   ce qui donne l'impression qu'elle sort du plancher de la ligne. ── */
.cp-titre .rv-l {
  display: inline-block;
  /* PAS d'overflow:hidden ici : la boîte de la lettre fait la hauteur de la
     ligne, donc masquer coupait la lettre en plein saut et le mot paraissait
     cassé (constaté en capture, "RelanceWork" illisible à mi-vague). Sans
     masque, la lettre reste entière et le geste se lit comme une vague. */
}
.cp-titre .rv-li {
  display: inline-block;
  will-change: transform;
}
/* Titres de pièces : Syne en sentence case, plus lourd que le 400 d'origine
   (herite d'Instrument Serif) — avec UNE seule famille, c'est la graisse et
   la casse qui separent les registres, plus le choix de la police. */
.collection-titre,
.cp-titre {
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* ═══════════════════════════════════════════════════════════
   REVEAL MASQUÉ DES GRANDS TITRES (2026-07-25, "un effet
   d'apparition comme lenis.dev").
   Constat qui a motivé la passe : les grands titres NE S'ANIMAIENT PAS
   (.svcp-titre, .collection-titre, .p3-titre : opacité 1, aucun
   transform), sauf .comp-titre qui héritait par accident du reveal de
   bloc via le sélecteur large `#competences .contenu > *` — donc un
   flou de 14px lourd sur un titre, et rien sur les autres.
   Ici : chaque mot remonte depuis derrière un bord qui le rogne, avec un
   retard par LIGNE (cf. lib/titres-lignes.js). Aucun flou — c'est net
   qui fait premium, le flou lourd salit les grandes lettres.
   ═══════════════════════════════════════════════════════════ */
.titre-lignes .ln-mot {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* le masque rognerait les jambages (p, g, j, y) si la boîte s'arrêtait
     à la hauteur du texte : on l'agrandit vers le bas et on annule le
     décalage par une marge négative. */
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}
.titre-lignes .ln-mot-i {
  display: inline-block;
  transform: translateY(108%);
  transition: transform 0.92s var(--ease) var(--ln-d, 0ms);
  will-change: transform;
}
.titre-lignes.vu-lignes .ln-mot-i {
  transform: none;
}

/* Le reveal de bloc existant portait un flou de 14px : très lourd sur de
   grandes lettres, ça lit comme un défaut de rendu plutôt que comme une
   entrée. Ramené à 6px — l'entrée se sent, le texte reste net. */
.lenis-reveal:not(.is-inview) {
  filter: blur(6px);
}

@media (prefers-reduced-motion: reduce) {
  .titre-lignes .ln-mot-i {
    transform: none !important;
    transition: none !important;
  }
  .titre-lignes .ln-mot {
    overflow: visible;
  }
}

/* ── Chaque planche SE DESSINE quand on arrive dessus : les traits se
   tracent (stroke-dashoffset), puis les aplats, libellés et textes
   apparaissent. C'est le geste qui rend le panneau immersif — on ne
   découvre pas une image déjà faite, on la voit se faire.
   ⚠️ Les éléments déjà pointillés (.pl-pointille, .pl-guide) sont EXCLUS
   du tracé : ils utilisent déjà stroke-dasharray pour leur aspect, animer
   leur dashoffset détruirait le pointillé. Ils se contentent d'un fondu. ── */
.planche-dessin [data-trace] {
  stroke-dasharray: var(--L);
  stroke-dashoffset: var(--L);
  transition: stroke-dashoffset 1.15s var(--ease) var(--td, 0ms);
}
.planche.dessine .planche-dessin [data-trace] {
  stroke-dashoffset: 0;
}
.planche-dessin .pl-fill,
.planche-dessin .pl-accent-fill,
.planche-dessin .pl-pt,
.planche-dessin .pl-guide,
.planche-dessin .pl-pointille,
.planche-dessin text {
  opacity: 0;
  transition: opacity 0.65s var(--ease) 0.55s;
}
.planche.dessine .planche-dessin .pl-fill,
.planche.dessine .planche-dessin .pl-accent-fill,
.planche.dessine .planche-dessin .pl-pt,
.planche.dessine .planche-dessin .pl-guide,
.planche.dessine .planche-dessin .pl-pointille,
.planche.dessine .planche-dessin text {
  opacity: 1;
}

/* ── DÉPLIAGE : sous 860px un plateau fixe à cinq feuilles superposées n'a
   pas la hauteur pour exister. La table se déplie — les feuilles repassent en
   flux vertical, l'une après l'autre. Chaque feuille garde la règle en deux
   temps, mais déclenchée par IntersectionObserver côté JS. ── */
@media (max-width: 860px) {
  .tab {
    /* la hauteur calculée en JS ne vaut que pour le plateau collant */
    height: auto !important;
  }
  .tab-plateau {
    position: static;
    height: auto;
    display: block;
    padding: 0;
    /* plus rien à cacher : les feuilles ne montent plus, elles sont en flux */
    overflow: visible;
  }
  .tab-feuilles {
    display: block;
    /* pas de profondeur en dépliage : les feuilles ne se recouvrent plus,
       un recul en Z ne ferait que les rétrécir sans rien signifier */
    perspective: none;
  }
  .planche {
    grid-area: auto;
    grid-template-columns: minmax(0, 1fr) !important;
    /* le gabarit fixe ne vaut que pour la table : déplié, c'est le contenu
       qui commande la hauteur */
    height: auto !important;
    min-height: 0 !important;
    max-width: none !important;
    text-align: left !important;
    justify-items: stretch !important;
    transform: none !important;
    margin-bottom: clamp(1rem, 3vh, 2rem);
  }
  .planche[data-format="haut"] .planche-dessin,
  .planche[data-format="large"] .planche-dessin {
    order: 1;
    /* PAS de hauteur fixe ici : avec `aspect-ratio: 420/300`, une hauteur
       definie fait DEDUIRE la largeur (250 x 1.4 = 349px) et le dessin
       deborde d'une carte large de 289px. On laisse la largeur commander. */
    width: 100%;
    height: auto;
  }
  .planche[data-format="haut"] .planche-cartel,
  .planche[data-format="large"] .planche-cartel {
    order: 2;
    max-width: none;
  }
  .planche-titre {
    /* le minimum de 1.6rem du clamp desktop coupe "AUTOMATISATION" dans une
       carte de ~290px de large : ici le titre suit la largeur de l'ecran */
    font-size: clamp(1.15rem, 5.6vw, 2.2rem);
    /* PAS hyphens:auto : il coupe "AU-TOMATISATION". Les points de cesure
       sont poses a la main dans le HTML avec &shy; (AUTOMATI-SATION). */
  }
}
@media (prefers-reduced-motion: reduce) {
  .planche-dessin [data-trace] {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    transition: none;
  }
  .planche-dessin .pl-fill,
  .planche-dessin .pl-accent-fill,
  .planche-dessin .pl-pt,
  .planche-dessin .pl-guide,
  .planche-dessin .pl-pointille,
  .planche-dessin text {
    opacity: 1;
    transition: none;
  }
  /* ── même dépliage que sous 860px, mais sans balayage ni tracé : les
     planches sont simplement là, dessinées. Le JS s'arrête de son côté
     (state.reduit), ces règles neutralisent ce qui reste en CSS. ── */
  .tab {
    height: auto !important;
  }
  .tab-plateau {
    position: static;
    height: auto;
    display: block;
    padding: 0;
    /* plus rien à cacher : les feuilles ne montent plus, elles sont en flux */
    overflow: visible;
  }
  .tab-feuilles {
    display: block;
    perspective: none;
  }
  .planche {
    grid-area: auto;
    height: auto !important;
    min-height: 0 !important;
    transform: none !important;
    margin-bottom: clamp(1rem, 3vh, 2rem);
  }
  /* le réglet reste, tiré sur toute la largeur (le JS pose --p à 1 en
     déplié) : c'est un trait de la planche, pas une animation */
}


/* ═══════════════════════════════════════════════════════════
   PIED DE PAGE — un seul, identique sur les trois pages (2026-07-26).
   Avant : trois etats differents. L'accueil avait deux colonnes
   Contacts/Navigation avec une grande zone vide a droite, work.html une
   version reduite avec d'autres liens, et 404.html n'en avait AUCUN.
   On ne garde que ce qui sert : ou aller, comment joindre, qui a fait.
   ⚠️ Selecteurs prefixes par `footer` : une classe `.pied` existe deja
   ailleurs (`.contact .pied`, le bloc de boutons du formulaire).
   ═══════════════════════════════════════════════════════════ */
footer .pied {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(1.2rem, 3vw, 3rem) clamp(1.5rem, 4vw, 4rem);
  font-size: 0.85rem;
}
footer .pied-marque {
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--creme);
  text-decoration: none;
}
footer .pied-nav,
footer .pied-liens {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.9rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
footer .pied-nav {
  justify-content: center;
}
footer .pied-nav a,
footer .pied-liens a {
  color: var(--creme);
  opacity: 0.68;
  text-decoration: none;
  transition: opacity 0.3s var(--ease);
}
footer .pied-nav a:hover,
footer .pied-liens a:hover,
footer .pied-nav a:focus-visible,
footer .pied-liens a:focus-visible {
  opacity: 1;
}
footer .pied-signature {
  grid-column: 1 / -1;
  margin: 0;
  padding-top: 1.4rem;
  border-top: 1px solid var(--ligne);
  font-size: 0.84rem;
  letter-spacing: 0.04em;
  color: var(--creme);
  opacity: 0.55;
  /* degage le bouton SON, fixe en bas a droite : la ligne se faisait couper */
  padding-right: clamp(6rem, 12vw, 9rem);
}
@media (max-width: 860px) {
  footer .pied {
    grid-template-columns: 1fr;
    justify-items: start;
  }
  footer .pied-nav {
    justify-content: flex-start;
  }
  footer .pied-signature {
    padding-right: 0;
  }
}

/* ═══════════════════════════════════════════════════════════
   MENTIONS LÉGALES (2026-07-26) — obligation legale pour un site
   professionnel en France (LCEN art. 6 III). Page volontairement sobre :
   on vient y verifier un fait, pas admirer une mise en scene. Meme sable,
   meme filets, meme micro-typographie que le reste du site.
   ═══════════════════════════════════════════════════════════ */
.legal {
  max-width: 62rem;
  margin: 0 auto;
  padding: clamp(7rem, 16vh, 11rem) var(--marge) clamp(4rem, 9vh, 7rem);
}
.legal-kicker {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--secondaire);
}
.legal-titre {
  margin: 0.7rem 0 0;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(2rem, 5vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--text);
}
.legal-sous {
  margin: 0.9rem 0 0;
  font-size: 0.82rem;
  color: var(--text-soft);
}
.legal-corps {
  margin-top: clamp(2.5rem, 6vh, 4rem);
  border-top: 1px solid var(--line);
}
.legal-corps > section {
  padding: clamp(1.8rem, 4vh, 2.6rem) 0;
  border-bottom: 1px solid var(--line);
  /* deux colonnes : le titre tient la marge, le texte respire a droite */
  display: grid;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 3rem);
  align-items: start;
}
.legal-corps h2 {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.legal-corps p {
  margin: 0 0 0.9rem;
  grid-column: 2;
  max-width: 62ch;
  line-height: 1.65;
}
.legal-corps p:last-child { margin-bottom: 0; }
.legal-corps a { color: var(--secondaire); }
.legal-liste {
  grid-column: 2;
  margin: 0;
  display: grid;
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  gap: 0.55rem 1.4rem;
  font-size: 1rem;
}
.legal-liste dt {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-soft);
  padding-top: 0.18rem;
}
.legal-liste dd { margin: 0;
  font-size: 1rem; }
/* ⚠️ Champs que SEUL l'editeur peut renseigner (statut, SIRET, adresse) :
   signales visuellement pour qu'ils ne partent jamais en ligne tels quels. */
.legal-liste [data-a-completer] {
  color: #9a3412;
}
.legal-liste dd[data-a-completer] {
  font-family: var(--mono);
  font-size: 0.86rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-bottom: 1px dashed currentColor;
  padding-bottom: 0.1rem;
}
.legal-retour {
  margin-top: clamp(2.5rem, 6vh, 4rem);
}
@media (max-width: 760px) {
  .legal-corps > section,
  .legal-liste {
    grid-template-columns: minmax(0, 1fr);
  }
  .legal-corps p,
  .legal-liste {
    grid-column: 1;
  }
}
