/* ═══════════════════════════════════════════════════════════════════
   Execudo
   Le hero (§ 2) reproduit à l'identique la version validée.
   Tout le reste est nouveau.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 1 · Fondations ───────────────────────────────────────────────── */

@font-face {
  font-family: "Switzer";
  src: url("./assets/font/Switzer-Variable.woff2") format("woff2-variations"),
       url("./assets/font/Switzer-Variable.woff") format("woff-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Fallback aux métriques de Switzer : le texte ne saute pas au chargement. */
@font-face {
  font-family: "Switzer Fallback";
  src: local("Helvetica Neue"), local("Arial");
  ascent-override: 98%;
  descent-override: 25%;
  line-gap-override: 9%;
  size-adjust: 100%;
}

:root {
  --paper: #f4f2ef;
  --ink: #11110f;
  --muted: #706d68; /* 4,61:1 sur le papier — juste au-dessus du seuil AA */
  --hairline: rgba(17, 17, 15, 0.16);

  /* Marge unique. Tout s'aligne dessus : header, titres, légendes, formulaire. */
  --edge: 1.35rem;

  /* Cadre du hero. Réutilisé par la section « Le travail réel ». */
  --frame-w: min(46vw, 46rem);
  --frame-h: calc(var(--frame-w) * 3 / 4);

  /* Hauteur du contact révélé. Le JS la mesure ; ceci est le repli. */
  --contact-h: 100svh;

  --ease: cubic-bezier(0.33, 0, 0, 1); /* sorties, ouvertures : longue détente */
  --ease-in: cubic-bezier(0.25, 0, 0, 0.75); /* entrées */
  --ease-micro: cubic-bezier(0.33, 0, 0.66, 1); /* survols */

  font-family: "Switzer", "Switzer Fallback", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-optical-sizing: auto;
  color: var(--ink);
  background: var(--paper);
  font-synthesis: none;
  /* Le crénage vient de la police, pas de « optimizeLegibility » : ce mot-clé
     charge toutes les tables de ligatures et coûte cher au premier rendu. */
  font-kerning: normal;
  font-feature-settings: "kern" 1;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-size: 14px;
  line-height: 1.2;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

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

/* Le corps de page glisse par-dessus le contact, qui reste fixé au bas. */
main {
  position: relative;
  z-index: 2;
  background: var(--paper);
  margin-bottom: var(--contact-h);
}

a,
button {
  color: inherit;
}

a {
  text-decoration: none;
}

button {
  border: 0;
  font: inherit;
}

:focus-visible {
  outline: 1.5px solid var(--ink);
  outline-offset: 3px;
}

.skip-link {
  position: fixed;
  z-index: 100;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.65rem 0.9rem;
  background: var(--ink);
  color: var(--paper);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* Grain papier. Fixe, très faible, jamais au-dessus du texte.
   Pas de mix-blend-mode : un overlay plein écran en blend force la
   composition de toute la page dans une seule couche. */
.grain {
  position: fixed;
  z-index: 60;
  inset: 0;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Flèches et croix. Elles étaient posées en caractères (↗ → ← ✕) que Switzer
   ne contient pas : les téléphones allaient les chercher dans la police
   emoji et affichaient une flèche en couleur, épaisse, hors sujet. Tracées
   ici, elles suivent la couleur et la graisse du texte partout. */
.glyph {
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  vertical-align: -0.12em;
  flex: none;
}

/* Index de chapitre — le seul repère de navigation du site. */
.chapter {
  margin: 0;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.chapter-light {
  color: rgba(244, 242, 239, 0.62);
}

/* Révélation ligne par ligne. Le JS enveloppe chaque ligne.

   Le masque fait exactement la hauteur d'interligne. Sur les titres serrés
   (0,99 à 1,02), cette hauteur est plus courte que les lettres : les jambages
   du g, du p et du y passaient sous le bord et se retrouvaient rasés. Le
   masque descend donc un peu plus bas, et la marge négative rend au texte
   l'interligne exact qu'il avait. */
.line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}

.line-in {
  display: block;
}

/* Le découpage attend la vraie police. Le temps qu'elle arrive, le bloc est
   retenu : sans cela on lirait le paragraphe en clair une fraction de seconde
   avant qu'il ne se masque pour se révéler. */
body.motion [data-lines]:not(.is-split) {
  visibility: hidden;
}

/* La couche GPU n'est réservée que tant que la ligne n'est pas montée.
   En permanence, c'était une couche par ligne de texte du site. */
body.motion [data-lines]:not(.is-in) .line-in {
  will-change: transform;
}

/* 130 % et non 105 % : le masque descend maintenant plus bas que la ligne,
   il faut sortir d'autant plus pour rester caché avant la révélation. */
body.motion [data-lines] .line-in {
  transform: translateY(130%);
  transition: transform 900ms var(--ease);
  transition-delay: calc(var(--i, 0) * 65ms);
}

body.motion [data-lines].is-in .line-in {
  transform: translateY(0);
}

/* Éléments qui se posent simplement. */
body.motion [data-rise] {
  opacity: 0;
  transform: translateY(1.1rem);
  transition: opacity 800ms var(--ease), transform 1000ms var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}

body.motion [data-rise].is-in {
  opacity: 1;
  transform: none;
}

/* ── 2 · Intro : le gribouillis se range ──────────────────────────── */

.intro {
  position: fixed;
  z-index: 80;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--paper);
  transition: opacity 700ms var(--ease);
  /* Filet. Le JS retire ce calque en trois secondes au plus ; s'il n'arrive
     jamais, ce délai évite qu'un plein écran de papier reste seul devant le
     site. Le <noscript> couvre le JS coupé, ceci couvre le script perdu. */
  animation: intro-secours 1ms linear 8s forwards;
}

@keyframes intro-secours {
  to {
    opacity: 0;
    visibility: hidden;
  }
}

.intro-film {
  width: min(78vw, 62rem);
  height: auto;
  transform: scale(1.08);
  transition: transform 1400ms var(--ease);
}

body.intro-out .intro {
  opacity: 0;
  pointer-events: none;
}

body.intro-out .intro-film {
  transform: scale(1);
}

body.intro-live {
  overflow: hidden;
}

body.intro-live .site-header,
body.intro-live .hero {
  opacity: 0;
}

.site-header,
.hero {
  transition: opacity 900ms var(--ease) 120ms;
}

/* ── 3 · Hero — VALIDÉ, structure et proportions conservées ───────── */

.site-header {
  position: absolute;
  z-index: 4;
  top: 0;
  left: 0;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  align-items: start;
  gap: 1rem;
  padding: 1.2rem var(--edge);
}

.brand {
  display: block;
  width: 9.25rem;
  transform-origin: left top;
  transition: opacity 420ms var(--ease-micro);
}

.brand img {
  width: 100%;
  height: auto;
  display: block;
}

.header-note {
  display: flex;
  justify-content: space-between;
  gap: 1.25rem;
  color: var(--muted);
}

/* La flèche ne doit jamais être renvoyée seule à la ligne sous le texte. */
.header-contact {
  justify-self: end;
  border-bottom: 1px solid currentColor;
  padding-bottom: 0.13rem;
  white-space: nowrap;
  transition: opacity 420ms var(--ease-micro);
}

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  overflow: clip;
  padding: 6rem var(--edge) 2rem;
}

.hero-figure {
  width: var(--frame-w);
  margin: 0;
}

.hero-media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: clip;
  background: #d9d7d2;
  transform: translateZ(0);
}

.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

.hero-media video {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  filter: saturate(0.76) contrast(1.03);
  transition: opacity 240ms ease;
}

/* Les deux colonnes s'alignent sur leur première ligne, pas sur leur centre :
   le décalage est celui d'un bloc de deux lignes centré (2 × 1.2em + le gap),
   donc l'une peut gagner une ligne sans faire glisser l'autre. */
.hero-side {
  position: absolute;
  top: 50%;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  transform: translateY(calc(-1.2em - 0.125rem));
  color: var(--muted);
}

.hero-side-left {
  left: var(--edge);
}

/* En miroir : les métiers réunis, une ligne chacun. */
.hero-side-right {
  right: var(--edge);
  align-items: flex-end;
  text-align: right;
}

.hero-copy {
  position: absolute;
  bottom: var(--edge);
  left: var(--edge);
  right: var(--edge);
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  align-items: end;
  gap: 1rem;
  pointer-events: none;
}

.hero-copy h1 {
  grid-column: 1 / 2;
  margin: 0;
  font-size: clamp(1.75rem, 2.55vw, 3.4rem);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.045em;
}

.hero-copy p {
  grid-column: 3 / 4;
  max-width: 26rem;
  margin: 0;
  justify-self: end;
  font-size: clamp(1rem, 1.24vw, 1.35rem);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.018em;
}

/* ── 4 · La thèse : deux temps de part et d'autre du dessin ───────── */

.thesis {
  position: relative;
  min-height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 6svh var(--edge) 6svh;
  overflow: clip;
  background: var(--paper);
}

.thesis-head {
  display: grid;
  gap: 2.2rem;
  max-width: 34rem;
}

.thesis-lead {
  margin: 0;
  font-size: clamp(1.25rem, 1.85vw, 1.95rem);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.028em;
  text-wrap: pretty;
}

.thesis-film {
  grid-row: 2;
  display: grid;
  place-items: center end;
  min-height: 0;
  margin: 2svh 3vw 1svh 0;
  pointer-events: none;
}

.thesis-film video {
  width: min(46vw, 46rem);
  aspect-ratio: 16 / 9;
  height: auto;
  display: block;
  object-fit: cover;
  filter: saturate(0.74) contrast(1.04);
}

.thesis-film-drawing {
  place-items: center;
  margin: -2svh 0;
}

/* Ratio explicite : en « auto », la boîte se dimensionnait sur le média une
   fois chargé et toute la page sautait de 171 px. */
.thesis-film-drawing video {
  width: auto;
  max-width: min(94vw, 84rem);
  max-height: 100%;
  aspect-ratio: 1280 / 676;
  object-fit: contain;
  filter: none;
}

/* Le second temps est ancré à droite : le dessin sépare les deux. */
/* Le second temps reste ancré à droite : le dessin sépare les deux. La colonne
   de gauche tenait une légende qui n'a jamais été écrite ; le bloc s'aligne
   maintenant seul, sans changer de place. */
.thesis-foot {
  grid-row: 3;
  display: grid;
  justify-items: end;
  align-items: end;
}

.thesis-copy {
  justify-self: end;
  max-width: 36rem;
  margin: 0;
  font-size: clamp(1rem, 1.28vw, 1.32rem);
  font-weight: 400;
  line-height: 1.26;
  letter-spacing: -0.018em;
  text-wrap: pretty;
}

/* ── 5 · Le travail réel : le cadre du hero s'ouvre ───────────────── */

.work {
  position: relative;
  height: 250svh;
  background: var(--paper);
}

.work-sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}

.work-frame {
  position: absolute;
  inset: 0;
  margin: 0;
  /* --p va de 0 (cadre du hero) à 1 (plein écran). */
  clip-path: inset(
    calc((100svh - var(--frame-h)) / 2 * (1 - var(--p, 0)))
    calc((100vw - var(--frame-w)) / 2 * (1 - var(--p, 0)))
  );
  will-change: clip-path;
}

.work-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 46%, rgba(10, 10, 9, 0.62));
  opacity: var(--veil, 0);
}

