/* ══════════════════════════════════════════════════════════════════
   ARCANE & BESTIAIRE · relookage
   Système typographique et de grille inspiré du template Cargo Y843.

   Ce que le template fait, et qu'on reprend :
     · une base rem fluide (--base-size) dont TOUT découle
     · une grille de 12 colonnes, gouttière 1.4rem
     · un en-tête épinglé, minimal, deux blocs seulement
     · des filets <hr> comme séparateurs plutôt que des cadres
     · un « cartel » : titre d'œuvre + métadonnées en petit, à 62 % d'encre
     · beaucoup de blanc au-dessus, la matière ancrée en bas

   Ce qu'on change, parce que la matière ici est du 16/9 :
     · les médias sont bridés en HAUTEUR dans la bande (largeur libre),
       et en LARGEUR dans l'index (hauteur libre)
     · le ratio réel de chaque plan est déclaré en --ar : les deux films
       en scope (2.22 et 2.67) rendent la bande irrégulière, ce qui est
       exactement l'effet du template avec ses images de formats mêlés
   ══════════════════════════════════════════════════════════════════ */

/* ── JETONS ──────────────────────────────────────────────────────── */

:root {
  /* La base de toute l'échelle. Cargo calcule 11.72 px sur un 1440,
     ce qui donnait des cartels à 8.8 px : juste sur le papier, mais
     illisible sur un écran de travail. On monte à 15 px, soit un quart
     de plus, et tous les corps suivent sans qu'aucun rapport ne bouge. */
  --base-size: clamp(14px, 1.05vw, 18px);
  --font-scale: 1;

  /* Papier presque blanc. Le template est en #FFF pur ; on garde ici
     un souffle de chaud, seul reste de l'ancienne charte greige. */
  --paper:     #FBFAF7;
  --paper-alt: #F2F0EA;
  --ink:       #100F0E;

  /* Les trois niveaux d'encre du template : plein, 62 %, 38 %.
     Toute la hiérarchie passe par là, pas par la graisse. */
  --ink-title: rgba(16, 15, 14, 0.86);
  --ink-2: rgba(16, 15, 14, 0.62);
  --ink-3: rgba(16, 15, 14, 0.38);
  --rule:  rgba(16, 15, 14, 0.16);

  /* L'ACCENT N'EST PAS UNE COULEUR, C'EST UN CRAN DE CONTRASTE.
     L'or, puis le bordeaux, ont été essayés et retirés : sur un site
     dont toute la matière est une image, une teinte de marque entre en
     concurrence avec les plans, et c'est le plan qui doit gagner.

     Ce qui distingue ici n'est donc pas une autre couleur, c'est la
     MÊME encre à pleine force. Le texte courant vit à 62 % ; ce qui
     appelle passe à 100 %. Le survol, l'état actif, l'étoile d'un
     champ obligatoire, un message d'erreur : tous montent d'un cran
     au lieu de changer de teinte.

     C'est la logique du template Cargo, où la hiérarchie passe par
     l'encre et jamais par la couleur. */
  --accent: #100F0E;

  /* Le même cran, pour ce qui se pose SUR un plan. */
  --accent-media: #FFFFFF;

  /* Le fond sur lequel se pose un plan : les barres d'un letterbox, la
     réserve d'un plan qui n'est pas encore chargé. Il NE SUIT PAS le
     thème — --ink devient clair en mode sombre, et un scope se
     retrouverait bordé de blanc. Une salle est noire dans les deux
     cas ; c'est aussi la seule façon de juger un étalonnage. */
  --media-ground: #08080A;

  /* (Le voile de l'accès client est un fond flouté ; les points qui
     l'animent sont dessinés dans un canvas, pas en CSS — voir
     buildDots() dans assets/relook.js.) */

  /* Le logo est noir : sur papier clair il reste noir, sur papier
     sombre il s'inverse. Ne sert qu'en mobile, la barre de bureau
     réglant la question par son mélange « difference ». */
  --logo-invert: none;

  --gutter: 1.4rem;
  --pad:    2.4rem;
  --nav-h:  5rem;

  /* Le grotesque. Monument Grotesk est sous licence Cargo et ne peut
     pas sortir de leur plateforme : Inter en tient lieu, c'est le
     néo-grotesque libre le plus proche de dessin et de couleur.
     Le mono ne sert qu'aux nombres et aux mentions techniques ;
     c'est un écart assumé au template, qui n'a qu'une seule fonte,
     mais un studio DIT parle en métadonnées. Pour revenir au modèle
     exact : mettre --font-mono à var(--font-sans). */
  --font-sans: 'Inter', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:     #0C0C0A;
    --paper-alt: #141412;
    --ink:       #F2F0EA;
    --ink-title: rgba(242, 240, 234, 0.90);
    --ink-2: rgba(242, 240, 234, 0.62);
    --ink-3: rgba(242, 240, 234, 0.38);
    --rule:  rgba(242, 240, 234, 0.18);
    --accent: #F2F0EA;
    --logo-invert: invert(1);
  }
}
:root[data-theme="dark"] {
  --paper:     #0C0C0A;
  --paper-alt: #141412;
  --ink:       #F2F0EA;
  --ink-title: rgba(242, 240, 234, 0.90);
  --ink-2: rgba(242, 240, 234, 0.62);
  --ink-3: rgba(242, 240, 234, 0.38);
  --rule:  rgba(242, 240, 234, 0.18);
  --accent: #F2F0EA;
  --logo-invert: invert(1);
}

/* ── SOCLE ───────────────────────────────────────────────────────── */

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

/* .cs pose display:grid, ce qui l'emporte sur le display:none que
   l'attribut hidden apporte par défaut : une section marquée hidden
   restait donc visible. On rend à l'attribut le dernier mot. */
[hidden] { display: none !important; }

html {
  font-size: var(--base-size);
  -webkit-text-size-adjust: none;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: calc(var(--font-scale) * 1.2rem);
  line-height: 1.38;
  font-weight: 450;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'ss01', 'cv05';
  overflow-x: hidden;
}

@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;
  }
}

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

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

/* Le filet : c'est le seul séparateur du template. Pas de cadre,
   pas d'ombre, pas de fond. Une ligne à 16 % et du blanc. */
hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0 0 0.9rem;
  height: 0;
}

::selection { background: var(--accent); color: var(--paper); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; top: -100%; left: var(--pad); z-index: 999;
  background: var(--ink); color: var(--paper);
  padding: 0.8rem 1.4rem; font-size: 0.85rem;
  letter-spacing: 0.06em; text-transform: uppercase;
}
.skip-link:focus { top: 0.8rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }


/* ── STYLES DE TEXTE ─────────────────────────────────────────────
   L'échelle exacte du template Y843. Le contraste ne se joue pas
   entre le titre et le corps (tous deux à 1.2rem) mais entre le
   titre d'œuvre et le cartel : 1.2 contre 0.75.                    */

.h1, h1 {
  font-size: calc(var(--font-scale) * 4.5rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  font-weight: 500;
  margin: 0;
}

.h2, h2 {
  font-size: calc(var(--font-scale) * 3.8rem);
  line-height: 0.96;
  letter-spacing: -0.03em;
  font-weight: 500;
  margin: 0;
}

/* Le titre d'œuvre : même corps que le texte courant, distingué par
   la graisse et par une encre à 80 %, comme dans le template. */
.work-title {
  display: block;
  font-size: calc(var(--font-scale) * 1.2rem);
  line-height: 1.24;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--ink-title);
}

/* Le cartel. 0.85rem, 62 % d'encre. Le template dit 0.75 ; on l'a
   monté d'un cran, la mesure d'origine étant trop fine à l'écran. */
.caption {
  display: block;
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.5;
  color: var(--ink-2);
  font-weight: 450;
  letter-spacing: 0.005em;
}

/* Le cartel indenté : le filet vertical qui tient les crédits. */
.caption-indented {
  display: block;
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.55;
  color: var(--ink-2);
  padding-left: 0.9rem;
  border-left: 1px solid var(--rule);
  margin: 0.7rem 0;
}

/* Les nombres, les années, les formats : en mono. */
.num, .mono {
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.8rem);
  letter-spacing: 0.05em;
  color: var(--ink-3);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

.lead {
  font-size: calc(var(--font-scale) * 2.1rem);
  line-height: 1.22;
  letter-spacing: -0.022em;
  font-weight: 450;
  margin: 0;
  text-wrap: balance;
}

/* Lien discret, souligné au survol seulement. Le template ne souligne
   jamais au repos : le texte doit rester une surface calme. */
.link {
  color: var(--ink-2);
  transition: color 0.18s;
}
.link:hover { color: var(--accent); }

.link-underline { text-decoration: underline; text-underline-offset: 0.22em;
  text-decoration-thickness: 1px; text-decoration-color: var(--rule); }
.link-underline:hover { text-decoration-color: var(--accent); color: var(--accent); }


/* ── GRILLE ──────────────────────────────────────────────────────
   L'équivalent des <column-set> / <column-unit> de Cargo : douze
   colonnes, gouttière 1.4rem. Les découpes du template sont 4/8 et
   4/7/1 ; on s'y tient.                                            */

.cs {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: 0;
  padding: 0 var(--pad);
  max-width: 2200px;
  margin: 0 auto;
  width: 100%;
}

.cu-1  { grid-column: span 1; }
.cu-2  { grid-column: span 2; }
.cu-3  { grid-column: span 3; }
.cu-4  { grid-column: span 4; }
.cu-5  { grid-column: span 5; }
.cu-6  { grid-column: span 6; }
.cu-7  { grid-column: span 7; }
.cu-8  { grid-column: span 8; }
.cu-12 { grid-column: span 12; }

/* Une colonne calée sur la cinquième piste. Elle disait seulement
   « grid-column-start: 5 », ce qui écrasait le début posé par « .cu-8 »
   et laissait la fin en auto : la colonne ne faisait plus qu'UNE piste,
   et le texte tombait un mot par ligne. Un raccourci complet dit les
   deux bouts. */
.start-5 { grid-column: 5 / 13; }

/* Une colonne d'appoint, calée à droite de la grille. Elle était
   écrite en style DANS la page, ce qui la rendait increvable : un
   style en ligne passe devant toute requête de média, et sur un
   téléphone le bloc restait accroché aux colonnes 9 à 13 d'une grille
   qui n'en avait plus que six. La phrase d'ouverture se retrouvait
   comprimée sur soixante pixels, un mot par ligne. */
.start-9 { grid-column: 9 / 13; }


/* ── EN-TÊTE ÉPINGLÉ ─────────────────────────────────────────────
   Le « page pinned-top fixed » du template : deux blocs, rien
   d'autre. Pas de burger, pas de logo en image, pas de fond.       */

/* La barre a deux états, et un seul script les commute.

   Sur le papier : encre normale, rien de plus.
   Au-dessus d'un plan : texte blanc sur un voile court, comme le hero.

   Le mélange « difference » tenait le rôle au premier jet, sans script,
   mais il lâchait sur les demi-teintes : la navigation devenait un gris
   sale au-dessus d'une image moyenne. Deux états explicites sont moins
   élégants sur le papier et bien plus sûrs à l'écran.               */

.pin {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  padding-top: 1.1rem;
  padding-bottom: 1.1rem;
  pointer-events: none;
  align-items: center;
}
.pin > * { pointer-events: auto; }

.pin::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0; bottom: -2.4rem;
  z-index: -1;
  background: linear-gradient(to bottom, rgba(0,0,0,0.55), transparent);
  opacity: 0;
  pointer-events: none;
}

.pin a, .pin span { color: var(--ink); }

/* Les transitions n'entrent en jeu qu'après le premier calcul d'état :
   sans cela la barre se voit passer du noir au blanc au chargement. */
.pin.ready a, .pin.ready span { transition: color 0.28s; }
.pin.ready::before { transition: opacity 0.28s; }
.pin.ready .pin-logo img { transition: filter 0.28s; }

.pin.on-media::before { opacity: 1; }
.pin.on-media a, .pin.on-media span { color: #fff; }
.pin.on-media .pin-logo img { filter: invert(1); }

/* Le logo suit le thème quand la barre est sur le papier. */
.pin-logo { display: inline-block; line-height: 0; }
.pin-logo img {
  height: 2.6rem;
  width: auto;
  filter: var(--logo-invert);
}

.pin-nav {
  text-align: right;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  letter-spacing: -0.008em;
}
.pin-nav a { transition: opacity 0.18s; }
.pin-nav a:hover { opacity: 0.55; }
.pin-nav a[aria-current="page"] { opacity: 0.55; }


/* ── LES PAGES-ÉCRAN ─────────────────────────────────────────────
   L'accueil et /works partagent la même forme : une page d'un seul
   écran, qui ne défile pas. Le plan occupe tout, la barre et la
   navigation flottent dessus.

   Ce qui les distingue tient en trois points :
     · l'accueil ne montre que les projets marqués band, /works les
       montre tous — c'est l'attribut data-hero-all sur la section ;
     · l'accueil ne porte pas de crédits, /works en porte — c'est la
       présence du bloc [data-hero-credits] ;
     · la navigation du bas mène à des sections sur l'accueil, à des
       catégories sur /works.

   La classe s'appelle « screen » et non « home » pour cette raison. */

body.screen {
  height: 100svh;
  overflow: hidden;      /* rien à faire défiler : tout tient à l'écran */
}

/* La barre reste épinglée sur le plan, y compris sur téléphone où
   elle redevient normalement un en-tête sur papier. */
body.screen .pin {
  position: fixed;
  background: none;
  border-bottom: 0;
  padding-bottom: 1.1rem;
}
/* PAS DE BANDE SOUS LA BARRE.
   Le voile court des pages de papier — un dégradé noir de cent pixels
   derrière l'en-tête — se lit sur une image comme un bandeau posé en
   haut de l'écran, avec une fin qu'on devine. Sur les pages-écran on
   le supprime et on rend la lisibilité au texte lui-même : une ombre
   courte et serrée, la même que sur le cartel du plan. L'image n'est
   plus touchée du tout. */
body.screen .pin::before { display: none; }
body.screen .pin { text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55); }
body.screen .pin-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }
/* Le bouton de section est un <button> : il n'hérite pas de la couleur
   du texte comme un lien, il faut le nommer. */
body.screen .pin a,
body.screen .pin span,
body.screen .pin button { color: #fff; }
body.screen .pin .pin-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }

/* Le logo d'accueil est plus grand que sur les pages intérieures :
   c'est la seule marque de la maison sur l'écran. */
body.screen .pin-logo img { height: clamp(2.55rem, 3.75vw, 3.75rem); }

.pin-right {
  display: flex;
  justify-content: flex-end;
  align-items: baseline;
  gap: clamp(1.2rem, 2.4vw, 2.2rem);
}

/* Les deux sections, en haut à droite de /works. Même corps que la
   navigation du bas : ce sont les mêmes portes, elles ont juste changé
   de mur. */
.pin-section {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  color: inherit;
  transition: color 0.2s;
}
body.screen .pin .pin-section:hover,
body.screen .pin .pin-section[aria-expanded="true"] { color: var(--accent-media); }

/* Le voile étant translucide, la barre d'accueil transparaissait au
   travers : un logo flou et un « Client Access » fantôme au-dessus du
   texte. Elle s'efface le temps du volet, la croix la remplace. */
body.screen .pin,
body.screen .hero-info,
body.screen .hero-nav,
body.screen .hero::before,
body.screen .hero::after { transition: opacity 0.28s ease; }

/* Pendant le volet, seule l'IMAGE reste : le cartel, la navigation, les
   points et les deux voiles du plan s'effacent. Deux raisons — ils
   transparaissaient sous le volet en taches floues, et le dégradé du
   bas assombrissait la moitié inférieure du texte alors que le voile,
   lui, est uniforme. */
body.sheet-open .pin,
body.sheet-open .hero-info,
body.sheet-open .hero-nav,
body.sheet-open .hero::before,
body.sheet-open .hero::after { opacity: 0; pointer-events: none; }

/* Sur téléphone la grille empile ses colonnes, ce qui ferait tomber le
   bouton client SOUS le logo. La barre d'accueil sort donc de la
   grille : deux blocs, un à chaque bout, sur une seule ligne. */
@media (max-width: 800px) {
  body.screen .pin {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
  }
  body.screen .pin > * { grid-column: auto; }
  .pin-client { font-size: calc(var(--font-scale) * 0.95rem); }
}

/* L'accès au portail client. Le template ne dessine aucun bouton ;
   celui-ci en est un quand même, parce qu'il ne s'adresse pas au même
   visiteur que le reste de la page — il faut qu'il se voie. Un filet
   d'un pixel suffit à le dire. */
.pin-client {
  /* Depuis qu'il ouvre un volet, c'est un <button> : sans cette remise
     à zéro le navigateur lui rend son fond gris, son cadre et sa marge
     intérieure, et il apparaît comme une bande au milieu du texte. */
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: inherit;
  color: inherit;

  display: inline-block;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  line-height: 1.2;
  white-space: nowrap;
  transition: color 0.2s;
}
body.screen .pin .pin-client:hover { color: var(--accent-media); }


