/* ============================================================
   Fonts
   ============================================================ */
/* Fichiers hébergés avec le site (assets/fonts). OTF pour l'instant :
   une conversion en WOFF2 les allégerait d'environ 30 %. */
@font-face {
  font-family: Panamera;
  src: url("assets/fonts/Panamera-Light.otf") format("opentype");
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: Panamera;
  src: url("assets/fonts/Panamera-Regular.otf") format("opentype");
  font-weight: 400;
  font-display: swap;
}

/* ============================================================
   Design tokens
   ============================================================ */
:root {
  --bg: #040610;
  --paper: #f2f5ff;
  --muted: #97a2b7;
  --soft: #c4ccdb; /* texte courant secondaire */
  --line: rgba(255, 255, 255, 0.13);
  --accent: #91a9ff;
  --surface: #091120; /* fond des médias avant chargement */
  --glass: rgba(22, 26, 42, 0.72);
  --sans: "Satoshi", Helvetica, Arial, sans-serif;
  --display: Panamera, serif;
  --pad: clamp(1.2rem, 4vw, 5rem);
  --gap: clamp(1rem, 2vw, 2rem);
  --radius: 8px;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --ease-glide: cubic-bezier(0.16, 1, 0.3, 1); /* "expo out" : grands mouvements */
  --space-xl: clamp(3.5rem, 6vw, 6rem);
  --space-lg: clamp(2.5rem, 4.5vw, 4.5rem);
  --space-md: clamp(1.5rem, 3vw, 2.5rem);
  --btn-h: 3rem; /* hauteur commune à tous les boutons (ronds et pilules) */
  color-scheme: dark;

  /* Échelle typographique — 8 tailles pour tout le site */
  --fs-micro: 0.72rem; /* mentions secondaires : tags sur image, n° de sommaire, indices */
  --fs-label: 0.8rem; /* libellés en capitales : eyebrows, étiquettes, compteurs */
  --fs-button: 0.85rem; /* texte des boutons */
  --fs-small: 0.92rem; /* petits textes courants */
  --fs-body: 1.05rem; /* texte courant court (listes, fiches) */
  --fs-text: 1.12rem; /* paragraphes de lecture */
  --fs-lead: clamp(1.05rem, 1.4vw, 1.3rem); /* chapôs */
  --fs-h3: clamp(1.25rem, 1.8vw, 1.6rem);
  --fs-h2: clamp(2rem, 3.6vw, 3rem);
  --fs-h1: clamp(2.4rem, 4.2vw, 3.2rem);
  --fs-display: clamp(3rem, 7vw, 7rem); /* très grands titres : contact, projet suivant */
  /* Approche des lettres */
  /* Interlignage : plus le texte est petit, plus il respire */
  --lh-small: 1.72; /* petits paragraphes */
  --lh-body: 1.68; /* textes courts */
  --lh-text: 1.68; /* paragraphes de lecture */
  --lh-lead: 1.56; /* chapôs */
  --ls-caps: 0.1em; /* textes en capitales */
  --ls-wide: 0.14em; /* eyebrows et boutons-pilules */
  --ls-display: 1px; /* titres en police d'affichage */
  --ls-tight: -0.04em; /* très grands textes en Satoshi */
  /* Couleurs complémentaires */
  --veil-light: rgba(4, 6, 16, 0.45); /* voiles sombres sur image */
  --veil: rgba(4, 6, 16, 0.65);
  --veil-strong: rgba(4, 6, 16, 0.9);
  --hover: rgba(255, 255, 255, 0.08); /* fond au survol */
  --line-strong: rgba(255, 255, 255, 0.28);
  --shadow: rgba(0, 0, 0, 0.65);
  /* Formes */
  --radius-lg: 12px;
  --radius-pill: 999px;
  /* Mouvement — 3 durées, 3 courbes */
  --dur-fast: 0.35s; /* survols, petits changements d'état */
  --dur-base: 0.6s; /* fondus, apparitions courtes */
  --dur-slow: 1.2s; /* grands déplacements */
  --ease-curtain: cubic-bezier(0.7, 0, 0.2, 1); /* rideaux de transition de page */
}
/* Boutons-pilules : hauteur fixe, donc jamais de libellé sur deux lignes */
.site-link,
.filter-tabs button,
.showreel-play,
.reel-close {
  white-space: nowrap;
}

/* ============================================================
   Reset & base
   ============================================================ */
* {
  box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--paper);
  font-family: var(--sans);
  font-weight: 300;
}
/* Halo bleu fixe derrière la page */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: radial-gradient(circle at 72% 32%, rgba(47, 91, 181, 0.24), transparent 38%), #060916;
  pointer-events: none;
}
/* Grain fin et statique (bruit SVG tuilé) au-dessus de tout */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 300;
  background: 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='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 0 0 / 180px 180px;
  opacity: 0.05;
  mix-blend-mode: overlay;
  pointer-events: none;
}
a {
  color: inherit;
  text-decoration: none;
}
/* Lien inline dans un texte courant (ex. nom d'entreprise cité en prose) */
.text-link {
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 0.2em;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
.text-link:hover {
  text-decoration-color: var(--accent);
}
img,
video {
  max-width: 100%;
}
button {
  font-family: inherit;
}
h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: var(--ls-display);
  margin: 0;
}
h2 {
  font-size: var(--fs-h2);
}
/* Titres en lignes équilibrées (pas de mot seul en fin de titre sur
   mobile), textes courants sans orphelin */
h1,
h2,
h3,
.case-intro {
  text-wrap: balance;
}
p,
dd,
li {
  text-wrap: pretty;
}
.shell {
  width: min(calc(100% - var(--pad) * 2), 96rem);
  margin: auto;
}
.eyebrow {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--muted);
}
.lead,
.nav,
.project p,
.story p,
.meta-value,
.footer-bar {
  font-weight: 400;
}

/* ============================================================
   Accessibilité
   ============================================================ */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 400;
  padding: 0.8rem 1.2rem;
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--bg);
  font-weight: 500;
  transform: translateY(-200%);
}
.skip-link:focus {
  transform: none;
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}
[tabindex="-1"]:focus {
  outline: none;
}
/* "Réduire les animations" : transitions CSS quasi instantanées */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}

/* Masque les éléments animés AVANT leur apparition, seulement si JS
   tourne (classe "js" posée en tête de page) : sans JS tout reste visible */
.js .reveal:not(.is-in),
.js .intro-reveal,
.js .split-lines-intro,
.js .kv-caption {
  opacity: 0;
}
.js .case-hero-media,
.js .media-mask {
  clip-path: inset(100% 0 0 0);
}
.reveal-line {
  display: block;
}
/* Mesure des lignes : un mot ne se coupe jamais en deux (ex. le trait
   d'union de « e-commerce »), sinon la ligne mesurée et la ligne
   affichée ne correspondent plus */
.split-word {
  white-space: nowrap;
}
/* Titres découpés en mots, chacun dans son masque (padding compensé :
   accents et jambages jamais rognés) */
.word-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  padding: 0.1em 0.04em 0.16em;
  margin: -0.1em -0.04em -0.16em;
}
.word {
  display: inline-block;
  transform-origin: 0% 100%;
}

/* ============================================================
   Filets séparateurs : dessinés en fond (et non en bordure) pour
   pouvoir se tracer de gauche à droite à leur apparition (script.js,
   RULES). --rt / --rb activent le filet du haut / du bas, --draw (0→1)
   sa longueur. Sans JS ou sans GSAP, --draw reste à 1 : filets visibles.
   ============================================================ */
@property --draw {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}
.services-list,
.service,
.footer,
.footer-bar,
.scene + .scene,
:is(.timeline, .principles) li,
.stat,
.case-facts > div {
  background-image: linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line));
  background-repeat: no-repeat;
  background-position: 0 0, 0 100%;
  background-size: calc(var(--draw) * var(--rt, 0) * 100%) 1px, calc(var(--draw) * var(--rb, 0) * 100%) 1px;
  transition: --draw var(--dur-slow) var(--ease-glide);
}
.js :is(.services-list, .service, .scene + .scene, :is(.timeline, .principles) li, .stat, .case-facts > div):not(.is-drawn) {
  --draw: 0;
}

/* Cadres d'image : l'image glisse et se recadre doucement au défilement
   (--sz / --sy animés par parallaxes dans script.js) */
.zoom-frame {
  overflow: hidden;
}
/* Coins arrondis nets : un média animé (zoom, parallaxe) dans un cadre
   arrondi laisse parfois un liseré de fond ou un angle carré au bord
   (Chrome, Safari). Le masque force un vrai découpage sur la couche. */
:is(.media-mask, .zoom-frame, .project-media, .case-video, .kv-frame) {
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  mask-image: radial-gradient(white, black);
}
.zoom-frame > :is(img, video) {
  transform: translateY(var(--sy, 0)) scale(var(--sz, 1));
}

/* ============================================================
   Navigation
   ============================================================ */
.nav-wrap {
  position: fixed;
  top: 1rem;
  width: 100%;
  z-index: 50;
  transition: transform 0.4s, opacity 0.4s;
}
.nav-wrap.is-hidden {
  transform: translateY(-140%);
}
.nav {
  height: 4rem;
  padding: 0 2.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--glass);
  backdrop-filter: blur(20px);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.nav-right {
  display: flex;
  align-items: center;
  gap: 1.75rem;
}
.desktop-nav {
  display: flex;
  gap: 2rem;
}
.desktop-nav a[aria-current="page"] {
  color: var(--accent);
}
/* Zones cliquables agrandies (au moins 44 px de haut au doigt) sans
   changer l'apparence : padding compensé par une marge négative */
.brand,
.desktop-nav a {
  padding-block: 0.7rem;
  margin-block: -0.7rem;
}
.brand {
  padding-inline: 0.5rem;
  margin-inline: -0.5rem;
}
.menu-toggle,
.menu-panel {
  display: none;
}

