/* ZWEEVE — direction artistique.

   Quatre couleurs, déclarées une fois. Les sections ne manipulent jamais une
   valeur directement : elles lisent --paper, --ink, --muted et --accent, ce
   qui permet d'inverser une section entière avec l'attribut data-light. */
:root {
  --black: #161616;  /* noir profond */
  --white: #F4F4F4;  /* blanc froid */
  --grey: #B6B6B6;   /* gris secondaire */
  --silver: #A9ADB5; /* argent — réservé aux filets */
  --blue: #4F86FF;   /* bleu électrique — annotations, numérotation, catégories.
                        Relevé depuis #2F6BFF : à sa valeur pure il tombait à
                        4,0:1 sur le noir, sous le seuil en micro-typographie. */

  /* rôles, sur fond sombre */
  --paper: var(--black);
  --ink: var(--white);
  --muted: var(--grey);
  --accent: var(--blue);

  /* fond des médias : un cran sous le noir du site */
  --pit: #0E0E0E;

  /* seul arrondi du site : la roue du portfolio */
  --wheel-radius: 14px;

  /* Une seule voix typographique à forte personnalité, déclinée en poids et
     en corps ; une neutre pour la micro-typographie. */
  --display: Syne, "Helvetica Neue", Arial, sans-serif;
  --sans: Inter, -apple-system, "Helvetica Neue", Arial, sans-serif;

  /* une seule échelle pour toute la micro-typographie */
  --micro-size: 0.7rem;
  --micro-track: 0.24em;

  /* hauteur de scène mesurée en JS (évite les sauts de 100vh mobile) */
  --stage-h: 100vh;
  /* Longueur de scroll de l'intro, en multiples de la scene.

     Huit ecrans demandaient une quarantaine de crans de molette avant
     d'arriver au plan final sur le flacon. Cinq gardent le meme montage —
     le film occupe toujours les quatre cinquiemes de la course, la sortie
     le dernier cinquieme — en reduisant le trajet de 37 %. */
  --intro-span: 5;
}

/* sections claires : les mêmes règles, les rôles inversés. L'argent y serait
   illisible (2,3:1), il est remplacé par un gris ardoise. */
[data-light] {
  --paper: var(--white);
  --ink: var(--black);
  --muted: #666666; /* 5,1:1 sur blanc froid */
  --accent: #1B46C4; /* bleu électrique assombri pour le fond clair : 6,6:1 */
}

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

/* Noir plein derrière tout : c'est ce qu'on voit sur les côtés de l'écran,
   sous le rebond du défilement et au-delà de la largeur du document. Le gris
   de la palette y jurait avec les sections noires. */
html { background: #000; }

body {
  margin: 0;
  background: #000;
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ─────────────────────────────── LA MARQUE ──────────────────────────────── */

/* Le retrait est une transition, pas une bascule : la marque se retire quand
   les chiffres géants d'Our Process passent dessous, et revient d'elle-même.
   Seule l'opacité est transitionnée — la couleur, elle, est pilotée image par
   image depuis le film d'ouverture. */
.brand {
  transition: opacity 0.4s ease;
  position: fixed;
  top: clamp(1.5rem, 3vh, 2.4rem);
  left: clamp(1.25rem, 4vw, 4rem);
  z-index: 3;
  font-size: clamp(0.8rem, 1vw, 0.95rem);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  font-weight: 400;
  /* blanche sur le film, elle repasse au blanc froid dès que la page gagne le
     coin, et au noir au-dessus des sections claires (GSAP) */
  color: #FFFFFF;
  pointer-events: none;
  user-select: none;
}

/* ───────────────────────────── INTRO — VOLCANO ───────────────────────────── */

.intro {
  position: relative;
  z-index: 2;
  height: calc(var(--stage-h) * var(--intro-span));
  /* Le portfolio est remonté d'un écran sous le volcan pour que l'enchaînement
     soit continu, et le volcan passe par-dessus. Sa boîte couvrait donc le
     premier écran de la roue et interceptait les clics : la première campagne
     ne s'ouvrait jamais. Il n'a aucun élément cliquable — on le rend
     transparent au pointeur. Le défilement, lui, n'est pas affecté. */
  pointer-events: none;
}

/* la scène reste collée en haut pendant toute la durée de l'intro.
   fond ivoire opaque : rien de la suite du site ne fuite avant la sortie. */
.intro__stage {
  position: sticky;
  top: 0;
  height: var(--stage-h);
  overflow: hidden;
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: transform;
}

/* cadre 16:9 à ~92% de la largeur : c'est la taille d'arrivée.
   le plein écran est obtenu par un scale uniforme au départ (pas de déformation,
   pas de reflow — uniquement du transform, donc compositing GPU). */
.intro__frame {
  position: relative;
  overflow: hidden;
  /* dimensions de départ avant que JS ne prenne la main : jamais de petit
     rectangle au centre. La première image sert d'affiche le temps que le
     canvas prenne le relais. */
  width: 100%;
  height: 100%;
  background: var(--black) url("../media/frames/00000.jpg") center / cover no-repeat;
  transform-origin: 50% 50%;
  will-change: transform;
  backface-visibility: hidden;
}

.intro__media {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  pointer-events: none;
}

.brand--retiree { opacity: 0; }

/* ───────────────────────── SUITE DU SITE (révélation) ───────────────────── */

/* ───────────────────────── PORTFOLIO — GRANDE ROUE ──────────────────────── */

/* remonte d'exactement une scène : la roue se trouve derrière la scène collée
   de l'intro et se découvre à mesure que la vidéo quitte l'écran. */
.wheel {
  position: relative;
  z-index: 1;
  margin-top: calc(var(--stage-h) * -1);
  background: var(--paper);
  /* repli : la hauteur exacte est posée en JS d'après le nombre de campagnes */
  height: calc(var(--stage-h) * 5.2);
}

.wheel__stage {
  position: sticky;
  top: 0;
  height: var(--stage-h);
  overflow: hidden;
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  perspective-origin: 50% 50%;
}


/* une section épinglée masque la barre de défilement : l'index dit où l'on en est */
.wheel__index {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--sans);
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  color: rgba(243, 241, 234, 0.45);
  font-variant-numeric: tabular-nums;
}

.wheel__index b {
  font-weight: 400;
  color: var(--ink);
}

.wheel__ring {
  position: relative;
  transform-style: preserve-3d;
  will-change: transform;
}

/* chaque campagne est un bouton : elle ouvre sa vue plein écran */
.wheel__item {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transform-style: preserve-3d;
  will-change: transform, opacity;
}

.wheel__item:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 14px;
}