/* ── HERO PLEIN ÉCRAN ────────────────────────────────────────────
   Un studio de post vend son image, pas sa mise en page. L'accueil
   s'ouvre donc sur un plan qui occupe tout l'écran, et la typographie
   du template vient s'y poser par-dessus.

   Les plans sont empilés et se fondent l'un dans l'autre. Deux au
   maximum sont chargés à la fois, le courant et le suivant : c'est ce
   qui empêche les 149 Mo de boucles de partir ensemble.

   Le texte posé dessus est en « difference », comme l'en-tête : il
   passe du noir au blanc selon le plan et reste lisible sur n'importe
   quelle image, sans voile ni dégradé.                               */

.hero {
  position: relative;
  width: 100%;
  height: 100svh;
  min-height: 30rem;
  background: var(--media-ground);
  overflow: hidden;
  isolation: isolate;
}

.hero-slot {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.55s ease;   /* court : un fondu long superpose deux films */
  background: var(--media-ground) center / cover no-repeat;
  background-image: var(--affiche);
}
.hero-slot.on { opacity: 1; }
.hero-slot video {
  width: 100%; height: 100%;
  object-fit: cover;
}
/* L'accueil remplit TOUJOURS l'écran, y compris pour un scope : ici
   le champ fit de works.js ne s'applique pas. Un plan letterboxé
   laisserait deux bandes noires en haut et en bas d'une page qui n'a
   que cette image à montrer. Le cadrage intégral d'un scope reste
   visible sur /works, qui est fait pour ça. */

/* Les voiles. Le mélange « difference » tenait sur le papier mais
   lâchait sur un ciel clair : le texte y virait à l'orange sale. Deux
   dégradés très courts, en haut et en bas, garantissent le contraste
   sur n'importe quel plan sans assombrir l'image au centre. C'est la
   solution du cinéma, pas celle du template, et c'est la bonne ici. */
.hero::before, .hero::after {
  content: '';
  position: absolute; left: 0; right: 0;
  z-index: 2;
  pointer-events: none;
}
.hero::before {
  top: 0; height: 32%;
  background: linear-gradient(to bottom, rgba(0,0,0,0.46), transparent);
}
/* Le voile du bas a été renforcé le jour où le cartel a cessé d'être
   collé au bord : il se lit maintenant vers 65 % de la hauteur, là où
   l'ancien dégradé n'était encore presque rien. Trois arrêts plutôt que
   deux, pour que la montée soit lente et qu'on ne voie pas la limite.
   La moitié haute de l'image reste intacte. */
.hero::after {
  bottom: 0; height: 56%;
  background: linear-gradient(to top,
    rgba(0,0,0,0.74) 0%,
    rgba(0,0,0,0.44) 38%,
    transparent 100%);
}

/* ── CE QUI EST POSÉ SUR LE PLAN ─────────────────────────────────
   Trois bandes, du bas vers le haut :

     ligne du bas   la navigation au centre, les pastilles à droite
     bande au-dessus  le cartel à gauche, les crédits à droite
     en haut        le logo et l'accès client, dans la barre

   Rien n'est caché derrière un bouton : sur l'accueil, tout ce qu'un
   projet a à dire est lisible sans un clic. C'est le contraire du
   choix fait sur /works, et c'est voulu — l'accueil montre UN film à
   la fois, il a la place ; l'index en montre sept à la suite.       */

/* La hauteur réservée à la ligne du bas : navigation + pastilles. */
:root { --hero-foot: 3.4rem; }
.hero:not(:has(.hero-nav)) { --hero-foot: 0rem; }

.hero-info {
  position: absolute;
  left: var(--pad); right: var(--pad);
  /* La réserve du bas ne sert qu'à dégager une navigation posée sous
     le cartel — les catégories, sur /works. L'accueil n'en a plus
     depuis qu'elles sont passées dans le menu, et le cartel y flottait
     à quatre-vingts pixels du bord sans rien à dégager. On ne réserve
     donc que si la page a de quoi réserver.

     L'encoche du bas de l'iPhone est ajoutée à part : c'est du
     matériel, pas de la mise en page. */
  bottom: calc(clamp(1.4rem, 4vh, 3rem) + var(--hero-foot)
               + env(safe-area-inset-bottom, 0px));
  z-index: 3;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 2.4rem;
  color: #fff;
  pointer-events: none;   /* le plan reste cliquable dessous */
}
.hero-info > * { pointer-events: auto; }

/* Un dégradé ne peut pas tout : sur un plan très clair — un village en
   plein soleil, une plage — le petit corps des crédits reste limite.
   Une ombre courte et serrée le décolle sans assombrir l'image d'un
   pour cent de plus. C'est ce que fait un générique de film. */
.hero-info,
.hero-nav { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); }

.hero-cap { max-width: 34rem; }
.hero-cap .num        { color: rgba(255,255,255,0.7); }
.hero-cap .work-title { color: #fff; }

/* Les crédits, en clair sur le plan — mais fermés au départ. Le
   visiteur qui veut l'équipe technique l'ouvre ; les autres gardent
   l'image. C'est le même choix que sur /works.

   Fermés, ils ne prennent aucune place et ne sont pas lus par les
   lecteurs d'écran ; la ligne du cartel ne bouge pas pour autant, la
   bande étant alignée sur son bas. */
.hero-credits {
  width: min(34rem, 42vw);
  flex: none;

  /* Fermés, ils sortent de la mise en page — pas seulement de la vue.
     En « visibility: hidden » ils gardaient leur place : sur un
     téléphone, où la bande s'empile, cela creusait un trou de trois
     cents pixels entre le cartel et la navigation. L'ouverture se fait
     donc en animation plutôt qu'en transition, seule façon d'animer un
     élément qui vient d'apparaître. */
  display: none;
}
.hero-credits[data-open] {
  display: block;
  animation: hero-credits-in 0.26s ease both;
}
@keyframes hero-credits-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-credits[data-open] { animation: none; }
}

/* La porte, sous le réalisateur. */
.hero-toggle {
  appearance: none; background: none; border: 0; margin: 0;
  padding: 0.55rem 0 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.68);
  transition: color 0.18s;
}
.hero-toggle:hover { color: var(--accent-media); }
.hero-toggle::before { content: '[ '; }
.hero-toggle::after  { content: ' ]'; }
.hero-credits .credits {
  border-left-color: rgba(255,255,255,0.24);
}
.hero-credits .credits div {
  color: rgba(255,255,255,0.88);
  line-height: 1.35;
  margin-bottom: 0.55rem;
}
.hero-credits .credits div:last-child { margin-bottom: 0; }
.hero-credits .credits dt-role {
  display: block;
  min-width: 0;
  color: rgba(255,255,255,0.62);
  font-size: calc(var(--font-scale) * 0.7rem);
  margin-bottom: 0.05rem;
}

/* La navigation, au milieu en bas. Trois entrées, rien d'autre : deux
   d'entre elles n'emmènent nulle part, elles ouvrent leur volet. */
.hero-nav {
  position: absolute;
  left: 50%; transform: translateX(-50%);
  bottom: clamp(1.4rem, 4vh, 3rem);
  z-index: 4;
  display: flex;
  gap: clamp(1.4rem, 3vw, 2.6rem);
  align-items: baseline;
}
.hero-nav a,
.hero-nav button {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  color: #fff;
  transition: opacity 0.18s, color 0.18s;
}
.hero-nav a:hover,
.hero-nav button:hover,
.hero-nav button[aria-expanded="true"] { color: var(--accent-media); }

/* Sur /works, ces mêmes entrées sont des catégories. Celle qui est
   choisie ne change pas de couleur — sur un plan, un mot en bordeaux
   se lit mal — mais se souligne du même filet que le réalisateur : le
   trait est déjà le signe de « ce qui se joue en ce moment » sur cette
   page, il n'y a pas de raison d'en inventer un second. */
.hero-nav button[aria-pressed] { position: relative; padding-bottom: 0.4rem; }
.hero-nav button[aria-pressed]::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.28s ease;
}
.hero-nav button[aria-pressed="true"]::after { transform: scaleX(1); }
.hero-nav button[aria-pressed="false"] { color: rgba(255,255,255,0.62); }
.hero-nav button[aria-pressed="false"]:hover { color: var(--accent-media); }

/* ── LE CADRE D'ACCÈS CLIENT ─────────────────────────────────────
   Un cadre posé sur le site, pas une page qui l'efface : le voile est
   assez clair pour qu'on voie le film continuer derrière. Le cadre
   lui-même est plus opaque, parce qu'on y tape — un champ de saisie
   sur une image qui bouge est illisible.                            */

/* Un seul rang, et le cadre s'y centre. Cette page a porté un temps
   son propre logo au-dessus du cadre : il faisait double emploi avec
   celui de la barre partagée, qui est au même endroit. C'est le logo
   de la page qui est parti, la barre étant, elle, commune aux douze.

   Le cadre remonte donc au centre. Il en descendait un peu, mais
   c'était pour ne pas laisser ce logo flotter seul en haut : la raison
   est partie avec lui. */
.gate {
  min-height: 100svh;
  display: grid;
  grid-template-rows: 1fr;
  justify-items: center;
  padding: clamp(2.6rem, 8vh, 5rem) var(--pad) clamp(2rem, 6vh, 4rem);
}

/* LE CADRE N'A NI BORDS NI FOND.
   Ni boîte, ni halo, ni assombrissement local : rien qui distingue la
   zone de saisie du reste de l'écran. Trois essais ont mené ici — un
   rectangle opaque, puis un halo doux, puis un halo étalé — et tous
   trois se voyaient, parce que TOUTE différence de densité posée
   quelque part se voit, si douce soit-elle.

   Ce qui tient la lisibilité est donc uniforme sur toute la fenêtre :
   le voile et le flou du fond, identiques d'un bord à l'autre, et une
   ombre portée courte sur le texte lui-même. Il n'y a plus rien à
   « sentir » : il n'y a plus de zone. */

.gate-card {
  position: relative;
  z-index: 1;
  align-self: center;

  /* Le centre optique est un peu au-dessus du centre géométrique :
     c'est la règle du site, la même que sur Services. Une marge basse
     sur un élément centré le remonte de la moitié de sa valeur.

     Elle rattrape au passage le déséquilibre des rembourrages, dont
     le haut est plus grand que le bas pour dégager la barre. */
  margin-bottom: clamp(2rem, 6.4vh, 4.4rem);
  width: min(26rem, 100%);
  padding: clamp(1.4rem, 2.6vw, 2rem) clamp(1.6rem, 3vw, 2.4rem);
  color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.42);
}

.gate-card .num        { color: rgba(255,255,255,0.62); }
.gate-card .work-title { color: #fff; margin-top: 0.1rem; }
.gate-card .caption    { color: rgba(255,255,255,0.72); margin-top: 0.4rem; }

.gate-field { display: block; margin-top: 1.5rem; }
.gate-label {
  display: block;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.5);
  margin-bottom: 0.35rem;
}

/* Un filet sous le champ, pas une boîte : le site n'a pas de boîtes.
   Le filet s'éclaire quand le champ prend le focus — c'est le même
   geste que le trait qui souligne la catégorie active. */
.gate-field input {
  display: block;
  width: 100%;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(255,255,255,0.3);
  border-radius: 0;
  padding: 0.35rem 0;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.05rem);
  color: #fff;
  transition: border-color 0.2s;
}
.gate-field input::placeholder { color: rgba(255,255,255,0.32); }
.gate-field input:focus {
  outline: none;
  border-bottom-color: #fff;
}
.gate-field input:-webkit-autofill {
  -webkit-text-fill-color: #fff;
  transition: background-color 9999s;   /* neutralise le fond jaune */
}

/* Le bouton n'a plus de cadre non plus : dans un cadre qui n'a aucune
   boîte, la pastille était devenue le seul contour de l'écran. Reste
   un mot, comme « Let's work » sur Services, qui est l'autre appel à
   l'action du site.

   PLUS DE SOULIGNEMENT AU REPOS. Il portait l'affordance, faute de
   cadre, mais deux traits au milieu d'un écran qui n'en a aucun autre
   se voyaient trop. Le trait ne paraît qu'au survol et au focus
   clavier : au repos, c'est la taille et le blanc plein qui disent
   que c'est là qu'on appuie. */
.gate-submit {
  display: inline-block;
  text-decoration: none;      /* c'est un <a> désormais, voir client/index.html */
  width: auto;
  margin-top: 1.9rem;
  padding: 0;
  appearance: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.3rem);
  font-weight: 500;
  letter-spacing: -0.012em;
  line-height: 1.2;
  color: #fff;
  text-decoration: none;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  transition: color 0.2s, text-decoration-color 0.2s;
}
.gate-submit:hover,
.gate-submit:focus-visible {
  color: var(--accent-media);
  text-decoration: underline;
  text-decoration-color: var(--accent-media);
}

/* Le bouton n'a plus de pastille pour l'écarter de la ligne suivante :
   l'espace doit maintenant être posé à la main. */
.gate-foot { margin-top: 1.7rem; }
.gate-foot a {
  color: rgba(255,255,255,0.82);
  text-decoration: none;      /* même règle que le bouton : rien au repos */
}

/* Le plan de fond, quand la page est servie seule. Dans le volet il
   n'est pas repris : c'est le film de la page en cours qu'on voit.
   La plaque est posée par-dessus, la même que dans le volet. */
/* Il ne porte PAS la classe .hero, et c'est délibéré : .hero::after
   est le voile du bas des pages-écran, et il occupe déjà ce
   pseudo-élément. Les deux règles se marchaient dessus et la plaque
   s'arrêtait à 56 % de la hauteur, en laissant une lisière visible en
   travers de l'image. */
.gate-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--media-ground);
  overflow: hidden;
  isolation: isolate;
}
.gate-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  background: rgba(8, 8, 10, 0.64);
  backdrop-filter: blur(16px) saturate(110%);
  -webkit-backdrop-filter: blur(16px) saturate(110%);
  pointer-events: none;
}

/* ── LES POINTS ──────────────────────────────────────────────────
   La houle de points de /bestiaire-grade, reprise telle quelle : même
   écart de 13 px, même vitesse, même calcul de houle. Elle est
   dessinée dans un canvas et non en CSS — un motif CSS ne bouge pas,
   et c'est le mouvement qui fait la matière.

   Fixe plutôt qu'absolu : le canvas doit couvrir la fenêtre entière,
   qu'il soit dans le volet ou sur la page servie seule. Le fondu en
   haut et en bas est celui de /bestiaire-grade. */
.gate-dots {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 14%, black 86%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, black 14%, black 86%, transparent 100%);
}

/* Sur le cadre sombre, le soulignement du lien doit être clair. */
.gate-foot a:hover,
.gate-foot a:focus-visible {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255,255,255,0.55);
}


/* ── LE VOILE DE L'ACCÈS CLIENT ──────────────────────────────────
   Un fond flouté, plus fermé que celui des sections — on y tape, il
   faut que le regard se pose —, et la houle de points de
   /bestiaire-grade par-dessus. Les points sont dessinés dans un
   canvas, pas ici : voir buildDots() dans assets/relook.js.        */

.sheet[data-veil="dots"] {
  background: rgba(8, 8, 10, 0.64);
  backdrop-filter: blur(16px) saturate(110%);
  -webkit-backdrop-filter: blur(16px) saturate(110%);
}
.sheet[data-veil="dots"] .gate { min-height: auto; }


/* ── LA BARRE DE PROGRESSION ─────────────────────────────────────
   Elle a remplacé les pastilles du coin bas droit. Le bloc réalisateur
   se rétrécit sur son texte — c'est tout le principe : la barre prend
   alors EXACTEMENT la largeur du nom et vient le souligner.

   Elle se remplit en huit secondes, le temps exact du plan. Ce n'est
   pas une décoration : c'est le minuteur, rendu visible. */

.hero-dir {
  display: inline-block;    /* se rétrécit sur le nom */
  max-width: 100%;
}

.hero-dir .caption { color: rgba(255,255,255,0.82); }

.hero-bar {
  display: block;
  width: 100%;
  height: 1.5px;
  margin-top: 0.5rem;
  background: rgba(255,255,255,0.26);
  overflow: hidden;
}
.hero-bar i {
  display: block;
  height: 100%;
  width: 100%;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left center;
}
@keyframes hero-progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Sans mouvement, le plan ne s'enchaîne pas tout seul : la barre n'a
   plus rien à compter, elle reste pleine et sert de simple filet. */
@media (prefers-reduced-motion: reduce) {
  .hero-bar i { transform: scaleX(1); animation: none !important; }
}

/* Le bouton est un bloc : il passe ainsi sous le bloc réalisateur, qui
   est en inline-block. Sa largeur reste celle de son texte pour ne pas
   offrir une zone cliquable large comme le cartel. */
.hero-toggle {
  display: block;
  width: -webkit-fit-content;
  width: fit-content;
}

/* La barre garde son mélange « difference » : sur le voile sombre du
   hero elle vire au blanc, sur le papier elle vire au noir. Elle se
   corrige donc toute seule au défilement, sans une ligne de script. */