/* ============================================================
   Hero (home) — 88svh : le début des projets affleure en bas
   ============================================================ */
.hero {
  min-height: 88svh;
  /* Nav flottante : 1rem + 4rem de haut, puis 2rem d'air (visible sur
     mobile, où le contenu dépasse l'écran et n'est plus centré) */
  padding-top: 7rem;
  position: relative;
  display: flex;
  align-items: center;
}
.hero-copy {
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(2rem, 5vw, 5rem);
}
.hero-text {
  flex: 1;
  min-width: 0;
}
.hero h1 {
  font-size: var(--fs-h1);
  line-height: 0.98;
  white-space: nowrap;
}
.hero h1 span,
.contact span {
  color: var(--accent);
}
.lead {
  max-width: 42rem;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  margin: 2.5rem 0 0;
  color: var(--soft);
}
.hero-ascii {
  position: relative;
  flex: 0 0 auto;
  width: min(clamp(34rem, 54vw, 52rem), 76svh);
  aspect-ratio: 1;
}
.hero-ascii canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: pan-y;
}

/* ============================================================
   Mise en page commune (home + pages projet)
   ============================================================ */
.work {
  padding: var(--space-xl) 0;
}
.story,
.stats,
.video {
  padding: var(--space-md) 0;
}
.hero + .work {
  padding-top: var(--space-md);
}
.section-heading,
.story-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
}
.section-heading {
  margin-bottom: var(--space-lg);
}
.section-heading .eyebrow,
.story-row > .eyebrow {
  grid-column: 1 / span 3;
}
.section-heading h2,
.story-row > div {
  grid-column: 6 / -1;
}
/* Variante empilée (savoir-faire) : le titre de section prend le style
   eyebrow (comme « Projets sélectionnés »), calé à gauche ; les titres de
   métiers en dessous portent la grande typographie */
.section-heading-stacked h2 {
  grid-column: 1 / -1;
}
.section-heading.section-heading-stacked h2.eyebrow {
  margin-top: 0;
  font: 500 var(--fs-label) / 1.3 var(--sans);
  letter-spacing: var(--ls-wide);
}

/* ============================================================
   Boutons & liens
   ============================================================ */
.site-link {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 2rem;
  font: 500 var(--fs-button)/1 var(--sans);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  height: var(--btn-h);
  padding: 0 1.6rem;
  transition: var(--dur-fast) var(--ease);
}
.site-link:hover {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--bg);
}
/* Boutons d'appel centrés sous une section (home) */
.work-cta,
.services > .site-link {
  display: flex;
  width: fit-content;
  margin: clamp(3rem, 6vw, 5rem) auto 0;
}
.skill-tag {
  font-size: var(--fs-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 0.5rem 1rem;
}
.skills-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.5rem;
}
.hero-roles {
  margin: 0 0 1.25rem;
}

/* Effet "roulette" au survol : chaque lettre a une jumelle cachée juste
   en dessous ; le décalage --i (posé en JS) crée une vague.
   --roll : hauteur d'une ligne, plus grande que 1em pour laisser la place
   aux accents (À, É) et jambages (ç, p) sans montrer la jumelle ; la
   marge négative garde la hauteur du bouton inchangée. */
.roll-text {
  --roll: 1.4em;
  display: inline-block;
  /* Ligne à la hauteur des lettres : sinon le texte se cale en haut d'une
     ligne plus haute et paraît décalé (ex. logo GQ dans la barre de nav) */
  line-height: 1;
}
.roll-char {
  display: inline-block;
  overflow: hidden;
  height: var(--roll);
  line-height: var(--roll);
  vertical-align: top;
  margin: calc((1em - var(--roll)) / 2) 0.06em calc((1em - var(--roll)) / 2) 0;
}
.roll-char-inner {
  display: block;
  transition: transform var(--dur-fast) var(--ease);
  transition-delay: calc(var(--i, 0) * 14ms);
}
.roll-line {
  display: block;
  height: var(--roll);
}
/* Logo GQ : deux lettres seulement, le décalage est plus marqué pour
   qu'on voie nettement le G puis le Q basculer */
.brand .roll-char-inner {
  transition-delay: calc(var(--i, 0) * 60ms);
}
:is(.brand, .filter-tabs button, .site-link, .desktop-nav a, .mobile-nav a):is(:hover, :focus-visible) .roll-char-inner {
  transform: translateY(calc(-1 * var(--roll)));
}

/* ============================================================
   Page Work : en-tête, filtres, grille
   ============================================================ */
.page-header {
  padding: clamp(9rem, 15vw, 13rem) 0 clamp(2.5rem, 5vw, 4rem);
}
.page-header h1 {
  font-size: var(--fs-h1);
  margin-top: 1.25rem;
}
.page-header .lead {
  max-width: 36rem;
  margin-top: 1.5rem;
}
/* Page Projets : les filtres suivent l'introduction de près (pas deux
   espacements de section cumulés) */
.page-header + .work {
  padding-top: var(--space-md);
}
.filter-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: clamp(3rem, 6vw, 5rem);
}
.filter-tabs button {
  font: 500 var(--fs-button)/1 var(--sans);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  height: var(--btn-h);
  padding: 0 1.3rem;
  cursor: pointer;
  transition: var(--dur-fast) var(--ease);
}
.filter-tabs button:hover {
  color: var(--paper);
  border-color: var(--paper);
}
.filter-tabs button.is-active {
  color: var(--bg);
  background: var(--paper);
  border-color: var(--paper);
}
.projects-grid {
  display: grid;
  /* auto-fill (et non auto-fit) : les colonnes vides sont gardées, donc
     une carte seule après un filtre garde sa taille. Le min empêche de
     dépasser 4 colonnes sur grand écran. */
  grid-template-columns: repeat(auto-fill, minmax(max(14rem, calc((100% - 3 * var(--gap)) / 4)), 1fr));
  gap: var(--space-lg) var(--gap);
}
.project > * {
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease);
}
/* Survol : seule la carte visée reste "au point".
   .has-mouse (posée en JS dès qu'une souris bouge) plutôt qu'une media
   query : certains postes déclarent "pas de survol" malgré une souris. */
.has-mouse .projects-grid:has(.project:hover) .project:not(:hover) > * {
  opacity: 0.4;
  filter: blur(1.5px);
}
.project-media {
  position: relative;
  aspect-ratio: 4/5;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface);
}
.project-media img {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-base) var(--ease);
}
.project:hover img {
  transform: translateY(var(--sy, 0)) scale(calc(var(--sz, 1) * 1.03));
}
.project-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding-top: 1.4rem;
}
/* Titre et sous-titre toujours sur une ligne, tronqués si besoin : les
   cartes gardent la même hauteur quelle que soit la longueur du titre */
.project-info > div {
  min-width: 0;
}
.project-info h3 {
  font-size: var(--fs-h3);
  letter-spacing: normal;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  /* Le masque des "…" coupait les jambages (g, p, q) : un peu d'espace
     sous la ligne, compensé pour ne rien décaler */
  padding-bottom: 0.15em;
  margin-bottom: -0.15em;
}
.project-info p {
  font-size: var(--fs-micro);
  font-weight: 500;
  color: var(--muted);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ============================================================
   Boutons icône (modèle commun : le "retour en haut") — cercle fin,
   fond verre flouté ; au survol, contour et icône passent à l'accent.
   ============================================================ */
.icon-btn {
  flex-shrink: 0;
  width: var(--btn-h);
  height: var(--btn-h);
  display: grid;
  place-items: center;
  overflow: hidden; /* découpe la flèche qui sort du cercle */
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--glass);
  backdrop-filter: blur(20px);
  color: var(--paper);
  font: 400 1.1rem/1 var(--sans);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.icon-btn:is(:hover, :focus-visible),
a:is(:hover, :focus-visible) .icon-btn {
  border-color: var(--accent);
  color: var(--accent);
}

/* Flèche animée : au survol, elle file dans la direction qu'elle pointe
   et sort du cadre, pendant que sa jumelle (::after) entre par le côté
   opposé et se pose à sa place. Direction : --x / --y (posés par classe),
   distance : --d. Déclenchée par le lien ou bouton qui la contient. */
.arrow {
  --d: 2em;
  position: relative;
  display: inline-block;
  transition: transform var(--dur-base) var(--ease-glide);
}
.arrow::after {
  content: attr(data-arrow);
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(calc(var(--x) * var(--d) * -1), calc(var(--y) * var(--d) * -1));
}
.arrow-up { --x: 0; --y: -1; }
.arrow-down { --x: 0; --y: 1; }
/* Les flèches horizontales du glyphe sont dessinées un peu bas : on les
   remonte pour qu'elles paraissent centrées (correction optique) */
.arrow-left { --x: -1; --y: 0; top: -0.1em; }
.arrow-right { --x: 1; --y: 0; top: -0.1em; }
.arrow-ne { --x: 1; --y: -1; }
:is(a, button):is(:hover, :focus-visible) .arrow {
  transform: translate(calc(var(--x) * var(--d)), calc(var(--y) * var(--d)));
}
/* Cartes projet et lien de contact : la flèche ↗ pivote de 45° vers la
   droite (↗ → →) au lieu de filer en diagonale ; pas de jumelle */
:is(.icon-btn, .contact-arrow) .arrow-ne {
  transition: transform var(--dur-base) var(--ease-glide);
}
:is(.icon-btn, .contact-arrow) .arrow-ne::after {
  content: none;
}
:is(a, button):is(:hover, :focus-visible) :is(.icon-btn, .contact-arrow) .arrow-ne {
  transform: rotate(45deg);
}
/* Flèches dans du texte : découpe autour du glyphe (clip-path, pour ne
   pas toucher à la ligne de base comme le ferait overflow) */
.arrow-clip {
  display: inline-block;
  font-style: normal;
  clip-path: inset(-0.2em -0.1em);
}
.arrow-clip .arrow {
  --d: 1.4em;
}

/* ============================================================
   Carrousel 3D des projets (home) : le JS pose la position de chaque
   carte et .is-active ; le rendu et les transitions vivent ici.
   ============================================================ */
.work-carousel {
  position: relative;
}
/* Les cartes latérales débordent jusqu'aux bords de l'écran, sans
   scroll horizontal (clip, pas hidden, pour ne pas casser le sticky) */
.work {
  overflow-x: clip;
  width: 100%;
  padding-inline: max(var(--pad), calc((100% - 96rem) / 2));
}
.carousel-stage {
  position: relative;
  aspect-ratio: 16/6;
  perspective: 1800px; /* sans preserve-3d : l'empilement suit les z-index */
  touch-action: pan-y;
  user-select: none;
  cursor: grab;
}
.carousel-stage:focus-visible {
  outline-offset: 8px;
  border-radius: var(--radius);
}
.carousel-stage.is-dragging {
  cursor: grabbing;
}
.carousel-card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(62%, 60rem);
  aspect-ratio: 16/9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 3rem 6rem -2rem var(--shadow);
  transition: transform var(--dur-slow) var(--ease-glide), opacity var(--dur-base) var(--ease-glide);
  will-change: transform;
  backface-visibility: hidden;
  -webkit-user-drag: none;
}
/* Voile sombre sur les voisines, levé au centre */
.carousel-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  background: var(--veil);
  transition: opacity var(--dur-slow) var(--ease-glide);
  pointer-events: none;
}
.carousel-card.is-active::after {
  opacity: 0;
}
.carousel-card:not(.is-active) {
  cursor: pointer;
}
.carousel-card:not(.is-active):hover::after {
  opacity: 0.55;
}
/* Parallaxe interne : l'image glisse à l'inverse de la carte (--shift) */
.carousel-card img,
.carousel-card video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transform: scale(1.14) translateX(calc(var(--shift, 0) * -5%));
  transition: transform var(--dur-slow) var(--ease-glide), opacity var(--dur-base) var(--ease);
}
.carousel-card video {
  opacity: 0;
}
.carousel-card.is-playing video {
  opacity: 1;
}
.carousel-card.is-active:hover :is(img, video) {
  transform: scale(1.18);
}
/* Projets web (desktop) : la maquette n'occupe que la moitié du visuel,
   on zoome dessus pour qu'elle remplisse la carte et qu'on lise le site */