.work-frame video {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 50% 46%;
  transform: scale(calc(1.08 - 0.07 * var(--p, 0)));
  will-change: transform;
}

.work-overlay {
  position: absolute;
  z-index: 2;
  inset: var(--edge);
  pointer-events: none;
  /* L'encre devient papier à mesure que le cadre s'ouvre. */
  color: color-mix(in oklab, var(--ink) calc((1 - var(--t, 0)) * 100%), #f6f5f2);
}

.work-overlay .chapter {
  position: absolute;
  top: 0;
  right: 0;
  color: inherit;
  opacity: 0.62;
}

.work-frame video {
  transition: opacity 220ms ease;
}

.work-candidate-label {
  position: absolute;
  top: 4.25rem;
  right: 0;
  margin: 0;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  opacity: 0.68;
}

.work-copy {
  position: absolute;
  bottom: 0;
  left: 0;
  max-width: 26rem;
  margin: 0;
  font-size: clamp(1.4rem, 2.1vw, 2.5rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.038em;
  text-wrap: balance;
}

/* ── 6 · Méthode : le média tient, le texte défile ────────────────── */

.method {
  display: grid;
  grid-template-columns: minmax(0, 53fr) minmax(0, 47fr);
  padding-top: 7svh;
}

.method-media {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100svh;
  display: flex;
  align-items: center;
  padding: var(--edge);
}

/* Le scale(1.01) de la vidéo la faisait sortir du cadre : elle débordait de
   4 px à gauche et cassait la marge sur laquelle tout le site s'aligne. */
/* 16/9 : le ratio natif des films. En 3/2 on jetait 16 % de chaque plan.
   Le cadre porte désormais le ratio, les trois plans s'y superposent. */
.method-frame {
  position: relative;
  width: 100%;
  margin: 0;
  aspect-ratio: 16 / 9;
  overflow: clip;
  background: #d9d7d2;
}

.method-film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 50% 44%;
  transform: scale(1.01);
  opacity: 0;
  transition: opacity 460ms var(--ease);
}

.method-film.is-on {
  opacity: 1;
}

.method-steps {
  padding: 0 var(--edge) 0 clamp(1.35rem, 4vw, 5rem);
}

.method-steps .chapter {
  padding-top: calc(var(--edge) + 0.15rem);
}

/* Une étape par écran. En dessous, deux titres se retrouvaient à l'écran en
   même temps et le film ne pouvait plus correspondre à ce qu'on lisait. */
.method-step {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 30rem;
}

/* Le filet tient le titre : c'est tout l'habillage de la colonne. */
.method-step::before {
  content: "";
  display: block;
  width: 100%;
  height: 1px;
  margin-bottom: 1.5rem;
  background: var(--hairline);
}

.method-step h2 {
  margin: 0 0 1.1rem;
  font-size: clamp(2rem, 3.1vw, 3.6rem);
  font-weight: 400;
  line-height: 0.96;
  letter-spacing: -0.05em;
}

.method-step p {
  margin: 0;
  font-size: clamp(1.05rem, 1.3vw, 1.32rem);
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: pretty;
}

/* ── 7 · Fiabilité : l'unique écran sombre, et il est court ──────── */

.reliability {
  position: relative;
  height: 100svh;
  display: flex;
  align-items: flex-end;
  padding: var(--edge);
  overflow: clip;
  background: #0a0a09;
}

.reliability-film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 54% 50%;
  transform: translateY(calc(var(--parallax, 0) * -3vh)) scale(1.06);
  will-change: transform;
}