/* ── ÉNONCÉ D'ACCUEIL ────────────────────────────────────────────
   Le template laisse le haut de l'écran vide. On y met le strict
   nécessaire, en haut à gauche, et on laisse respirer.             */

.statement {
  padding-top: clamp(3rem, 9vh, 8rem);
  padding-bottom: clamp(2.4rem, 6vh, 5rem);
}
.statement .lead { margin-bottom: 1.2rem; }


/* ══════════════════════════════════════════════════════════════════
   CE QUI SUIT N'EST PLUS UTILISÉ PAR AUCUNE PAGE.

   /works était un index défilant ; il est devenu une page-écran comme
   l'accueil. Les classes .work, .work-media, .work-label, .work-panel,
   .work-view et .filters n'apparaissent donc plus nulle part.

   Gardé au cas où vous voudriez revenir à une liste. Supprimable avec
   le bloc correspondant de assets/relook.js le jour où vous serez sûr.
   Attention : .credits et .panel-grid, eux, SERVENT ENCORE — ce sont
   les crédits du cartel de /works. Ne pas les emporter.
   ══════════════════════════════════════════════════════════════════ */

/* ── INDEX DES ŒUVRES · PLEIN CADRE ──────────────────────────────
   Un projet = un écran entier, l'image bord à bord, sans marge de
   papier ni gouttière. Le texte est POSÉ dessus, comme un générique.

   L'équipe technique n'est plus affichée d'office : elle dort dans un
   volet que le visiteur ouvre s'il la veut. Un plan ne doit pas être
   accompagné d'un tableau de crédits qu'on ne lit pas ; il doit être
   regardé, et le reste doit être disponible à un geste près.

   Le plan remplit le cadre (object-fit: cover) : sur un écran plus
   haut qu'un 16/9, quelques pour cent de haut et de bas sortent du
   champ. Pour un plan qu'on refuse de rogner — un scope, un cadrage
   qu'on ne touche pas — mettre fit: 'contain' dans works.js : l'image
   reste bord à bord et se letterboxe sur l'encre.                   */

/* Pas d'ancrage de défilement (scroll-snap) : essayé, retiré. Sur
   /works l'en-tête et les filtres ne font que 250 px, et le navigateur
   les enjambait pour se caler sur le premier plan — on ne pouvait plus
   se poser sur les filtres. Le plein écran suffit à donner la cadence
   d'un projet par écran ; il n'a pas besoin qu'on tienne la molette. */

.work {
  position: relative;
  height: 100svh;
  min-height: 26rem;
  background: var(--media-ground);
  overflow: hidden;
  isolation: isolate;
}

.work-media {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--media-ground) center / cover no-repeat;
}
.work-media video {
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s;
}
.work-media.is-loaded video { opacity: 1; }

/* Le plan qu'on ne rogne pas. Plutôt que de le letterboxer au milieu
   d'un écran entier — ce qui rend deux bandes noires mortes —, on
   donne à la section la hauteur EXACTE du film. Le plan touche alors
   les deux bords, il n'est pas rogné, et il n'y a aucune bande : le
   projet occupe simplement moins d'un écran, ce qui est la vérité de
   son format. Le plafond garde le tout dans la fenêtre. */
.work[data-fit="contain"] {
  height: auto;
  aspect-ratio: var(--ar, 2.39);
  max-height: 100svh;
  min-height: 0;
}
.work[data-fit="contain"] .work-media       { background-size: contain; }
.work[data-fit="contain"] .work-media video { object-fit: contain; }

/* Les deux voiles. En haut pour la barre de navigation qui passe
   par-dessus, en bas pour le cartel. Très courts : ils garantissent
   le contraste sans assombrir le centre de l'image, donc sans mentir
   sur l'étalonnage. */
.work-media::before, .work-media::after {
  content: '';
  position: absolute; left: 0; right: 0;
  pointer-events: none;
  z-index: 1;
}
.work-media::before {
  top: 0; height: 26%;
  background: linear-gradient(to bottom, rgba(0,0,0,0.44), transparent);
}
.work-media::after {
  bottom: 0; height: 46%;
  background: linear-gradient(to top, rgba(0,0,0,0.62), transparent);
}

/* Le cartel posé sur l'image, en bas à gauche. Il ne reçoit pas les
   clics : ceux-ci traversent jusqu'au plan, qui est le lien. Seul le
   bouton d'ouverture les reprend. */
.work-label {
  position: absolute;
  left: var(--pad); bottom: clamp(1.4rem, 4vh, 3rem);
  z-index: 3;
  color: #fff;
  max-width: 34rem;
  pointer-events: none;
}
.work-label .num        { color: rgba(255,255,255,0.72); }
.work-label .work-title { color: #fff; font-size: calc(var(--font-scale) * 1.6rem); }
.work-label .caption    { color: rgba(255,255,255,0.78); }

/* L'ouverture du volet, sur /works. En mono, discrète, sur une ligne
   à part : c'est une porte, pas un titre. L'accueil n'en a pas — il
   affiche tout. */
.work-toggle {
  appearance: none; background: none; border: 0; padding: 0.55rem 0 0;
  margin: 0; cursor: pointer;
  pointer-events: auto;
  display: inline-block;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.68);
  transition: color 0.18s;
}
.work-toggle:hover  { color: var(--accent); }
.work-toggle::before { content: '[ '; }
.work-toggle::after  { content: ' ]'; }

/* Le volet lui-même, en bas à droite, aligné sur la même ligne de
   pied que le cartel. Fermé, il ne prend ni place ni voix. */
.work-panel {
  position: absolute;
  right: var(--pad); bottom: clamp(1.4rem, 4vh, 3rem);
  z-index: 4;
  width: min(34rem, 44vw);
  padding: 1.1rem 1.3rem 1.2rem;
  background: rgba(12, 11, 10, 0.60);
  border: 1px solid rgba(255,255,255,0.14);
  backdrop-filter: blur(16px) saturate(115%);
  -webkit-backdrop-filter: blur(16px) saturate(115%);
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.7rem);
  transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s linear 0.28s;
}
.work-panel[data-open] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s;
}

/* Sous 1100 px, cartel et volet ne tiennent plus côte à côte : le
   volet passe en haut du cadre, pleine largeur. Le cartel et son
   bouton [ Close ] restent visibles en bas — sur un téléphone il n'y
   a pas de touche Échap pour se rattraper. */
@media (max-width: 1100px) {
  .work-panel {
    left: var(--pad); right: var(--pad); width: auto;
    top: calc(var(--nav-h) + clamp(0.8rem, 2.5vh, 1.6rem));
    bottom: auto;
    max-height: 56svh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
}

.panel-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: 0;
}
.panel-grid > * { min-width: 0; }

/* Les crédits sur fond sombre : le filet et les encres se relèvent. */
.work-panel .credits { border-left-color: rgba(255,255,255,0.22); }
.work-panel .credits div { color: rgba(255,255,255,0.86); }
/* Dans le volet, le rôle passe AU-DESSUS du nom au lieu d'être à sa
   gauche : deux colonnes étroites ne laissent pas 8 rem au rôle sans
   couper les noms en deux. Empilé, c'est aussi la forme d'un vrai
   générique de fin. */
.work-panel .credits dt-role {
  display: block;
  min-width: 0;
  color: rgba(255,255,255,0.48);
  font-size: calc(var(--font-scale) * 0.7rem);
  margin-bottom: 0.05rem;
}
.work-panel .credits div {
  line-height: 1.35;
  margin-bottom: 0.62rem;
}
.work-panel .credits div:last-child { margin-bottom: 0; }

.work-view {
  display: inline-block;
  margin-top: 0.8rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.68);
  transition: color 0.18s;
}
.work-view:hover { color: var(--accent); }

/* Les crédits : rôle en 38 % à gauche, nom en 62 % à droite. */
.credits { margin: 0; padding-left: 0.9rem;
  border-left: 1px solid var(--rule); }
.credits div {
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.55;
  color: var(--ink-2);
}
.credits dt-role {
  display: inline-block;
  min-width: 8.4rem;
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.76rem);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* La fiche réduite, pour les listes qui ne sont pas des projets
   (les deux filmographies sur /filmography). */
.entry { padding-bottom: clamp(2.4rem, 5vh, 4.4rem); align-items: start; }

/* ── LES DEUX FILMOGRAPHIES, CÔTE À CÔTE ─────────────────────────
   Elles se lisaient l'une sous l'autre, chacune sur toute la largeur,
   chacune ouverte par deux filets. Six colonnes chacune, un filet par
   colonne et rien d'autre : les portraits séparent mieux qu'un trait,
   et l'œil compare deux personnes au lieu de les parcourir. */
.filmo-duo {
  align-items: start;
  padding-bottom: clamp(2.6rem, 6vh, 4.6rem);
  row-gap: clamp(2.6rem, 6vh, 4rem);
}
.entry-duo { display: block; }
.entry-duo .portrait { margin: clamp(1rem, 2.4vh, 1.6rem) 0 clamp(1rem, 2.4vh, 1.5rem); }
.entry-duo .work-title { display: block; margin-bottom: 0.6rem; }

/* Le métier, sous le nom, dans l'encre du corps : c'est une précision,
   pas un second nom. */
.entry-role { color: var(--ink-2); }

.filmo-fin { padding-bottom: clamp(3rem, 7vh, 5rem); }

@media (max-width: 760px) {
  .entry-duo { grid-column: span 12; }
}
.entry-body { display: block; }
.entry-body .work-title { margin-bottom: 0.5rem; }
.entry-view {
  display: inline-block; margin-top: 0.4rem;
  font-size: calc(var(--font-scale) * 0.85rem);
  color: var(--ink-2); transition: color 0.18s;
}
.entry:hover .entry-view { color: var(--accent); }


/* ── FILTRES ─────────────────────────────────────────────────────
   En caption, pas en boutons : le template n'a aucun bouton dessiné. */

.filters { padding-bottom: 2.2rem; }
.filters button {
  appearance: none; background: none; border: 0; padding: 0;
  margin-right: 1.1rem;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 0.85rem);
  color: var(--ink-3);
  cursor: pointer;
  transition: color 0.18s;
}
.filters button:hover { color: var(--ink); }
.filters button[aria-pressed="true"] { color: var(--ink); }
.filters button[aria-pressed="true"]::before { content: '· '; color: var(--accent); }


/* ── EN-TÊTE DE SECTION ──────────────────────────────────────────
   C'est ici qu'on ouvre un chapitre : « What We Do », « Information ».
   La classe existait dans le balisage mais n'avait aucune règle — un
   en-tête était donc mis en page exactement comme une ligne de liste,
   et venait se coller à la section précédente. C'est ce qui rendait le
   bas de l'accueil confus ; les filets n'y étaient pour rien.

   La respiration au-dessus fait tout le travail : un blanc large
   annonce un chapitre, un blanc court annonce une ligne. Le rapport
   est de un à trois, comme dans le template.                        */

.index-head {
  padding-top: clamp(3.4rem, 8vh, 6.5rem);
  padding-bottom: clamp(1.6rem, 3vh, 2.4rem);
  align-items: start;
}

/* Le lien de clôture d'une liste — « [ See all works… ] ». Il ferme
   la section, il n'en ouvre pas une : il garde le pas des lignes. */
.row-more { padding-bottom: 0; }


/* ── LISTES NUMÉROTÉES (services) ────────────────────────────────
   La même grammaire que l'index : filet, numéro, titre, corps.     */

.row { padding-bottom: clamp(1.9rem, 3.6vh, 2.8rem); align-items: start; }
.row-body { color: var(--ink-2);
  font-size: calc(var(--font-scale) * 1.2rem); line-height: 1.45; max-width: 46ch; }


/* ── L'APPEL À L'ACTION ──────────────────────────────────────────
   « Let's work » n'est pas un titre de section, c'est le seul endroit
   de la page où l'on demande quelque chose au visiteur. Il est donc
   plus gros que les titres qui l'entourent, et il EST le lien — pas un
   titre suivi d'un lien : le clic tombe sur le mot qu'on vient de lire.

   Le soulignement est là au repos, pas seulement au survol. Le reste du
   site ne souligne rien ; c'est précisément ce qui le désigne.       */

.cta-title {
  display: inline-block;
  font-size: calc(var(--font-scale) * 1.9rem);
  line-height: 1.08;
  letter-spacing: -0.022em;
  font-weight: 500;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.16em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  transition: color 0.2s, text-decoration-color 0.2s;
}
.cta-title:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}


/* ── LE VOLET DE SECTION ─────────────────────────────────────────
   Services et About ne quittent pas l'accueil : leur contenu vient
   se poser par-dessus, sur le papier, et se referme d'une croix.

   Le contenu n'est pas recopié ici : il est chargé depuis la page
   elle-même au premier clic. Une seule source pour chaque texte —
   corriger services.html corrige le volet du même coup.            */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 200;

  /* Le film continue de tourner derrière, et il doit se VOIR : le voile
     est assez léger pour qu'on reconnaisse le plan, le flou assez court
     pour qu'on distingue ce qui bouge. C'est le texte qui se défend
     tout seul — ombre portée plus bas —, pas le fond qui l'aide. */
  background: rgba(8, 8, 10, 0.52);
  backdrop-filter: blur(6px) saturate(112%);
  -webkit-backdrop-filter: blur(6px) saturate(112%);

  /* Le contenu vient d'une page conçue pour le papier. Plutôt que de le
     réécrire, on retourne les jetons : tout ce qui s'appuie dessus —
     titres, corps, filets, cartels — passe en clair sur sombre d'un
     seul coup. C'est le seul intérêt d'avoir tout fait passer par des
     variables. */
  --paper:     transparent;
  --paper-alt: rgba(255,255,255,0.06);
  --ink:       #FFFFFF;
  --ink-title: rgba(255,255,255,0.94);
  --ink-2:     rgba(255,255,255,0.74);
  --ink-3:     rgba(255,255,255,0.46);
  --rule:      rgba(255,255,255,0.26);
  --accent:    #FFFFFF;
  --logo-invert: invert(1);
  color: var(--ink);

  /* Le voile étant léger, c'est l'ombre qui tient la lisibilité, comme
     sur le cartel de l'accueil. Courte et serrée : elle décolle le
     texte sans le rendre pâteux. */
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.65);

  opacity: 0;
  visibility: hidden;
  transform: translateY(1.4rem);
  transition: opacity 0.32s ease, transform 0.32s ease, visibility 0s linear 0.32s;
}
.sheet:focus { outline: none; }
.sheet[data-open] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.32s ease, transform 0.32s ease, visibility 0s;
}

/* Le contenu défile DANS le volet, pas derrière : la page d'accueil
   reste exactement où on l'a laissée. */
.sheet-scroll {
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;

  /* Un contenu plus court que l'écran se centre en hauteur au lieu de
     se tasser en haut avec du vide dessous. Le centrage passe par des
     marges automatiques et non par « justify-content », qui rognerait
     le haut d'un contenu plus haut que la fenêtre. */
  display: flex;
  flex-direction: column;
}
/* Comme sur /requests : le bloc se centre au centre OPTIQUE et non au
   centre géométrique. La réserve du bas est plus large que celle du
   haut, ce qui remonte le texte d'environ trois pour cent. Un bloc
   posé au milieu exact paraît toujours un peu tombé. */
.sheet-body {
  width: 100%;
  margin-block: auto;
  padding-bottom: clamp(3.5rem, 8vh, 6rem);
}

/* La croix. Petite, épinglée en haut à droite, au-dessus de tout ce
   que le contenu peut apporter — y compris sa propre barre. */
.sheet-close {
  position: fixed;
  top: clamp(0.9rem, 2.2vh, 1.6rem);
  right: var(--pad);
  z-index: 210;
  appearance: none; padding: 0.5rem; margin: 0;
  border: 0;

  /* Pas de pastille derrière : une croix se reconnaît à sa forme, elle
     n'a pas besoin d'un fond pour se faire voir. L'ombre portée lui
     suffit sur un plan clair. */
  background: none;
  color: #fff;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
  line-height: 0;
  cursor: pointer;
  opacity: 0.62;
  transition: opacity 0.18s, color 0.18s;
}
.sheet-close:hover { opacity: 1; color: var(--accent); }
.sheet-close svg { display: block; }

/* Le contenu récupéré arrive avec la respiration de sa page, calculée
   pour une barre fixe qui n'existe pas ici : on la lui retire, et on
   dégage la place de la croix. */
.sheet-body > :first-child { padding-top: clamp(3.4rem, 7vh, 5.5rem); }
.sheet-body .foot { padding-bottom: clamp(2.4rem, 5vh, 4rem); }

/* ── LES SERVICES EN QUATRE COLONNES ─────────────────────────────
   Dans le volet, les cinq services ne s'empilent plus : les quatre
   premiers se rangent côte à côte, et l'infrastructure — qui les tient
   tous — s'étale sous eux d'un bord à l'autre.

   Le texte reste celui de services.html. Ce sont des attributs posés
   là-bas (data-svc="col" / "wide" / "cta") qui disent la place de
   chacun : pas un rang dans le document, ce qui casserait le jour où
   vous ajoutez un service. La page services.html, elle, ne bouge pas.

   Le volet devient une grille de douze colonnes. Chaque section rendue
   par la page est un élément de cette grille : pleine largeur par
   défaut, un quart pour les quatre services.                        */