@media (min-width: 761px) {
  .carousel-card.is-web img {
    transform: scale(1.35) translateX(calc(var(--shift, 0) * -4%));
  }
  .carousel-card.is-web.is-active:hover img {
    transform: scale(1.39);
  }
}
/* Vidéo verticale (9:16) : montrée entière, au centre et sur toute la
   hauteur de la carte, plutôt que recadrée en 16:9, sur un aplat sombre
   en dégradé gris (lumière douce en haut, comme un écran projeté), avec
   une ombre portée profonde. Hors lecture, son poster tient la place. */
.carousel-card.is-vertical {
  background:
    radial-gradient(60% 55% at 50% 30%, rgba(255, 255, 255, 0.07), transparent 70%),
    linear-gradient(170deg, #2a2b2e 0%, #18191b 55%, #0d0d0f 100%);
}
.carousel-card.is-vertical video {
  inset: 7% auto;
  left: 50%;
  width: auto;
  height: 86%;
  aspect-ratio: 9/16;
  object-fit: cover;
  border-radius: var(--radius);
  box-shadow:
    0 2.5rem 4rem -1.5rem rgba(0, 0, 0, 0.85),
    0 0.75rem 1.5rem -0.75rem rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(255, 255, 255, 0.06);
  opacity: 1;
  transform: translateX(-50%) scale(0.96);
  transition: transform var(--dur-slow) var(--ease-glide);
}
.carousel-card.is-vertical.is-playing video {
  transform: translateX(-50%);
}
.carousel-card.is-vertical.is-active:hover video {
  transform: translateX(-50%) scale(1.03);
}
.carousel-stage.is-dragging :is(.carousel-card, .carousel-card img, .carousel-card video) {
  transition: none;
}
/* Pastille "View project" au survol de la carte active */
.carousel-cta {
  position: absolute;
  z-index: 5;
  left: 50%;
  top: 50%;
  padding: 0.85rem 1.4rem;
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--bg);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.85);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-base) var(--ease);
  pointer-events: none;
}
.carousel-card.is-active:is(:hover, :focus-visible) .carousel-cta {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.carousel-btn {
  position: absolute;
  top: 50%;
  z-index: 20;
  margin-top: -1.5rem;
}
/* Flèches juste à côté de la carte centrale (3rem + 0.75rem d'écart) */
.carousel-prev {
  left: calc(50% - min(31%, 30rem) - 3.75rem);
}
.carousel-next {
  right: calc(50% - min(31%, 30rem) - 3.75rem);
}

/* Titres empilés au même endroit ; fondu enchaîné avec mise au point */
.carousel-titles {
  display: grid;
  margin-top: clamp(0.3rem, 0.8vw, 0.6rem);
  text-align: center;
}
.carousel-title {
  grid-area: 1 / 1;
  opacity: 0;
  filter: blur(6px);
  transform: translateY(-0.5rem);
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease), transform var(--dur-base) var(--ease-glide);
}
.carousel-title.is-active {
  opacity: 1;
  filter: none;
  transform: none;
  pointer-events: auto;
  transition: opacity var(--dur-slow) var(--ease-glide) 0.2s, filter var(--dur-slow) var(--ease-glide) 0.2s, transform var(--dur-slow) var(--ease-glide) 0.2s;
}
.work-carousel .carousel-title.is-entering {
  transform: translateY(0.75rem);
  transition: none;
}
.carousel-title h3 {
  font-size: var(--fs-h2);
}
.carousel-title p {
  margin: 0.75rem 0 0;
}
.carousel-title p {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
}

/* Entrée en scène : le titre suit les cartes */
.carousel-titles {
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}
.js .work-carousel:not(.is-in) .carousel-titles {
  opacity: 0;
  transform: translateY(1rem);
}
.js .work-carousel:not(.is-in) .carousel-btn {
  opacity: 0;
  pointer-events: none;
}
.work-carousel.is-in .carousel-titles {
  transition-delay: 0.45s;
}

/* ============================================================
   What I do (home) : au survol, la ligne visée reste au point
   ============================================================ */
.services {
  padding: var(--space-xl) 0;
}
.services-list {
  list-style: none;
  margin: 0;
  padding: 0;
  --rt: 1;
}
.service {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  align-items: center;
  padding: clamp(1.6rem, 3vw, 2.6rem) 0;
  --rb: 1;
}
.service > * {
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease), transform var(--dur-base) var(--ease-glide), color var(--dur-base) var(--ease);
}
/* Verbe d'introduction : petites capitales en couleur d'accent, il dit
   ce que le métier apporte (remplace l'ancienne numérotation) */
.service-verb {
  grid-column: 1 / span 2;
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--accent);
}
.service-title {
  grid-column: 3 / span 5;
  font-size: clamp(1.5rem, 2.5vw, 2.15rem); /* sous le titre de section */
}
.service-text {
  grid-column: 9 / -1;
  margin: 0;
  max-width: 26rem;
  font: 300 var(--fs-body)/var(--lh-body) var(--sans);
  color: var(--muted);
}
/* Savoir-faire détaillés : liste à puces rondes, alignées sur le texte */
.service-points {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0.45rem;
}
.service-points li {
  position: relative;
  padding-left: 1.1rem;
}
.service-points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--lh-body) * 0.5em - 2px);
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.6;
}
.has-mouse .services-list:hover .service:not(:hover) > * {
  opacity: 0.35;
  filter: blur(2px);
}
.has-mouse .service:hover .service-title {
  transform: translateX(0.6rem);
  color: var(--accent);
}

/* ============================================================
   Showreel (home) : cadre 16:9 qui s'ouvre au scroll (GSAP) ;
   "Play reel" suit le curseur
   ============================================================ */
.showreel {
  padding: var(--space-lg) 0;
}
.showreel-frame {
  position: relative;
  width: min(100%, 68rem);
  margin: 0 auto;
  aspect-ratio: 16/9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  cursor: pointer;
}
.js .showreel-frame {
  clip-path: inset(7% 9% 7% 9% round 14px);
}
.showreel-video {
  position: absolute;
  z-index: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.showreel-play,
.reel-close {
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--paper);
  font: 500 var(--fs-button)/1 var(--sans);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  cursor: pointer;
}
/* Couche graphique propre et z-index explicite : sans ça, Chrome (Windows)
   peut dessiner la vidéo en lecture par-dessus le bouton, qui ne
   réapparaît qu'au survol */
.showreel-play {
  position: absolute;
  z-index: 2;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  will-change: transform;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  height: var(--btn-h);
  padding: 0 1.4rem;
  background: var(--veil-light);
  backdrop-filter: blur(14px);
}
/* Souris : bouton caché, il apparaît (fondu + mise au point) au survol
   de la vidéo ou au focus clavier. Tactile : toujours visible. "scale"
   est séparé de "transform", que le JS utilise pour suivre le curseur. */
.has-mouse .showreel-play {
  opacity: 0;
  filter: blur(6px);
  scale: 0.9;
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease), scale var(--dur-base) var(--ease-glide);
}
.has-mouse .showreel-frame:hover .showreel-play,
.has-mouse .showreel-play:focus-visible {
  opacity: 1;
  filter: blur(0px);
  scale: 1;
}
.showreel-play-label {
  transition: letter-spacing var(--dur-base) var(--ease-glide);
}
.showreel-frame:hover .showreel-play-label,
.showreel-play:focus-visible .showreel-play-label {
  letter-spacing: 0.2em;
}
.showreel-play-icon {
  border-style: solid;
  border-width: 0.36rem 0 0.36rem 0.6rem;
  border-color: transparent transparent transparent var(--accent);
}