.wheel__media {
  overflow: hidden;
  background: var(--pit);
  border-radius: var(--wheel-radius);
}

.wheel__media img {
  display: block;
  border-radius: var(--wheel-radius);
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  user-select: none;
}


.wheel__title {
  font-family: var(--display);
  font-size: clamp(1.25rem, 1.9vw, 1.7rem);
  line-height: 1;
  color: var(--ink);
  letter-spacing: 0;
}


@media (max-width: 760px) {
}

/* ─────────────────────────────── MANIFESTO ──────────────────────────────── */

/* Section tenue : on la traverse, la scène reste collée pendant ce temps. */
.manifesto {
  /* noir plein, comme le portail : la sculpture de particules se détache mieux
     sur du vrai noir que sur le noir profond de la palette */
  --mani-paper: #000;
  --mani-ink: var(--ink);

  position: relative;
  z-index: 1;
  height: calc(var(--stage-h) * 2.6);
  background: var(--mani-paper);
  color: var(--mani-ink);
}

.manifesto__stage {
  position: sticky;
  top: 0;
  height: var(--stage-h);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 clamp(1.25rem, 4vw, 4rem);
}

/* masque de dévoilement : chaque mot monte depuis sa propre ligne */
.reveal {
  display: block;
  overflow: hidden;
}

.reveal__in {
  display: block;
}

.word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* de l'air sous la ligne de base : les jambages ne sont pas rognés.
     0,12 em ne suffisait pas aux grandes tailles de Syne. */
  padding-bottom: 0.22em;
  margin-bottom: -0.22em;
}

.word__in {
  display: inline-block;
  /* Pas de will-change ici. Promouvoir chaque mot en couche de composition
     faisait rogner la peinture à la boîte de l'élément : tout ce qui descend
     sous la ligne de descente de la police — la queue du g — disparaissait.
     GSAP promeut lui-même ce qu'il anime, le temps de l'animation. */
}

/* asymétrie : l'ouverture tient dans la colonne de gauche… */
.manifesto__opening {
  font-weight: 500;
  margin: 0 0 clamp(2rem, 6vh, 4.5rem);
  max-width: 14ch;
  font-family: var(--display);
  font-size: clamp(1.5rem, 2.8vw, 2.4rem);
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--mani-ink);
}

/* « WORLDS. » surligné : le bleu passe derrière, les lettres restent
   blanches. Le fond est porté par le masque de chaque mot — il épouse donc
   exactement sa boîte, sans déborder sur l'espace entre les mots. */
.manifesto__mark .word {
  background: var(--blue);
}

.manifesto__mark .word__in {
  color: var(--white);
}

/* …les deux mots occupent la largeur calée en JS et grandissent au passage */
.manifesto__huge {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  transform-origin: 0% 50%;
  will-change: transform;
}

.manifesto__fit {
  white-space: nowrap;
}

/* …et la chute bascule dans la colonne de droite, allumée mot à mot */
.manifesto__coda {
  margin: clamp(2.5rem, 7vh, 5rem) 0 0 auto;
  width: min(34ch, 100%);
  font-size: clamp(0.95rem, 1.35vw, 1.2rem);
  line-height: 1.75;
  color: var(--mani-ink);
}

.manifesto__line {
  display: block;
}

.manifesto__word {
  display: inline-block;
  white-space: pre;
  will-change: opacity;
}

@media (max-width: 760px) {
  .manifesto { height: calc(var(--stage-h) * 2.3); }
  .manifesto__coda { margin-left: 0; }
}

/* ───────────────────────────── OUR PROCESS ──────────────────────────────── */

/* Une fenêtre sur un ruban vertical : la section est tenue, sa hauteur est
   posée en JS (une station par écran), et le ruban défile derrière une
   ouverture fixe. */
.process {
  position: relative;
  z-index: 1;
  background: #000;
  color: var(--ink);
}