/* Le contenu se centre en hauteur comme il le faisait dans le volet :
   marges automatiques sur le <main>, et body.veiled en colonne. */
body.veiled > main { margin-block: auto; width: 100%; }

[data-svc-grid] {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  align-items: start;

  /* Les services ne s'étalent pas d'un bord à l'autre de l'écran : ils
     tiennent dans une colonne centrale. Sur un 1440 cela laisse deux
     marges franches, et les quatre titres se lisent d'un seul balayage
     de l'œil au lieu de trois. */
  max-width: 74rem;

  /* margin-inline et non « margin: 0 auto » : le raccourci remettait
     les marges VERTICALES à zéro, et c'est justement elles qui, en
     valant auto, centrent le volet en hauteur. Le bloc se retrouvait
     collé en haut de l'écran. La réserve du bas est reprise ici aussi,
     le raccourci de padding l'écrasait de la même façon. */
  margin-inline: auto;
  padding: 0 var(--pad) clamp(3.5rem, 8vh, 6rem);
}
[data-svc-grid] > *              { grid-column: 1 / -1; }
[data-svc-grid] > [data-svc="col"] { grid-column: span 3; }

/* Un service en colonne : plus de découpe 4/8, tout s'empile dans sa
   colonne. La section perd sa propre marge, la grille du volet la
   fournit déjà. */
[data-svc-grid] > .cs {
  padding-left: 0;
  padding-right: 0;
  max-width: none;
}
[data-svc-grid] > [data-svc="col"] {
  grid-template-columns: minmax(0, 1fr);
}
[data-svc-grid] > [data-svc="col"] > * { grid-column: 1 / -1; }

/* Dans une colonne d'un quart, le corps de 1.2rem est trop gros et sa
   mesure de 46ch ne veut plus rien dire. */
[data-svc-grid] > [data-svc="col"] .row-body {
  font-size: calc(var(--font-scale) * 0.95rem);
  line-height: 1.42;
  max-width: none;
}
[data-svc-grid] > [data-svc="col"] .work-title { margin-top: 0.15rem; }
[data-svc-grid] > [data-svc="col"] .credits    { margin-top: 0.9rem; }

/* L'infrastructure s'étale : elle garde la découpe 4/8 de la page,
   titre à gauche, texte à droite, et sa liste passe en trois colonnes
   pour occuper la largeur au lieu de faire une échelle. */
[data-svc-grid] > [data-svc="wide"] .credits {
  columns: 3;
  column-gap: var(--gutter);
}
[data-svc-grid] > [data-svc="wide"] .credits div {
  break-inside: avoid;
}

/* Le volet n'a pas besoin qu'on lui répète son propre nom. */
[data-svc-grid] > .index-head { display: none; }

/* ── UN SEUL TRAIT HORIZONTAL ────────────────────────────────────
   Les filets d'en-tête de la page disparaissent : dans quatre colonnes
   étroites, ils faisaient quatre traits courts et hachés qui coupaient
   la lecture au lieu de la tenir. Les seuls traits qui restent sont les
   VERTICAUX — le filet à gauche de chaque liste technique — parce que
   ceux-là accompagnent la colonne au lieu de la barrer.

   Il reste un trait, et un seul : le long, sous les quatre services,
   au-dessus de la ligne de contact. C'est la seule coupure de la page,
   donc elle veut dire quelque chose.                                */
[data-svc-grid] hr { display: none; }

[data-svc-grid] > [data-svc="wide"],
[data-svc-grid] > [data-svc="cta"] {
  border-top: 1px solid var(--rule);
  margin-top: clamp(1.6rem, 4vh, 3rem);
  padding-top: clamp(1.8rem, 4.5vh, 3.2rem);
}
[data-svc-grid] > [data-svc="cta"] { align-items: baseline; }

/* LA RESPIRATION DES TROIS BANDES.
   L'énoncé, les quatre services, l'appel à l'action. Ce sont trois
   choses de nature différente, et c'est le blanc entre elles qui le
   dit : plus large entre l'énoncé et les services qu'entre deux
   colonnes, plus large encore avant le trait de l'appel. */
[data-svc-grid] > .statement {
  /* 5,5 rem suffisent à dégager la barre, qui fait 72 px : au-delà, le
     bloc entier descendait et le bas de l'écran se vidait. */
  padding-top: clamp(3.4rem, 7vh, 5.5rem);
  padding-bottom: clamp(3rem, 7.5vh, 5.4rem);
}
[data-svc-grid] > .statement .lead { max-width: 34rem; }

/* Sous 1100 px, quatre colonnes deviennent deux ; sous 760, une. */
@media (max-width: 1100px) {
  [data-svc-grid] > [data-svc="col"] { grid-column: span 6; }
  [data-svc-grid] > [data-svc="wide"] .credits { columns: 2; }
}
@media (max-width: 760px) {
  [data-svc-grid] > [data-svc="col"] { grid-column: 1 / -1; }
  [data-svc-grid] > [data-svc="wide"] .credits { columns: 1; }
}


/* Le temps du chargement — quelques dizaines de millisecondes en
   local, davantage sur une connexion lente. */
.sheet-body:empty::before {
  content: 'Loading…';
  display: block;
  padding: 6rem var(--pad);
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.85rem);
  color: var(--ink-3);
}


/* Festival Presence n'est pas la suite de The Studio, c'est autre
   chose : un blanc franc le dit mieux qu'un filet. */
.about-fest { padding-top: clamp(3.4rem, 8vh, 6.5rem); }

/* Sur About, la première section dégage la barre. */
body.veiled > main > .statement:first-child {
  padding-top: clamp(3.4rem, 7vh, 5.5rem);
}


/* ── LE BANDEAU DÉFILANT ─────────────────────────────────────────
   Les festivals passent de droite à gauche, sans fin et sans arrêt.

   La boucle est sans couture parce que la liste est présente DEUX fois
   dans la piste — le script s'en charge — et que la piste recule de
   très exactement la moitié de sa largeur. À l'instant où elle a fini,
   le second jeu se trouve pile là où le premier avait commencé, et on
   repart sans que rien ne saute.

   L'écart entre deux noms est un padding, pas un « gap » : avec un gap
   les deux moitiés n'auraient pas la même largeur — il en manquerait
   un entre les jeux — et la boucle dériverait d'un cran à chaque tour. */

.marquee {
  width: 100%;
  overflow: hidden;
  padding: 1.3rem 0;

  /* Les noms n'apparaissent ni ne disparaissent net : ils se fondent
     dans le papier aux deux bords. C'est ce qui fait qu'un bandeau
     coulisse au lieu de défiler. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee-slide 38s linear infinite;
}
.marquee-set { display: flex; align-items: center; }

.marquee-item {
  display: inline-flex;
  align-items: center;
  padding-right: clamp(3rem, 5.5vw, 5.5rem);
  white-space: nowrap;
  font-size: calc(var(--font-scale) * 1.05rem);
  font-weight: 450;
  letter-spacing: -0.005em;
  color: var(--ink-2);
}

/* Les logos sont détourés au chargement, dans un canvas : voir
   detourer() dans assets/relook.js. Ils arrivent ici déjà blancs sur
   transparent, il n'y a donc plus qu'à les poser. */
/* La hauteur de référence vit dans une variable : le script pose sur
   chaque logo un facteur qui la module selon sa forme, et l'ensemble
   reste responsive. */
.marquee { --logo-h: clamp(2.4rem, 4vw, 3.6rem); }

.marquee-item img {
  display: block;
  height: var(--logo-h);
  width: auto;
  opacity: 1;
}

/* Sur le papier, le blanc ne se verrait pas : on le retourne. */
/* LES LOGOS SONT DÉJÀ BLANCS quand ils arrivent ici : detourer() les a
   peints en blanc sur transparent. Sur un fond sombre, il n'y a donc
   rien à leur faire.

   UNE SEULE EXCEPTION : about.html servie seule sur un papier clair,
   où le blanc ne se verrait pas. Le volet, lui, est toujours sombre,
   et il n'existe que sur les pages-écran : les exclure par leur classe
   suffit à l'épargner.

   La règle précédente disait « body:not(.veiled) », ce qui attrapait
   aussi l'accueil et retournait les logos en NOIR dans le volet. Une
   règle plus spécifique venait après pour l'annuler, mais elle était
   moins spécifique qu'elle : elle ne l'annulait pas. */
.marquee-item img { filter: none; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) body:not(.screen):not(.veiled) .marquee-item img {
    filter: invert(1);
  }
}
:root[data-theme="light"] body:not(.screen):not(.veiled) .marquee-item img {
  filter: invert(1);
}

@keyframes marquee-slide {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Un défilement perpétuel est exactement ce qu'un visiteur sensible au
   mouvement ne peut pas supporter. Sans mouvement, le bandeau devient
   une liste qui se replie sur plusieurs lignes. */
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; width: 100%; }
  .marquee-set   { flex-wrap: wrap; row-gap: 0.4rem; }
  .marquee { -webkit-mask-image: none; mask-image: none; padding-inline: var(--pad); }
}


/* ── LES PACKS DIT ───────────────────────────────────────────────
   Une page de lecture, pas un écran : des prix, des listes, un
   formulaire. Elle suit donc la grammaire de papier du site. Les prix
   et les textes viennent de /dit/packs.js, qui n'a pas été touché.  */

/* LE PLAN D'OUVERTURE.
   La page ne s'ouvre pas sur du texte : elle s'ouvre sur la
   configuration complète, en tournage. Le visiteur voit avant de lire,
   et il voit ce qu'un paragraphe met dix lignes à décrire.

   Pas plein écran : 72 % de la hauteur. Une image qui prend tout
   oblige à défiler pour découvrir qu'il y a une page dessous ; en
   laissant paraître le début du texte, on dit qu'elle continue. */
.dit-hero {
  position: relative;
  height: 72svh;
  min-height: 22rem;
  background: var(--media-ground);
  overflow: hidden;
  isolation: isolate;
}
.dit-hero img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Le hangar est clair en haut : la barre blanche s'y perdait. Un
   dégradé haut, deux fois plus haut que la barre, la rattrape sans
   qu'on le lise comme un bandeau — c'est la grammaire de l'affiche,
   pas celle du gabarit. */
.dit-hero::before {
  content: '';
  position: absolute; inset: 0 0 auto 0;
  height: 30%;
  background: linear-gradient(to bottom,
    rgba(0,0,0,0.46) 0%, rgba(0,0,0,0.2) 45%, transparent 100%);
  pointer-events: none;
  z-index: 1;
}
.dit-hero::after {
  content: '';
  position: absolute; inset: auto 0 0 0;
  height: 62%;
  background: linear-gradient(to top,
    rgba(0,0,0,0.78) 0%, rgba(0,0,0,0.42) 40%, transparent 100%);
  pointer-events: none;
}
.dit-hero-cap {
  position: absolute;
  left: 0; right: 0; bottom: clamp(1.8rem, 5vh, 3.4rem);
  z-index: 2;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.55);
}
.dit-hero-cap .num  { color: rgba(255,255,255,0.72); }
.dit-hero-cap .lead { color: #fff; margin-top: 0.4rem; }

/* La légende qui dit CE QU'ON REGARDE. Sans elle, le visiteur voit un
   beau plan ; avec elle, il voit le pack le plus complet, ce qui est
   toute la raison de la mettre là. */
.dit-hero-note { padding-top: 1rem; }
.dit-hero-note .caption { color: var(--ink-3); }

.dit-modes-row { padding-top: clamp(3.4rem, 9vh, 7rem); }

/* Le choix du type de production. Deux entrées, pas deux boutons :
   c'est le filet du bas qui dit laquelle est retenue, comme les
   catégories de /works. */
.dit-modes { display: flex; flex-wrap: wrap; gap: clamp(1.4rem, 3vw, 2.6rem); }
.dit-mode {
  appearance: none; background: none; border: 0; padding: 0 0 0.5rem; margin: 0;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  position: relative;
  transition: color 0.18s;
}
.dit-mode .work-title { color: var(--ink-3); transition: color 0.18s; }
.dit-mode .caption    { color: var(--ink-3); transition: color 0.18s; }
.dit-mode::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1.5px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.28s ease;
}
.dit-mode[aria-pressed="true"]::after { transform: scaleX(1); }
.dit-mode[aria-pressed="true"] .work-title { color: var(--ink-title); }
.dit-mode[aria-pressed="true"] .caption    { color: var(--ink-2); }
.dit-mode:hover .work-title { color: var(--accent); }

/* Une carte de pack. Pas de cadre : un filet en tête, comme partout
   ailleurs sur le site, et la colonne fait le reste. */
.dit-pack { padding-bottom: clamp(2.6rem, 6vh, 4.4rem); }
.dit-pack .work-title { margin-top: 0.2rem; }
/* Deux lignes réservées à l'accroche. La plus longue des trois en
   prend deux, les autres une : sans réserve, le prix du premier pack
   descendait d'une ligne et les trois chiffres ne se lisaient plus sur
   le même trait. C'est la même correction que le fanion, un cran plus
   bas. */
.dit-pack .caption {
  margin-top: 0.25rem;
  min-height: 3em;   /* deux lignes pleines : interligne 1,5 */
}

/* Réservée même vide : c'est elle qui aligne les trois titres. */
.dit-flag {
  display: block;
  min-height: 1.45em;
  color: var(--accent);
}

.dit-price { margin: 1.3rem 0 1.6rem; }
.dit-from  { color: var(--ink-3); }

/* Le nombre est la seule chose que le visiteur cherche : il est en
   mono, en chiffres tabulaires, et nettement plus gros que le reste. */
.dit-figure {
  display: block;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 2.1rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-title);
}
.dit-cur { font-size: 0.5em; letter-spacing: 0.06em; margin-left: 0.35em; }
.dit-note { color: var(--ink-3); margin-top: 0.45rem; font-style: italic; }

.dit-desc {
  font-size: calc(var(--font-scale) * 0.95rem);
  line-height: 1.45;
  color: var(--ink-2);
  margin-bottom: 1rem;
}
.dit-lead { font-size: calc(var(--font-scale) * 1.2rem); line-height: 1.35; margin-bottom: 0.6rem; }

/* Les inclusions, dans le filet vertical des crédits. Les exclusions
   dans le même filet, en encre plus pâle : ce qu'on ne fournit pas se
   lit, mais ne se dispute pas la place. */
.dit-list {
  margin: 0 0 1rem;
  padding-left: 0.9rem;
  border-left: 1px solid var(--rule);
}
.dit-list dt-role {
  display: block;
  margin-bottom: 0.35rem;
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.dit-list div {
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.5;
  color: var(--ink-2);
}
.dit-list.is-out div { color: var(--ink-3); }

/* CE QUI SE REPLIE.
   Une carte ne montre que ce qui sert à choisir : le nom, une ligne,
   le prix. Le paragraphe et les deux listes attendent derrière « ce
   qui est compris ». Rien n'est perdu, tout est à un clic — ce qui
   change, c'est qu'on ne le reçoit plus en pleine figure au moment où
   l'on compare trois tarifs.

   C'est un <details> natif : clavier, lecteur d'écran et recherche
   dans la page fonctionnent sans qu'on ait à les écrire. */
.dit-more { margin-bottom: 1.2rem; }
.dit-more summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.18s;
}
.dit-more summary::-webkit-details-marker { display: none; }
.dit-more summary::before { content: '[ '; }
.dit-more summary::after  { content: ' + ]'; }
.dit-more[open] summary::after { content: ' – ]'; }
.dit-more summary:hover { color: var(--accent); }
.dit-more-body { padding-top: 1rem; }
.dit-more-body .dit-list:last-child { margin-bottom: 0; }

.dit-cta {
  appearance: none; background: none; border: 0; padding: 0; margin-top: 0.4rem;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.05rem);
  font-weight: 500;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  transition: color 0.2s, text-decoration-color 0.2s;
}
.dit-cta:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* Le formulaire ne s'ouvre qu'une fois un pack choisi : demander ses
   coordonnées à quelqu'un qui n'a rien choisi, c'est lui demander de
   payer avant d'avoir vu. */
.dit-form { display: none; padding-top: clamp(2rem, 5vh, 4rem); }
.dit-form.is-open { display: grid; }

.dit-form .gate-field { margin-top: 1.4rem; }
.dit-form .gate-label { color: var(--ink-3); }
.dit-form .gate-field input,
.dit-form .gate-field textarea {
  display: block;
  width: 100%;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.35rem 0;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.05rem);
  line-height: 1.45;
  color: var(--ink);
  resize: vertical;
}
.dit-form .gate-field input::placeholder,
.dit-form .gate-field textarea::placeholder { color: var(--ink-3); }
.dit-form .gate-field input:focus,
.dit-form .gate-field textarea:focus { outline: none; border-bottom-color: var(--ink); }
.dit-form .is-wrong { border-bottom-color: var(--accent) !important; }