/* Lecteur plein écran avec le son */
.reel-modal {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--veil-strong);
  backdrop-filter: blur(18px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-glide), visibility 0s var(--dur-base);
}
.reel-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-base) var(--ease-glide);
}
.reel-modal video {
  width: min(92vw, 150svh);
  aspect-ratio: 16/9;
  border-radius: var(--radius);
  background: #000;
  transform: scale(0.94);
  filter: blur(10px);
  transition: transform var(--dur-slow) var(--ease-glide), filter var(--dur-slow) var(--ease-glide);
}
.reel-modal.is-open video {
  transform: none;
  filter: blur(0px);
}
.reel-close {
  position: absolute;
  top: 1.5rem;
  right: 1.5rem;
  display: inline-flex;
  align-items: center;
  height: var(--btn-h);
  padding: 0 1.4rem;
  background: transparent;
  transition: background-color var(--dur-fast) var(--ease);
}
.reel-close:hover {
  background: var(--hover);
}

/* ============================================================
   Bandeau clients (home) : logos en masques, couleur posée ici
   ============================================================ */
.clients {
  padding: var(--space-lg) 0 var(--space-xl);
}
.clients > .eyebrow {
  margin-bottom: var(--space-md);
}
.marquee {
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee.is-dragging {
  cursor: grabbing;
}
.marquee-track {
  display: flex;
  gap: clamp(1rem, 3vw, 3rem);
  width: max-content;
  will-change: transform;
}
.client-logo {
  flex: 0 0 auto;
  width: clamp(6.5rem, 11vw, 10rem);
  aspect-ratio: 1;
  background-color: var(--muted);
  -webkit-mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  transition: background-color var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease);
}
.marquee:hover .client-logo {
  opacity: 0.4;
}
.marquee .client-logo:hover {
  opacity: 1;
  background-color: var(--paper);
}
.marquee.is-static {
  mask-image: none;
}
.marquee.is-static .marquee-track {
  width: auto;
  flex-wrap: wrap;
  justify-content: center;
}

/* ============================================================
   Footer / contact
   ============================================================ */
.footer {
  /* En bas : la hauteur du bouton "retour en haut" (fixe, en bas à
     droite) plus ses marges, pour qu'il ne recouvre jamais les liens */
  padding: var(--space-xl) 0 calc(var(--btn-h) + 3rem);
  --rt: 1;
  /* Grille à 12 colonnes, comme le reste du site :
     libellé · appel à l'action (gauche) + contacts (droite, sur la même
     ligne de base que « Écrivez-moi. ») · barre légale en pied.
     Bords gauche et droit communs à toutes les lignes. */
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gap);
}
.footer > .eyebrow {
  grid-column: 1 / -1;
  margin: 0;
}
.contact {
  grid-column: 1 / span 8;
  justify-self: start;
  align-self: last baseline;
  display: inline-block;
  font-size: var(--fs-display);
  /* Assez d'air pour l'accent des capitales (« Écrivez-moi ») sous la
     ligne du dessus */
  line-height: 0.98;
  letter-spacing: var(--ls-tight);
  margin: 1.5rem 0 var(--space-lg);
}
/* Contacts directs : même traitement pour les deux (picto + texte),
   posés sur la ligne de base de l'appel à l'action */
.footer-contacts {
  grid-column: 9 / -1;
  justify-self: end;
  align-self: last baseline;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: baseline;
  gap: 0.75rem 2.25rem;
  margin-bottom: var(--space-lg);
}
.footer-contact {
  font-size: var(--fs-body);
  line-height: 1.4;
  white-space: nowrap;
  color: var(--soft);
  transition: color var(--dur-fast) var(--ease);
}
/* Pictos de même hauteur, centrés sur le corps des minuscules (inline :
   la ligne de base du lien reste celle du texte) */
.footer-contact svg {
  height: 0.95em;
  width: auto;
  margin-right: 0.55em;
  vertical-align: -0.14em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  overflow: visible;
}
.footer-contact svg.is-filled {
  fill: currentColor;
  stroke: none;
}
/* Soulignement qui se trace de gauche à droite sous le texte, comme les
   liens légaux */
.footer-contact > span:not(.sr-only) {
  padding-bottom: 0.15em;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0% 1px no-repeat;
  transition: background-size var(--dur-base) var(--ease-glide);
}
.footer-contact:is(:hover, :focus-visible) {
  color: var(--paper);
}
.footer-contact:is(:hover, :focus-visible) > span {
  background-size: 100% 1px;
}
/* Barre du bas : copyright à gauche, liens légaux à droite, en petit
   et discret. Les liens se soulignent de gauche à droite au survol. */
.footer-bar {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem 2rem;
  padding-top: 1.5rem;
  --rt: 1; /* filet du haut, visible d'emblée comme tout le footer */
  font-size: var(--fs-small);
  color: var(--muted);
}
.footer-copy {
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.75rem;
}
.footer-links a {
  /* Zone cliquable plus haute au doigt, soulignement inchangé */
  padding: 0.6rem 0 0.15rem;
  margin-top: -0.6rem;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0% 1px no-repeat;
  transition: color var(--dur-fast) var(--ease), background-size var(--dur-base) var(--ease-glide);
}
.footer-links a:is(:hover, :focus-visible),
.footer-links a[aria-current="page"] {
  color: var(--paper);
  background-size: 100% 1px;
}
/* Tablette et mobile : appel à l'action, puis contacts alignés à gauche
   sur le même bord */
@media (max-width: 1100px) {
  .contact,
  .footer-contacts {
    grid-column: 1 / -1;
    justify-self: start;
    justify-content: flex-start;
  }
  .contact {
    margin-bottom: 2rem;
  }
}
@media (max-width: 760px) {
  .footer-bar {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Pages légales (mentions, accessibilité) : colonne de lecture étroite */
.legal {
  max-width: 44rem;
  padding-bottom: var(--space-xl);
}
.legal h2 {
  margin-top: var(--space-lg);
  font-size: var(--fs-h3);
}
.legal p,
.legal li {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--soft);
}
.legal p {
  margin: 1rem 0 0;
}
.legal ul {
  margin: 1rem 0 0;
  padding-left: 1.2rem;
}
.legal li + li {
  margin-top: 0.4rem;
}
.legal a {
  color: var(--paper);
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 0.2em;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
.legal a:hover {
  text-decoration-color: var(--accent);
}
.legal .todo {
  color: var(--accent);
}

/* ============================================================
   About — hero : pile de photos + fiche de faits
   ============================================================ */
.about-hero {
  position: relative;
  padding: clamp(7rem, 11vw, 9rem) 0 var(--space-lg);
}
/* Composition sur la grille : texte colonnes 1-7, pile de photos centrée
   dans les colonnes 9-12. Deux niveaux : le titre et le chapô s'alignent
   sur le haut de la pile, les repères (Actuellement, Expérience,
   Secteurs) descendent sur sa base, nettement détachés du paragraphe */
.about-hero-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-lg) var(--gap);
  align-items: stretch;
}
.about-hero-copy {
  grid-column: 1 / span 7;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.about-hero-copy h1 {
  max-width: 46rem;
  font-size: var(--fs-h1);
  margin-top: 1rem;
}
.about-hero-copy .lead {
  max-width: 32rem;
  margin-top: 1.5rem;
}
.about-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) clamp(2rem, 5vw, 4.5rem);
  /* poussés en bas de la colonne, jamais à moins de --space-xl du chapô */
  margin: auto 0 0;
  padding-top: var(--space-xl);
}
.about-facts dd {
  margin: 0.5rem 0 0;
}
.about-hero-portrait {
  grid-column: 9 / -1;
  justify-self: center;
  align-self: center;
}

/* Pile de 3 photos en éventail : le CSS décrit les 3 positions,
   initAboutPhotoStack fait tourner l'ordre */
@property --depth {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}
.photo-stack {
  position: relative;
  width: 21rem; /* largeur propre : la colonne n'a pas d'autre contenu */
  max-width: 100%;
  aspect-ratio: 4/5;
  cursor: pointer;
}
.photo-stack:focus-visible {
  outline-offset: 1.5rem;
  border-radius: var(--radius);
}
.photo-stack-card {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg);
  box-shadow: 0 2rem 4rem -1.5rem var(--shadow);
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease),
    --depth var(--dur-base) var(--ease);
  /* Dérive au curseur (--mx/--my de -1 à 1, posés par initStackDrift),
     amplitude selon la profondeur --depth : la carte de devant bouge le plus */
  --depth: 1;
  translate: calc(var(--mx, 0) * var(--depth) * 0.9rem) calc(var(--my, 0) * var(--depth) * 0.6rem);
  rotate: calc(var(--mx, 0) * var(--depth) * 1.6deg);
}
.photo-stack-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-stack-card.is-mid {
  --depth: 0.55;
  transform: translate(-8%, 5%) rotate(-7deg) scale(0.94);
  opacity: 0.92;
}
.photo-stack-card.is-back {
  --depth: 0.25;
  transform: translate(7%, 9%) rotate(6deg) scale(0.89);
  opacity: 0.78;
}

/* ============================================================
   About — parcours : sommaire fixe à gauche (étape en cours
   surlignée, trait qui se remplit), étapes à droite
   ============================================================ */
/* Séparée du haut de page par l'espace, sans trait */
.about-block {
  padding: var(--space-xl) 0;
}
.storyboard {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  align-items: start;
}
.story-index {
  grid-column: 1 / span 3;
  position: sticky;
  top: 7rem;
}
.story-index-list {
  position: relative;
  display: flex;
  flex-direction: column;
  margin-top: 1rem;
  padding-left: 1.5rem;
}
.story-index-line {
  position: absolute;
  left: 0;
  top: 0.4rem;
  bottom: 0.4rem;
  width: 1px;
  background: var(--line);
}
/* Apparition : le trait vertical se trace de haut en bas avec les liens
   du sommaire (.is-drawn posé au premier lien révélé, voir RULES) */