.process__window {
  position: sticky;
  top: 0;
  height: var(--stage-h);
  overflow: hidden;
  /* Aucun retrait vertical ici : il décalerait le ruban vers le bas sans
     réduire la hauteur des stations, et chacune se retrouverait sous le
     centre. Le retrait appartient donc aux stations. */
  padding: 0 clamp(1.25rem, 4vw, 4rem);
}

/* L'en-tête ne bouge pas : c'est le repère fixe contre lequel le ruban
   défile. Sans lui, on ne verrait plus que des stations qui passent. */
.process__head {
  position: absolute;
  top: clamp(3rem, 7vh, 5rem);
  left: clamp(1.25rem, 4vw, 4rem);
  right: clamp(1.25rem, 4vw, 4rem);
  z-index: 2;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  pointer-events: none;
}

.process__label {
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--accent);
}

.process__lead {
  margin: 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.4vw, 1.3rem);
  letter-spacing: -0.01em;
  color: var(--muted);
}

.process__strip {
  will-change: transform;
}

/* Une station par écran : le ruban avance d'exactement une hauteur de
   fenêtre à chaque étape. Les deux colonnes sont proches de l'équilibre —
   le déséquilibre vient du rapport des corps, pas de la largeur. */
.process__station {
  height: var(--stage-h);
  padding: clamp(2.5rem, 7vh, 4.5rem) 0;
  display: grid;
  /* la colonne du chiffre est la plus étroite des deux : c'est ce qui serre
     la gouttière centrale, sinon la station se creuse en son milieu */
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(1.5rem, 3.6vw, 4rem);
}

/* Le numéro n'est plus en Syne : à la graisse 800 ses chiffres sont larges et
   tassés, et le 0 du 02 s'ouvre en ovale — on lisait une déformation là où il
   n'y avait qu'un caractère de display. La neutre du site, en corps énorme et
   graisse légère, donne des chiffres d'une seule chasse, d'une seule hauteur,
   sans aucune transformation. */
.process__num {
  justify-self: start;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(4.5rem, 17vw, 15.5rem);
  /* 0.78 écrasait la boîte sous la hauteur des chiffres. */
  line-height: 1;
  letter-spacing: -0.05em;
  /* Là était la déformation : en chasse proportionnelle, Syne donne 106 px au
     1 et 219 au 4 — « 01 » mesurait 319 de large, « 04 » 432. Quatre numéros
     de largeurs différentes, et l'œil lit un chiffre écrasé. En chiffres
     tabulaires, tous font exactement la même chasse. */
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  /* anthracite éclairci : lisible sans jamais venir disputer le titre */
  color: color-mix(in srgb, var(--white) 20%, transparent);
  /* compensation optique du blanc de chasse à gauche, pas un rognage */
  margin-left: -0.05em;
  user-select: none;
}

.process__body {
  max-width: 48ch;
}

.process__step {
  display: block;
  margin-bottom: clamp(0.7rem, 1.6vh, 1.1rem);
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--muted);
}

.process__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(2.4rem, 5.6vw, 5rem);
  /* Syne descend bas : à 1 la queue des jambages sortirait de la boîte. */
  line-height: 1.06;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}

/* La description est du texte à lire, pas une légende : elle est posée au
   corps d'un chapô, en gris clair, et sa colonne est assez large pour que la
   phrase tienne en trois lignes. */
.process__note {
  margin: clamp(1rem, 2.2vh, 1.5rem) 0 0;
  max-width: 34ch;
  font-size: clamp(1.0625rem, 1.45vw, 1.375rem);
  line-height: 1.62;
  letter-spacing: 0.002em;
  color: color-mix(in srgb, var(--white) 84%, transparent);
}

@media (max-width: 760px) {
  /* Plus d'épinglage : les stations se suivent et respirent chacune leur
     tour. Tenir une section sur un écran de téléphone est pénible. */
  .process__window {
    position: static;
    height: auto;
    overflow: visible;
    padding-bottom: clamp(4rem, 12vh, 7rem);
  }

  .process__head {
    position: static;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    margin-bottom: clamp(2.5rem, 8vh, 4rem);
  }

  .process__station {
    height: auto;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.75rem;
    padding: clamp(2.5rem, 8vh, 4rem) 0;
    border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  }

  .process__num {
    margin-left: -0.05em;
    font-size: clamp(4.5rem, 28vw, 10rem);
  }

  .process__body,
  .process__note {
    max-width: none;
  }

  /* jamais sous 16px : c'est le corps de lecture, pas une annotation */
  .process__note {
    font-size: clamp(1rem, 4.3vw, 1.15rem);
    line-height: 1.6;
  }

}

/* ─────────────────────── LECTEUR PLEIN ÉCRAN (PORTFOLIO) ────────────────── */

/* Monté par portail sur <body>, donc hors de la roue : aucune de ses
   transformations 3D ne peut le contraindre. Il doit se poser PAR-DESSUS la
   page, pas s'y ajouter — sans position fixe il s'empile en fin de document
   et la page saute à la fin au moment du clic. */
.viewer {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3.5rem);
  background: rgba(0, 0, 0, 0.93);
  animation: viewer-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes viewer-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.viewer__frame {
  position: relative;
  margin: 0;
  width: min(100%, 1500px);
  animation: viewer-rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes viewer-rise {
  from { opacity: 0; transform: scale(0.965); }
  to { opacity: 1; transform: none; }
}

.viewer__frame video {
  display: block;
  width: 100%;
  max-height: 82vh;
  object-fit: contain;
  background: #000;
  border-radius: 10px;
}

.viewer__caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  margin-top: 1rem;
}