.dit-legend {
  display: block;
  margin-top: 2.2rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-3);
}
.dit-form form > .dit-legend:first-of-type { margin-top: 0.6rem; }

.dit-duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gutter); }
.dit-hint { color: var(--ink-3); margin-top: 0.5rem; }

.dit-error { display: none; color: var(--accent); margin-bottom: 0.8rem; }
.dit-error.is-on { display: block; }

.dit-change {
  appearance: none; background: none; border: 0; padding: 0; margin-top: 0.8rem;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.18s;
}
.dit-change:hover { color: var(--accent); }
.dit-change::before { content: '[ '; }
.dit-change::after  { content: ' ]'; }

.dit-submit {
  appearance: none; background: none; border: 0; padding: 0; margin-top: 2.2rem;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.3rem);
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  transition: color 0.2s, text-decoration-color 0.2s;
}
.dit-submit:hover { color: var(--accent); text-decoration-color: var(--accent); }
.dit-submit:disabled { opacity: 0.5; cursor: default; }

/* Le pot de miel. Hors écran plutôt que display:none : certains robots
   ignorent les champs cachés, aucun n'ignore ceux qu'il peut lire. */
.dit-hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* La bascule de langue. Cette page est la seule du site à être
   bilingue : son public est en partie francophone, et les prix se
   lisent dans sa langue. */
.dit-lang { display: inline-flex; gap: 0.7rem; align-items: baseline; }
.dit-lang button {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.8rem);
  letter-spacing: 0.06em;
  color: var(--ink-3);
  transition: color 0.18s;
}
.dit-lang button[aria-pressed="true"] { color: var(--ink); }
.dit-lang button:hover { color: var(--accent); }


/* ══════════════════════════════════════════════════════════════════
   BESTIAIRE GRADE

   La page du plugin rejoint la charte du studio : la barre, le menu,
   Inter et IBM Plex Mono, aucun accent de couleur. Elle avait une
   serif et un ocre à elle ; ils sont partis avec le reste.

   Elle reste noire, par la classe « papier-noir » : une page qui vend
   un outil d'étalonnage montre des images, et des images se regardent
   sur du noir.
   ══════════════════════════════════════════════════════════════════ */

.bg-nom {
  font-size: calc(var(--font-scale) * 3.4rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: 500;
  color: var(--ink-title);
  margin: 0.5rem 0 1.1rem;
}
.bg-portes  { display: block; margin-top: clamp(1.4rem, 3.5vh, 2.4rem); }
.bg-compat  { display: block; margin-top: 0.7rem; color: var(--ink-3); }

/* La bobine de démonstration : masquée tant que le fichier n'existe
   pas, révélée par son propre chargement. Voir le commentaire dans la
   page. */
.bg-demo { margin-top: clamp(2rem, 5vh, 3.4rem); }
.bg-demo video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
  background: var(--media-ground);
}

.bg-titre {
  font-size: calc(var(--font-scale) * 2.1rem);
  line-height: 1.14;
  letter-spacing: -0.022em;
  font-weight: 500;
  color: var(--ink-title);
  margin: 0.4rem 0 0;
  max-width: 22ch;
}
.bg-sous { margin-top: 0.9rem; max-width: 58ch; }

/* ── LES DIX MODULES, DEUX PAR RANG ──────────────────────────────
   L'avant et l'après en haut, le texte dessous. C'est la mise en page
   d'origine, et elle vaut mieux qu'un empilement : la page fait la
   moitié de la longueur, et l'œil compare deux modules d'un coup.

   Trois modules n'ont pas encore leurs images : la paire se retire
   alors, et le module se lit en texte seul. */
.bg-mods {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(1.6rem, 4vw, 3.4rem);
  row-gap: clamp(3rem, 7vh, 5rem);
  padding: 0 var(--pad) clamp(3rem, 7vh, 5rem);
  max-width: 2200px;
  margin: 0 auto;
}
.bg-mod .num        { display: block; margin-top: clamp(1.1rem, 2.6vh, 1.7rem); }
.bg-mod .work-title { display: block; margin: 0.15rem 0 0.5rem; }
.bg-mod .row-body   { max-width: 46ch; }

/* ── LE VOLET GLISSANT ───────────────────────────────────────────
   Un seul cadre, et non deux images côte à côte : la surface double,
   et c'est le visiteur qui découvre l'étalonnage en poussant le trait.

   La source est posée SUR l'étalonnage et rognée à la position du
   trait. On rogne plutôt qu'on ne redimensionne : l'image ne bouge
   pas d'un pixel sous le rognage, alors qu'une largeur qui varie la
   déformerait à chaque image. */
.bg-ba {
  --x: 50%;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--media-ground);
  cursor: ew-resize;
  touch-action: pan-y;    /* le glissement horizontal nous appartient */
  user-select: none;
}

/* Un cadre dont les fichiers manquent devient INVISIBLE, et non
   absent : « visibility » garde la boîte, donc la hauteur, donc
   l'alignement des rangs. Un module sans images restait sinon collé
   en haut de sa colonne pendant que son voisin descendait de la
   hauteur d'une image. Sur ce fond noir, la place réservée ne se voit
   pas. */
.bg-ba[data-vide] { visibility: hidden; }

.bg-ba img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  display: block;
  -webkit-user-drag: none;
}
.bg-avant { clip-path: inset(0 calc(100% - var(--x)) 0 0); }

/* Le trait, et son rond. Un filet d'un pixel suffirait à montrer où
   l'on est, mais rien ne dirait qu'on peut le prendre : le rond le
   dit, sans flèche — le site n'en a aucune. */
.bg-poignee {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--x);
  width: 1px;
  margin-left: -0.5px;
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.45);
  cursor: ew-resize;
}
.bg-poignee::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 7px; height: 46px;
  margin: -23px 0 0 -3.5px;
  border: 1px solid rgba(255, 255, 255, 0.86);
  border-radius: 999px;
  background: rgba(10, 10, 12, 0.28);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
  transition: transform 0.18s ease, background-color 0.18s ease;
}
/* Au survol la poignée s'allonge un peu plutôt que de grossir : une
   barre verticale qui gonfle en largeur perdrait sa finesse. */
.bg-ba:hover .bg-poignee::after,
.bg-poignee:focus-visible::after { transform: scaleY(1.14); background: rgba(10,10,12,0.42); }
.bg-poignee:focus-visible { outline: none; }

/* Les étiquettes sont posées SUR l'image, aux deux coins bas, et
   tiennent par leur ombre : le site n'a pas de pastilles. Elles
   s'effacent quand le trait vient les recouvrir — une étiquette
   « Before » sur une moitié devenue « after » mentirait. */
.bg-etiq {
  position: absolute;
  bottom: clamp(0.5rem, 1.4vw, 0.85rem);
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  pointer-events: none;
  transition: opacity 0.2s;
}
.bg-etiq-a { left:  clamp(0.6rem, 1.6vw, 1rem); }
.bg-etiq-b { right: clamp(0.6rem, 1.6vw, 1rem); }
.bg-ba[data-x-min] .bg-etiq-a,
.bg-ba[data-x-max] .bg-etiq-b { opacity: 0; }

/* ── L'ARGUMENT ──────────────────────────────────────────────────
   La chaîne de nœuds, en liste. Les flèches d'origine sont devenues
   des filets : le site n'a aucune flèche. */
.bg-arg { align-items: start; padding-bottom: clamp(2.6rem, 6vh, 4.4rem); }

.bg-chaine {
  display: flex;
  flex-wrap: wrap;
  gap: 0.34rem 0.6rem;
  margin: 0.7rem 0 0;
}
.bg-chaine span {
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.74rem);
  letter-spacing: 0.03em;
  color: var(--ink-2);
  padding: 0.28rem 0.55rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.bg-chaine-une span {
  color: var(--ink-title);
  border-color: rgba(255,255,255,0.42);
}
.bg-apres-t { display: block; margin-top: 1.6rem; }

/* ── LES TARIFS ──────────────────────────────────────────────────
   Trois cadres, exactement ceux de /requests : ce ne sont pas des
   paragraphes qu'on lit, ce sont des cibles qu'on clique, et un
   contour dit où finit la cible mieux qu'aucun mot. */
.bg-tarifs { align-items: stretch; padding-bottom: 1.2rem; }

.bg-plan {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: clamp(1.4rem, 2.6vw, 2rem);
  background: rgba(255, 255, 255, 0.012);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.25s, background-color 0.25s;
}
.bg-plan:hover,
.bg-plan:focus-visible {
  border-color: rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.03);
}
/* Le plan mis en avant se distingue par son filet, pas par une
   couleur : le site n'en a aucune. */
.bg-plan-fort { border-color: rgba(255, 255, 255, 0.34); }

.bg-eco {
  font-style: normal;
  margin-left: 0.5rem;
  color: var(--ink-title);
}
.bg-prix {
  display: block;
  margin-top: 0.9rem;
  font-size: calc(var(--font-scale) * 2.6rem);
  line-height: 1;
  font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--ink-title);
  font-variant-numeric: tabular-nums;
}
.bg-mois {
  font-size: calc(var(--font-scale) * 1rem);
  font-weight: 450;
  letter-spacing: 0;
  color: var(--ink-2);
}

.bg-liste {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0 0 0 0.9rem;
  border-left: 1px solid var(--rule);
}
.bg-liste li {
  font-size: calc(var(--font-scale) * 0.95rem);
  line-height: 1.5;
  color: var(--ink-2);
  margin-bottom: 0.24rem;
}

/* La porte du cadre. Soulignée au repos : c'est elle qu'on vient
   chercher, et le cadre entier est cliquable. */
.bg-porte {
  margin-top: auto;
  padding-top: 1.3rem;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255,255,255,0.42);
}
.bg-plan:hover .bg-porte { text-decoration-color: #fff; }

.bg-garantie { padding-bottom: clamp(2.6rem, 6vh, 4.4rem); }

/* ── LA FAQ ──────────────────────────────────────────────────────
   Des volets, comme « what's included » sur /dit. */
.bg-faq { padding-bottom: clamp(2.6rem, 6vh, 4.4rem); }
.bg-faq details { border-top: 1px solid var(--rule); }
.bg-faq details:last-child { border-bottom: 1px solid var(--rule); }
.bg-faq summary {
  list-style: none;
  cursor: pointer;
  padding: clamp(0.9rem, 2.2vh, 1.3rem) 0;
  font-size: calc(var(--font-scale) * 1.1rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  color: var(--ink-title);
  transition: color 0.2s;
}
.bg-faq summary::-webkit-details-marker { display: none; }
.bg-faq summary:hover { color: var(--ink-2); }
.bg-faq details[open] summary { color: var(--ink-2); }
.bg-faq details .row-body { padding: 0 0 clamp(1rem, 2.4vh, 1.5rem); }

/* ── LA SORTIE ───────────────────────────────────────────────────
   Même geste que « Let's work » sur Services. */
.bg-fin { align-items: start; padding-bottom: clamp(4rem, 10vh, 7rem); }
.bg-cta {
  display: inline-block;
  margin-top: 0.4rem;
  font-size: calc(var(--font-scale) * 1.9rem);
  line-height: 1.1;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255,255,255,0.34);
  transition: text-decoration-color 0.2s;
}
.bg-cta:hover { text-decoration-color: #fff; }

@media (max-width: 800px) {
  .bg-plan { margin-bottom: 1rem; }
  .bg-mods { grid-template-columns: minmax(0, 1fr); }
}


/* ── LA GALERIE (/works) ─────────────────────────────────────────
   Une planche-contact. On voit les sept projets d'un coup, on filtre,
   on ouvre celui qu'on veut en plein écran.

   Elle vit sur du papier noir : les affiches de film y perdent leur
   bord, et l'œil ne compte plus sept rectangles mais sept images. */

.galerie-page {
  padding-top: clamp(6rem, 13vh, 9rem);
  padding-bottom: clamp(4rem, 10vh, 7rem);
}

.galerie-tete { padding-bottom: clamp(1.4rem, 3.5vh, 2.4rem); }
.galerie-tete .num { display: block; margin-bottom: clamp(0.8rem, 2vh, 1.3rem); }

/* Les catégories, en ligne. Celle qui est active porte un trait
   court sous elle — le même geste que sur l'accueil. */
.galerie-filtres {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.1rem, 2.6vw, 2rem);
  row-gap: 0.6rem;
}
.galerie-filtres button {
  appearance: none; background: none; border: 0; padding: 0 0 0.3rem; margin: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.15rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  white-space: nowrap;
  color: var(--ink-3);
  position: relative;
  transition: color 0.2s;
}
.galerie-filtres button:hover { color: var(--ink); }
.galerie-filtres button[aria-pressed="true"] { color: var(--ink-title); }
.galerie-filtres button::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.28s ease;
}
.galerie-filtres button[aria-pressed="true"]::after { transform: scaleX(1); }

/* La planche. Trois de front sur un écran large : assez pour comparer,
   assez grand pour reconnaître un plan. */
.galerie {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: clamp(2rem, 5vh, 3.4rem);
  padding: 0 var(--pad);
}

.vignette {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  display: block;
  width: 100%;
  text-align: left;
  font-family: inherit;
  color: inherit;
}

/* L'affiche. Elle s'éclaircit au survol plutôt que de bouger : un
   déplacement, sur sept vignettes, ferait sauter la planche entière. */
.vignette-plan {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--media-ground) center / cover no-repeat;
  filter: brightness(0.82);
  transition: filter 0.32s ease;
}
.vignette:hover .vignette-plan,
.vignette:focus-visible .vignette-plan { filter: brightness(1); }

.vignette .vignette-num  { display: block; margin-top: 0.85rem; }
.vignette .work-title    { display: block; margin-top: 0.18rem; }
.vignette .caption       { display: block; margin-top: 0.1rem; }
.vignette:hover .work-title { color: var(--accent); }

@media (max-width: 1000px) { .galerie { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .galerie { grid-template-columns: minmax(0, 1fr); } }


/* ── LE PLEIN ÉCRAN D'UN PROJET ──────────────────────────────────
   Le film prend tout, le cartel se pose dessus en bas à gauche, les
   crédits à sa droite. C'est exactement la grammaire de l'accueil :
   on ouvre un projet, on ne change pas de site. */

.vue {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--media-ground);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.32s ease, visibility 0s linear 0.32s;
}
.vue[data-open] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.32s ease, visibility 0s;
}
html.vue-ouverte { overflow: hidden; }

.vue-plan {
  position: absolute;
  inset: 0;
  background: var(--media-ground) center / cover no-repeat;
  background-image: var(--affiche);
}
.vue-plan video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Les deux voiles de l'accueil, repris tels quels : deux dégradés
   courts, en haut et en bas, qui tiennent le contraste sans
   assombrir l'image au centre. */
.vue-plan::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(0,0,0,0.46) 0%, transparent 26%),
    linear-gradient(to top,    rgba(0,0,0,0.62) 0%, transparent 34%);
}