.reliability::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(6deg, rgba(6, 6, 5, 0.9) 6%, rgba(6, 6, 5, 0.12) 66%);
}

.reliability-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: end;
  gap: clamp(2rem, 6vw, 6rem);
}

.reliability-inner .chapter {
  grid-column: 1 / -1;
}

.reliability-copy {
  max-width: 18ch;
  margin: 1.6rem 0 0;
  color: #f2f0ec;
  font-size: clamp(1.7rem, 3.1vw, 3.7rem);
  font-weight: 400;
  line-height: 0.99;
  letter-spacing: -0.05em;
  text-wrap: balance;
}

.reliability-note {
  max-width: 30rem;
  margin: 0 0 0.5rem;
  color: rgba(242, 240, 236, 0.72);
  font-size: clamp(0.98rem, 1.18vw, 1.18rem);
  line-height: 1.32;
  letter-spacing: -0.014em;
  text-wrap: pretty;
}

/* ── 8 · Contact : la page glisse, le papier apparaît dessous ─────── */

.contact {
  position: fixed;
  z-index: 1;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100svh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--edge) var(--edge);
  overflow: clip;
  background: var(--paper);
}

/* Les quatre blocs se partagent la hauteur : aucun vide ne s'accumule en bas. */
.contact-inner {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  padding-top: var(--edge);
}