.viewer__title {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.5vw, 1.25rem);
  color: var(--white);
}

.viewer__meta {
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--grey);
}

/* La fermeture reste accessible au clavier : c'est elle qui reçoit le focus
   à l'ouverture. */
.viewer__close {
  position: absolute;
  top: clamp(1rem, 3vw, 2.2rem);
  right: clamp(1rem, 3vw, 2.2rem);
  width: 2.8rem;
  height: 2.8rem;
  display: grid;
  place-items: center;
  appearance: none;
  border: 1px solid color-mix(in srgb, var(--white) 30%, transparent);
  border-radius: 50%;
  background: none;
  color: var(--white);
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color 0.3s ease, background 0.3s ease;
}

.viewer__close:hover,
.viewer__close:focus-visible {
  border-color: var(--white);
  background: color-mix(in srgb, var(--white) 10%, transparent);
}

@media (max-width: 760px) {
  .viewer__frame video { max-height: 64vh; }
  .viewer__caption { flex-direction: column; gap: 0.35rem; }
}

/* ──────────────────────────────── CONTACT ──────────────────────────────── */

.contact {
  position: relative;
  z-index: 1;
  background: #000; /* noir plein */
  color: var(--ink);
  /* Aucune gouttière horizontale ici : la scène recadre ce qui dépasse, et
     une gouttière sur la section amputait donc le canevas de sa largeur —
     d'où deux bandes noires sur les côtés de l'écran. Les blocs de texte
     portent désormais leur propre retrait. */
  padding: 0 0 clamp(2rem, 5vh, 3rem);
}

/* entrée tenue : la hauteur exacte est posée en JS (2,2 écrans) */
.contact__hero-stage {
  position: sticky;
  top: 0;
  min-height: var(--stage-h);
  overflow: hidden;
  display: grid;
  place-items: center;
  /* Le retrait est ici, pas sur la section : le canevas, en position
     absolue, se cale sur la boîte entière et couvre donc toute la largeur,
     tandis que le texte reste dans la gouttière. */
  padding: 0 clamp(1.25rem, 4vw, 4rem);
}

/* ── le portail ───────────────────────────────────────────────────────────── */
/* Le canevas déborde les marges de la section : la scène garde son gouttière
   pour le texte et le formulaire, l'image occupe toute la largeur. */