.vue-info {
  position: absolute;
  left: var(--pad); right: var(--pad);
  bottom: calc(clamp(1.4rem, 4vh, 3rem) + env(safe-area-inset-bottom, 0px));
  z-index: 3;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 2.4rem;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.vue-cap .num        { color: rgba(255,255,255,0.72); }
.vue-cap .work-title { display: block; margin-top: 0.2rem; }
.vue-cap .caption    { display: block; color: rgba(255,255,255,0.82); }

/* Une seule colonne de crédits, et de la place pour elle. Le générique
   est en deux colonnes sur l'accueil, où il s'ouvre à la demande et
   prend l'écran ; ici il partage le bas avec le cartel, et deux
   colonnes de cent vingt pixels cassaient chaque nom en deux lignes. */
.vue-credits { width: min(26rem, 44%); }
.vue-credits .panel-grid { grid-template-columns: minmax(0, 1fr); }
.vue-credits .panel-grid > * + * { margin-top: 0; }
.vue-credits .credits div     { color: rgba(255,255,255,0.9); }
.vue-credits .credits dt-role { color: rgba(255,255,255,0.55); }

/* Sur un écran large, le générique est là, à droite du cartel : il n'y
   a rien à cacher et le bouton n'a pas lieu d'être. */
.vue-toggle { display: none; }

/* Sous mille pixels, les crédits passent sous le cartel — côte à côte
   ils se disputeraient une largeur qu'aucun des deux n'a — et ils
   passent DERRIÈRE un bouton : déployés, ils couvraient la moitié du
   plan qu'on venait d'ouvrir pour le voir. */
@media (max-width: 1000px) {
  .vue-info { flex-direction: column; align-items: stretch; gap: 0.9rem; }
  .vue-credits { width: 100%; }

  .vue-toggle { display: inline-block; margin-top: 0.7rem; }
  .vue-credits { display: none; }
  .vue-credits[data-open] { display: block; }
}



/* ── LA PAGE PAPIER, EN NOIR ─────────────────────────────────────
   Les portraits sont photographiés sur fond noir. Sur du papier clair
   on voyait le bord de chaque image ; sur du noir il disparaît, et le
   cadrage redevient une décision du studio plutôt qu'un accident de
   fichier.

   Même mécanique que le volet de section : on retourne les jetons, et
   tout ce qui s'appuie dessus — titres, corps, filets, cartels, logo —
   bascule d'un coup. Pas une règle de ces pages n'a eu à être
   réécrite.

   Ce n'est PAS body.veiled, et c'est voulu : celui-là est fait pour un
   film qui bouge dessous, avec ombre portée et centrage automatique.
   Ici le fond est plein, le texte n'a rien à combattre, et la page
   défile sur toute sa longueur. */
body.papier-noir {
  --paper:     #08080A;
  --paper-alt: rgba(255,255,255,0.05);
  --ink:       #F2F0EA;
  --ink-title: #FFFFFF;
  --ink-2:     rgba(255,255,255,0.74);
  --ink-3:     rgba(255,255,255,0.46);
  --rule:      rgba(255,255,255,0.18);
  --accent:    #FFFFFF;
  --logo-invert: invert(1);

  background: var(--paper);
  color: var(--ink);
}

/* Le noir et blanc des portraits perd son sens sur fond noir : c'est
   le fond lui-même qui fait l'unité. On leur rend leurs couleurs, et
   le survol n'a plus rien à révéler. */
body.papier-noir .portrait {
  filter: none;
  background: transparent;
}

/* ── UNE PAGE PAPIER QUI TIENT SUR UN ÉCRAN ──────────────────────
   Même mécanique que l'ouverture ci-dessous, mais appliquée à la page
   entière : ce qu'elle a à dire tient dans l'écran, et rien n'oblige
   à défiler pour l'atteindre. « safe » pour la même raison — un écran
   trop court doit rendre la main au défilement plutôt que de rogner
   par le haut. */
.page-plein {
  min-height: 100svh;
  display: grid;
  align-content: safe center;
  padding-block: clamp(5rem, 11vh, 8rem) clamp(5rem, 13vh, 9rem);
}
.page-plein > .statement { padding-bottom: clamp(2.4rem, 6vh, 4.2rem); }

/* Deux blocs côte à côte, un filet chacun. */
.contact-duo { align-items: start; row-gap: clamp(2.4rem, 6vh, 3.6rem); }
.contact-duo .work-title { display: block; margin-top: 0.15rem; }
@media (max-width: 760px) {
  .contact-duo > * { grid-column: span 12; }
}

/* ── L'OUVERTURE D'UNE PAGE PAPIER ───────────────────────────────
   Les sept pages papier réservaient en dur la hauteur de l'ancienne
   navigation : leur première phrase se posait à cent soixante pixels
   du haut et laissait tout le bas de l'écran vide. Elle occupe
   maintenant l'écran entier et s'y centre.

   Le centre optique est un peu au-dessus du centre géométrique : c'est
   la règle du site, ici comme sur l'accès client. Le rembourrage du
   bas est donc plus grand que celui du haut — c'est lui qui remonte
   le bloc.

   « safe center » plutôt que « center » : sur un écran plus court que
   la phrase, un centrage ordinaire rogne par le haut, et ce qui est
   rogné n'est atteignable par aucun défilement. Là où le mot-clé n'est
   pas compris, la règle entière tombe et l'on retrouve un alignement
   en haut, qui est exactement le comportement d'avant. */
.statement-open {
  min-height: 100svh;
  align-content: safe center;
  padding-block: clamp(5rem, 11vh, 8rem) clamp(6rem, 15vh, 11rem);
}

/* ── LA BARRE PARTAGÉE ───────────────────────────────────────────
   La même sur les douze pages : « Menu » à gauche, le logo au milieu,
   le drapeau et l'accès client à droite. Elle est fixe, sans fond, et
   ses éléments prennent leur couleur des jetons : sur un plan, la
   classe « sur-plan » les passe au blanc.

   Le logo est posé au milieu de la PAGE et non de l'espace restant :
   il est sorti du flux, sans quoi la largeur de « Menu » le
   décalerait. */

.bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: clamp(1.1rem, 2.6vh, 1.7rem) var(--pad);
  pointer-events: none;
}
.bar > * { pointer-events: auto; }

/* ── LA BARRE QUAND LA PAGE DÉFILE ────────────────────────────────
   Une barre fixe et sans fond pose un problème dès que la page est
   longue : le texte lui passe dessous et les deux se superposent.
   La réponse habituelle est de lui donner un fond — c'est justement
   ce qu'on ne veut nulle part ici.

   Alors elle s'efface. On descend : elle se retire vers le haut, et
   on lit sans rien au-dessus. On remonte d'un cran : elle revient,
   parce que remonter, c'est déjà chercher à sortir. En haut de page
   elle reste, toujours.

   C'est écrit pour toutes les pages, mais seules celles qui défilent
   le verront : ailleurs, aucun défilement ne déclenche rien. */
.bar {
  transition: transform 0.34s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s linear;
}
body.bar-cachee .bar {
  transform: translateY(-115%);
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .bar { transition: none; }
}

.bar-logo {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  line-height: 0;
}
.bar-logo img {
  /* Huit pour cent de plus : depuis que « Menu » et « Client Access »
     sont devenus deux marques, le logo est seul à porter du texte dans
     la barre, et il peut le porter plus haut. */
  height: calc(clamp(2.2rem, 3.2vw, 3rem) * 1.08);
  width: auto;
  filter: var(--logo-invert);
}

.bar-menu,
.bar-client {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  letter-spacing: -0.008em;
  color: var(--ink);
  transition: color 0.2s;
}

/* ── DEUX ICÔNES ET UN LOGO ──────────────────────────────────────
   « Menu » à gauche et « Client Access » à droite étaient deux mots,
   et le second changeait de longueur d'une langue à l'autre — le logo
   ne tenait jamais tout à fait le milieu. Deux marques de même poids
   l'encadrent maintenant, et la barre est symétrique dans les deux
   langues.

   Deux traits pour le menu, pas trois : au-dessus d'une barre qui ne
   porte qu'un logo et un compte, deux suffisent à dire « menu », et
   le troisième n'ajoute que du poids.

   Les mots ne sont pas supprimés mais masqués à l'œil seul : les
   boutons gardent un nom pour qui les écoute, et ce nom suit la
   langue — ce qu'un aria-label figé dans le balisage ne saurait pas
   faire. */
.bar-menu, .bar-client { display: inline-flex; align-items: center; }

.bar-menu-icon,
.bar-client-icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.bar-client-icon circle:first-of-type { fill: none; }

.bar-menu-mot,
.bar-client-mot,
.menu-close-mot {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* La croix du menu répond à l'icône qui l'a ouvert : un mot d'un côté
   et une marque de l'autre se seraient regardés en chiens de faïence. */
.menu-close-icon {
  display: block;
  width: 1.45rem;
  height: 1.45rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}
.bar-right { display: flex; align-items: center; gap: clamp(1.2rem, 2.4vw, 2.2rem); }

/* ── L'ACCÈS CLIENT EN PETIT ÉCRAN ───────────────────────────────
   « Client Access » et « Espace client » sont longs, et sur un
   téléphone ils poussent le logo hors du milieu. Le mot cède la place
   à un cadenas, qui dit la même chose sans rien pousser.

   Le buste dans son cercle, celui que tout le monde lit comme « mon
   compte ». Ni cadenas, qui parle de serrure et non d'entrée, ni
   chevron entrant, qui est une flèche et le site n'en a aucune.

   Le lien mène directement à Arcane Delivery. Il a existé un temps
   une page /client/ entre les deux, avec ses champs : ils ne
   pouvaient pas fonctionner depuis ce domaine, et une page qui ne
   fait qu'annoncer la suivante est une page de trop. 

   Le mot n'est pas supprimé mais masqué à l'œil seul : le lien garde
   un nom pour qui l'écoute, et ce nom suit la langue, ce qu'un
   aria-label figé dans le balisage ne saurait pas faire. */
.bar-client { display: inline-flex; align-items: center; }

@media (max-width: 640px) {
}
.bar-menu:hover, .bar-client:hover { color: var(--accent); }

/* Sur un plan, tout passe au blanc et prend une ombre courte : c'est
   la seule variante, et elle se pose par une classe sur le body. */
body.sur-plan .bar-menu,
body.sur-plan .bar-client { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.55); }
body.sur-plan .bar-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }
body.sur-plan .bar-menu:hover,
body.sur-plan .bar-client:hover { color: rgba(255,255,255,0.62); }
body.sur-plan .frame-lang svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.4); }

/* Sur /dit la barre change en chemin : elle passe d'une photo au
   papier. La transition n'est armée qu'après le premier calcul, sans
   quoi on la verrait basculer au chargement. */
body.bar-prete .bar-menu,
body.bar-prete .bar-client { transition: color 0.28s, text-shadow 0.28s; }
body.bar-prete .bar-logo img { transition: filter 0.28s; }


/* ── LE MENU ─────────────────────────────────────────────────────
   Il se déplie par-dessus la page, sur le même voile que les volets.
   Les entrées mènent aux PAGES : une règle sans exception vaut mieux
   qu'une règle avec, et c'était le problème du site jusqu'ici.      */

.menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(8, 8, 10, 0.72);
  backdrop-filter: blur(18px) saturate(110%);
  -webkit-backdrop-filter: blur(18px) saturate(110%);
  color: #fff;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
  /* Une colonne, et non un centrage par justify-content : onze entrées
     et un drapeau ne tiennent pas sur un portable court, et un contenu
     centré qui déborde se fait rogner par le haut, sans recours. Les
     marges automatiques centrent aussi bien et cèdent quand il le
     faut : le menu devient alors défilant, ce qu'il doit être. */
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.menu:focus { outline: none; }
.menu[data-open] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.3s ease, visibility 0s;
}
html.menu-ouvert { overflow: hidden; }

.menu-close {
  position: fixed;
  top: clamp(1.1rem, 2.6vh, 1.7rem);
  left: var(--pad);
  z-index: 210;
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  color: #fff;
  transition: color 0.2s;
}
.menu-close:hover { color: rgba(255,255,255,0.62); }

.menu-list {
  list-style: none;
  margin: auto;               /* centré, mais qui cède au lieu de rogner */
  margin: auto;               /* voir .menu : centre, mais cède */
  padding: clamp(4rem, 11vh, 7rem) var(--pad) clamp(3rem, 8vh, 5rem);
  text-align: center;
}
.menu-list li { margin: 0; }

/* Les entrées se lèvent l'une après l'autre : le décalage est court,
   il donne l'ordre de lecture sans faire attendre. */
.menu-item {
  display: inline-block;
  padding: clamp(0.28rem, 0.9vh, 0.5rem) 0;
  font-size: calc(var(--font-scale) * 2.6rem);
  line-height: 1.12;
  letter-spacing: -0.026em;
  font-weight: 500;
  color: rgba(255,255,255,0.92);
  transition: color 0.2s, opacity 0.4s ease, transform 0.4s ease;
  opacity: 0;
  transform: translateY(0.6rem);
}
.menu-item:hover { color: #fff; }
/* LE SECOND RANG. Il portait 1,15 rem à 60 % de blanc, et « Material
   request » y avait l'air de s'excuser d'exister. C'est pourtant une
   demande commerciale, au même titre qu'un contact. Il monte donc à
   1,5 rem et à 82 %, assez pour qu'on le voie sans qu'il dispute la
   place aux six pages. */
.menu-item--rang2 {
  font-size: calc(var(--font-scale) * 1.5rem);
  color: rgba(255,255,255,0.82);
  letter-spacing: -0.01em;
  font-weight: 450;
}
.menu-item--rang2:hover { color: #fff; }

/* La mention qui suit le nom d'un logiciel. En mono capitales, comme
   tous les libellés techniques du site, et dimensionnée en em : elle
   suit le rang de son entrée sans qu'on ait à la régler deux fois. */
/* La marque du logiciel, posée devant son nom. Elle est dimensionnée
   en em : elle suit le rang de son entrée, et la ligne reste alignée
   sur son milieu optique. */
.menu-mark--vide { visibility: hidden; }

.menu-mark {
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  border-radius: 0.24em;
  margin-right: 0.5em;
  vertical-align: -0.16em;

  /* Un cerne d'un pixel : les deux plaques sont sombres, le menu aussi,
     et sans lui la marque se dissout dans le fond au lieu de se lire
     comme une icône. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}
.menu-mark--vide { box-shadow: none; }

.menu-note {
  margin-left: 0.62em;
  font-family: var(--font-mono);
  font-size: 0.6em;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.42);
  transition: color 0.2s;
}
.menu-item:hover .menu-note { color: rgba(255,255,255,0.68); }

.menu[data-open] .menu-item { opacity: 1; transform: none; }

/* Le décalage d'apparition est calculé au rendu, entrée par entrée :
   avec onze lignes réparties en trois familles, une liste de règles
   nth-child se serait désaccordée au premier ajout. */

/* Trois familles : les pages, ce qu'on demande au studio, ce qu'il
   fabrique. Un filet ouvre chacune des deux dernières. */
/* LES DEUX FILETS. Ils étaient collés au nom du dessus et flottaient
   loin de celui du dessous : deux pixels d'un côté, quarante-deux de
   l'autre.

   Deux causes, l'une masquant l'autre. La marge haute n'a jamais été
   appliquée — « .menu-list li » la remettait à zéro, et une classe
   seule ne pèse pas contre une classe suivie d'un type. D'où le
   sélecteur allongé ici. Et le vide sous chaque nom est plus grand
   qu'il n'en a l'air : l'entrée est un inline-block, la ligne lui
   ajoute le talon de sa police, et ce talon compte. Le rembourrage
   haut du filet devait donc être petit, pas grand, à l'inverse de
   l'intuition.

   Les deux distances sont maintenant égales à l'œil, mesurées d'une
   ligne de base au haut des capitales suivantes. */
.menu-list .menu-line--coupe {
  margin-top: clamp(1.2rem, 3.2vh, 1.7rem);
  padding-top: clamp(0.25rem, 0.7vh, 0.45rem);
  border-top: 1px solid rgba(255,255,255,0.16);
}

/* LA MESURE DU MENU. Les filets suivaient la plus longue entrée, si
   bien qu'ils changeaient de longueur d'une langue à l'autre :
   « Filmography » et « Filmographies » ne font pas la même largeur, et
   le trait sautait sous les yeux au changement de langue. Une mesure
   fixe le tient. Elle est aussi ce qui donne au menu sa colonne. */
.menu-list {
  width: min(28rem, 100%);
}

/* LE PIED DU MENU. Il tient le bas de la colonne, dans la mesure de la
   liste, et sépare le studio du légal aux deux bords. C'est la seule
   porte vers les mentions légales depuis la disparition du bandeau. */
.menu-foot {
  /* La mesure retire elle-même les marges de page : un rembourrage
     intérieur en plus faisait passer le légal à la ligne, à trois
     pixels près. */
  width: min(28rem, calc(100% - 2 * var(--pad)));
  margin: 0 auto clamp(1.4rem, 4vh, 2.6rem);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.4rem 1.4rem;
  color: rgba(255,255,255,0.42);
  opacity: 0;
  transform: translateY(0.4rem);
  transition: opacity 0.4s ease 0.42s, transform 0.4s ease 0.42s;
}
.menu[data-open] .menu-foot { opacity: 1; transform: none; }

.menu-foot-legal { display: flex; gap: 1rem; }
.menu-foot a {
  color: inherit;
  text-decoration: none;
  transition: color 0.2s;
}
.menu-foot a:hover { color: rgba(255,255,255,0.85); }

/* Le drapeau, en bas du menu. C'est un réglage qu'on fait une fois :
   il n'a rien à faire en permanence dans la barre. */
/* LE DRAPEAU EN HAUT À DROITE. Il faisait le pied du menu, et c'était
   sa faiblesse : posé sous une liste centrée, il se lisait comme une
   onzième entrée, et il fallait le pousser hors du flux pour qu'il
   tienne le bas — d'où le chevauchement quand la liste défilait.

   En haut à droite, il n'est plus une entrée : il devient ce qu'il est,
   un réglage. Il répond à « Close » en haut à gauche, et il tombe très
   exactement là où la barre pose l'accès client, à la même hauteur.
   Le menu n'a plus qu'une seule colonne à lire. */
.menu-lang {
  position: fixed;
  top: clamp(1.1rem, 2.6vh, 1.7rem);
  right: var(--pad);
  z-index: 210;
}
/* display:block est indispensable : .frame-lang est un <span>, donc
   en ligne. Ailleurs, position:fixed le rendait bloc et sa largeur
   s'appliquait ; remis en statique, il retombait en ligne et sa boîte
   valait zéro. Le drapeau existait mais ne mesurait rien. */
.menu-lang .frame-lang {
  position: static;
  display: block;
  top: auto; right: auto;
  width: 30px;
  height: 15px;
}
.menu-lang svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.35); }
.menu-lang button:hover svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.85); }