.contact-inner > .chapter {
  padding-bottom: clamp(2rem, 6svh, 4rem);
}

/* ── Les deux moitiés ─────────────────────────────────────────────── */

/* Le bloc se pose sous le chapitre et occupe tout ce qui reste : la section
   remplit l'écran, il n'y a plus de blanc qui traîne. */
.contact-split {
  margin: auto;
  width: 100%;
  max-width: 84rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: stretch;
}

/* Gauche : ce qu'on dit, et le bouton qui en découle. */
.contact-say {
  display: grid;
  justify-items: start;
  align-content: space-between;
  gap: clamp(1.1rem, 2.6svh, 1.7rem);
}

.contact-say h2 {
  margin: -0.11em 0 0;
  font-size: clamp(2.1rem, 4.3vw, 3.95rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.048em;
}

.contact-pitch {
  max-width: 40rem;
  margin: 0;
  color: var(--muted);
  font-size: clamp(1.05rem, 1.42vw, 1.42rem);
  line-height: 1.34;
  letter-spacing: -0.018em;
  text-wrap: pretty;
}

/* Le seul aplat noir de la page : on ne peut pas le manquer. */
.brief-btn {
  display: inline-flex;
  align-items: baseline;
  gap: 1.2rem;
  border: 1px solid var(--ink);
  padding: 1.05rem 1.6rem;
  background: var(--ink);
  color: var(--paper);
  cursor: pointer;
  font-size: clamp(0.98rem, 1.12vw, 1.12rem);
  letter-spacing: -0.015em;
  transition: background-color 480ms var(--ease), color 480ms var(--ease);
}

.brief-btn-note {
  margin: -0.6rem 0 0;
  color: var(--muted);
  font-size: 0.86rem;
}

/* Droite : la voie directe, annoncée en une phrase. */
.contact-do {
  display: grid;
  align-content: space-between;
  justify-items: start;
  gap: clamp(1.1rem, 2.6svh, 1.7rem);
  min-width: 0;
  padding-left: clamp(1rem, 2.5vw, 3rem);
}

.contact-intro {
  max-width: none;
  margin: 0;
  color: var(--ink);
  font-size: clamp(0.86rem, 1.15vw, 1.1rem);
  font-weight: 500;
  letter-spacing: -0.014em;
  line-height: 1.3;
}

/* ── Le formulaire : des filets, pas des boîtes ───────────────────── */

.contact-form {
  display: grid;
  gap: clamp(1rem, 2.4svh, 1.6rem);
  width: 100%;
  max-width: 100%;
}

.contact-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.9rem, 2.2svh, 1.5rem) 1.4rem;
}

.contact-fields label {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
  color: var(--muted);
}

.contact-fields .field-message {
  grid-column: 1 / -1;
}

/* Le piège à robots. Masqué sans display:none, que certains robots repèrent,
   et sorti du flux pour ne rien décaler. */