.portal__canvas {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* Le titre traverse l'anneau : posé par-dessus, il déborde de part et
   d'autre. Sorti du flux, sa largeur ne contraint jamais la scène. */
.portal__text {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: 100%;
  text-align: center;
  pointer-events: none;
}

.portal__text--gone { opacity: 0; }

.portal__enter {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  place-content: center;
  gap: 0.9rem;
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(0.78rem, 1.1vw, 0.95rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.portal__arrow { font-size: 1.2em; letter-spacing: 0; }

/* l'état est porté par React : même comportement à la souris et au clavier */
.portal__text--hot .contact__title { opacity: 0; }
.portal__text--hot .portal__enter { opacity: 1; }

/* Sans survol — tout appareil tactile — l'invitation ne s'affichait jamais :
   elle n'apparait qu'a l'etat « chaud », declenche par le pointeur. Le
   visiteur mobile lisait donc « LET'S CREATE TOGETHER » sans rien qui lui
   dise que le titre s'ouvre, et le formulaire restait hors d'atteinte.

   Au doigt, l'invitation est donc posee en permanence, sous le titre plutot
   qu'a sa place : les deux se lisent ensemble, et le titre ne s'efface plus
   puisque rien ne vient le remplacer. */
@media (hover: none) {
  .portal__enter {
    position: static;
    opacity: 1;
    margin-top: clamp(1.1rem, 3.5vh, 2rem);
  }

  .portal__text--hot .contact__title { opacity: 1; }
}

/* zone sensible circulaire, posée sur l'anneau : le clavier y accède */
.portal__hit {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: min(94vh, 1020px);
  height: min(52vh, 560px);
  max-width: 86vw;
  max-height: 86vw;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
}

.portal__hit:disabled { cursor: default; }
.portal__hit:focus-visible { outline: 1px solid var(--accent); outline-offset: 12px; }

.portal__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── l'écran du formulaire, après la traversée ────────────────────────────── */
.vision {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: 70rem;
  display: grid;
  /* La colonne du formulaire a été resserrée, et le bloc entier rétréci : les
     deux côtés se rapprochent du centre au lieu de tenir les bords. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
  align-items: center;
  /* Deux écarts distincts : large entre les colonnes, serré entre le retour et
     le formulaire qu'il surmonte. */
  column-gap: clamp(1.5rem, 4vw, 4rem);
  row-gap: clamp(1rem, 2.4vh, 1.6rem);
  animation: vision-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Un voile sous le bloc, pas un cartouche : le bleu est assez fort pour
   manger les filets et les libellés, qui sont dessinés au trait.

   C'était un dégradé radial, et ses deux boutons — le retour en haut, l'envoi
   en bas — tombaient en plein dans sa zone de fondu : posés sur un fond à
   moitié bleu, ils paraissaient flous. C'est maintenant un fond plein que le
   flou n'adoucit qu'au-dehors : le palier couvre le bloc entier, bords
   compris, et la transition se fait à 16 % hors cadre. */
.vision::before {
  content: "";
  position: absolute;
  inset: -18% -6%;
  z-index: -1;
  pointer-events: none;
  background: rgba(0, 0, 0, 0.95);
  border-radius: 22% / 32%;
  filter: blur(52px);
}

/* Après l'envoi, le titre s'efface et la confirmation reste seule : le bloc
   n'a plus deux colonnes à tenir. */
.vision--envoye {
  grid-template-columns: minmax(0, 1fr);
  max-width: 46rem;
}

.vision--envoye .vision__back { grid-column: 1; }

@keyframes vision-in {
  from { opacity: 0; transform: translateY(26px); }
  to { opacity: 1; transform: none; }
}

/* Le retour est dans le flux, posé juste au-dessus du formulaire et aligné
   sur lui. Il flottait au-dessus du bloc en retrait négatif : il sortait de
   l'écran dès que la scène était courte, et on se retrouvait enfermé dans le
   formulaire. Rejeté dans le coin, il se perdait sur le bleu — ici il tombe
   sous l'œil, dans la zone sombre, au départ de la première ligne. */
.vision__back {
  grid-column: 2;
  justify-self: start;
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 1px solid color-mix(in srgb, var(--ink) 55%, #000);
  border-radius: 999px;
  background: transparent;
  padding: 0.7rem 1.3rem;
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-indent: var(--micro-track);
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.35s ease, color 0.35s ease, border-color 0.35s ease;
}

.vision__back:hover,
.vision__back:focus-visible {
  background: var(--ink);
  border-color: var(--ink);
  color: #000;
}

/* la flèche ne porte pas l'interlettrage du libellé */
.vision__back > span { text-indent: 0; }

/* « LET'S CREATE TOGETHER » : capitales largement interlettrées */
.vision__title {
  /* calée sur la gouttière plutôt que sur le bord gauche : le titre vient au
     contact du formulaire au lieu de s'exiler dans l'angle */
  justify-self: end;
  margin: 0;
  font-family: var(--sans);
  font-weight: 200;
  font-size: clamp(1.9rem, 4vw, 3.6rem);
  line-height: 1.22;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
}

/* Le titre occupe la largeur : sa taille est calée en JS, pas devinée en vw.
   Le portail demande un trait fin — la graisse d'affichage y est trop lourde,
   on passe sur le grotesque en 200. */
.contact__title {
  margin: 0;
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(1.6rem, 4.4vw, 4.2rem);
  line-height: 1;
  letter-spacing: 0.27em;
  /* L'interlettrage s'ajoute aussi aux espaces : sans ce rattrapage, les mots
     s'écartent bien plus que les lettres et la ligne se disloque. On resserre
     les mots sans toucher au dessin des lettres. */
  word-spacing: -0.24em;
  /* Rattrapage du centrage : l'interlettrage laisse une espace après la
     DERNIÈRE lettre, que le centrage compte dans la largeur de la ligne. Il
     faut donc la rendre à moitié — une unité entière décalait le texte de
     sept pixels vers la droite, mesure faite. */
  text-indent: 0.027em;
  text-transform: uppercase;
  transition: opacity 0.5s ease;
}

.contact__fit { white-space: nowrap; }

.contact__sub {
  margin: clamp(1rem, 2.5vh, 2rem) 0 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.2rem, 3.2vw, 2.9rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--muted);
}






/* ── repli des reflets, sans WebGL ───────────────────────────────────────── */

/* Quand le navigateur ne donne pas de contexte graphique, les reflets du
   portail ne peuvent pas être peints. Ces deux couches les refont au dégradé :
   des voiles bleus posés sur les bords, un centre qui reste noir pour que
   l'inscription se lise, et une dérive très lente. */
.contact__hero-stage--repli::before,
.contact__hero-stage--repli::after {
  content: "";
  position: absolute;
  inset: -20%;
  z-index: 0;
  pointer-events: none;
}

/* Le bleu est posé en fond plein, pas en taches : c'est ce qui donnait sa
   force au portail. Les quatre foyers ne font que le relever par endroits. */
.contact__hero-stage--repli::before {
  background:
    radial-gradient(52% 50% at 10% 16%, color-mix(in srgb, #9CB8FF 92%, transparent), transparent 76%),
    radial-gradient(46% 44% at 90% 24%, color-mix(in srgb, #7FA4FF 88%, transparent), transparent 76%),
    radial-gradient(54% 48% at 14% 88%, color-mix(in srgb, #86A9FF 86%, transparent), transparent 76%),
    radial-gradient(48% 46% at 88% 84%, color-mix(in srgb, #9CB8FF 90%, transparent), transparent 76%),
    var(--blue);
  filter: blur(36px);
  animation: repli-derive 26s ease-in-out infinite alternate;
}

/* Et le centre est creusé en noir. Pas un dégradé radial : il donne une
   ellipse parfaite, et l'œil lit aussitôt le cercle. C'est une forme pleine
   aux coins arrondis, volontairement dissymétrique, qu'un flou large vient
   fondre — le bord n'a alors plus de géométrie lisible. */
.contact__hero-stage--repli::after {
  inset: 7% 9%;
  background: #000;
  border-radius: 26% 20% 24% 30% / 34% 28% 32% 26%;
  filter: blur(64px);
}

@keyframes repli-derive {
  from { transform: translate3d(-1.5%, -1%, 0) scale(1.02); }
  to { transform: translate3d(1.5%, 1.5%, 0) scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .contact__hero-stage--repli::before { animation: none; }
}

/* ── formulaire : grandes lignes, aucun encadré ───────────────────────────── */
.contact__form {
  display: grid;
  gap: clamp(1.1rem, 2.6vh, 1.7rem);
}

.field {
  position: relative;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}


.field__label {
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--muted);
}

.field__input {
  appearance: none;
  width: 100%;
  padding: 0.2rem 0 0.8rem;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, #000);
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(0.95rem, 1.2vw, 1.05rem);
  line-height: 1.5;
  resize: vertical;
}

.field__input:focus { outline: none; }

/* le filet se trace depuis la gauche à la prise de focus */
.field__rule {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}

.field__input:focus ~ .field__rule { transform: scaleX(1); }
.field__input:focus-visible ~ .field__rule { height: 2px; }

/* ── menu de type de projet : une liste à nous, pas celle du système ─────── */
.choix { position: relative; display: block; }

.choix__bouton {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  text-align: left;
  cursor: pointer;
}

.choix__bouton--vide { color: var(--muted); }

/* le chevron, dessiné au trait comme la flèche de l'intro */
.choix__fleche {
  flex: none;
  width: 9px;
  height: 9px;
  margin-bottom: 0.3rem;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.choix__bouton[aria-expanded="true"] .choix__fleche {
  transform: rotate(225deg) translate(-2px, -2px);
}

/* Posée par-dessus, jamais dans le flux : la colonne ne doit pas sauter à
   l'ouverture. Le fond est opaque — sous lui passe le canevas du portail. */
.choix__liste {
  position: absolute;
  z-index: 4;
  top: calc(100% + 0.5rem);
  left: 0;
  right: 0;
  margin: 0;
  padding: 0.4rem 0;
  list-style: none;
  max-height: 17rem;
  overflow-y: auto;
  background: #0B0B0B;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, #000);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.75);
  animation: choix-in 0.28s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes choix-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

.choix__liste:focus { outline: none; }

.choix__option {
  padding: 0.75rem clamp(0.9rem, 2vw, 1.2rem);
  font-size: clamp(0.9rem, 1.1vw, 1rem);
  line-height: 1.4;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}

/* Le survol et le parcours au clavier marquent la même chose, donc ils ont
   le même dessin : une seule idée de « l'option visée ». */
.choix__option--vise {
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  color: var(--ink);
}

.choix__option--pris { color: var(--ink); }

.choix__option--pris::after {
  content: "";
  float: right;
  width: 5px;
  height: 9px;
  margin-top: 0.3rem;
  border-right: 1px solid var(--accent);
  border-bottom: 1px solid var(--accent);
  transform: rotate(45deg);
}

/* ── erreurs, aides et confirmation ──────────────────────────────────────── */

.field__aide,
.field__faute {
  font-size: 0.75rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
}

.field__aide { color: color-mix(in srgb, var(--muted) 80%, #000); }

/* Le rouge du site n'existe pas : la faute se dit au filet et au mot, pas à
   la couleur d'alerte. */
.field__faute { color: #E8A0A0; }

.field--faute .field__rule {
  transform: scaleX(1);
  background: #E8A0A0;
}

.contact__status--faute { color: #E8A0A0; }

/* La confirmation : deux lignes qui montent, rien d'autre. */
.envoye {
  animation: envoye-in 0.8s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes envoye-in {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: none; }
}

.envoye__titre {
  margin: 0;
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(1.4rem, 2.6vw, 2.2rem);
  line-height: 1.25;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  animation: envoye-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.1s both;
}

.envoye__note {
  margin: clamp(1rem, 2.4vh, 1.6rem) 0 0;
  max-width: 34ch;
  font-size: clamp(0.95rem, 1.15vw, 1.0625rem);
  line-height: 1.6;
  color: var(--muted);
  animation: envoye-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.22s both;
}

/* Le cadre d'envoi : présent, mais sans surface. */
.cadre-muet {
  position: absolute;
  width: 0;
  height: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* Le leurre : hors de l'écran, jamais atteint au clavier. `display: none`
   serait ignoré par une partie des robots, qui ne remplissent que ce qui est
   dans la page. */
.pastille {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.contact__send {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem 2.5rem;
}

.contact__submit {
  appearance: none;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--micro-size);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 1.05rem 2.2rem;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.4s ease, color 0.4s ease;
}

.contact__submit:hover,
.contact__submit:focus-visible { background: var(--ink); color: #000; }

.contact__submit[disabled] { opacity: 0.5; cursor: default; }

.contact__status {
  margin: 0;
  max-width: 46ch;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--muted);
}

/* ── signature et pied de page ────────────────────────────────────────────── */
.contact__sign {
  margin: clamp(6rem, 18vh, 13rem) clamp(1.25rem, 4vw, 4rem) 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(3rem, 15vw, 15rem);
  line-height: 0.85;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  user-select: none;
}

.footer {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  margin: clamp(2.5rem, 6vh, 4rem) clamp(1.25rem, 4vw, 4rem) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 20%, #000);
  padding-top: 1.2rem;
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--muted);
}

/* L'adresse, au même corps que le reste du pied de page : elle se signale
   par son filet, pas par sa taille. */
.footer__mail {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 35%, transparent);
  padding-bottom: 0.15rem;
  transition: border-color 0.35s ease, color 0.35s ease;
}

.footer__mail:hover,
.footer__mail:focus-visible { border-bottom-color: currentColor; }

.footer__socials { display: flex; gap: 1.5rem; }
.footer__socials a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
.footer__socials a:hover, .footer__socials a:focus-visible { border-bottom-color: currentColor; }

@media (max-width: 760px) {
  /* La scene ne colle plus en haut sur mobile : le formulaire doit pouvoir
     defiler sous elle. Elle reste pour autant positionnee et clippee.

     En `static` elle cessait d'etre le referent du canevas, qui se calait
     alors sur la section entiere : haut de 472 px pour une scene de 114, il
     recouvrait la signature et le pied de page, adresse comprise. D'ou une
     derniere section qui paraissait vide. `relative` lui rend son referent,
     `overflow: hidden` l'empeche de deborder.

     La hauteur minimale rend a la scene son statut de plan d'ouverture : sans
     elle, elle se reduisait a la hauteur du seul titre. */
  .contact__hero-stage {
    position: relative;
    min-height: 62vh;
    overflow: hidden;
    /* un retrait horizontal, sinon le formulaire touche les deux bords */
    padding: clamp(2.5rem, 8vh, 5rem) clamp(1.25rem, 4vw, 4rem)
             clamp(2.5rem, 8vh, 5rem);
  }
  .vision { grid-template-columns: 1fr; row-gap: 1.6rem; }
  .vision__back { grid-column: 1; }
  /* en colonne unique, le calage sur la gouttière n'a plus d'objet : il
     rejetterait le titre contre le bord droit */
  .vision__title { justify-self: start; }
  .footer { flex-direction: column; align-items: flex-start; gap: 0.6rem; }
}

/* ─────────────────────── ANNOTATIONS ÉDITORIALES ───────────────────────── */

/* 01 — VOLCANO : la ligne de marque et l'invitation à descendre. */

/* La ligne de marque, calée sous le logotype : même gouttière, même
   micro-typographie, un cran plus discrète. */
.intro__studio {
  position: absolute;
  z-index: 2;
  top: calc(clamp(1.5rem, 3vh, 2.4rem) + 1.5rem);
  left: clamp(1.25rem, 4vw, 4rem);
  margin: 0;
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: #E8E4DE;
  opacity: 0.88;
}

/* L'invitation à descendre : centrée en bas, au corps de la micro-typographie
   du site. Rien de plus qu'un mot et un trait — c'est un repère, pas un
   bouton. */
.intro__scroll {
  position: absolute;
  z-index: 2;
  /* Centrée par la boîte plutôt que par une transformation : le bloc occupe
     toute la largeur de la scène et c'est le flex qui centre. Rien à
     recalculer quand la police arrive, et GSAP — qui touche à cet élément
     pour l'éteindre — n'a aucune transformation à réécrire. */
  left: 0;
  right: 0;
  bottom: clamp(1.5rem, 4vh, 2.6rem);
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.6rem, 1.4vh, 1rem);
  color: #E8E4DE;
}

.intro__scroll-mot {
  font-size: 0.8rem;
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  /* l'interlettrage pousse le bloc d'un demi-cran vers la droite : on reprend
     la moitié de l'approche, jamais la totalité */
  text-indent: calc(var(--micro-track) / 2);
}

.intro__arrow {
  display: block;
  width: 10px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

/* 02 — PORTFOLIO : titre à gauche, cartouche en bas, photographies intactes */
.wheel__head {
  position: absolute;
  z-index: 2;
  top: calc(clamp(1.5rem, 3vh, 2.4rem) + 2.1rem);
  left: clamp(1.25rem, 4vw, 4rem);
  max-width: 22ch;
}

.wheel__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.4rem, 2.6vw, 2.4rem);
  line-height: 1;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: var(--ink);
}

.wheel__index {
  display: block;
  margin-top: 1.1rem;
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.wheel__index b { font-weight: 400; color: var(--ink); }

/* le cartouche vit à côté de la roue : rien ne recouvre les images */
.now {
  position: absolute;
  z-index: 2;
  left: clamp(1.25rem, 4vw, 4rem);
  bottom: clamp(1.5rem, 5vh, 3rem);
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 1rem;
  align-items: baseline;
  max-width: 26ch;
}

.now__num {
  grid-row: 1 / 3;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.6rem, 3vw, 2.6rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.now__name {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.5vw, 1.3rem);
  color: var(--ink);
}

.now__cat {
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--accent);
}

@media (max-width: 760px) {
  .wheel__head { max-width: 16ch; }
  .now { max-width: 60vw; }
}

/* ────────────────────────────── EXPERTISE ───────────────────────────────── */

/* Plus rien n'est révélé ici : les masques des mots ne servent donc plus qu'à
   rogner les jambages — le g de « Campaign Imagery » y perdait sa queue. On
   les ouvre, la dérive au scroll n'en a pas besoin. */
.expertise .word,
.expertise .reveal {
  overflow: visible;
}

/* Section posée, plus tenue : le texte y est écrit d'emblée, donc elle fait
   sa hauteur naturelle — un écran — au lieu d'être épinglée. */
.expertise {
  position: relative;
  z-index: 1;
  background: var(--paper);
  color: var(--ink);
  /* Le gris de rôle est relevé pour cette section seulement. À 18 px sur fond
     clair, le #666 des sections claires manque de corps ; #454545 monte à
     8,9:1 sans jamais venir concurrencer le noir des titres. */
  --muted: #454545;
}

.expertise__stage {
  position: sticky;
  top: 0;
  min-height: var(--stage-h);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(3rem, 7vh, 5rem) clamp(1.25rem, 4vw, 4rem);
}

/* Le bloc de tête rentre dans la page, et c'est la marque qui l'impose : elle
   est fixe en haut à gauche, la section défile dessous, et le titre finissait
   toujours par la traverser. Mesuré : la marque occupe 58 → 153 px, et la
   dérive au scroll déplace le titre de ±31. Un retrait de 8,75 rem met le
   bloc hors de portée à toutes les largeurs, dérive comprise. */
.expertise__top {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: end;
  gap: clamp(1.5rem, 4vw, 4rem);
  padding-left: 8.75rem;
}

.expertise__label {
  grid-column: 1 / -1;
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  text-transform: uppercase;
  color: var(--accent);
}

.expertise__title {
  margin: clamp(0.8rem, 2vh, 1.6rem) 0 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 4.6vw, 4.2rem);
  line-height: 0.95;
  letter-spacing: -0.035em;
}

.expertise__title .reveal:last-child {
  /* décalage éditorial : la seconde ligne rentre dans la page */
  margin-left: clamp(0rem, 6vw, 6rem);
}

/* Le chapô et les descriptions partagent exactement le même réglage : 16 px
   au plus étroit, 18 px sur ordinateur, graisse 400 — en 300 la police perd
   son trait dès qu'elle passe sur du blanc. */
.expertise__aside,
.skill__note {
  /* Un corps fixe, pas une échelle : en vw, la description retombait à 16 px
     dès qu'on quittait le grand écran — un 1280 est un ordinateur. */
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--muted);
}

.expertise__aside {
  margin: 0 0 0.5rem;
  max-width: 32ch;
}

/* les savoir-faire descendent en escalier plutôt qu'en colonne */
.expertise__list {
  list-style: none;
  margin: clamp(2rem, 6vh, 4.5rem) 0 0;
  padding: 0;
}

@property --rule {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

.skill {
  position: relative;
  display: grid;
  /* la colonne de droite reprend du large : les descriptions tiennent en deux
     lignes pleines au lieu de trois hachées */
  grid-template-columns: 4rem minmax(0, 0.85fr) minmax(0, 1.3fr);
  gap: 1rem clamp(1.5rem, 4vw, 4rem);
  align-items: baseline;
  padding: clamp(1.1rem, 2.9vh, 2.1rem) 0;
}

/* le filet se trace depuis la gauche, au même rythme que les mots */
.skill::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 1px;
  background: color-mix(in srgb, var(--ink) 18%, var(--paper));
  transform: scaleX(var(--rule, 1));
  transform-origin: 0 50%;
}

.skill:nth-child(even) { margin-left: clamp(0rem, 8vw, 8rem); }

.skill__num {
  font-size: var(--micro-size);
  letter-spacing: var(--micro-track);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.skill__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.15rem, 2.2vw, 1.9rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
}

.skill__note {
  margin: 0;
}

.expertise__coda {
  margin: clamp(1.8rem, 5vh, 3.5rem) 0 0 auto;
  max-width: 24ch;
  text-align: right;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.05rem, 2vw, 1.8rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--accent);
}

@media (max-width: 760px) {
  .expertise__stage {
    position: static;
    min-height: 0;
    overflow: visible;
    padding: clamp(5rem, 13vh, 9rem) clamp(1.25rem, 4vw, 4rem);
  }
  .expertise__top { grid-template-columns: 1fr; align-items: start; padding-left: 0; }
  .expertise__aside,
  .skill__note { font-size: 1rem; }
  .skill { grid-template-columns: 3rem 1fr; }
  .skill__note { grid-column: 2 / -1; }
  .skill:nth-child(even) { margin-left: 0; }
  .expertise__coda { text-align: left; margin-left: 0; }
}


/* ─────────────────────────────── SCULPTURE ─────────────────────────────── */

/* Posée dans la scène du manifeste, sous son texte. */
.sculpture__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* pleine opacité : le moindre voile ôte du contraste et se lit comme du flou */
  opacity: 1;
}

/* le texte du manifeste passe devant la matière */
.manifesto__stage > *:not(.sculpture__canvas) {
  position: relative;
  z-index: 1;
}

/* Sous 16 px, Safari iOS zoome de lui-meme sur un champ qu'on touche, puis
   laisse la page decalee : la mise en page parait cassee et la saisie devient
   penible. Les champs passent donc a 16 px au doigt — la seule valeur qui
   desarme ce comportement. Les etiquettes gagnent un point au passage. */
@media (hover: none) {
  .field__input,
  .choix__bouton,
  .choix__option { font-size: 16px; }

  .field__label { font-size: 0.78rem; }
}