@media (prefers-reduced-motion: reduce) {
  .menu-item { transition: color 0.2s; opacity: 1; transform: none; }
}


/* ── LE CADRE ────────────────────────────────────────────────────
   Quatre coins et un logo au milieu, tous fixes. Le contenu défile
   dessous sans jamais les croiser : la colonne fait 46 rem et se
   centre, les coins sont contre les bords. Sur un écran de bureau il
   reste plus de trois cents pixels de chaque côté, ils ne se
   rencontrent donc jamais.

   Le conteneur ne prend pas les clics, seuls ses enfants le font :
   sans quoi une plaque transparente couvrirait la page entière.    */

.frame {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
}
.frame > * { pointer-events: auto; position: fixed; }

/* Sur une page de papier, seuls le retour et le drapeau sont repris :
   pas de logo au centre ni de coins en bas, ces pages ayant déjà leur
   pied de page. */

/* Sur Services et About, le cadre porte aussi le logo : ces pages
   n'ont plus de barre du tout. Leur première section doit donc lui
   laisser la place. */
.frame--light ~ main > .statement:first-child,
.frame--light ~ .statement:first-of-type { padding-top: clamp(7rem, 15vh, 10rem); }

/* Sur /dit, les deux vivent DANS la barre plutôt que dans un cadre
   fixe : la page a déjà son logo à gauche, et deux chromes superposés
   se seraient marché dessus. */
.frame-back--inline,
.frame-lang--inline { position: static; top: auto; left: auto; right: auto; }
.frame-lang--inline { display: inline-block; }

/* La barre de /dit en trois zones. Le logo est posé au MILIEU DE LA
   PAGE et non au milieu de l'espace restant : il est donc sorti du
   flux, sans quoi la largeur du retour à gauche le décalerait vers la
   droite, et il bougerait au survol quand le mot se déplie. */
.dit-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: var(--pad);
  padding-right: var(--pad);
}
.dit-bar-logo {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  line-height: 0;
}
.dit-bar-logo img {
  height: clamp(2.2rem, 3.2vw, 3rem);
  width: auto;
  filter: var(--logo-invert);
}
.pin.on-media .dit-bar-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }
.pin.ready .dit-bar-logo img { transition: filter 0.28s; }
.dit-bar .frame-back--inline { font-size: calc(var(--font-scale) * 1.2rem); }

/* Tant que la photo passe sous la barre, tout y devient blanc : une
   barre d'une seule couleur disparaîtrait sur la photo ou sur le
   papier, jamais sur les deux. */
.pin.on-media .frame-back--inline { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.55); }
.pin.on-media .frame-lang--inline svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.5); }
.pin.ready .frame-back--inline { transition: color 0.28s, text-shadow 0.28s; }

.frame-logo {
  top: clamp(1.4rem, 3.5vh, 2.4rem);
  left: 50%;
  transform: translateX(-50%);
  line-height: 0;
}
.frame-logo img {
  height: clamp(2.2rem, 3.2vw, 3rem);
  width: auto;
  filter: var(--logo-invert);
}
body.veiled .frame-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }

/* LE RETOUR.
   Au repos, un seul chevron. La souris approche, le mot se déplie à
   sa droite : une largeur qui passe de zéro à sa mesure, doublée d'un
   fondu. Le mot est là dans le document même quand il ne se voit pas,
   donc un lecteur d'écran et le clavier le trouvent. */
/* Le cadre sert désormais sur le papier comme sur un plan : sa couleur
   vient donc des jetons, et c'est body.veiled qui la force au blanc
   quand il y a une image derrière. */
.frame-back {
  top: clamp(1.4rem, 3.5vh, 2.4rem);
  left: var(--pad);
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
  font-size: calc(var(--font-scale) * 1.2rem);
  font-weight: 500;
  color: var(--ink);
  transition: color 0.2s;
}
body.veiled .frame-back { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.frame-chev { display: inline-block; }
.frame-word {
  display: inline-block;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
              opacity 0.24s ease,
              padding-left 0.34s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.frame-back:hover .frame-word,
.frame-back:focus-visible .frame-word {
  max-width: 14rem;
  opacity: 1;
  padding-left: 0.45rem;
}
/* Pas de changement de couleur au survol : c'est le mot qui apparaît
   qui signale le survol, et il le fait déjà largement. */

@media (prefers-reduced-motion: reduce) {
  .frame-word { transition: none; }
}

/* UN SEUL DRAPEAU, QUI SE RETOURNE.
   Celui qu'on voit est la langue vers laquelle on IRAIT : le cliquer y
   mène, et le carton se retourne pour montrer l'autre, qui devient à
   son tour la destination.

   Les deux boutons restent dans le document : ce sont les deux faces
   du même carton, et le script continue de les trouver par leur
   identifiant, sans qu'on ait eu à le toucher. C'est la classe « on »
   qu'il pose déjà qui décide du sens de la rotation, lue par :has().

   La face cachée ne prend pas les clics : backface-visibility suffit
   dans la plupart des navigateurs, pointer-events le garantit dans
   tous. */

.frame-lang {
  top: clamp(1.4rem, 3.5vh, 2.4rem);
  right: var(--pad);
  width: 24px;
  height: 12px;
  perspective: 700px;
}

.frame-lang-flip {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.55s cubic-bezier(0.4, 0.1, 0.2, 1);
}
/* Anglais actif : la face avant montre le drapeau français, la
   destination. Français actif : le carton a fait un demi-tour. */
.frame-lang-flip:has(#lang-fr.on) { transform: rotateY(180deg); }

.frame-lang button {
  position: absolute;
  inset: 0;
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  line-height: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
#lang-fr { transform: rotateY(0deg); }
#lang-en { transform: rotateY(180deg); }

.frame-lang-flip:has(#lang-fr.on) #lang-fr { pointer-events: none; }
.frame-lang-flip:not(:has(#lang-fr.on)) #lang-en { pointer-events: none; }

.frame-lang svg {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 1px;
  box-shadow: 0 0 0 1px var(--rule);
}
.frame-lang button:hover svg { box-shadow: 0 0 0 1px var(--ink-3); }
body.veiled .frame-lang svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.35); }
body.veiled .frame-lang button:hover svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.85); }

@media (prefers-reduced-motion: reduce) {
  .frame-lang-flip { transition: none; }
}

/* Les deux coins du bas. */
/* Le drapeau du volet, en face de la croix. Il est dans le volet et
   non dans le cadre : il n'existe que tant qu'un contenu traduit est
   affiché. */
.sheet-lang {
  position: fixed;
  top: clamp(0.9rem, 2.2vh, 1.6rem);
  left: var(--pad);
  right: auto;
  z-index: 210;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}
.sheet-lang svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.35); }
.sheet-lang button:hover svg { box-shadow: 0 0 0 1px rgba(255,255,255,0.85); }

.frame-corner {
  bottom: clamp(1.2rem, 3vh, 2rem);
  font-size: calc(var(--font-scale) * 0.8rem);
  line-height: 1.4;
  color: var(--ink-3);
}
body.veiled .frame-corner {
  color: rgba(255,255,255,0.6);
  text-shadow: 0 1px 3px rgba(0,0,0,0.6);
}
.frame-corner a:hover { color: var(--ink); }
body.veiled .frame-corner a:hover { color: #fff; }
.frame-bl { left: var(--pad); }
.frame-br { right: var(--pad); text-align: right; }
.frame-corner a { color: inherit; transition: color 0.18s; }


/* ── UNE PAGE DE LECTURE POSÉE SUR UN PLAN ───────────────────────
   C'est le traitement de l'accès client, étendu à une page qui défile.
   Le plan et son voile sont fixes, le texte passe devant.

   Aucun style de formulaire n'a eu à être réécrit pour cela : la page
   RETOURNE LES JETONS, comme le fait le volet de section. Encre,
   papier, filets, les trois niveaux de gris — tout ce qui s'appuie
   dessus bascule d'un coup en clair sur sombre. C'est le seul intérêt
   d'avoir fait passer toute la charte par des variables.            */

body.veiled {
  --paper:     transparent;
  --paper-alt: rgba(255,255,255,0.07);
  --ink:       #FFFFFF;
  --ink-title: rgba(255,255,255,0.94);
  --ink-2:     rgba(255,255,255,0.76);
  --ink-3:     rgba(255,255,255,0.52);
  --rule:      rgba(255,255,255,0.26);
  --accent:    #FFFFFF;
  --logo-invert: invert(1);

  background: var(--media-ground);
  color: var(--ink);
}

/* Sans fond garanti sous lui, le texte se défend par une ombre courte,
   comme le cartel d'un plan. */
body.veiled .statement,
body.veiled .request-body,
body.veiled .foot,
body.veiled .pin { text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); }

body.veiled .pin-logo img { filter: invert(1) drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }
body.veiled .pin::before { display: none; }

/* Les champs de saisie ne suivent PAS l'inversion : un navigateur
   dessine leur texte avec sa propre couleur si on ne la lui impose
   pas, et le remplissage automatique repeint le fond en jaune. */
body.veiled .form-input,
body.veiled .form-select,
body.veiled .form-textarea { color: #fff; text-shadow: none; }
body.veiled .form-select option { color: var(--media-ground); background: #fff; }
body.veiled .form-input:-webkit-autofill,
body.veiled .form-textarea:-webkit-autofill {
  -webkit-text-fill-color: #fff;
  transition: background-color 9999s;
}

/* L'icône de calendrier des champs de date est dessinée en noir par le
   navigateur : sur un fond sombre elle disparaît. */
body.veiled input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.55;
  cursor: pointer;
}
body.veiled input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* La zone de dépôt s'éclaire au survol au lieu de s'assombrir. */
body.veiled .upload-zone:hover,
body.veiled .upload-zone.drag { background: rgba(255,255,255,0.07); }


/* ── LA DEMANDE DE MATÉRIEL ──────────────────────────────────────
   Les noms de classes sont ceux de la page d'origine, gardés tels
   quels : son script les cherche par leur nom, et renommer pour faire
   joli aurait cassé un envoi de certificats sans qu'on s'en aperçoive.
   C'est donc la feuille de style, et elle seule, qui a changé.

   Le parcours reste en quatre volets : ce qu'il vous faut, la demande,
   qui vous êtes, le récapitulatif. Un seul est à l'écran à la fois,
   ce qui est la seule façon de faire tenir un formulaire de vingt
   champs sans écraser personne.                                    */

/* LA COLONNE EST AU MILIEU.
   Un formulaire collé au bord gauche laisse les deux tiers de l'écran
   vides à sa droite : on lit de travers, et la page a l'air inachevée.
   Au milieu, il n'y a plus qu'une chose à regarder.

   L'énoncé partage la même colonne, sinon l'un serait centré et
   l'autre contre le bord. Le texte, lui, reste aligné à gauche :
   des intitulés de champs centrés seraient illisibles. */
.request-intro,
.request-body > .pane {
  width: 100%;
  max-width: 46rem;
  margin-inline: auto;
}

/* LE TEXTE SE CENTRE EN HAUTEUR.
   Le premier volet ne fait que quelques lignes : tassé en haut, il
   laissait la moitié basse de l'écran vide et la page avait l'air
   inachevée. Les marges automatiques répartissent le vide au-dessus
   et au-dessous, à parts égales.

   Elles ne rognent rien : quand le contenu est plus haut que la
   fenêtre, comme le formulaire DCP, une marge automatique vaut zéro
   et la page défile normalement. C'est ce que « justify-content:
   center » n'aurait pas fait, lui qui coupe le haut.

   Les deux réserves, en haut et en bas, sont de la même mesure : la
   première dégage le logo, la seconde les deux coins du cadre, et
   leur égalité fait que le texte visible tombe au milieu et non un
   cran plus bas. */
body.veiled {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

/* LE CENTRE OPTIQUE, PAS LE CENTRE GÉOMÉTRIQUE.
   Un bloc posé au milieu exact d'un écran paraît trop bas : l'œil
   place le centre d'une composition plus haut que ne le fait la règle.
   La réserve du bas est donc plus large que celle du haut, ce qui
   remonte le texte d'environ trois pour cent de la hauteur. Le partage
   tombe autour de 46 / 54, qui est la mesure des typographes pour un
   titre dans une page.

   Cela sert aussi la lecture : le regard descend du logo au texte puis
   aux deux coins du bas, et un bloc légèrement haut laisse cette
   descente se faire. */
/* La mesure comprend ses marges intérieures, et non l'inverse.
   « content-box » avait été posé pour que 46rem compte le texte seul ;
   mais la règle du dessus donne aussi « width: 100% », et cent pour
   cent de contenu PLUS deux fois la marge de page débordait de trente-
   six pixels très exactement. Sur un téléphone, la page partait de
   travers. La mesure inclut donc la marge, et on la lui rend. */
.request-intro {
  margin-top: auto;
  padding: clamp(6rem, 12vh, 8rem) var(--pad) clamp(2.6rem, 7vh, 5rem);
  max-width: calc(46rem + 2 * var(--pad));
}

.request-body {
  margin-bottom: auto;
  padding: 0 var(--pad) clamp(8rem, 18vh, 13rem);
}

/* Cette page est un <main> sur un corps « veiled », et elle héritait
   donc de « body.veiled > main { margin-block: auto } », écrit pour
   Services et About. Une marge haute automatique de plus, et le vide
   libre se partageait en TROIS au lieu de deux : entre l'introduction
   et les choix s'ouvrait un écart aussi grand que celui du haut de
   page, comme si les deux blocs ne parlaient pas de la même chose.

   L'introduction porte déjà la marge haute ; celle-ci n'a rien à
   pousser. Le sélecteur est aussi précis que celui qu'il corrige,
   et il vient après. */
body.veiled > .request-body { margin-top: 0; }

.pane { display: none; }
.pane.active { display: block; }

/* Le choix d'entrée : screener ou DCP. Deux entrées, pas deux boîtes. */
.mode-label {
  display: block;
  margin: clamp(2rem, 5vh, 3.4rem) 0 1.4rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* Les deux entrées côte à côte, pas l'une sous l'autre : c'est un
   choix entre deux, et on choisit mieux ce qu'on voit ensemble. */
.mode-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.6rem, 3vw, 2.8rem);
  align-items: stretch;     /* les deux cadres font la même hauteur */
}
@media (max-width: 700px) {
  .mode-grid { grid-template-columns: minmax(0, 1fr); }
}

/* LES DEUX CHOIX SONT ENCADRÉS. C'est la seule boîte du site, et elle
   se justifie : ce ne sont pas deux paragraphes qu'on lit, ce sont
   deux cibles qu'on clique, et un contour dit où finit la cible mieux
   qu'aucun mot.

   Un filet d'un pixel à seize pour cent, un rayon de quatorze : assez
   pour exister sur l'image, trop peu pour peser. Au survol le filet
   se lève et le fond prend trois pour cent de blanc — rien ne bouge,
   c'est la densité seule qui répond. */
.mode-card {
  appearance: none; margin: 0; cursor: pointer;
  width: 100%; text-align: left;
  font-family: inherit;

  /* Une colonne explicite, et non le bloc par défaut : un <button>
     centre son contenu en hauteur, si bien que des deux cadres mis à
     la même hauteur, celui qui a le texte le plus long remontait son
     titre de neuf pixels. Les deux titres doivent partir du même
     trait. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;

  padding: clamp(1.3rem, 2.6vw, 1.9rem) clamp(1.4rem, 2.8vw, 2rem);
  background: rgba(255, 255, 255, 0.012);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  transition: border-color 0.25s, background-color 0.25s;
}
.mode-card:hover,
.mode-card:focus-visible {
  border-color: rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.03);
}

/* La description ne pousse plus le cadre : elle tient dans sa colonne. */
.mode-card .mode-card-desc { max-width: none; }
.mode-card-title {
  display: block;
  font-size: calc(var(--font-scale) * 1.6rem);
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--ink-title);
  transition: color 0.2s;
}
.mode-card:hover .mode-card-title { color: var(--accent); }
.mode-card-desc {
  display: block;
  margin-top: 0.4rem;
  max-width: 34rem;
  font-size: calc(var(--font-scale) * 0.95rem);
  line-height: 1.45;
  color: var(--ink-2);
}
/* Le retour d'un volet à l'autre. */
.back-btn {
  appearance: none; background: none; border: 0; padding: 0;
  margin: clamp(2rem, 5vh, 3.4rem) 0 1.6rem;
  cursor: pointer;
  display: inline-flex; gap: 0.5rem; align-items: baseline;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.18s;
}
.back-btn:hover { color: var(--accent); }

/* Les champs : un filet dessous, jamais une boîte. */
.form-section-title {
  margin-top: 2.6rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.78rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.form-group { margin-top: 1.5rem; }
.form-label {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.form-label.required::after { content: ' *'; color: var(--accent); }

.form-input, .form-select, .form-textarea {
  display: block;
  width: 100%;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.35rem 0;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.05rem);
  line-height: 1.45;
  color: var(--ink);
  resize: vertical;
}
.form-textarea { min-height: 6rem; }
.form-input::placeholder, .form-textarea::placeholder { color: var(--ink-3); }
.form-input:focus, .form-select:focus, .form-textarea:focus {
  outline: none;
  border-bottom-color: var(--ink);
}
/* Un select vide se lit comme un champ vide, pas comme une réponse. */
.form-select.placeholder { color: var(--ink-3); }
.field-error { border-bottom-color: var(--accent) !important; }

.form-hint, .form-note {
  display: block;
  margin-top: 0.5rem;
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.5;
  color: var(--ink-3);
}

.date-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gutter); }
.date-sublabel {
  display: block;
  margin-bottom: 0.2rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Le dépôt de certificats. Une zone, pas un bouton : on peut y glisser
   des fichiers, et le script le sait. */
.upload-zone {
  appearance: none; background: none; cursor: pointer;
  display: block; width: 100%; text-align: left;
  padding: 1.1rem 1.2rem;
  border: 1px dashed var(--rule);
  font-family: inherit;
  transition: border-color 0.2s, background-color 0.2s;
}
.upload-zone:hover, .upload-zone.drag {
  border-color: var(--accent);
  background: var(--paper-alt);
}
.upload-icon { display: block; font-size: 1.2rem; color: var(--ink-3); }
.upload-title {
  display: block; margin-top: 0.3rem;
  font-size: calc(var(--font-scale) * 1.05rem);
  color: var(--ink-title);
}
.upload-hint {
  display: block; margin-top: 0.25rem;
  font-size: calc(var(--font-scale) * 0.8rem);
  color: var(--ink-3);
}

.file-list:not(:empty) { margin-top: 0.9rem; }
.file-row {
  display: flex; align-items: baseline; gap: 0.7rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: calc(var(--font-scale) * 0.85rem);
  color: var(--ink-2);
}
.file-mark { color: var(--accent); }
.file-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.file-size { font-family: var(--font-mono); font-size: 0.9em; color: var(--ink-3); }
.file-remove {
  appearance: none; background: none; border: 0; padding: 0 0.2rem;
  cursor: pointer; line-height: 1;
  font-size: 1.1rem; color: var(--ink-3);
  transition: color 0.18s;
}
.file-remove:hover { color: var(--accent); }

/* Les actions et les erreurs. */
.form-submit-row { margin-top: 2.4rem; }
.btn-primary, .btn-ghost, .ticket-copy {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  display: inline-flex; gap: 0.5rem; align-items: baseline;
  font-family: inherit;
  font-size: calc(var(--font-scale) * 1.3rem);
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--ink-title);
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  transition: color 0.2s, text-decoration-color 0.2s;
}
.btn-primary:hover, .btn-ghost:hover, .ticket-copy:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}
.btn-primary:disabled { opacity: 0.5; cursor: default; }
.btn-ghost, .ticket-copy { font-size: calc(var(--font-scale) * 1.05rem); }