.leurre {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

.contact-fields em {
  font-style: normal;
  opacity: 0.7;
}

.contact-fields input,
.contact-fields textarea {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  padding: 0.3rem 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 1.02rem;
  border-radius: 0;
  transition: border-color 320ms var(--ease-micro);
}

.contact-fields textarea {
  resize: vertical;
  min-height: 5rem;
  line-height: 1.35;
}

.contact-fields input:focus,
.contact-fields textarea:focus {
  outline: none;
  border-color: var(--ink);
}

.contact-send {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  flex-wrap: wrap;
}

.contact-note {
  flex: 1;
  min-width: 12rem;
  margin: 0;
  color: var(--muted);
  line-height: 1.35;
}

.contact-note:empty {
  display: none;
}

/* Le bouton se remplit d'encre au survol. Pas de fond plein au repos. */
.cta {
  display: inline-flex;
  align-items: baseline;
  gap: 1.4rem;
  border: 1px solid var(--ink);
  padding: 1.05rem 1.6rem;
  background: var(--ink);
  color: var(--paper);
  cursor: pointer;
  font-size: clamp(0.98rem, 1.12vw, 1.12rem);
  letter-spacing: -0.015em;
  transition: background-color 480ms var(--ease), color 480ms var(--ease);
}

.cta[disabled] {
  opacity: 0.45;
  cursor: default;
}

.cta-arrow {
  transition: transform 480ms var(--ease);
}

.contact-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--hairline);
  margin: 0 calc(var(--edge) * -1) calc(var(--edge) * -1);
  padding: 0.8rem var(--edge);
  color: var(--ink);
}

/* Repli : si le contact ne tient pas dans l'écran, il repasse dans le flux. */
/* Le logo remplace la signature : discret, aligné sur la ligne du pied. */
.contact-footer-mark {
  width: clamp(4.6rem, 6vw, 6rem);
  height: auto;
}

.contact.is-tall {
  position: relative;
  z-index: 2;
}

/* Écran peu haut — la plupart des portables. Le contact doit tenir dans la
   fenêtre, sinon il repasse dans le flux et perd sa révélation. On resserre
   plutôt que de retirer quoi que ce soit. */
@media (min-width: 901px) and (max-height: 780px) {
  .contact-inner > .chapter {
    padding-bottom: clamp(1.2rem, 4svh, 2.4rem);
  }

  .contact-split {
    gap: clamp(1.5rem, 4vw, 4rem);
  }

  .contact-say {
    gap: 1.1rem;
  }

  .contact-say h2 {
    font-size: clamp(2.2rem, 3.4vw, 3.4rem);
  }

  .contact-form {
    gap: 0.95rem;
  }

  .contact-fields {
    gap: 0.75rem 1.4rem;
  }
}

/* ── 8b · Qualification ───────────────────────────────────────────── */

.brief {
  width: 100vw;
  max-width: none;
  height: 100svh;
  max-height: none;
  margin: 0;
  border: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  overflow: clip;
}

.brief::backdrop {
  background: rgba(17, 17, 15, 0.4);
}

.brief[open] {
  display: flex;
  flex-direction: column;
  animation: brief-in 520ms var(--ease) both;
}

@keyframes brief-in {
  from { opacity: 0; transform: translateY(1.4rem); }
  to   { opacity: 1; transform: none; }
}

.brief-bar {
  height: 1px;
  background: var(--hairline);
}

.brief-bar span {
  display: block;
  height: 100%;
  width: 25%;
  background: var(--ink);
  transform-origin: 0 50%;
  transition: width 620ms var(--ease);
}

.brief-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 1.2rem var(--edge);
}

.brief-close {
  background: transparent;
  cursor: pointer;
  color: var(--muted);
  transition: color 420ms var(--ease-micro);
}

.brief-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0 var(--edge) var(--edge);
}

.brief-step {
  flex: 1;
  display: none;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  border: 0;
  margin: 0;
  padding: 0;
  max-width: 46rem;
}

.brief-step.is-current {
  display: flex;
  animation: step-in 460ms var(--ease) both;
}

@keyframes step-in {
  from { opacity: 0; transform: translateY(0.9rem); }
  to   { opacity: 1; transform: none; }
}

.brief-q {
  padding: 0;
  margin: 0 0 2.2rem;
  font-size: clamp(1.75rem, 3.2vw, 3.2rem);
  font-weight: 400;
  line-height: 0.98;
  letter-spacing: -0.048em;
  text-wrap: balance;
}

.choices {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.8rem;
}

.choices button {
  border: 1px solid var(--hairline);
  padding: 0.62rem 1rem;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  font-size: 1rem;
  letter-spacing: -0.012em;
  transition: border-color 320ms var(--ease-micro),
              background-color 320ms var(--ease-micro),
              color 320ms var(--ease-micro);
}

.choices button[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

.brief-precise {
  display: grid;
  gap: 0.35rem;
  max-width: 32rem;
  margin-top: 1.4rem;
  color: var(--muted);
}

.brief-precise input,
.brief-fields input {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  padding: 0.35rem 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 1.05rem;
  border-radius: 0;
  transition: border-color 320ms var(--ease-micro);
}

.brief-precise input:focus,
.brief-fields input:focus {
  outline: none;
  border-color: var(--ink);
}

.brief-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.6rem;
  max-width: 44rem;
}

.brief-fields label {
  display: grid;
  gap: 0.35rem;
  color: var(--muted);
}

.brief-fields em {
  font-style: normal;
  opacity: 0.7;
}