.story-index-line {
  transform-origin: top;
  transition: transform var(--dur-slow) var(--ease-glide);
}
.js .story-index:not(.is-drawn) .story-index-line {
  transform: scaleY(0);
}
.story-index-line span {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: top;
}
.js .story-index-line span {
  transform: scaleY(0);
}
.story-index a {
  display: flex;
  gap: 0.9rem;
  padding: 0.55rem 0;
  font-size: var(--fs-small);
  font-weight: 400;
  color: var(--muted);
  transition: color var(--dur-fast) var(--ease);
}
.story-index a span {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  padding-top: 0.15rem;
  font-variant-numeric: tabular-nums;
}
.story-index a:hover,
.story-index a.is-active {
  color: var(--paper);
}
.story-index a.is-active span,
.scene-slate span:first-child {
  color: var(--accent);
}
.scenes {
  grid-column: 6 / -1;
}
.scene {
  padding-bottom: var(--space-xl);
}
.scene + .scene {
  padding-top: var(--space-xl);
  --rt: 1;
}
.scene:last-child {
  padding-bottom: 0;
}
/* Même cadre 3:2 pour chaque étape : c'est lui qui donne le rythme */
.scene-frame {
  display: block;
  margin: 0;
  aspect-ratio: 3/2;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.scene-frame img,
.scene-frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-base) var(--ease);
}
a.scene-frame:hover img {
  transform: scale(1.03);
}
.scene-frame-duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: calc(var(--gap) / 2);
  background: none;
}
.duo-cell {
  display: block;
  border-radius: var(--radius);
}
.scene-slate {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  margin: 1.5rem 0 0;
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.scene-title {
  margin-top: 1rem;
  font-size: var(--fs-h2);
}
/* Sans étiquette au-dessus, le titre prend la place sous l'image */
.scene-frame + .scene-title {
  margin-top: 1.75rem;
}
.scene > .scene-title:first-child {
  margin-top: 0;
}
.scene-text {
  max-width: 36rem;
  margin: 1.25rem 0 0;
  font-size: var(--fs-text);
  line-height: var(--lh-text);
  color: var(--soft);
}
/* Parcours (liste) : filet en haut, titre en police d'affichage, texte doux. */
.timeline {
  list-style: none;
  margin: 2.5rem 0 0;
  padding: 0;
  max-width: 40rem;
}
.timeline li {
  padding: 1.5rem 0;
  --rt: 1;
}
.timeline li:last-child {
  --rb: 1;
}
.timeline h3 {
  font-size: var(--fs-h3);
}
.timeline p {
  margin: 0.6rem 0 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--soft);
}
/* Principes : trois colonnes courtes, sans numéro (le sommaire numérote
   déjà les sections ; un filet suffit à séparer les idées) */
.principles {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  margin: 2rem 0 0;
  padding: 0;
}
.principles li {
  padding-top: 1.5rem;
  --rt: 1;
}
.principles h3 {
  font-size: var(--fs-h3);
}
.principles p {
  margin: 0.6rem 0 0;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--soft);
}
@media (max-width: 760px) {
  .principles {
    grid-template-columns: 1fr;
  }
}
/* Parcours : colonne de dates à gauche, contenu à droite.
   Hiérarchie : poste (affichage) → entreprise (petites capitales)
   → description (texte doux), avec des espacements croissants. */
.timeline {
  max-width: 48rem;
}
.timeline li {
  display: grid;
  grid-template-columns: 11.5rem 1fr;
  column-gap: var(--gap);
  padding: 2rem 0;
}
.timeline-period {
  padding-top: 0.5rem;
  white-space: nowrap;
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.timeline .timeline-company {
  margin: 0.45rem 0 0;
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.timeline .timeline-company + p {
  margin-top: 1rem;
  max-width: 32rem;
}
@media (max-width: 760px) {
  .timeline li {
    grid-template-columns: 1fr;
    row-gap: 0.6rem;
  }
  .timeline-period {
    padding-top: 0;
  }
}
/* Outils : une rangée de logos monochromes (SVG en masque, teintés par
   la couleur du texte), le nom en petite étiquette dessous */
/* Colonnes égales : les logos sont espacés régulièrement, quelle que
   soit la longueur des noms */
.tool-logos {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 2.5rem 1rem;
  max-width: 48rem;
  margin: 2.5rem 0 0;
  padding: 0;
}
.tool-logos li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  color: var(--soft);
  transition: color var(--dur-fast) var(--ease);
}
.tool-logos li:hover {
  color: var(--paper);
}
.tool-logo {
  width: 2.25rem;
  height: 2.25rem;
  background: currentColor;
  -webkit-mask: var(--logo) center / contain no-repeat;
  mask: var(--logo) center / contain no-repeat;
}
.tool-name {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
/* Sous-titres (Expérience / Formation / Outils) : même étiquette,
   même niveau, sous le grand titre de la section */
.list-label {
  margin: 2rem 0 0;
  font-family: var(--sans);
  line-height: 1.4;
}
.scene-title + .list-label {
  margin-top: 2.5rem;
}
.scene-title + .list-label + .timeline {
  margin-top: 0.5rem;
}
.timeline + .list-label {
  margin-top: 3.5rem;
}
/* Formation : secondaire, donc une simple liste discrète, sans filets.
   Diplôme en texte doux, école en retrait de couleur à la suite. */
.education {
  list-style: none;
  display: grid;
  gap: 0.45rem;
  margin: 1rem 0 0;
  padding: 0;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--soft);
}
.education span {
  color: var(--muted);
}
.education span::before {
  content: "—";
  margin: 0 0.6em;
}
@media (max-width: 760px) {
  .tool-logos {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 2rem 1rem;
  }
}
.scene-video {
  margin: 1.75rem 0 0;
  aspect-ratio: 16/9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.scene-video video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ============================================================
   Pages projet
   ============================================================ */
/* Retour à la liste : lien discret au-dessus du titre, la flèche
   glisse vers la gauche au survol */
.case-back {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  /* Remonté juste sous la navigation et nettement détaché du titre :
     c'est une action de navigation, pas une étiquette du projet */
  /* Zone cliquable plus haute au doigt, position inchangée */
  padding-block: 0.7rem;
  margin-top: -0.7rem;
  margin-bottom: calc(clamp(2.5rem, 5vw, 4rem) - 0.7rem);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
  transition: color var(--dur-fast) var(--ease);
}
.case-back:hover,
.case-back:focus-visible {
  color: var(--paper);
}
/* Retour au projet précédent (initPrevCaseReturn) : pastille fixe sous la
   nav, avec une jauge (--pull, 0 → 1) qui se remplit quand on continue à
   remonter en haut de page. Mise au point en entrant, comme le reste. */
.prev-case {
  position: fixed;
  top: 6rem;
  left: 50%;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.75rem 1.25rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass);
  backdrop-filter: blur(20px);
  font-size: var(--fs-small);
  color: var(--soft);
  white-space: nowrap;
  opacity: 0;
  filter: blur(6px);
  transform: translate(-50%, -0.75rem);
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease), transform var(--dur-base) var(--ease-glide);
}
.prev-case.is-visible {
  opacity: 1;
  filter: blur(0px);
  transform: translate(-50%, 0);
  pointer-events: auto;
}
.prev-case-label {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.prev-case strong {
  font-weight: 500;
  color: var(--paper);
}
.prev-case .arrow {
  color: var(--accent);
}
.prev-case-bar {
  position: relative;
  height: 2px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--line);
}
.prev-case-bar span {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(var(--pull, 0));
  transition: transform 0.15s linear;
}
.prev-case-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.prev-case-short {
  display: none;
}
/* Mobile : pastille compacte sur une ligne (« ↑ Revenir à Projet »),
   à la largeur de son texte ; l'en-tête de la page descend d'autant pour
   que la pastille ne recouvre jamais le lien retour ni le nom du projet */
@media (max-width: 760px) {
  .prev-case {
    max-width: calc(100% - 2 * var(--pad));
    gap: 0.5rem;
    padding: 0.6rem 1rem 0.7rem;
  }
  .prev-case-long {
    display: none;
  }
  .prev-case-short {
    display: inline;
  }
  .has-prev-case .case-overview {
    padding-top: 10.5rem;
  }
}

/* Bouton "Voir tous les projets" : bien détaché de l'écran "Projet suivant" */
.case-end {
  padding-bottom: clamp(6rem, 12vw, 10rem);
}
.case-hero-media {
  position: relative;
  height: 75svh;
  min-height: 24rem;
  max-height: 52rem;
  overflow: hidden;
}
.case-bg {
  position: absolute;
  inset: 0;
  background: var(--surface) center 42% / cover;
}
/* "En bref" : accroche à gauche, fiche projet à droite */
.case-overview {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  align-items: start;
  /* Premier bloc de la page : 7rem pour passer sous la nav flottante */
  padding: 7rem 0 clamp(3rem, 6vw, 5rem);
}
/* Lien retour sur sa propre ligne : la fiche (Rôle, Livrables…) s'aligne
   ainsi sur le bloc titre seul, à la même place dans toutes les études de
   cas, avec ou sans bouton « Voir le site » */
.case-overview > .case-back {
  grid-column: 1 / -1;
  justify-self: start;
  margin-bottom: calc(clamp(2.5rem, 5vw, 4rem) - 0.7rem - var(--gap));
}
.case-overview-lead {
  grid-column: 1 / span 5;
}
/* Nom du projet : h1 de la page, en petit (style eyebrow) au-dessus de
   l'accroche, qui porte la grande typographie */