.form-error-note, .send-error {
  display: none;
  margin-top: 0.9rem;
  font-size: calc(var(--font-scale) * 0.9rem);
  line-height: 1.5;
  color: var(--accent);
}
.form-error-note.show, .send-error.show { display: block; }
.send-error a { color: var(--accent); text-decoration: underline; }

/* Le récapitulatif, avant l'envoi. La même grammaire que les crédits
   d'un film : rôle à gauche, valeur à droite. */
.recap { margin-top: 1.6rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
.recap-title {
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 0.5rem;
}
.recap-row { display: flex; gap: 0.9rem; padding: 0.25rem 0; font-size: calc(var(--font-scale) * 0.9rem); }
.recap-key { flex: 0 0 10rem; color: var(--ink-3); }
.recap-val { flex: 1 1 auto; min-width: 0; color: var(--ink-2); overflow-wrap: anywhere; }

/* Le volet final. C'est le seul moment de la page où l'on annonce
   quelque chose plutôt que de demander : il a droit à de l'espace. */
.done { padding-top: clamp(2.6rem, 7vh, 5rem); }
.done-mark { display: block; font-size: 1.6rem; color: var(--accent); }
.done-title {
  display: block;
  margin-top: 0.8rem;
  font-size: calc(var(--font-scale) * 2.1rem);
  line-height: 1.18;
  letter-spacing: -0.022em;
  font-weight: 450;
  color: var(--ink-title);
}
.done-sub { display: block; margin-top: 0.7rem; color: var(--ink-2); max-width: 34rem; line-height: 1.5; }
.done-detail { display: block; margin-top: 0.5rem; color: var(--ink-3); }
/* Deux actions côte à côte : sans écart elles se lisaient comme une
   seule phrase (« New request Back to site »). */
.done-actions {
  margin-top: 2rem;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.2rem, 3vw, 2.2rem);
  align-items: baseline;
}

.ticket-box { margin-top: 1.8rem; padding-left: 0.9rem; border-left: 1px solid var(--rule); }
.ticket-label {
  display: block;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.7rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ticket-num {
  display: block;
  margin: 0.2rem 0 0.6rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 1.6rem);
  letter-spacing: 0.02em;
  color: var(--ink-title);
}


/* ── PIED ────────────────────────────────────────────────────────── */

.foot { padding-top: 4rem; padding-bottom: 2.4rem; }
.foot .caption a { color: var(--ink-2); }
.foot .caption a:hover { color: var(--accent); }


/* ── SECTIONS ────────────────────────────────────────────────────── */

.sec { padding-top: clamp(4rem, 10vh, 9rem); }
.sec-label { padding-bottom: 1.4rem; }


/* ── MOBILE ──────────────────────────────────────────────────────
   Le template garde sa grille 4/8 sur mobile (mobile-stack="false"),
   ce qui écrase les vignettes. En 16/9 c'est intenable : ici on
   empile. C'est le seul endroit où l'on s'écarte franchement du
   modèle, et c'est pour la même raison qu'on l'aurait fait chez eux. */

@media (max-width: 800px) {
  :root {
    --base-size: 13px;
    --pad: 1.4rem;
    --gutter: 1rem;
    /* La barre n'est plus fixe sur mobile : les pages n'ont donc plus
       à lui réserver de hauteur sous elle. */
    --nav-h: 0rem;
  }

  .cs { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .cu-1, .cu-2, .cu-3, .cu-4, .cu-5, .cu-6,
  .cu-7, .cu-8, .cu-12 { grid-column: span 6; }
  .start-5, .start-9 { grid-column: span 6; }

  /* L'accueil garde l'écran entier du téléphone : c'est le plan qui
     doit accueillir, pas la mise en page. */
  .hero { height: 100svh; min-height: 22rem; }
  .hero-cap { max-width: none; }

  /* Le logo tient seul le milieu de la barre, sans rien à côté de
     lui : dix pour cent de plus lui rendent sa présence. */
  .bar-logo img { height: calc(clamp(2.2rem, 3.2vw, 3rem) * 1.155); }

  /* SOUS LA BARRE, DE L'AIR. La première phrase d'une page sur film
     réservait 7vh, soit cinquante-neuf pixels sur un téléphone — et
     la barre en fait soixante-trois. Le texte commençait donc très
     exactement où elle finit, sans un souffle entre les deux. */
  body.veiled > main > .statement:first-child {
    padding-top: clamp(6.4rem, 13vh, 9.6rem);
  }

  /* LA DEMANDE DE MATÉRIEL, SUR UN TÉLÉPHONE.

     Le texte et les deux choix parlent de la même chose, et cent
     quarante-huit pixels les séparaient : trois espacements empilés —
     le bas de l'introduction, le haut du corps, le haut de l'intitulé.
     Aucun n'est fautif seul ; c'est leur somme qui coupait la page en
     deux moitiés sans rapport. On les resserre tous les trois.

     Et les deux cartes prenaient un quart de l'écran chacune. Le titre
     descend d'un cran, les marges intérieures se resserrent : elles
     restent deux cibles franches sans occuper la page. */
  .request-intro {
    padding-top: clamp(4rem, 8.5vh, 6rem);
    padding-bottom: clamp(1.4rem, 3vh, 2.2rem);
  }
  .mode-label    { margin-top: clamp(1rem, 2.4vh, 1.6rem); }

  /* La réserve du bas est taillée pour le formulaire DCP, qui est
     long ; sur le premier volet, qui tient en deux cartes, elle ne
     réservait que du vide et poussait tout le bloc vers le haut. */
  .request-body { padding-bottom: clamp(4rem, 10vh, 7rem); }

  .mode-grid { gap: 1rem; }
  .mode-card {
    padding: clamp(0.95rem, 3.4vw, 1.3rem) clamp(1rem, 3.6vw, 1.4rem);
    border-radius: 12px;
  }
  .mode-card-title { font-size: calc(var(--font-scale) * 1.28rem); }

  /* La phrase d'ouverture de Services se détachait de soixante-dix-neuf
     pixels du premier service. Sur un écran large, cette respiration
     sépare une accroche de quatre colonnes ; sur un téléphone, où tout
     est empilé, elle coupait simplement la lecture en deux. */
  [data-svc-grid] > .statement {
    padding-bottom: clamp(1.8rem, 4vh, 3rem);
  }

  /* LES CATÉGORIES SUR UN TÉLÉPHONE. En une seule ligne elles ne
     tiennent pas, et la ligne se cassait AU MILIEU des intitulés :
     « Feature / Film », « Music / Video ». Chaque intitulé reste
     entier, la rangée passe à la ligne entre eux, et elle se centre.

     Elle descend aussi un peu : deux lignes de catégories venaient
     mordre le bord de l'écran. */
  .hero-nav {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 0.5rem;
    max-width: calc(100% - 2 * var(--pad));
    bottom: calc(clamp(1.4rem, 3.2vh, 2.4rem) + env(safe-area-inset-bottom, 0px));
  }
  .hero-nav a, .hero-nav button { white-space: nowrap; }

  /* Le cartel descend, mais pas jusqu'au bord : trente-quatre pixels
     collaient la barre de progression au bas de l'écran. */
  .hero-info {
    bottom: calc(clamp(2.2rem, 5.2vh, 3.2rem) + var(--hero-foot)
                 + env(safe-area-inset-bottom, 0px));
  }



  /* Sur un téléphone le plan garde l'écran entier : le recadrage
     vertical de object-fit lui rend la hauteur qu'un 16/9 pleine
     largeur ne pouvait pas avoir. */
  .work { height: 88svh; min-height: 22rem; }

  /* Un 2.67 sur un téléphone ne ferait plus que 140 px de haut : on
     lui rend un plancher. Les bandes réapparaissent, mais le cadrage
     est intact et le plan reste lisible. */
  .work[data-fit="contain"] { min-height: 42svh; }
  .work-label { left: var(--pad); right: var(--pad); max-width: none; }
  .work-label .work-title { font-size: calc(var(--font-scale) * 1.35rem); }

  /* Le volet est déjà passé en haut du cadre (règle 1100 px) ; il ne
     lui reste qu'à replier ses deux colonnes en une. */
  .panel-grid { grid-template-columns: minmax(0, 1fr); }
  .panel-grid > * + * { margin-top: 0.7rem; }

  .h1, h1 { font-size: calc(var(--font-scale) * 3.2rem); }
  .h2, h2 { font-size: calc(var(--font-scale) * 2.6rem); }
  .lead   { font-size: calc(var(--font-scale) * 1.5rem); }

  .credits dt-role { min-width: 0; display: block; }

  /* Empilé, un filet par colonne devient un filet au milieu du bloc :
     sur /index il coupait chaque service entre son titre et son texte.
     On ne garde que celui de la première colonne, qui devient la ligne
     unique du bloc. Le pied garde les siens : ses deux moitiés sont
     deux blocs distincts, pas un seul coupé en deux. */
  .index-head > *:not(:first-child) hr,
  .row       > *:not(:first-child) hr,
  .row-more  > *:not(:first-child) hr { display: none; }

  /* Le filet retiré, les colonnes empilées se touchaient : le texte
     d'un service commençait sous son titre sans un souffle, et les
     trois blocs d'« Information » se lisaient comme un seul. */
  .row > * + * { margin-top: 1.1rem; }

  /* Six intitulés fixés en haut d'un écran de téléphone mangeraient
     trois lignes en permanence. La barre redevient donc un simple
     en-tête qui défile avec la page, et son corps descend d'un cran. */
  .pin {
    position: static;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    padding-bottom: 0.9rem;
  }
  /* Barre statique, toujours sur le papier : l'état « sur un plan »
     ne peut pas se produire, on le neutralise. */
  .pin::before { display: none; }
  .pin.on-media a, .pin.on-media span { color: var(--ink); }
  .pin.on-media .pin-logo img { filter: var(--logo-invert); }
  .pin-logo img { height: 2.1rem; }
  .pin-nav  { font-size: calc(var(--font-scale) * 0.95rem); font-weight: 450; }

}

/* Sous 900 px, le cartel et les crédits ne tiennent plus côte à côte :
   ils s'empilent, cartel d'abord. La bande reçoit un plafond de hauteur
   et défile en interne — sur un petit écran, huit lignes de crédits
   plus un titre peuvent dépasser le cadre, et rien ne doit sortir de
   l'image. */
@media (max-width: 900px) {
  :root { --hero-foot: 3.6rem; }

  .hero-info {
    flex-direction: column;
    align-items: stretch;
    gap: 0.9rem;
    max-height: 54svh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .hero-credits { width: 100%; }
  .hero-credits .credits div    { margin-bottom: 0.42rem; }
  .hero-credits .credits div,
  .hero-credits .credits dt-role { line-height: 1.28; }
}

/* Les deux colonnes de crédits tiennent encore sur un téléphone si
   l'on serre le corps : c'est ce qui les empêche de manger la moitié
   de l'image en hauteur. */
@media (max-width: 800px) {
  .hero-credits .panel-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero-credits .panel-grid > * + * { margin-top: 0; }
  .hero-credits .credits div     { font-size: calc(var(--font-scale) * 0.78rem); }
  .hero-credits .credits dt-role { font-size: calc(var(--font-scale) * 0.66rem); }
  .hero-nav { gap: 1.6rem; }
  .hero-nav a, .hero-nav button { font-size: calc(var(--font-scale) * 1.05rem); }
}


/* ── PORTRAITS ───────────────────────────────────────────────────
   Cadrés en 4/5, format de fiche. Le seul endroit du site où une
   image n'est pas du 16/9.                                         */

.portrait {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--paper-alt);
  filter: grayscale(1) contrast(1.04);
  transition: filter 0.4s;
}
a:hover .portrait { filter: grayscale(0) contrast(1); }


/* ── LISTES À TROIS COLONNES ─────────────────────────────────────
   Filmographies et inventaire. Année (ou quantité) en mono à gauche,
   intitulé au centre, mention à droite. Une grille, pas un tableau :
   les lignes se replient proprement sur mobile.                     */

.filmo-list { display: block; }

.filmo {
  display: grid;
  grid-template-columns: 5.2rem minmax(0, 1fr) minmax(0, 14rem);
  column-gap: var(--gutter);
  padding: 0.42rem 0;
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}
.filmo:last-child { border-bottom: 0; }

.filmo-year {
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.76rem);
  letter-spacing: 0.04em;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.filmo-title {
  font-size: calc(var(--font-scale) * 1.05rem);
  line-height: 1.3;
  color: var(--ink-title);
}
.filmo-meta {
  font-size: calc(var(--font-scale) * 0.85rem);
  line-height: 1.4;
  color: var(--ink-2);
}


/* ── CHIFFRES CLÉS ───────────────────────────────────────────────
   Le seul endroit où l'on se permet un grand corps. Six cases, une
   ligne, sans cadre : ce sont des nombres posés sur du blanc.       */

.stats {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: 1.4rem;
  padding: 0.4rem 0 0.6rem;
}
.stat { display: block; }
.stat-n {
  display: block;
  /* Un chiffre ne se casse pas en deux : « 3 kVA » passait à la ligne
     et cassait la ligne de base de toute la rangée, seul contre cinq. */
  white-space: nowrap;
  font-size: calc(var(--font-scale) * 2.4rem);
  line-height: 1;
  font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--ink-title);
  font-variant-numeric: tabular-nums;
}
.stat-l {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--font-mono);
  font-size: calc(var(--font-scale) * 0.74rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}


/* ── DIVERS ──────────────────────────────────────────────────────── */

.filters-wrap { padding-bottom: 0; }

/* La navigation se replie sans se transformer en menu burger : le
   template n'en a pas, et six intitulés tiennent sur deux lignes. */
.pin-nav { display: flex; flex-wrap: wrap; gap: 0 0.45em; justify-content: flex-end; }


@media (max-width: 800px) {
  .filmo {
    grid-template-columns: 4.4rem minmax(0, 1fr);
    row-gap: 0.15rem;
  }
  .filmo-meta { grid-column: 2; }

  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stat-n { font-size: calc(var(--font-scale) * 1.9rem); }

  .portrait { aspect-ratio: 3 / 2; margin-bottom: 0.9rem; }

  .pin-nav { justify-content: flex-start; margin-top: 0.3rem; }
  .pin { padding-top: 0.7rem; padding-bottom: 0.7rem; }
}