/* Ce que la personne a déjà dit, rappelé sans qu'elle ait à remonter. */
.brief-recap {
  max-width: 44rem;
  margin: -1.2rem 0 2rem;
  color: var(--muted);
  line-height: 1.5;
}

.brief-recap:empty {
  display: none;
}

.brief-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  border-top: 1px solid var(--hairline);
  padding-top: 1rem;
}

.brief-back-skip {
  display: flex;
  align-items: baseline;
  gap: 1.4rem;
}

.brief-back,
.brief-skip,
.brief-next {
  background: transparent;
  cursor: pointer;
  font-size: 1.02rem;
}

.brief-back,
.brief-skip {
  color: var(--muted);
  transition: color 420ms var(--ease-micro);
}

.brief-back[hidden] {
  display: none;
}

.brief-next {
  border-bottom: 1px solid currentColor;
  padding-bottom: 0.15rem;
  color: var(--ink);
  transition: opacity 420ms var(--ease-micro);
}

.brief-note {
  flex: 1;
  margin: 0;
  color: var(--muted);
  text-align: center;
}

/* La phrase d'accroche du formulaire tient sur une ligne quand la colonne
   est assez large. En dessous, elle se plie plutôt que de déborder. */
@media (min-width: 1250px) {
  .contact-intro {
    white-space: nowrap;
  }
}

/* ── 9 · Mobile ──────────────────────────────────────────────────── */