.case-name {
  font-family: var(--sans);
  line-height: 1.3;
}
.case-intro {
  margin: 1.25rem 0 0;
  font-family: var(--display);
  font-size: var(--fs-h2);
  line-height: 1.05;
  letter-spacing: var(--ls-display);
}
.case-overview-lead .site-link {
  margin-top: 2rem;
}
.case-overview .case-facts {
  grid-column: 7 / -1;
  /* fiche alignée en bas du bloc titre (ou du bouton « Voir le site ») */
  align-self: end;
}
.case-facts {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  column-gap: var(--gap);
  margin: 0;
}
.case-facts > div {
  --rt: 1;
  padding: 1.25rem 0 1.5rem;
}
.case-facts dd {
  margin: 0;
}
.meta-label {
  display: block;
  font-size: var(--fs-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--muted);
  margin-bottom: 0.5rem;
}
.meta-value {
  font-size: var(--fs-body);
  line-height: 1.4;
}
.story-row {
  padding: var(--space-md) 0;
}
.story-row p {
  font-size: var(--fs-text);
  line-height: var(--lh-text);
  color: var(--soft);
  max-width: 36rem;
}
.video video,
.gallery-track img {
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius);
  display: block;
}
.video video {
  aspect-ratio: 16/9;
  background: var(--surface);
}

/* ============================================================
   Études de cas web : maquettes en grille de cartes (esprit Awwwards).
   Option du projet (stage.cards) : cartes sur la scène de marque
   (dégradé --stage-from → --stage-to, halo --stage-glow).
   ============================================================ */
.web-screens {
  --stage-from: #0b1426;
  --stage-to: #16223b;
  --stage-glow: rgba(145, 169, 255, 0.25);
  padding: var(--space-md) 0;
}
.screen {
  margin: 0;
}
.screen-stage {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(70% 60% at 50% 18%, var(--stage-glow), transparent 70%),
    linear-gradient(165deg, var(--stage-from), var(--stage-to));
  isolation: isolate;
}
/* Grain très léger : la scène a de la matière, pas un aplat numérique */
.screen-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 90% at 50% 120%, rgba(0, 0, 0, 0.45), transparent 60%);
  pointer-events: none;
}
.screen-device {
  position: relative;
  border-radius: calc(var(--radius) * 0.75);
  box-shadow:
    0 2.5rem 5rem -1.5rem rgba(0, 0, 0, 0.65),
    0 1rem 2rem -1rem rgba(0, 0, 0, 0.45);
}
.screen-frame {
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: inherit;
  overflow: hidden;
  background: var(--surface);
}
.screen-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}
/* Grille (esprit galerie Awwwards) : cartes neutres toutes identiques
   (4:3), l'écran centré avec beaucoup d'air ; un écran mobile (.is-mobile)
   se tient debout au centre de sa carte. Survol : la carte s'éclaircit à
   peine, l'écran reste immobile. */
.screens-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
}
.web-screens .screen-stage {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  padding: 0 7%;
  background: color-mix(in srgb, var(--paper) 5%, var(--bg));
  transition: background var(--dur-base) var(--ease);
}
.web-screens .screen-stage::after {
  display: none;
}
.web-screens .screen-device {
  width: 100%;
  border-radius: 0.4rem;
  box-shadow: 0 1.5rem 3rem -1.25rem rgba(0, 0, 0, 0.6);
}
.web-screens .screen:hover .screen-stage {
  background: color-mix(in srgb, var(--paper) 8%, var(--bg));
}
/* Option du projet (stage.cards) : toutes les cartes sur la scène de
   marque, comme l'écran vedette */
.web-screens.is-staged .screen-stage,
.web-screens.is-staged .screen:hover .screen-stage {
  background:
    radial-gradient(70% 60% at 50% 12%, var(--stage-glow), transparent 70%),
    linear-gradient(165deg, var(--stage-from), var(--stage-to));
}
.web-screens.is-staged .screen-stage::after {
  display: block;
  transition: opacity var(--dur-base) var(--ease);
}
.web-screens.is-staged .screen:hover .screen-stage::after {
  opacity: 0.6;
}
/* Écran mobile : debout, environ 86 % de la hauteur de la carte. Calé
   sur la largeur (carte 4:3 → 0,86 × 3/4 × 9/19,5 ≈ 30 %), une hauteur en
   % n'étant pas fiable dans la grille */
.screen.is-mobile .screen-device {
  width: 30%;
  border-radius: 1rem;
}
.screen.is-mobile .screen-frame {
  aspect-ratio: 9 / 19.5;
}
/* Vignette mobile : toujours deux téléphones côte à côte (pair) */
.web-screens .screen.is-pair .screen-stage {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6%;
}
.screen-frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.screen-caption {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  margin-top: 1rem;
  font-size: var(--fs-body);
}
@media (min-width: 1280px) {
  .screens-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 760px) {
  .screens-grid {
    grid-template-columns: 1fr;
  }
  .web-screens .screen-stage {
    padding: 0 6%;
  }
}

/* Vidéo verticale (9:16) : texte court à gauche, vidéo 9:16 à
   droite, jamais plus haute que l'écran pour être vue en entier */
.video-vertical {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  align-items: center;
}
.video-vertical-text {
  grid-column: 1 / span 5;
}
.video-vertical-caption {
  max-width: 26rem;
  margin: 1rem 0 0;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--soft);
}
.video-vertical .case-video {
  grid-column: 7 / -1;
  justify-self: center;
}
.video-vertical video {
  width: auto;
  height: min(85svh, 52rem);
  aspect-ratio: 9/16;
}

/* Clips additionnels (logo anim, écran de chargement…) : plusieurs vidéos
   visibles en même temps, à taille réduite, plutôt qu'empilées plein écran */
.more-videos > .eyebrow {
  margin-bottom: 1.5rem;
}
/* Vidéos de même taille, côte à côte, alignées en haut ; légende sous
   chacune, alignée à gauche comme les légendes des maquettes */
.more-videos-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
  align-items: start;
}
.more-videos-grid.is-single {
  grid-template-columns: minmax(0, 52rem);
  justify-content: center;
}
/* Vidéo carrée seule : taille raisonnable, centrée */
.more-videos-grid.is-single .more-videos-item[style*="1/1"] {
  width: min(100%, 32rem);
  justify-self: center;
}
.more-videos-item .case-video video {
  display: block;
  width: 100%;
  /* video.ratio dans projects-data.js (ex. "1/1") ; 16:9 par défaut */
  aspect-ratio: var(--video-ratio, 16 / 9);
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface);
}
.more-videos-label {
  margin-top: 1rem;
  color: var(--muted);
}
@media (max-width: 760px) {
  .more-videos-grid {
    grid-template-columns: 1fr;
  }
}

/* Vidéo de case study + bouton son : invisible au repos, il apparaît au
   survol de la vidéo (toujours visible sur écran tactile) */
.case-video {
  position: relative;
  border-radius: var(--radius);
}
.sound-toggle {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.5rem;
  padding: 0 1rem 0 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--glass);
  backdrop-filter: blur(20px);
  color: var(--paper);
  font: 500 var(--fs-label)/1 var(--sans);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease), transform var(--dur-base) var(--ease-glide),
    border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
/* Souris (.has-mouse) : caché au repos, il apparaît au survol. Sans
   souris (tactile) : toujours visible, en bas à droite. */
.has-mouse .sound-toggle {
  opacity: 0;
  filter: blur(4px);
  transform: translateY(0.4rem);
}
.has-mouse :is(.case-video, .kv-frame):hover .sound-toggle,
.sound-toggle:focus-visible {
  opacity: 1;
  filter: blur(0px);
  transform: none;
}
.sound-toggle:hover {
  border-color: var(--accent);
  color: var(--accent);
}
/* Souris : le bouton part du centre et suit le curseur (followCursor pose
   "transform") ; l'apparition passe par "scale" pour ne pas entrer en
   conflit. Toute la vidéo est cliquable (au doigt aussi). */
.is-follow {
  cursor: pointer;
}
.has-mouse .is-follow .sound-toggle {
  right: auto;
  bottom: auto;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  will-change: transform;
  scale: 0.9;
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease), scale var(--dur-base) var(--ease-glide),
    border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.has-mouse .is-follow:hover .sound-toggle,
.has-mouse .is-follow .sound-toggle:focus-visible {
  transform: translate(-50%, -50%);
  scale: 1;
}
.sound-icon {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sound-speaker {
  fill: currentColor;
}
/* Coupé : croix ; actif : ondes */
.sound-waves,
.sound-toggle.is-on .sound-off {
  display: none;
}
.sound-toggle.is-on .sound-waves {
  display: inline;
}
.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
}
.stat {
  --rt: 1;
  padding-top: 1.5rem;
}
.stat-value {
  display: block;
  font-size: var(--fs-h1);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.stat-label {
  display: block;
  margin-top: 0.6rem;
  font-size: var(--fs-label);
  font-weight: 500;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
}
/* Key visuals : en-tête de section puis grille de deux colonnes lue en
   zigzag (colonne de droite décalée), très aérée. Toutes les images
   au même format 4:3, recadrées autour de --pos. Léger parallaxe entre
   les colonnes en JS (parallaxes). */
.key-visuals {
  padding: var(--space-xl) 0 var(--space-lg);
}
/* Deux colonnes au même pas (--kv-gap) ; la droite est décalée d'un
   demi-pas exact : demi-hauteur de carte (image 4:3 = 75 % de la largeur
   de colonne + légende ≈ 4.5rem) + demi-écart. Rythme parfaitement
   régulier : 01, 02, 03, 04 à intervalles égaux. */
.kv-grid {
  --kv-gap: clamp(5rem, 9vw, 8rem);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  column-gap: clamp(2rem, 6vw, 6.5rem);
  align-items: start;
}
.kv-col {
  display: grid;
  gap: var(--kv-gap);
}
.kv-col + .kv-col {
  padding-top: calc((var(--kv-h, 75%) + 4.5rem + var(--kv-gap)) / 2);
}
/* Variante paysage 3:2 (visuels plus panoramiques) */
.kv-wide {
  --kv-h: 66.667%;
}
.kv-wide .kv-frame {
  aspect-ratio: 3 / 2;
}
.kv {
  margin: 0;
}
.kv-frame {
  position: relative; /* bouton son des visuels vidéo */
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--surface);
}
.kv-frame :is(img, video) {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
}
.kv-caption {
  margin-top: 1.5rem;
}
.kv-text {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.kv-label {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.kv-title {
  font: 400 var(--fs-h3)/1 var(--display);
  letter-spacing: var(--ls-display);
  color: var(--paper);
}
@media (max-width: 760px) {
  .kv-grid {
    --kv-gap: var(--space-lg);
    grid-template-columns: 1fr;
    row-gap: var(--kv-gap);
  }
  /* Une seule colonne : on "dissout" les colonnes et l'ordre 01 → 04
     (style order posé en JS) reprend la main */
  .kv-col {
    display: contents;
  }
}
.gallery-label {
  margin-bottom: 1.5rem;
}
/* Galerie : défilement horizontal piloté par GSAP (desktop) */
.gallery {
  margin: 0.5rem 0 var(--space-md);
}
/* La piste démarre alignée sur la grille (même marge que .shell) : la
   première image est entière quand la section arrive à l'écran */
.gallery-viewport {
  position: relative;
  overflow: hidden;
  height: 48vh;
  display: flex;
  align-items: center;
  padding-inline: max(var(--pad), calc((100% - 96rem) / 2));
}
/* Couche du glisser (décalage posé en JS), entre le cadre et la piste */
.gallery-drag {
  flex: none;
  display: flex;
  align-items: center;
  height: 100%;
  will-change: transform;
}
.gallery-viewport.is-draggable {
  cursor: grab;
  user-select: none;
  touch-action: pan-y;
}
.gallery-viewport.is-dragging {
  cursor: grabbing;
}
.gallery-viewport img {
  -webkit-user-drag: none;
  pointer-events: none;
}
/* Étiquette "← Glisser →" : accompagne le curseur (resté visible, en main)
   et le suit (followCursor), centrée sous la pointe ; elle
   apparaît au survol en se mettant au point. */
.gallery-cursor {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  display: none;
  align-items: center;
  gap: 0.6rem;
  height: 2.75rem;
  padding: 0 1.2rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--glass);
  backdrop-filter: blur(20px);
  color: var(--paper);
  font: 500 var(--fs-label)/1 var(--sans);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  filter: blur(4px);
  scale: 0.85;
  transform: translate(-50%, -50%);
  /* Centrée juste sous la pointe ("translate" s'ajoute au suivi posé en
     JS) : lue comme une légende du curseur, sans le masquer */
  translate: 0 calc(50% + 1.5rem);
  transform-origin: top center;
  will-change: transform;
  transition: opacity var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease), scale var(--dur-base) var(--ease-glide);
}
.gallery-cursor .arrow {
  color: var(--accent);
}
.has-mouse .is-draggable .gallery-cursor {
  display: inline-flex;
}
.has-mouse .is-draggable:hover .gallery-cursor {
  opacity: 1;
  filter: blur(0px);
  scale: 1;
}
.has-mouse .is-draggable.is-dragging .gallery-cursor {
  scale: 0.9;
  opacity: 0.75;
}.gallery-track {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--gap);
  width: max-content;
  height: 100%;
  will-change: transform;
}
.gallery-track img {
  flex: 0 0 auto;
  height: 32vh;
  width: auto;
  aspect-ratio: 16/9;
}
/* Variante carrée (slides de carrousel) */
.gallery-square .gallery-viewport {
  height: 62vh;
}
.gallery-square .gallery-track img {
  height: 54vh;
  aspect-ratio: 1;
}
/* ============================================================
   Projet suivant : fin de page en plan-séquence. Le projet suivant
   s'ouvre dès que le bloc remplit l'écran. Deux variables posées par
   initNextCase : --r (0 → 1 pendant l'arrivée du bloc) fait sortir la
   couverture du noir et passer du flou au net (fondu entre une copie
   floue figée et l'image nette : seule l'opacité varie, très léger à
   animer) ; --p remplit la barre jusqu'à l'enchaînement. Sans JS, 1.
   ============================================================ */
.next-case {
  position: relative;
  /* Un écran : le projet suivant s'ouvre dès que le bloc le remplit */
  height: 100svh;
}
.next-case-link {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  padding: 0 var(--pad);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
}
.next-case-media {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--bg);
}
.next-case-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.next-case-blur {
  filter: blur(24px);
  transform: scale(1.12);
  opacity: min(1, calc(var(--r, 1) * 2.5)); /* sort du noir (0 → 0,4) */
}
.next-case-sharp {
  opacity: clamp(0, calc((var(--r, 1) - 0.35) / 0.65), 1); /* puis la mise au point */
  transform: scale(calc(1.06 - var(--r, 1) * 0.06));
}
/* Voile pour garder le titre lisible sur n'importe quelle couverture */
.next-case-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(var(--veil-light), var(--veil));
}
.next-case-title {
  font-size: var(--fs-display);
  line-height: 1;
}
/* Barre de progression sur toute la largeur, en bordure haute du bloc :
   visible dès qu'il arrive à l'écran */
.next-case-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  background: color-mix(in srgb, var(--paper) 20%, transparent);
}
/* Remplissage en couleur d'accent avec un léger halo : bien lisible
   même sur une couverture claire */