@media (max-width: 900px) {
  :root {
    /* Le cadre ne doit jamais pousser le titre hors de l'écran. */
    --frame-w: min(calc(100vw - 2 * var(--edge)), calc(52svh * 4 / 3));
    --frame-h: calc(var(--frame-w) * 3 / 4);
  }

  body {
    /* 14 px sur un écran tenu à bout de bras, c'est une note de bas de page. */
    font-size: 15px;
  }

  /* Toutes les échelles du site sont taillées en vw de bureau : sur un
     téléphone elles retombent toutes sur leur minimum et la page devient
     plate. Ici, chaque niveau retrouve son rang. */

  /* Le plancher tient compte du plus petit écran encore en circulation :
     à 320 px, « Des systèmes utiles. » doit rester sur une seule ligne. */
  .hero-copy h1 {
    font-size: clamp(2rem, 9.2vw, 3rem);
    line-height: 0.96;
  }

  .hero-copy p {
    font-size: 1.05rem;
    line-height: 1.25;
  }

  .thesis-lead {
    font-size: clamp(1.45rem, 5.9vw, 2rem);
    line-height: 1.16;
  }

  .thesis-copy {
    font-size: 1.05rem;
    line-height: 1.32;
  }

  .work-copy {
    font-size: clamp(1.75rem, 7.4vw, 2.4rem);
    line-height: 1.02;
  }

  .method-step h2 {
    font-size: clamp(2.1rem, 8.6vw, 2.8rem);
  }

  .method-step p {
    font-size: 1.05rem;
    line-height: 1.3;
  }

  .reliability-copy {
    font-size: clamp(1.9rem, 8vw, 2.7rem);
    line-height: 1.02;
  }

  .reliability-note {
    font-size: 1rem;
    line-height: 1.38;
  }

  .contact-say h2 {
    font-size: clamp(2.05rem, 8.4vw, 2.75rem);
  }

  .contact-pitch {
    font-size: 1.05rem;
    line-height: 1.36;
  }

  .brief-q {
    font-size: clamp(1.7rem, 7.2vw, 2.4rem);
  }

  /* Le dessin d'ouverture tenait dans 78 % d'un écran de bureau ; sur un
     téléphone, la même proportion en fait une vignette. */
  .intro-film {
    width: 90vw;
  }

  .site-header {
    grid-template-columns: 1fr auto;
    padding-top: max(1.2rem, env(safe-area-inset-top));
  }

  .header-note {
    display: none;
  }

  .hero {
    align-items: center;
    padding-top: 7rem;
    padding-bottom: 9rem;
  }

  .hero-side {
    display: none;
  }

  .hero-copy {
    grid-template-columns: 1fr;
    gap: 1rem;
    bottom: 2.4rem;
  }

  .hero-copy h1,
  .hero-copy p {
    grid-column: 1;
    justify-self: start;
  }

  .hero-copy p {
    max-width: 23rem;
  }

  .thesis {
    min-height: auto;
    padding-bottom: 10svh;
  }

  .thesis-head {
    gap: 1.6rem;
  }

  .thesis-film {
    margin: 5svh 0 4svh;
    place-items: center;
  }

  .thesis-film video {
    width: 100%;
    max-width: 100%;
    max-height: none;
  }

  .thesis-film-drawing video {
    width: 100%;
    max-width: 100%;
  }

  .thesis-foot {
    justify-items: start;
  }

  .thesis-copy {
    justify-self: start;
  }

  /* L'ouverture du cadre est jouée sur la première moitié de la course :
     à 230svh, il restait plus d'un écran de défilement sur une image fixe. */
  .work {
    height: 185svh;
  }

  .work-overlay {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }

  .work-overlay .chapter {
    order: 0;
  }

  .work-candidate-label {
    top: 5.7rem;
    left: 0;
    right: auto;
  }

  .method {
    grid-template-columns: 1fr;
  }

  /* Le film est en 16/9 : dans 52svh il flottait au milieu de deux grandes
     bandes vides. On resserre le cadre autour de lui.

     Le fond n'est pas un détail : en une seule colonne, le texte de l'étape
     remonte sous le film collé en haut. Sans aplat, il ressortait dans les
     marges du cadre et se retrouvait tranché en plein milieu d'une phrase. */
  .method-media {
    position: sticky;
    z-index: 2;
    height: 36svh;
    padding-bottom: 0.4rem;
    background: var(--paper);
    transition: opacity 420ms var(--ease);
  }

  /* Le collage dure jusqu'au bout de la section : le film restait planté en
     haut pendant que le plan sombre montait par le bas, avec une bande de
     papier vide entre les deux. Il s'efface dès que le plan sombre s'installe,
     et le noir prend l'écran d'un seul tenant. */
  .method-media.is-gone {
    opacity: 0;
  }

  /* Sans ce fondu, le titre de l'étape qui remonte est tranché net sur le
     bord du bandeau — on lit « Donner forme » à moitié décapité. Il s'efface
     maintenant sur deux centimètres avant de passer dessous. */
  .method-media::after {
    content: "";
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    top: 100%;
    height: 2.4rem;
    background: linear-gradient(var(--paper), rgba(244, 242, 239, 0));
    pointer-events: none;
  }

  .method-steps {
    padding: 0 var(--edge) 6svh;
  }

  .method-steps .chapter {
    padding-top: 3.5svh;
  }

  .method-step {
    min-height: 62svh;
  }

  /* Le footer révélé demande une hauteur stable : sur mobile, où la barre
     d'URL fait varier le viewport, on repasse le contact dans le flux. */
  main {
    margin-bottom: 0;
  }

  .contact {
    position: relative;
    z-index: 2;
    height: auto;
  }

  /* Taillée en vw, cette phrase tombait à 13,7 px sur téléphone — deux crans
     sous le corps de texte qu'elle introduit. */
  .contact-intro {
    font-size: 1rem;
    line-height: 1.34;
  }

  .contact-form,
  .contact-fields,
  .contact-do {
    height: auto;
    grid-template-rows: none;
  }

  .contact-fields textarea {
    resize: vertical;
    height: auto;
    min-height: 4rem;
  }

  /* Le film de la méthode reste collé en haut jusqu'au bout de sa section,
     pendant que le plan sombre monte par le bas : les deux cohabitaient un
     demi-écran durant, séparés par une bande de papier vide. Le plan sombre
     passe devant et le recouvre, comme un rideau qui tombe. */
  .reliability {
    z-index: 3;
  }

  .reliability-inner {
    grid-template-columns: 1fr;
    gap: 1.8rem;
  }

  .reliability-note {
    margin-bottom: 0;
  }

  /* Sur ordinateur, le contact se dévoile par glissement : la pause vient du
     mouvement. Empilé dans le flux, il démarrait à 25 px du plan sombre, sans
     reprendre son souffle. Le blanc fait ici le travail de la transition. */
  .contact-inner {
    padding-top: clamp(2.6rem, 9svh, 4.5rem);
  }

  .contact-inner > .chapter {
    padding-bottom: 1.8rem;
  }

  /* Le propos, puis le formulaire, l'un sous l'autre. */
  .contact-split {
    grid-template-columns: 1fr;
    gap: 2.6rem;
    align-content: start;
  }

  /* Le retrait de gauche séparait les deux colonnes du bureau. Empilé, il ne
     sépare plus rien : il décale le formulaire de la marge du site. */
  .contact-do {
    border-top: 1px solid var(--hairline);
    padding-top: 2.2rem;
    padding-left: 0;
  }

  /* Deux colonnes de champs sur un téléphone, c'est deux champs trop étroits. */
  .contact-fields {
    grid-template-columns: 1fr;
  }

  .contact-form {
    max-width: none;
  }

  .contact-send {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.9rem;
  }

  /* Empilé et centré, le pied donnait un logo au milieu, une phrase sur deux
     lignes qui semblait fuir à gauche, et l'heure recentrée. Tout se range
     sur la marge du site, comme le reste de la page. */
  .contact-footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.45rem;
    margin-top: 3rem;
    padding-top: 1.1rem;
  }

  .brief-q {
    margin-bottom: 1.6rem;
  }

  .brief-fields {
    grid-template-columns: 1fr;
    gap: 1.2rem;
  }

  .brief-body {
    overflow-y: auto;
    /* Arrivé en bout de course, le questionnaire ne doit pas entraîner la
       page qui se trouve derrière lui. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: max(var(--edge), env(safe-area-inset-bottom));
  }

  .brief-step {
    justify-content: flex-start;
    padding-top: 2svh;
  }

  .brief-head {
    padding-top: max(1.2rem, env(safe-area-inset-top));
  }

  /* ── Cibles tactiles ─────────────────────────────────────────────────
     Un doigt ne vise pas comme un curseur. Ces liens faisaient 20 px de
     haut : on leur donne de la garde sans déplacer leur ligne de base. */

  .header-contact,
  .brief-close,
  .brief-back,
  .brief-skip,
  .brief-next {
    padding-top: 0.7rem;
    padding-bottom: 0.75rem;
    margin-top: -0.7rem;
    margin-bottom: -0.7rem;
  }

  .brief-next,
  .header-contact {
    padding-bottom: 0.85rem;
  }

  .choices button {
    padding: 0.8rem 1.05rem;
  }

  .choices {
    gap: 0.55rem;
  }

  /* Le pied de page touche le bas de l'écran : sous la barre de gestes,
     la ligne devenait illisible sur les iPhone récents. */
  .contact {
    padding-bottom: max(var(--edge), env(safe-area-inset-bottom));
  }

  /* Le label enveloppe déjà son champ : la zone touchable est celle des deux.
     Le champ n'a donc pas besoin de garde en haut, et le formulaire cesse de
     s'étirer sur un écran et demi. */
  .contact-fields input,
  .contact-fields textarea,
  .brief-precise input,
  .brief-fields input {
    padding: 0.3rem 0 0.55rem;
  }

  .contact-fields {
    gap: 1.15rem 1.4rem;
  }

  .contact-fields label {
    gap: 0.15rem;
  }

  .contact-fields textarea {
    min-height: 3.4rem;
  }

}

/* Écran de téléphone couché : plus rien n'a de hauteur, et l'encoche passe
   sur le côté. Les blocs pleine page se remettent dans le flux. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 520px) {
  :root {
    --edge: max(1.35rem, env(safe-area-inset-left), env(safe-area-inset-right));
  }

  .hero {
    padding-top: 5.5rem;
    padding-bottom: 6rem;
  }

  .method-media {
    height: 62svh;
  }

  .method-step {
    min-height: 90svh;
  }

  .reliability {
    height: auto;
    min-height: 100svh;
    padding-top: 5rem;
  }
}

/* ── 10 · Mouvement réduit : tout se pose, rien ne bouge ──────────── */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .intro {
    display: none;
  }

  body.intro-live {
    overflow: auto;
  }

  body.intro-live .site-header,
  body.intro-live .hero {
    opacity: 1;
  }

  body.motion [data-lines] .line-in,
  body.motion [data-rise] {
    opacity: 1;
    transform: none;
  }

  .work {
    height: auto;
  }

  .work-sticky {
    position: relative;
    height: 82svh;
  }

  .work-frame {
    clip-path: none;
  }

  .work-frame video {
    transform: none;
  }

  .work-overlay {
    color: #f6f5f2;
  }

  .work-frame::after {
    opacity: 1;
  }

  .reliability-film {
    transform: none;
  }
}

/* ── 11 · Survol : réservé aux pointeurs qui survolent vraiment ────
   Au doigt, l'état de survol reste collé après le tap et le bouton
   paraît cassé. Les retours tactiles sont gérés en :active. ── */

@media (hover: hover) {
  .brand:hover {
    opacity: 0.58;
  }

  .header-contact:hover {
    opacity: 0.48;
  }

  .brief-btn:hover {
    background: transparent;
    color: var(--ink);
  }

  .brief-btn:hover .cta-arrow {
    transform: translateX(0.3rem);
  }

  .cta:hover {
    background: transparent;
    color: var(--ink);
  }

  .cta:hover .cta-arrow {
    transform: translate(0.25rem, -0.25rem);
  }

  .brief-close:hover {
    color: var(--ink);
  }

  .choices button:hover {
    border-color: var(--ink);
  }

  .brief-back:hover,
  .brief-skip:hover {
    color: var(--ink);
  }

  .brief-next:hover {
    opacity: 0.55;
  }

}

/* ── 12 · Retour tactile ──────────────────────────────────────────────
   Sans survol, il ne reste rien pour dire qu'un appui a été pris. Un
   enfoncement d'un cheveu suffit : il se sent plus qu'il ne se voit. */

.brief-btn,
.cta {
  transition: background-color 480ms var(--ease), color 480ms var(--ease),
              transform 220ms var(--ease-micro);
}

.choices button {
  transition: border-color 320ms var(--ease-micro),
              background-color 320ms var(--ease-micro),
              color 320ms var(--ease-micro),
              transform 220ms var(--ease-micro);
}

.brief-btn:active,
.cta:active,
.choices button:active {
  transform: scale(0.982);
}

.brand:active,
.header-contact:active,
.brief-close:active,
.brief-back:active,
.brief-skip:active,
.brief-next:active {
  opacity: 0.5;
}