.next-case-bar span {
  position: absolute;
  inset: 0;
  background: var(--accent);
  box-shadow: 0 0 18px 1px var(--accent), 0 0 5px var(--accent);
  transform-origin: left;
  transform: scaleX(var(--p, 0));
}
@media (prefers-reduced-motion: reduce) {
  .next-case {
    height: auto;
  }
  .next-case-link {
    position: relative;
    height: 80svh;
  }
  .next-case-bar {
    display: none;
  }
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1024px) {
  .about-hero-copy,
  .about-hero-portrait {
    grid-column: 1 / -1;
  }
  /* Pile centrée et un peu réduite : l'éventail des cartes de derrière
     (décalées de ±8 %) reste dans l'écran */
  .about-hero-portrait {
    justify-self: center;
  }
  .photo-stack {
    width: min(16rem, 64vw);
  }
  .about-facts {
    padding-top: var(--space-lg);
  }
  .hero-copy {
    flex-direction: column;
    align-items: stretch;
  }
  /* Portrait réduit : entièrement visible sous le texte dès le premier
     écran (sur tablette, il garde plus de place) */
  .hero-ascii {
    width: min(52vw, 26rem, 34svh);
    /* Peu de marge : le haut du canvas est déjà vide au-dessus de la tête */
    margin: clamp(0rem, 2vw, 3.5rem) auto 0;
  }
  /* Parcours : plus de place pour le sommaire fixe */
  .storyboard {
    display: block;
  }
  /* Une colonne : chaque section s'ouvre sur son titre (et son étiquette),
     puis l'image ; jamais deux visuels qui s'enchaînent (la pile de photos
     du haut de page, puis la vidéo de "Ma méthode de travail") */
  .scene {
    display: flex;
    flex-direction: column;
  }
  .scene > * {
    min-width: 0;
  }
  .scene-slate {
    order: -2;
  }
  .scene-title {
    order: -1;
  }
  .scene > .scene-frame {
    margin-top: 1.75rem;
  }
  .scene > .scene-frame + .scene-title {
    margin-top: 0;
  }
  .story-index {
    display: none;
  }
}
/* Galerie en colonne : mobile/tablette, ou sans animations */
@media (max-width: 1024px), (prefers-reduced-motion: reduce) {
  .gallery-viewport,
  .gallery-track {
    height: auto;
    overflow: visible;
  }
  /* Bloc normal (et non flex) : sinon la piste prend la largeur réelle
     des images une fois chargées et fait déborder toute la page */
  .gallery-viewport,
  .gallery-drag {
    display: block;
  }
  .gallery-drag {
    height: auto;
  }
  .gallery-cursor {
    display: none !important;
  }
  .gallery-track {
    flex-direction: column;
    width: 100%;
  }
  .gallery-track img {
    width: 100%;
    height: auto;
  }
  /* Slides carrées : deux par ligne plutôt qu'une longue colonne */
  .gallery-square .gallery-viewport {
    height: auto;
  }
  .gallery-square .gallery-track {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
  .gallery-square .gallery-track img {
    height: auto;
  }
}

@media (max-width: 760px) {
  .hero h1 {
    font-size: clamp(1.8rem, 7.5vw, 2.4rem);
  }
  /* Portrait en tête, bord à bord : c'est le visuel du premier écran,
     comme sur desktop où il occupe la moitié de la page. Le texte vient
     se poser sur le bas du portrait, déjà fondu dans le noir. */
  .hero {
    padding-top: 4.75rem;
    align-items: flex-start;
  }
  .hero-ascii {
    order: -1;
    width: min(calc(100% + 2 * var(--pad)), 62svh);
    margin: 0 auto -22%;
  }
  .hero-text {
    position: relative;
    z-index: 1;
  }
  /* Pastilles compactes : taille et espacements suivent la largeur
     d'écran. Les deux premières tiennent sur une ligne jusqu'à 320px,
     la troisième passe à la ligne si la place manque. */
  .hero-roles {
    gap: clamp(0.35rem, 1.6vw, 0.6rem);
  }
  .hero-roles .skill-tag {
    font-size: clamp(0.58rem, 2.9vw, 0.8rem);
    letter-spacing: 0.06em;
    padding: 0.45rem clamp(0.6rem, 2.6vw, 1rem);
    white-space: nowrap;
  }
  .desktop-nav {
    display: none;
  }
  .menu-toggle {
    display: block;
    position: relative;
    z-index: 41;
    width: 2.8rem;
    height: 2.8rem;
    border: 0;
    background: none;
    cursor: pointer;
  }
  /* Menu mobile : une seule chorégraphie, jouée à l'endroit à l'ouverture
     et à l'envers à la fermeture.
     Ouverture : le rideau descend → les liens se mettent au point en
     cascade ; le burger rapproche ses traits puis pivote en croix.
     Fermeture : les liens repartent (ordre inverse) → le rideau remonte ;
     la croix se redresse puis ses traits s'écartent. */
  .menu-toggle {
    --menu-ease: cubic-bezier(0.76, 0, 0.24, 1); /* accélère puis se pose, dans les deux sens */
  }
  .menu-toggle span {
    position: absolute;
    left: 0.5rem;
    right: 0.5rem;
    top: 1.15rem;
    height: 1px;
    background: var(--paper);
    /* fermeture : la croix se redresse, puis les traits s'écartent */
    transition: transform 0.3s var(--menu-ease), top 0.25s var(--menu-ease) 0.25s;
  }
  .menu-toggle span + span {
    top: 1.7rem;
  }
  .menu-is-open .menu-toggle span {
    top: 1.4rem;
    transform: rotate(45deg);
    /* ouverture : les traits se rejoignent, puis pivotent */
    transition: top 0.25s var(--menu-ease), transform 0.35s var(--menu-ease) 0.2s;
  }
  .menu-is-open .menu-toggle span + span {
    transform: rotate(-45deg);
  }
  /* Rideau : il descend du haut de l'écran, bord bas légèrement arrondi
     pendant le mouvement (même langage que les rideaux des images) */
  .menu-panel {
    display: grid;
    place-items: center;
    position: fixed;
    inset: 0;
    z-index: 40;
    background: var(--bg);
    visibility: hidden;
    clip-path: inset(0 0 100% 0 round 0 0 50% 50% / 0 0 3rem 3rem);
    /* fermeture : il attend la sortie des liens, puis remonte */
    transition: clip-path 0.7s cubic-bezier(0.76, 0, 0.24, 1) 0.22s, visibility 0s 0.92s;
  }
  .menu-is-open .menu-panel {
    visibility: visible;
    clip-path: inset(0 0 0 0 round 0 0 0 0 / 0 0 0 0);
    transition: clip-path 0.8s cubic-bezier(0.76, 0, 0.24, 1);
  }
  .mobile-nav {
    display: flex;
    flex-direction: column;
    font-size: var(--fs-display);
    gap: 1rem;
  }
  /* Liens : mise au point (flou → net) en montant, en cascade ; sortie
     dans l'ordre inverse, du dernier au premier */
  .mobile-nav a {
    opacity: 0;
    filter: blur(8px);
    transform: translateY(1.2rem);
    transition: opacity 0.4s var(--ease), filter 0.4s var(--ease), transform 0.45s var(--ease);
    transition-delay: calc((2 - var(--n, 0)) * 0.05s);
  }
  .mobile-nav a:nth-child(2) {
    --n: 1;
  }
  .mobile-nav a:nth-child(3) {
    --n: 2;
  }
  .menu-is-open .mobile-nav a {
    opacity: 1;
    filter: blur(0);
    transform: none;
    transition: opacity 0.6s var(--ease), filter 0.7s var(--ease-glide), transform 0.9s var(--ease-glide);
    transition-delay: calc(0.42s + var(--n, 0) * 0.08s);
  }

  /* Pleine largeur. Les sélecteurs reprennent ceux du desktop
     (.section-heading h2, .story-row > div, .service-title…), sinon ces
     derniers, plus spécifiques, gardent leur colonne étroite */
  .section-heading > *,
  .section-heading h2,
  .section-heading .eyebrow,
  .story-row > *,
  .story-row > div,
  .story-row > .eyebrow,
  .service > *,
  .service > :is(.service-verb, .service-title, .service-text) {
    grid-column: 1 / -1;
  }
  .section-heading h2,
  .story-row > div {
    margin-top: 1.5rem;
  }
  .projects-grid {
    gap: clamp(2.5rem, 10vw, 4.5rem) 0;
  }
  /* Mobile : cartes en portrait (4:5), plus larges, qui occupent l'écran
     (visuel cardCover si le projet en a un, via <picture>) */
  .carousel-stage {
    aspect-ratio: 10/11;
  }
  .carousel-card {
    width: 82%;
    aspect-ratio: 4/5;
  }
  .carousel-btn {
    display: none;
  }
  .js .showreel-frame {
    clip-path: inset(4% 4% 4% 4% round 10px);
  }
  .case-hero-media {
    height: 58svh;
    min-height: 20rem;
  }
  .case-overview > *,
  .case-overview .case-facts {
    grid-column: 1 / -1;
  }
  .case-overview .case-facts {
    margin-top: 1rem;
  }
  .case-facts {
    grid-template-columns: 1fr;
  }
  .stats {
    grid-template-columns: repeat(2, 1fr);
  }
  .video-vertical-text,
  .video-vertical .case-video {
    grid-column: 1 / -1;
  }
  .video-vertical video {
    height: auto;
    width: min(100%, 26rem);
  }
  /* Filtres : bande défilante horizontale, bord à bord de l'écran (le
     premier filtre reste aligné sur la marge du texte). Les pastilles
     gardent leur largeur naturelle : jamais écrasées ni superposées.
     Un fondu à droite signale qu'il y en a d'autres. */
  .filter-tabs {
    flex-wrap: nowrap;
    gap: 0.5rem;
    margin-inline: calc(-1 * var(--pad));
    padding-inline: var(--pad);
    scroll-padding-inline: var(--pad);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
  }
  .filter-tabs::-webkit-scrollbar {
    display: none;
  }
  /* Marge de fin : le dernier filtre peut sortir du fondu */
  .filter-tabs::after {
    content: "";
    flex: 0 0 1.5rem;
  }
  .filter-tabs button {
    flex: 0 0 auto;
    scroll-snap-align: start;
    height: 2.75rem;
    padding: 0 1.1rem;
    font-size: 0.75rem;
  }
  /* Titres et services des cartes : sur deux lignes plutôt que tronqués */
  .project-info h3,
  .project-info p {
    white-space: normal;
    overflow: visible;
  }
  .project-info p {
    margin-top: 0.35rem;
    line-height: 1.5;
  }
  /* Titres du carrousel : à l'échelle de la carte, lignes équilibrées
     (pas de "· 2025" orphelin) */
  .carousel-title h3 {
    font-size: clamp(1.4rem, 6vw, 1.65rem);
    text-wrap: balance;
  }
  .carousel-title p {
    margin-top: 0.5rem;
    font-size: var(--fs-micro);
    line-height: 1.5;
    text-wrap: balance;
  }
}
/* Petits textes : jamais sous ~12,5 px sur téléphone */
@media (max-width: 760px) {
  :root {
    --fs-micro: 0.78rem;
  }
}
/* Sans souris (tactile), le bouton son passe en bas à gauche : en bas à
   droite, il tombait sous le bouton flottant "retour en haut" */
html:not(.has-mouse) .sound-toggle {
  right: auto;
  left: 1rem;
}

/* ============================================================
   Loader d'entrée (home) : lettres, barre, compteur, rideau. Sobre :
   aucun visuel de projet
   ============================================================ */
body.is-loading {
  overflow: hidden;
}
/* Déjà vu pendant la session : masqué dès le chargement (script du <head>) */
.no-loader .loader {
  display: none;
}
.loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.6rem;
  background: var(--bg);
}
.loader-mark {
  font: 400 clamp(3.25rem, 8vw, 6rem) / 1 var(--display);
  letter-spacing: var(--ls-display);
  overflow: hidden; /* masque des lettres */
  padding: 0.12em 0.06em;
}
.loader-letter {
  display: inline-block;
  opacity: 0;
  transform-origin: 0% 100%;
}
/* Barre : un peu épaisse et contrastée, pour bien se lire */
.loader-bar {
  width: min(50vw, 18rem);
  height: 2px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--paper) 18%, transparent);
  position: relative;
  overflow: hidden;
  opacity: 0;
}
.loader-bar span {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: var(--accent);
}
.loader-count {
  font: 500 var(--fs-label)/1 var(--sans);
  letter-spacing: var(--ls-caps);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  opacity: 0;
}

/* ============================================================
   Transition entre les pages : rideau + trait d'accent.
   Sortie : le rideau monte, la page se floute. Entrée : la page démarre
   couverte (classe posée dans le <head>), le rideau se lève.
   ============================================================ */
.page-transition,
html.pt-enter::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--bg);
}
.page-transition::after,
html.pt-enter::after {
  content: "";
  position: fixed;
  left: 50%;
  top: 50%;
  z-index: 301;
  width: min(50vw, 18rem);
  height: 1px;
  background: var(--accent);
}
.page-transition {
  clip-path: inset(100% 0 0 0);
  transition: clip-path 0.46s var(--ease-curtain);
}
.page-transition.is-active {
  clip-path: inset(0);
}
.page-transition::after {
  transform: translate(-50%, -50%) scaleX(0);
  transform-origin: left center;
  transition: transform 0.32s var(--ease-curtain) 0.16s;
}
.page-transition.is-active::after {
  transform: translate(-50%, -50%) scaleX(1);
}
body.is-leaving > :not(.page-transition) {
  filter: blur(5px);
  opacity: 0.5;
  transition: filter 0.45s ease, opacity 0.45s ease;
}
html.pt-enter::before {
  clip-path: inset(0);
  transition: clip-path 0.62s var(--ease-curtain) 0.12s;
  pointer-events: none;
}
html.pt-enter::after {
  transform: translate(-50%, -50%) scaleX(1);
  transform-origin: right center;
  transition: transform 0.3s var(--ease-curtain);
  pointer-events: none;
}
html.pt-enter.pt-out::before {
  clip-path: inset(0 0 100% 0);
}
html.pt-enter.pt-out::after {
  transform: translate(-50%, -50%) scaleX(0);
}
/* Filet de sécurité : sans script, le rideau disparaît au bout de 4 s */
html.pt-enter::before,
html.pt-enter::after {
  animation: pt-failsafe 0s 4s forwards;
}
@keyframes pt-failsafe {
  to {
    visibility: hidden;
  }
}

/* ============================================================
   Bouton "retour en haut"
   ============================================================ */
.back-to-top {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 80;
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.5rem);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), visibility 0s var(--dur-fast), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.back-to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
