/* ============================================================================
   L'HOMME SANS TÊTE — Lionel Euphrasie
   Système visuel. Toutes les couleurs sont ÉCHANTILLONNÉES dans les planches,
   jamais devinées. Voir le tableau --hst-* ci-dessous : chaque valeur porte
   sa source.
   ---------------------------------------------------------------------------
   RÈGLE DE MOUVEMENT. On n'anime JAMAIS « transform » en bloc : les couches de
   profondeur portent leur propre translate/scale/rotate et s'écraseraient
   l'une l'autre. On utilise les propriétés SÉPARÉES (translate, scale,
   rotate), qui se composent au lieu de se remplacer et coûtent la même chose
   au GPU.
   ========================================================================= */

/* --- Progression : typée, donc interpolable et lisible par le compositeur -- */
@property --p     { syntax: '<number>'; inherits: true;  initial-value: 0; }
@property --entree{ syntax: '<number>'; inherits: true;  initial-value: 0; }

/* ---------------------------------------------------------------- POLICES  */
@font-face {
  font-family: 'Archivo'; font-style: normal;
  font-weight: 100 900; font-stretch: 62% 125%; font-display: swap;
  src: url('fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo'; font-style: normal;
  font-weight: 100 900; font-stretch: 62% 125%; font-display: swap;
  src: url('fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/plexmono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/plexmono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('fonts/plexmono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plex Mono'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('fonts/plexmono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ----------------------------------------------------------------- JETONS  */
:root {
  /* Encres relevées dans les dessins — source entre parenthèses */
  --hst-vert:      #aecc30;   /* t-shirt du personnage (couverture)          */
  --hst-vert-vif:  #c6e84a;   /* le même, éclairci pour tenir le seuil AA    */
  --hst-jean:      #4e54a2;   /* pantalon (couverture)                       */
  --hst-nuit:      #1018b8;   /* fond du monogramme HLM                      */
  --hst-encre:     #22185a;   /* encre du lettrage du titre                  */
  --hst-portail-v: #84c03c;   /* spirale, fin du prologue                    */
  --hst-portail-b: #186ca8;   /* spirale, fin du prologue                    */
  --hst-ciel:      #78a8d8;   /* ciel des planches                           */
  --hst-pierre:    #9c9078;   /* façades, dominante du monde (25,7 %)        */
  --hst-sang:      #c0392b;   /* sceau de cire, la hache                     */

  /* DEUX RÔLES, DEUX COULEURS (12/09/2026). Le vert fluo servait à douze
     choses — il ne signalait plus rien. Il est désormais réservé à l'ACTION
     (acheter, voir la boutique) ; tout le reste (repères, étiquettes,
     jauges, focus) prend le bleu du ciel des planches et du lettrage. */
  --action: var(--hst-vert-vif);
  --accent: var(--hst-ciel);

  /* Fonds : la galerie. Nuit très légèrement bleutée, jamais du noir pur. */
  --fond:      #07080b;
  --fond-haut: #0c0e14;
  --fond-carte:#12151d;
  --voile:     rgba(7,8,11,.72);

  /* Papiers */
  --papier:    #f4f2ec;
  --texte:     #e8e7e1;
  --texte-2:   #a9aab2;   /* 7,1:1 sur --fond : tient largement AA           */
  --texte-3:   #7c7e88;   /* réservé au NON-texte (filets, puces)            */
  --filet:     rgba(244,242,236,.14);
  --filet-fort:rgba(244,242,236,.28);

  /* Formes */
  --r-s: 4px;  --r-m: 10px;  --r-l: 18px;

  /* Reliefs : ombres DURES (l'œuvre est au trait, pas au flou) + halo doux */
  --relief-1: 0 1px 0 rgba(244,242,236,.10), 0 10px 24px -12px rgba(0,0,0,.9);
  --relief-2: 0 1px 0 rgba(244,242,236,.14), 0 26px 60px -24px rgba(0,0,0,1);
  --relief-3: 0 1px 0 rgba(244,242,236,.18), 0 60px 120px -40px rgba(0,0,0,1);

  /* Tempo */
  --t-court: 220ms;  --t-moyen: 420ms;  --t-long: 900ms;
  --sortie:  cubic-bezier(.22,1,.36,1);      /* décélère franchement         */
  --doux:    cubic-bezier(.65,.05,.36,1);

  /* Gouttières */
  --marge: clamp(20px, 5vw, 90px);
  --colonne: min(1280px, 100% - var(--marge) * 2);

  color-scheme: dark;
}

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

html {
  scroll-behavior: smooth;
  /* L'ancre ne doit pas passer SOUS le menu fixe */
  scroll-padding-top: 86px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--fond);
  color: var(--texte);
  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: clamp(16px, .5vw + 15px, 18px);
  line-height: 1.62;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

/* Le corps du site ne se révèle qu'une fois la première planche décodée */
body.chargement { overflow: hidden; }

img, video { display: block; max-width: 100%; }
/* Le navigateur donne AUX <figure> une marge de 1em 40px. Non remise a zero,
   elle rognait 80 px de large a chaque planche du prologue : les cartes
   n'occupaient que 212 px d'une colonne de 292, sans que rien ne le signale. */
figure { margin: 0; }
figcaption { margin: 0; }
/* Les œuvres ne sont JAMAIS recadrées : « contain », jamais « cover ». */
img { height: auto; object-fit: contain; }

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

:where(button, a, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-s);
}

::selection { background: color-mix(in oklab, var(--hst-ciel) 42%, transparent); color: inherit; }

/* --------------------------------------------------------------- TYPO ---- */
.mono {
  font-family: 'Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: clamp(10px, .32vw + 9px, 12px);
  letter-spacing: .18em;
  text-transform: uppercase;
  font-weight: 400;
}

/* Les grands titres : Archivo condensée, très grasse, chasse resserrée.
   Contraste voulu avec le trait manuel — l'étiquette du musée, pas l'œuvre. */
.titre {
  font-weight: 800;
  font-stretch: 80%;
  letter-spacing: -.035em;
  line-height: .92;
  margin: 0;
  text-wrap: balance;
}
.titre--geant { font-size: clamp(44px, 9.2vw, 154px); font-stretch: 74%; letter-spacing: -.045em; }
.titre--grand { font-size: clamp(34px, 5.6vw, 88px); }
.titre--moyen { font-size: clamp(26px, 3.2vw, 46px); font-stretch: 88%; letter-spacing: -.025em; }

.chapo {
  font-size: clamp(18px, 1.25vw + 14px, 27px);
  line-height: 1.45;
  font-weight: 300;
  color: var(--texte);
  max-width: 34ch;
  text-wrap: pretty;
}
.texte { color: var(--texte-2); max-width: 62ch; text-wrap: pretty; }
.texte strong { color: var(--texte); font-weight: 600; }

/* Filet + étiquette : la signalétique d'exposition */
.etiquette {
  display: flex; align-items: center; gap: 14px;
  color: var(--accent);
  margin-bottom: clamp(18px, 2vw, 30px);
}
.etiquette::before {
  content: ''; width: clamp(24px, 4vw, 58px); height: 1px;
  background: currentColor; opacity: .55; flex: none;
}

/* ------------------------------------------------------------ STRUCTURE -- */
.bande { position: relative; }
.colonne { width: var(--colonne); margin-inline: auto; }

/* Les sections respirent : une exposition n'entasse pas. */
.bande--aere { padding-block: clamp(64px, 8.5vh, 120px); }

/* ------------------------------------------------- GRAIN + VIGNETTAGE ---- */
/* Fixes, donc repeints une seule fois : jamais sur un élément qui défile. */
.matiere {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  mix-blend-mode: overlay; opacity: .42;
  background-image: var(--grain);
  background-size: 220px 220px;
}
.vignette {
  position: fixed; inset: 0; z-index: 59; pointer-events: none;
  background: radial-gradient(120% 85% at 50% 45%,
              transparent 42%, rgba(0,0,0,.30) 78%, rgba(0,0,0,.62) 100%);
}

/* Grain : bruit fractal SVG en data URI (la CSP autorise « img-src data: »).
   220×220 répété — assez fin pour lire comme du papier, assez grand pour ne
   pas moirer sur les écrans à forte densité. */
:root {
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
}

/* ==========================================================================
   LE MENU — fixe, discret, il n'entre qu'une fois l'ouverture franchie.
   ========================================================================== */
.menu {
  position: fixed; inset-inline: 0; top: 0; z-index: 50;
  display: flex; align-items: center; gap: clamp(14px, 3vw, 40px);
  padding: 0 var(--marge);
  height: 64px;
  /* backdrop-filter : autorisé ici car l'élément est FIXE (il ne défile pas,
     donc il ne repeint pas à chaque image). */
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  background: linear-gradient(to bottom, rgba(7,8,11,.86), rgba(7,8,11,.52));
  border-bottom: 1px solid transparent;
  /* Sortie par le haut tant qu'on est dans l'ouverture */
  translate: 0 -100%;
  opacity: 0;
  transition: translate var(--t-moyen) var(--sortie),
              opacity   var(--t-moyen) var(--sortie),
              border-color var(--t-moyen) linear;
}
.menu[data-visible='oui'] {
  translate: 0 0; opacity: 1;
  border-bottom-color: var(--filet);
}

.menu__marque {
  display: inline-flex; align-items: center; gap: 9px;
  flex: none; margin-right: auto;
  min-height: 44px;                     /* cible tactile : jamais sous 44 px */
  font-weight: 700; font-stretch: 78%; letter-spacing: -.02em;
  font-size: 15px;
}
.menu__hlm {
  font-family: 'Plex Mono', monospace; font-weight: 600;
  font-size: 12px; letter-spacing: .12em;
  color: var(--accent);
  border: 1px solid currentColor; border-radius: var(--r-s);
  padding: 2px 6px; line-height: 1;
}
.menu__titre { color: var(--texte); }
@media (max-width: 620px) { .menu__titre { display: none; } }

.menu__nav { display: flex; align-items: center; gap: clamp(10px, 2vw, 30px); }
.menu__lien {
  position: relative;
  font-family: 'Plex Mono', monospace;
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--texte-2);
  padding: 22px 0;                       /* cible tactile ≥ 44 px de haut */
  transition: color var(--t-court) ease;
  white-space: nowrap;
}
.menu__lien::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 18px;
  height: 1px; background: var(--accent);
  scale: 0 1; transform-origin: left;
  transition: scale var(--t-moyen) var(--sortie);
}
.menu__lien:hover, .menu__lien[aria-current='true'] { color: var(--texte); }
.menu__lien:hover::after,
.menu__lien[aria-current='true']::after { scale: 1 1; }

/* Sous 860 px les intitulés ne tiennent plus : on ouvre un tiroir maison.
   Fermé par défaut. */
.menu__bouton {
  display: none; flex: none;
  width: 44px; height: 44px; margin-right: -10px;
  background: none; border: 0; cursor: pointer; color: var(--texte);
  align-items: center; justify-content: center;
}
.menu__bouton span, .menu__bouton span::before, .menu__bouton span::after {
  content: ''; display: block; width: 19px; height: 1.5px;
  background: currentColor; border-radius: 2px;
  transition: translate var(--t-court) var(--sortie),
              rotate    var(--t-court) var(--sortie),
              opacity   var(--t-court) linear;
}
.menu__bouton span::before { translate: 0 -6px; }
.menu__bouton span::after  { translate: 0  4.5px; }
.menu[data-tiroir='ouvert'] .menu__bouton span { background: transparent; }
.menu[data-tiroir='ouvert'] .menu__bouton span::before { translate: 0 0; rotate: 45deg; }
.menu[data-tiroir='ouvert'] .menu__bouton span::after  { translate: 0 -1.5px; rotate: -45deg; }

@media (max-width: 860px) {
  .menu__bouton { display: flex; }
  .menu__nav {
    position: fixed; inset: 64px 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 8px var(--marge) 26px;
    /* OPAQUE, pas « presque ». Un tiroir de navigation doit masquer ce qu'il
       recouvre : a 96 % la page continuait de transparaitre derriere les
       intitules, et deux textes se superposaient. */
    background: var(--fond);
    box-shadow: 0 24px 60px -20px rgba(0,0,0,.9);
    border-bottom: 1px solid var(--filet);
    /* Fermé par défaut, et retiré du parcours clavier quand il l'est */
    display: none;
  }
  .menu[data-tiroir='ouvert'] .menu__nav { display: flex; }
  .menu__lien { padding: 15px 0; font-size: 13px; border-bottom: 1px solid var(--filet); }
  .menu__lien::after { display: none; }
  .menu__lien:last-child { border-bottom: 0; }
}

/* Le fil de progression du parcours, collé sous le menu */
.menu__jauge {
  position: absolute; left: 0; bottom: -1px; height: 2px;
  width: 100%; transform-origin: left;
  scale: var(--avancement, 0) 1;
  background: linear-gradient(90deg, var(--hst-portail-b), var(--hst-ciel));
}

/* ==========================================================================
   LE CHARGEMENT — un trait qui se dessine, puis le rideau s'ouvre.
   ========================================================================== */
.rideau {
  position: fixed; inset: 0; z-index: 80;
  display: grid; place-content: center; justify-items: center; gap: 20px;
  background: var(--fond);
  transition: opacity 700ms var(--doux), visibility 700ms;
}
.rideau[hidden] { display: none; }
.rideau--parti { opacity: 0; visibility: hidden; pointer-events: none; }
.rideau__trait {
  width: min(46vw, 320px); height: 1px; background: var(--filet-fort);
  position: relative; overflow: hidden;
}
.rideau__trait::after {
  content: ''; position: absolute; inset: 0;
  background: var(--accent);
  transform-origin: left; scale: var(--avance, 0) 1;
  transition: scale 260ms linear;
}
.rideau__compte { color: var(--texte-3); text-align: center; }

/* ==========================================================================
   LE SYSTÈME DE PROFONDEUR
   --------------------------------------------------------------------------
   Ce n'est pas du faux parallaxe (des vitesses inventées à la main) : c'est
   une VRAIE perspective. Chaque plan est posé à une profondeur --z réelle
   dans une scène qui a une focale (--persp). Le navigateur en déduit seul
   la vitesse de dérive — un plan lointain glisse peu, un plan proche fuit
   vite. C'est ce qui fait que l'œil lit du volume et pas un décalage.

   --z      profondeur au repos, en pixels (négatif = au loin)
   --vz     ce que le plan parcourt en Z pendant la traversée de la section
   --p      progression de la section, 0 → 1, écrite par scene.js
   Le « scale » compense l'éloignement : un plan posé à -900 doit rester à sa
   taille apparente, sinon on ne voit qu'un décor qui rétrécit.
   ========================================================================== */
.profondeur { --persp: 1000; --persp-px: 1000px; }

/* PIÈGE. « perspective » ne s'applique qu'aux ENFANTS DIRECTS de l'élément qui
   la porte. Posée sur la section, elle ne touchait pas les plans (petits-
   enfants) : le translateZ ne produisait plus aucune perspective, mais le
   scale de compensation, lui, s'appliquait toujours — tout était agrandi de
   26 % sans contrepartie, et le titre débordait de l'écran. Elle doit donc
   vivre sur le CADRE. Et « transform-style: preserve-3d » ne pourrait pas la
   remplacer : ces cadres ont « overflow: hidden », qui force le rendu à plat. */
.ouverture__cadre, .ville__cadre, .portail__cadre {
  perspective: var(--persp-px, 1000px);
  perspective-origin: 50% 48%;
}
.plan {
  --zc: calc(var(--z, 0) + var(--p) * var(--vz, 0));
  position: absolute; inset: 0;
  display: grid; place-content: center;
  transform-style: preserve-3d;
  will-change: translate, scale;
  translate: 0 var(--y, 0px) calc(var(--zc) * 1px);
  scale: calc((var(--persp) - var(--zc)) / var(--persp));
}
/* Un plan qui atteint la caméra doit s'effacer AVANT de traverser l'objectif */
.plan--fuyant { opacity: calc(1 - var(--p) * var(--fuite, 1.15)); }

/* ==========================================================================
   L'OUVERTURE
   ========================================================================== */
.ouverture { height: 165vh; position: relative; }
.ouverture__cadre {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: grid; place-items: center;
  background:
    radial-gradient(120% 90% at 50% 60%, #0d1018 0%, var(--fond) 62%);
}

/* La spirale du portail. Dessinée en CSS à partir des DEUX verts et du bleu
   relevés dans la dernière planche du prologue — c'est le motif canonique de
   l'œuvre, pas une décoration. */
.spirale {
  width: min(94vmin, 880px); aspect-ratio: 1;
  border-radius: 50%;
  background: repeating-radial-gradient(circle at 50% 50%,
      color-mix(in oklab, var(--hst-portail-b) 58%, #04060c)  0 1.1%,
      color-mix(in oklab, var(--hst-portail-v) 38%, #04120a) 1.1% 2.6%,
      transparent 2.6% 6.4%);
  opacity: .5;
  mask-image: radial-gradient(circle at 50% 50%,
      transparent 6%, #000 26%, #000 54%, transparent 92%);
  rotate: calc(var(--p) * 66deg);
  filter: blur(.4px);
  animation: tourne 150s linear infinite;
}
@keyframes tourne { to { rotate: 360deg; } }
.spirale::after {       /* le puits central, qui aspire */
  content: ''; position: absolute; inset: 34%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--fond) 30%, transparent 72%);
}

/* Le logotype : le LETTRAGE MANUEL de Lionel. Aucune police ne l'imite. */
.logotype {
  width: min(72vw, 880px); height: auto;
  filter:
    drop-shadow(0 0 46px rgba(16,24,184,.55))
    drop-shadow(0 18px 40px rgba(0,0,0,.85));
}

.ouverture__sur {
  color: var(--papier);
  text-align: center;
  margin: 0;
}
.ouverture__sous {
  text-align: center;
  max-width: 34ch; margin: 0 auto;
  color: var(--texte-2);
  font-size: clamp(15px, .6vw + 13px, 19px);
  line-height: 1.5;
}
.ouverture__pied {
  position: absolute; inset-inline: 0; bottom: clamp(22px, 4vh, 46px);
  display: grid; justify-items: center; gap: 12px;
  color: var(--texte-3);
  opacity: calc(1 - var(--p) * 3.4);
}
.ouverture__fleche {
  width: 1px; height: 40px; background: var(--filet-fort); position: relative; overflow: hidden;
}
.ouverture__fleche::after {
  content: ''; position: absolute; inset-inline: 0; height: 14px;
  background: var(--accent);
  animation: descend 1.9s var(--doux) infinite;
}
@keyframes descend {
  0%   { translate: 0 -16px; opacity: 0; }
  35%  { opacity: 1; }
  100% { translate: 0 42px;  opacity: 0; }
}

/* Le pied du cadre. Sans lui, l'« overflow: hidden » du cadre sticky coupe la
   spirale sur une ligne horizontale nette au moment où il sort de l'écran :
   on voit franchement la limite. Ce fondu l'éteint avant la coupure. */
.ouverture__cadre::after {
  content: ''; position: absolute; z-index: 4; inset: auto 0 0 0;
  height: clamp(120px, 22vh, 260px);
  background: linear-gradient(to bottom, transparent, rgba(7,8,11,.55) 62%, var(--fond));
  pointer-events: none;
}

/* Le seuil : le noir qui se referme quand on a traversé le portail. */
.seuil {
  position: absolute; inset: 0; z-index: 5; pointer-events: none;
  background: radial-gradient(circle at 50% 48%,
              transparent 0%, transparent 26%, var(--fond) 62%);
  /* Le seuil ne se referme qu'a la toute fin : avant, il vidait l'ecran
     pendant pres d'un ecran entier de defilement. */
  opacity: calc((var(--p) - .86) * 7);
}

/* ==========================================================================
   LES APPARITIONS
   Un objet n'apparaît pas : il SE DÉTACHE. Il monte, se redresse (son plan
   bascule de quelques degrés vers l'œil), et son ombre se resserre en même
   temps — exactement ce que fait une feuille qu'on soulève d'une table.
   L'ombre qui suit la bascule est ce qui rend la profondeur crédible ;
   sans elle, la rotation lit comme un bug d'affichage.
   ========================================================================== */
[data-apparait] {
  --e: 0;                                   /* 0 = posé à plat, 1 = détaché  */
  opacity: calc(.001 + var(--e));
  translate: 0 calc((1 - var(--e)) * var(--monte, 34px));
  transition:
    opacity   var(--t-long) var(--sortie) var(--retard, 0ms),
    translate var(--t-long) var(--sortie) var(--retard, 0ms),
    rotate    var(--t-long) var(--sortie) var(--retard, 0ms),
    scale     var(--t-long) var(--sortie) var(--retard, 0ms),
    filter    var(--t-long) var(--sortie) var(--retard, 0ms);
}
[data-apparait][data-vu='oui'] { --e: 1; }

/* Une carte qui se redresse vers l'œil */
[data-apparait='detache'] {
  --monte: 56px;
  rotate: x calc((1 - var(--e)) * 9deg);
  scale: calc(.965 + var(--e) * .035);
  filter: drop-shadow(0 calc(8px + (1 - var(--e)) * 46px)
                      calc(18px + (1 - var(--e)) * 52px)
                      rgba(0,0,0,calc(.55 + (1 - var(--e)) * .35)));
}
/* Un bloc de texte : montée sobre, aucune rotation (le texte ne bascule pas,
   ça le rend illisible pendant la transition) */
[data-apparait='monte'] { --monte: 28px; }

/* Une ligne qui s'ouvre depuis la gauche */
[data-apparait='ouvre'] {
  --monte: 0px;
  clip-path: inset(0 calc((1 - var(--e)) * 100%) 0 0);
  transition: clip-path 1100ms var(--sortie) var(--retard, 0ms),
              opacity 300ms linear var(--retard, 0ms);
}

/* ==========================================================================
   LE PROLOGUE — 1430. La sentence.
   ========================================================================== */
.prologue {
  background:
    linear-gradient(to bottom,
      var(--fond) 0%,
      color-mix(in oklab, var(--hst-pierre) 13%, var(--fond)) 45%,
      color-mix(in oklab, var(--hst-pierre) 18%, var(--fond)) 72%,
      var(--fond) 100%);
}
.prologue__intro { display: grid; gap: clamp(20px, 3vw, 34px); }
.prologue__intro > .etiquette,
.prologue__intro > .chapo { max-width: 46ch; }
.prologue__annee {
  /* Une seule ligne dès qu'il y a la place : la phrase est courte, la couper
     en deux n'apporte rien. Sous 760 px, on laisse le texte se replier. */
  white-space: nowrap;
  font-family: 'Plex Mono', monospace; font-weight: 600;
  /* Calibré sur « Il y a fort longtemps » (21 signes), pas sur un mot court :
     à l'ancienne taille, la phrase occupait trois lignes géantes. */
  font-size: clamp(28px, 4.6vw, 68px); line-height: 1.04;
  letter-spacing: -.02em;
  /* Ce mot porte du sens : il lui faut une vraie encre, pas un contour
     fantôme. En contour il mesurait 1,05:1 — illisible. */
  color: color-mix(in oklab, var(--hst-pierre) 82%, var(--papier));
  opacity: .92;
}

@media (max-width: 760px) { .prologue__annee { white-space: normal; } }

/* Les planches du prologue, posées en quinconce comme sur une table */
.planches-prologue {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: clamp(18px, 2.6vw, 40px);
  margin-top: clamp(44px, 7vh, 92px);
  perspective: 1400px;
}
.planche-carte {
  position: relative;
  background: var(--papier);
  border-radius: var(--r-s);
  overflow: hidden;
  box-shadow: var(--relief-2);
  transform-style: preserve-3d;
}
/* Le reflet : une bande de lumière qui balaie la carte au survol. Elle
   n'existe que sur les appareils qui ONT un survol — sur un écran tactile
   elle resterait figée en travers de l'image. */
@media (hover: hover) {
  .planche-carte::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(104deg,
       transparent 38%, rgba(255,255,255,.42) 50%, transparent 62%);
    translate: -130% 0;
    transition: translate 900ms var(--doux);
    mix-blend-mode: overlay;
  }
  .planche-carte:hover::after { translate: 130% 0; }
  .planche-carte { transition: translate var(--t-moyen) var(--sortie),
                               box-shadow var(--t-moyen) var(--sortie); }
  .planche-carte:hover { translate: 0 -8px; box-shadow: var(--relief-3); }
}
.planche-carte img { width: 100%; }
/* Le titre de la planche se lit EN HAUT à gauche, là où l'œil entre dans la
   page — en bas, il arrivait après le dessin (retour de Brice, 12/09). */
.planche-carte figcaption {
  position: absolute; inset-inline: 0; top: 0;
  padding: 11px 14px 26px;
  background: linear-gradient(to bottom, rgba(6,6,8,.90), transparent);
  color: var(--papier);
}

/* Le coup de hache : un éclat qui traverse la bande */
.shlakkk {
  display: grid; place-items: center; gap: 18px;
  padding-block: clamp(60px, 10vh, 130px);
  text-align: center;
}
.shlakkk__mot {
  font-weight: 900; font-stretch: 68%;
  font-size: clamp(50px, 13vw, 220px); line-height: .84;
  letter-spacing: -.05em;
  color: var(--hst-sang);
  text-shadow: 0 0 70px rgba(192,57,43,.5);
  rotate: -3.5deg;
  /* L'éclat suit la progression : il frappe au centre de la bande */
  scale: calc(.78 + var(--p) * .34);
  opacity: calc(1 - abs(var(--p) - .5) * 2.6);
}

/* ==========================================================================
   LE PARCOURS — le cœur du site.
   --------------------------------------------------------------------------
   PREMIÈRE IDÉE, ABANDONNÉE APRÈS MESURE. Je voulais faire défiler la planche
   horizontalement au scroll : le lecteur pousse lui-même le personnage à
   travers la ville. Le geste est juste — cette bande dessinée se lit bien en
   travelling — mais les planches ont un rapport de 1,48 (1754 × 1188), pas un
   format panoramique. Sur un écran 16/9, les faire déborder latéralement
   oblige à les rogner en haut et en bas. On ne rogne pas l'œuvre.

   CE QUI EST FAIT À LA PLACE. La planche arrive DE LOIN, se redresse et se
   pose, entière, éclairée comme une pièce d'exposition. La profondeur est
   réelle (translateZ dans une scène à focale), pas un décalage simulé. Le
   travelling, lui, n'a pas disparu : il est devenu un viseur séparé — voir
   « LE DÉTAIL » plus bas — où le recadrage est assumé comme tel.
   ========================================================================== */
.ville { position: relative; height: var(--hauteur, 205vh); }
.ville__cadre {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(to bottom,
     color-mix(in oklab, var(--hst-ciel) 9%, var(--fond)),
     var(--fond) 82%);
}

/* Le ciel, au plus loin : il dérive à peine. */
.ville__ciel {
  position: absolute; inset: -12%;
  background: radial-gradient(85% 70% at 32% 22%,
      color-mix(in oklab, var(--hst-ciel) 24%, transparent), transparent 72%);
  translate: calc(var(--p) * -6%) 0;
}

/* Le nom de la ville DÉRIVE À CONTRE-SENS de la planche : c'est ce
   contre-mouvement qui creuse l'image. */
.ville__nom {
  position: absolute; z-index: 1;
  font-weight: 900; font-stretch: 66%;
  font-size: clamp(72px, 20vw, 330px); line-height: .8;
  letter-spacing: -.055em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(244,242,236,.26);
  white-space: nowrap; pointer-events: none;
  translate: calc(-6% + var(--p) * 13%) 0;
}

/* LA PLANCHE. Elle vient de loin et se pose.
   --p 0 → 0,42 : l'approche.   au-delà : elle est posée, on la lit. */
.ville__scene {
  position: relative; z-index: 2;
  --pose: clamp(0, var(--p) / .42, 1);          /* l'approche, ramenée à 0..1 */
  display: grid; place-items: center;
  width: min(92vw, 1180px);
  transform-style: preserve-3d;
}
/* LA PIÈCE porte le mouvement — pas l'image seule. Le reflet est son enfant :
   s'il était transformé à part, il glisserait à côté de ce qu'il éclaire. */
.ville__piece {
  --z-appro: calc((1 - var(--pose)) * -760);
  position: relative; display: block; margin: 0; line-height: 0;
  /* PIÈGE. La valeur de repli était « auto ». Dans une grille en
     « justify-items: center », auto vaut fit-content : la pièce se mesurait
     alors sur son image, et l'image sur sa pièce (width: 100%). Ce cercle se
     résout à ZÉRO — la planche faisait 0 px, ne croisait donc jamais le champ
     de vision, et son « loading=lazy » ne se déclenchait jamais. Elle ne se
     chargeait pas, donc scene.js ne pouvait pas la mesurer, donc elle restait
     à zéro. Le repli doit être une largeur RÉELLE, pas auto. */
  width: var(--piece-w, min(100%, 900px));
  max-width: 100%;
  translate: 0 calc((1 - var(--pose)) * 40px) calc(var(--z-appro) * 1px);
  scale: calc((1000 - var(--z-appro)) / 1000);
  rotate: x calc((1 - var(--pose)) * 7deg);
  opacity: calc(.08 + var(--pose) * .92);
  border-radius: var(--r-s);
  background: var(--papier);
  /* Le relief : arête claire en haut, ombre DURE et portée en bas. L'ombre se
     RESSERRE à mesure que la pièce se pose — c'est elle qui rend l'approche
     crédible ; sans elle, la rotation lit comme un défaut d'affichage. */
  box-shadow:
    0 1px 0 rgba(255,255,255,.22),
    0 calc(18px + (1 - var(--pose)) * 70px)
      calc(40px + (1 - var(--pose)) * 90px)
      -16px rgba(0,0,0,calc(.7 + (1 - var(--pose)) * .3));
  will-change: translate, scale, rotate;
}
.ville__planche {
  display: block;
  width: 100%; height: auto;            /* entière : jamais rognée */
  border-radius: var(--r-s);
}
/* Le <picture> est « inline » : laissé tel quel, il casse la chaîne de
   hauteur et ajoute un blanc sous l'image. On l'efface de la mise en page. */
.ville__piece picture { display: contents; }

/* Le reflet qui balaie la pièce une fois posée. */
.ville__lustre {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  border-radius: var(--r-s); overflow: hidden;
  opacity: calc(clamp(0, (var(--p) - .30) * 5, 1) * clamp(0, (.72 - var(--p)) * 5, 1));
}
.ville__lustre::after {
  content: ''; position: absolute; inset: -20%;
  background: linear-gradient(106deg,
     transparent 42%, rgba(255,255,255,.40) 50%, transparent 58%);
  translate: calc(-60% + var(--p) * 150%) 0;
  mix-blend-mode: overlay;
}

.ville__cartouche {
  position: absolute; z-index: 4; left: var(--marge); right: var(--marge);
  bottom: clamp(16px, 3vh, 38px);
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  pointer-events: none;
  opacity: clamp(0, (var(--p) - .30) * 6, 1);
  translate: 0 calc((1 - clamp(0, (var(--p) - .30) * 6, 1)) * 14px);
}
.cartouche__bloc { display: grid; gap: 5px; }
.cartouche__lieu {
  font-weight: 700; font-stretch: 84%; letter-spacing: -.02em;
  font-size: clamp(18px, 1.9vw, 30px); line-height: 1.05;
  color: var(--papier); margin: 0;
  text-shadow: 0 2px 18px rgba(0,0,0,.9);
}
.cartouche__meta { color: var(--texte-2); margin: 0; text-shadow: 0 1px 10px rgba(0,0,0,.9); }

.cartouche__code {
  display: inline-flex; align-items: center; gap: 9px; margin: 0;
  font-family: 'Plex Mono', monospace; font-weight: 600;
  font-size: clamp(12px, 1vw, 15px); letter-spacing: .12em;
  color: var(--texte);
  background: rgba(7,8,11,.62);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--filet); border-radius: var(--r-s);
  padding: 7px 11px;
}
.cartouche__lettre {
  color: #0b0d05; background: var(--accent);
  border-radius: 2px; padding: 1px 6px; font-weight: 600;
}

.ville__jauge {
  position: absolute; z-index: 4; left: var(--marge); right: var(--marge);
  bottom: 0; height: 2px; background: var(--filet);
}
.ville__jauge::after {
  content: ''; position: absolute; inset: 0;
  background: var(--accent);
  transform-origin: left; scale: var(--p) 1;
}

.ville__traque {
  position: absolute; z-index: 4; top: clamp(74px, 10vh, 110px); right: var(--marge);
  max-width: 28ch; text-align: right;
  color: var(--texte-2); margin: 0;
  opacity: calc(clamp(0, (var(--p) - .42) * 5, 1) * clamp(0, (.88 - var(--p)) * 6, 1));
}

/* ==========================================================================
   LE DÉTAIL — le travelling, enfin.
   --------------------------------------------------------------------------
   Ici le recadrage est ASSUMÉ : on ne montre pas la planche, on montre un
   détail d'elle, dans un viseur panoramique, et on le parcourt. C'est une
   pratique d'exposition (« détail de la planche »), pas un rognage subi.
   Réservé aux planches assez définies pour supporter l'agrandissement :
   scene.js mesure et n'active la section que si la source suit.
   ========================================================================== */
.detail { position: relative; height: 300vh; }
.detail__cadre {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: grid; align-content: center; gap: clamp(18px, 3vh, 34px);
  padding-inline: var(--marge);
  background: var(--fond);
}
.detail__viseur {
  position: relative; overflow: hidden;
  width: 100%; height: min(56svh, 44vw);
  border-radius: var(--r-m);
  border: 1px solid var(--filet);
  box-shadow: var(--relief-3);
  background: var(--papier);
}
.detail__bande {
  /* --vise : OÙ l'on regarde dans la hauteur de la planche (0 = haut, 1 = bas).
     Sans ce réglage, le viseur se centre bêtement au milieu et cadre le décor
     au lieu de la trajectoire — sur la Part-Dieu, les panneaux d'affichage
     plutôt que les escaliers où passe le personnage. */
  position: absolute; left: 0;
  top: calc(var(--vise, .5) * 100%);
  height: var(--zoom-h, 150%); width: auto; max-width: none;
  translate: calc(var(--p) * var(--course, 0px) * -1)
             calc(var(--vise, .5) * -100%);
  will-change: translate;
}
/* Les repères du viseur : c'est un appareil de lecture, ça doit se voir. */
.detail__viseur::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to right, rgba(7,8,11,.55), transparent 12%,
                    transparent 88%, rgba(7,8,11,.55));
}
.detail__reglet {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; color: var(--texte-2);
}
.detail__piste { height: 2px; background: var(--filet); position: relative; }
.detail__piste::after {
  content: ''; position: absolute; inset: 0; background: var(--accent);
  transform-origin: left; scale: var(--p) 1;
}

/* ==========================================================================
   LE PORTAIL — la transition entre deux villes.
   Les séquences paraissent dans le désordre, reliées par des portails
   téléportatifs : la transition n'est donc pas un effet, c'est un ÉLÉMENT DU
   RÉCIT. On y entre, l'écran se referme, on ressort ailleurs.
   ========================================================================== */
.portail { height: 132vh; position: relative; }
.portail__cadre {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: grid; place-items: center; background: var(--fond);
}
.portail__disque {
  width: min(86vmin, 880px); aspect-ratio: 1; border-radius: 50%;
  background: repeating-conic-gradient(from calc(var(--p) * 300deg) at 50% 50%,
      var(--hst-portail-b) 0 3deg,
      var(--hst-portail-v) 3deg 6.4deg,
      #06340a 6.4deg 9.6deg);
  /* On plonge dedans : il grandit jusqu'a avaler l'ecran. */
  scale: calc(.62 + var(--p) * var(--p) * 6.6);
  /* On ne s'éteint pas complètement : passé le seuil, il reste une lueur —
     l'écran n'est jamais totalement noir. */
  opacity: calc(.34 + var(--p) * .5 - clamp(0, (var(--p) - .72) * 1.3, .42));
  filter: blur(calc(var(--p) * var(--p) * 22px));
  /* Le trou central appartient au disque : porte par un element separe, son
     halo recouvrait le disque entier avant meme qu'on entre. */
  mask-image: radial-gradient(circle at 50% 50%,
      transparent 0, transparent calc(9% + var(--p) * 16%),
      #000 calc(20% + var(--p) * 18%), #000 78%, transparent 97%);
}

.portail__mot {
  position: absolute; z-index: 2; text-align: center;
  padding: clamp(12px, 2.4vw, 22px) clamp(18px, 3vw, 34px);
  border-radius: 100px;
  background: radial-gradient(closest-side, rgba(6,7,10,.82), rgba(6,7,10,.55) 70%, transparent);
  color: var(--papier);
  opacity: calc(1 - abs(var(--p) - .46) * 2.3);
  mix-blend-mode: difference;
  text-shadow: 0 2px 30px rgba(0,0,0,.8);
}

/* ==========================================================================
   LA RÈGLE DU JEU — les chiffres
   ========================================================================== */
.compteurs {
  display: grid; gap: clamp(26px, 4vw, 56px);
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  margin-block: clamp(40px, 7vh, 86px);
}
.compteur { display: grid; gap: 8px; border-top: 1px solid var(--filet); padding-top: 18px; }
.compteur__valeur {
  font-weight: 800; font-stretch: 72%;
  font-size: clamp(52px, 7.4vw, 124px); line-height: .86;
  letter-spacing: -.045em;
  color: var(--papier);
  font-variant-numeric: tabular-nums;
}
.compteur__legende { color: var(--texte-2); }

.etapes { display: grid; gap: clamp(16px, 2vw, 26px); counter-reset: etape;
          grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); }
.etape {
  position: relative; counter-increment: etape;
  background: var(--fond-carte);
  border: 1px solid var(--filet); border-radius: var(--r-m);
  padding: clamp(20px, 2.4vw, 32px);
  display: grid; gap: 10px; align-content: start;
  box-shadow: var(--relief-1);
}
.etape::before {
  content: counter(etape, decimal-leading-zero);
  font-family: 'Plex Mono', monospace; font-weight: 600; font-size: 12px;
  letter-spacing: .14em; color: var(--accent);
}
.etape h3 { margin: 0; font-size: clamp(18px, 1.3vw + 14px, 24px); font-weight: 700;
            font-stretch: 88%; letter-spacing: -.02em; }
.etape p { margin: 0; color: var(--texte-2); font-size: 15.5px; }

/* LE BANDEAU DE LA RÈGLE
   Le dessin d'origine est sur fond vert de détourage — un aplat fluo qui
   écrasait toute la section. Fond retiré ; mais son texte est écrit à l'encre
   NOIRE, pensée pour un fond clair : posé sur la nuit du site, il devient
   illisible. On lui rend donc un support de papier, au lieu de retoucher le
   trait de l'auteur. */
.regle-carte {
  display: grid; justify-items: center;
  margin-top: clamp(34px, 5vh, 64px);
  padding: clamp(22px, 3.6vw, 52px) clamp(18px, 3vw, 46px);
  border-radius: var(--r-m);
  background: #ddd8ca var(--grain-papier) 0 0 / 260px 260px;
  box-shadow: var(--relief-3);
  display: grid; justify-items: center;
}
.bandeau-regle {
  width: min(100%, 760px); height: auto;
  filter: drop-shadow(0 8px 14px rgba(40,30,12,.28));
}

/* ==========================================================================
   LE CODE — les lettres relevées
   ========================================================================== */
.code-grille {
  display: grid; gap: clamp(8px, 1.1vw, 15px);
  grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
  margin-top: clamp(34px, 5vh, 64px);
}
.code-case {
  aspect-ratio: 1; display: grid; place-items: center;
  background: var(--fond-carte);
  border: 1px solid var(--filet); border-radius: var(--r-s);
  font-family: 'Plex Mono', monospace; font-weight: 600;
  font-size: clamp(20px, 2.4vw, 34px);
  color: var(--papier);
  position: relative;
  box-shadow: inset 0 1px 0 rgba(244,242,236,.06);
}
.code-case[data-etat='connue'] { border-color: color-mix(in oklab, var(--hst-vert-vif) 50%, transparent); }
.code-case[data-etat='connue']::after {
  content: ''; position: absolute; inset: -1px; border-radius: var(--r-s);
  box-shadow: 0 0 26px -4px color-mix(in oklab, var(--hst-vert-vif) 60%, transparent);
  pointer-events: none;
}
.code-case[data-etat='inconnue'] { color: var(--texte-3); }
.code-case small {
  position: absolute; top: 5px; left: 6px;
  font-size: 9px; letter-spacing: .08em; color: var(--texte-3); font-weight: 400;
}

/* ==========================================================================
   L'ATELIER — la preuve du geste
   ========================================================================== */
.atelier { display: grid; gap: clamp(34px, 5vw, 74px); align-items: center;
           grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .atelier { grid-template-columns: 0.86fr 1.14fr; } }

.carnet {
  position: relative; justify-self: center;
  width: min(520px, 92%);
  border-radius: 14px; overflow: hidden;
  box-shadow: var(--relief-3), 0 0 0 1px var(--filet);
  rotate: -1.6deg;
  background: #14161d;
}
.carnet video { width: 100%; height: auto; }
.carnet__etiquette {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  color: var(--papier);
  background: rgba(7,8,11,.66);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--filet); border-radius: var(--r-s);
  padding: 5px 9px;
}

/* L'engagement : aucune image de ce site n'est générée par ordinateur. */
.serment {
  border-left: 2px solid var(--accent);
  padding-left: clamp(16px, 2vw, 26px);
  margin-top: clamp(26px, 4vh, 46px);
  display: grid; gap: 10px;
}
.serment p { margin: 0; }
.serment__titre { color: var(--accent); }

/* ==========================================================================
   L'AUTEUR
   ========================================================================== */
.auteur { display: grid; gap: clamp(30px, 4.4vw, 68px); align-items: start;
          grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .auteur { grid-template-columns: 1.1fr .9fr; align-items: start; } }
.auteur__portrait {
  /* La figure EPOUSE l'image. Avec une largeur de 100 % et un « contain »,
     le fond de la carte dépassait de part et d'autre d'une couverture au
     format portrait : deux bandes sombres, que rien ne justifiait. */
  position: relative; width: fit-content; max-width: 100%; margin-inline: auto;
  border-radius: var(--r-m); overflow: hidden;
  box-shadow: var(--relief-2);
}
.auteur__portrait img { display: block; width: auto; max-width: 100%; max-height: 64svh; }
.auteur__fiche { display: grid; gap: 2px; }
.auteur__ligne {
  display: grid; grid-template-columns: minmax(96px, auto) 1fr; gap: 16px;
  padding: 13px 0; border-bottom: 1px solid var(--filet);
  align-items: baseline;
}
.auteur__ligne dt { color: var(--texte-3); }
.auteur__ligne dd { margin: 0; color: var(--texte); }

/* ==========================================================================
   LE MERCH — une salle de la boutique, pas la destination du site
   ========================================================================== */
.boutique {
  display: grid; gap: clamp(16px, 2vw, 28px);
  grid-template-columns: repeat(auto-fit, minmax(min(248px, 100%), 1fr));
  margin-top: clamp(38px, 6vh, 72px);
}
.article {
  position: relative; display: grid; grid-template-rows: auto 1fr;
  background: var(--fond-carte);
  border: 1px solid var(--filet); border-radius: var(--r-m);
  overflow: hidden;
  box-shadow: var(--relief-1);
  transition: translate var(--t-moyen) var(--sortie),
              box-shadow var(--t-moyen) var(--sortie),
              border-color var(--t-moyen) linear;
}
@media (hover: hover) {
  .article:hover { translate: 0 -7px; box-shadow: var(--relief-3);
                   border-color: var(--filet-fort); }
}
/* Les visuels produits sont désormais NORMALISÉS AU CARRÉ (voir normalise.py).
   Une vignette en 4/3 les rognait ou leur ajoutait des bandes selon le sens. */
.article__vue {
  aspect-ratio: 1; display: grid; place-items: center; overflow: hidden;
  background: radial-gradient(120% 100% at 50% 0%,
      color-mix(in oklab, var(--hst-nuit) 26%, var(--fond-carte)), var(--fond-carte));
  padding: 22px;
}
.article__vue img { max-height: 100%; width: auto; filter: drop-shadow(0 16px 30px rgba(0,0,0,.7)); }
.article__corps { padding: clamp(16px, 1.8vw, 24px); display: grid; gap: 7px; align-content: start; }
.article__nom { font-weight: 700; font-stretch: 88%; letter-spacing: -.015em;
                font-size: 17px; margin: 0; }
.article__desc { margin: 0; color: var(--texte-2); font-size: 14.5px; }
.article__pied { display: flex; align-items: center; justify-content: space-between;
                 gap: 12px; margin-top: 8px; }
.article__prix { font-family: 'Plex Mono', monospace; font-weight: 600; font-size: 15px; }
.fanion {
  display: inline-block;
  font-family: 'Plex Mono', monospace; font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase;
  border: 1px solid var(--filet-fort); border-radius: 100px;
  padding: 4px 9px; color: var(--texte-2);
}

/* Boutons */
.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 48px;                      /* cible tactile : jamais sous 44 px */
  padding: 13px 22px;
  border-radius: 100px; border: 1px solid transparent;
  font-weight: 600; font-size: 15px; letter-spacing: -.01em;
  cursor: pointer;
  transition: translate var(--t-court) var(--sortie),
              background-color var(--t-court) linear,
              border-color var(--t-court) linear;
}
.bouton--plein { background: var(--hst-vert-vif); color: #0b0d05; }
.bouton--plein:hover { background: #d6f45e; translate: 0 -2px; }
.bouton--creux { border-color: var(--filet-fort); color: var(--texte); background: transparent; }
.bouton--creux:hover { border-color: var(--papier); translate: 0 -2px; }

/* ==========================================================================
   LA FAQ — les tiroirs sont FERMÉS par défaut
   ========================================================================== */
.faq { display: grid; margin-top: clamp(30px, 5vh, 56px); border-top: 1px solid var(--filet); }
.faq details { border-bottom: 1px solid var(--filet); }
.faq summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: clamp(17px, 2vw, 25px) 0;      /* ≥ 44 px de haut */
  font-weight: 600; font-stretch: 90%; letter-spacing: -.015em;
  font-size: clamp(16px, 1vw + 13px, 20px);
  transition: color var(--t-court) linear;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--accent); }
.faq summary::after {
  content: ''; flex: none; width: 13px; height: 13px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  rotate: 45deg; translate: 0 -3px;
  transition: rotate var(--t-moyen) var(--sortie), translate var(--t-moyen) var(--sortie);
}
.faq details[open] summary::after { rotate: -135deg; translate: 0 3px; }
.faq__reponse { padding-bottom: clamp(18px, 2.4vw, 28px); color: var(--texte-2); max-width: 68ch; }
.faq__reponse p { margin: 0 0 .85em; }
.faq__reponse p:last-child { margin-bottom: 0; }

/* ==========================================================================
   LE PIED
   ========================================================================== */
.pied {
  border-top: 1px solid var(--filet);
  padding-block: clamp(48px, 8vh, 96px) clamp(30px, 5vh, 54px);
  background: var(--fond-haut);
}
.pied__haut { display: grid; gap: clamp(28px, 4vw, 64px); align-items: start; }
@media (min-width: 760px) {
  /* Le bloc du logo porte une image large et deux lignes de texte ; les deux
     listes qui suivent tiennent en trois liens. Des colonnes égales laissaient
     un trou à gauche et tassaient le lettrage. */
  .pied__haut { grid-template-columns: 1.6fr .8fr 1fr; }
}
.pied__bas {
  display: flex; flex-wrap: wrap; gap: 14px 26px;
  justify-content: space-between; align-items: center;
  margin-top: clamp(40px, 6vh, 70px); padding-top: 24px;
  border-top: 1px solid var(--filet); color: var(--texte-3);
}
.pied a:hover { color: var(--accent); }
.pied__liens { display: grid; gap: 11px; }
.pied__liens a {
  color: var(--texte-2); width: fit-content;
  min-height: 44px; display: inline-flex; align-items: center;
}
.pied__liens { gap: 0; }

/* ==========================================================================
   MOUVEMENT RÉDUIT
   On ne se contente pas de couper les transitions : chaque élément doit se
   retrouver dans son état FINAL lisible, sinon la page reste vide pour qui a
   désactivé les animations. C'est la faute classique, et elle est grave :
   elle rend le site inutilisable pour ceux-là mêmes qu'on croyait protéger.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-apparait] { --e: 1 !important; opacity: 1 !important; translate: none !important;
                    rotate: none !important; scale: none !important; filter: none !important; }
  .plan { translate: none !important; scale: none !important; }
  .ville { height: auto !important; }
  .ville__cadre { position: static; height: auto; padding-block: clamp(40px, 7vh, 80px); }
  .ville__scene { width: 100%; }
  .ville__piece { translate: none !important; scale: none !important;
                  rotate: none !important; opacity: 1 !important; display: block; }
  .ville__planche { max-height: none; }
  .ville__lustre { display: none; }
  .detail { height: auto; }
  .detail__cadre { position: static; height: auto; padding-block: clamp(40px, 7vh, 80px); }
  .detail__viseur { height: auto; overflow-x: auto; }   /* on défile à la main */
  .detail__bande { position: static; height: auto; width: auto; max-width: none;
                   translate: none !important; }
  .ville__nom { position: static; translate: none !important; font-size: clamp(40px, 9vw, 90px);
                white-space: normal; margin-bottom: 14px; }
  .ville__cartouche, .ville__traque, .ville__jauge { position: static; opacity: 1 !important;
                margin-top: 16px; text-align: left; }
  .ouverture { height: auto; }
  .ouverture__cadre { position: static; height: auto; padding-block: 18vh; }
  .portail { height: auto; }
  .portail__cadre { position: static; height: auto; padding-block: 12vh; }
  .portail__disque { scale: 1 !important; filter: none !important; }
  .portail__mot { position: static; opacity: 1 !important; }
  .spirale { animation: none; rotate: 0deg; }
  .seuil, .matiere { display: none; }
  .shlakkk__mot { scale: 1 !important; opacity: 1 !important; }
}

/* Écrans courts et paysage téléphone : le sticky plein écran étouffe. */
@media (max-height: 520px) and (orientation: landscape) {
  .ouverture__cadre, .ville__cadre, .portail__cadre { height: 100vh; }
  .ville__planche { max-height: 62vh; }
  .detail__viseur { height: 52vh; }
  .ouverture__pied { display: none; }
}

/* ==========================================================================
   LA TRACE DE WAX — le curseur
   --------------------------------------------------------------------------
   Sur un curb, un skateur passe la bougie avant de slider ; il reste une trace
   de craie qui s'efface toute seule. Le curseur reprend ce geste.
   La classe « a-du-wax » n'est posée par scene.js que sur un appareil qui a
   vraiment un pointeur fin : sur un écran tactile, il n'y a aucun curseur à
   remplacer, et masquer celui du système n'aurait aucun sens.
   ========================================================================== */
:root { --wax: #e4dcc4; }          /* craie pâle, à peine teintée */

.wax-toile {
  position: fixed; inset: 0; z-index: 95;
  pointer-events: none;
}
/* LE CRAYON. Sa POINTE est à (4,40) dans un viewBox de 44 : la marge négative
   l'amène exactement sur le pixel visé, sinon la trace partirait d'à côté. */
.wax-crayon {
  position: fixed; top: 0; left: 0; z-index: 96;
  width: 26px; height: 26px;
  /* la pointe est en (4,4) d'un viewBox de 44, rendu a 26 : 4 x 26/44 = 2,4 px */
  margin: -2.4px 0 0 -2.4px;
  pointer-events: none;
  translate: -200px -200px;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.35));
  transition: scale 170ms var(--sortie), rotate 260ms var(--sortie),
              opacity 160ms linear;
  transform-origin: 2.4px 2.4px;     /* on pivote autour de la pointe */
}
.wax-crayon svg { display: block; overflow: visible; }
/* Teintes DÉSATURÉES : le jaune franc d'un crayon d'écolier attirait l'œil
   plus que le contenu. Ici, du bois pâle, du graphite, un rose éteint. */
.wax-crayon__mine   { fill: #2f2f36; }
.wax-crayon__bois   { fill: #e9dfc8; }
.wax-crayon__fut    { fill: #cfc6b2; }
.wax-crayon__virole { fill: #a9aeb4; }
.wax-crayon__gomme  { fill: #c2938c; }
/* Un liseré posé, pas un cerne. */
.wax-crayon__corps  { stroke: rgba(0,0,0,.20); stroke-width: .6; stroke-linejoin: round; }

/* Au-dessus de ce qui se clique, le crayon se redresse et grandit : c'est ce
   qui remplace l'affordance du curseur système, qu'on vient de cacher. */
.wax-crayon--actif { scale: 1.16; rotate: 10deg; }
.wax-crayon--appui { scale: .88; rotate: -6deg; }

/* On ne cache le curseur système QUE si le nôtre est effectivement en place. */
html.a-du-wax, html.a-du-wax * { cursor: none !important; }
/* Sauf pour la saisie : un champ de texte sans curseur est intenable. */
html.a-du-wax :where(input, textarea, select) { cursor: auto !important; }

@media (prefers-reduced-motion: reduce) {
  .wax-toile, .wax-crayon { display: none !important; }
  html.a-du-wax, html.a-du-wax * { cursor: auto !important; }
}

/* ==========================================================================
   LA BARRE DE DÉFILEMENT
   Par défaut le navigateur en pose une claire, qui tranche violemment sur la
   nuit du site — c'est la seule pièce d'interface qu'on ne dessine jamais, et
   ça se voit. On l'habille, dans les deux syntaxes : la standard (Firefox et
   les versions récentes) et celle de WebKit, qui reste nécessaire pour régler
   la largeur et les coins.
   ========================================================================== */
html {
  scrollbar-color: var(--barre-pouce, #2a2f3d) var(--barre-piste, #0a0c11);
  scrollbar-width: thin;
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track {
  background: var(--barre-piste, #0a0c11);
  border-left: 1px solid var(--filet);
}
::-webkit-scrollbar-thumb {
  background: var(--barre-pouce, #2a2f3d);
  border-radius: 10px;
  border: 2px solid var(--barre-piste, #0a0c11);   /* le liseré affine le pouce */
}
::-webkit-scrollbar-thumb:hover { background: var(--barre-pouce-survol, #3c4354); }
::-webkit-scrollbar-corner { background: var(--barre-piste, #0a0c11); }

/* ==========================================================================
   LA LOUPE — une planche en grand
   ========================================================================== */
.loupe {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center;
  padding: clamp(14px, 3vw, 48px);
  background: rgba(4,5,8,.94);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  opacity: 0;
  transition: opacity 260ms var(--doux);
  cursor: zoom-out;
}
.loupe--visible { opacity: 1; }
.loupe[hidden] { display: none; }
.loupe__image {
  width: 100%; height: 100%;
  min-width: 0; min-height: 0;
  /* entière, jamais rognée : c'est tout l'intérêt de l'agrandir */
  object-fit: contain;
  border-radius: var(--r-s);
  box-shadow: 0 40px 90px -30px rgba(0,0,0,1);
  scale: .965;
  transition: scale 320ms var(--sortie);
}
.loupe--visible .loupe__image { scale: 1; }
.loupe__fermer {
  position: absolute; top: clamp(10px, 2vw, 24px); right: clamp(10px, 2vw, 24px);
  width: 48px; height: 48px;             /* cible tactile */
  display: grid; place-items: center;
  background: rgba(255,255,255,.08); border: 1px solid var(--filet);
  border-radius: 50%; cursor: pointer; color: var(--papier);
  transition: background-color var(--t-court) linear;
}
.loupe__fermer:hover { background: rgba(255,255,255,.18); }
.loupe__fermer span, .loupe__fermer span::before {
  content: ''; display: block; width: 17px; height: 1.6px;
  background: currentColor; border-radius: 2px; rotate: 45deg;
}
.loupe__fermer span::before { rotate: 90deg; }

/* La page derrière ne doit pas défiler pendant qu'on regarde. */
body.loupe-ouverte { overflow: hidden; }

/* Ce qui est agrandissable doit se voir comme tel. */
[data-agrandir] { cursor: zoom-in; }
html.a-du-wax [data-agrandir] { cursor: none !important; }
@media (prefers-reduced-motion: reduce) {
  .loupe { transition: none; }
  .loupe__image { scale: 1 !important; transition: none; }
}

/* ==========================================================================
   LE SÉLECTEUR DE LANGUE
   Deux pages RÉELLES, pas un basculement JavaScript : le HTML servi doit être
   dans la langue qu'il annonce, sinon les moteurs (et les robots d'IA) ne
   voient jamais que la version d'origine. Chaque lien pointe vers la page
   équivalente, et les deux se déclarent l'une l'autre par « hreflang ».
   ========================================================================== */
.menu__langue {
  display: inline-flex; align-items: center; gap: 2px;
  flex: none; margin-left: clamp(6px, 1.4vw, 18px);
  font-family: 'Plex Mono', monospace; font-size: 11px; letter-spacing: .12em;
}
.menu__langue a {
  min-width: 34px; min-height: 44px;      /* cible tactile */
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--texte-3);
  border-radius: var(--r-s);
  transition: color var(--t-court) linear, background-color var(--t-court) linear;
}
.menu__langue a:hover { color: var(--texte); }
.menu__langue a[aria-current] {
  color: #0b0d05; background: var(--papier); font-weight: 600;
}
.menu__langue span { color: var(--texte-3); opacity: .5; }

/* Le sélecteur reste visible hors du tiroir : changer de langue ne doit pas
   demander d'ouvrir un menu. */
@media (max-width: 860px) {
  /* Dans le DOM, le bouton du tiroir précède le sélecteur (il doit rester
     proche de la marque pour les lecteurs d'écran). À l'affichage, on veut
     l'inverse : la langue, puis le bouton tout à droite. */
  .menu__langue { margin-left: auto; margin-right: 2px; order: 1; }
  .menu__bouton { order: 2; }
}

/* La pastille d'aperçu : ces planches sont des extraits, pas l'œuvre entière —
   il faut que ça se dise, et à l'endroit où l'œil arrive. */
.ville__apercu {
  position: absolute; z-index: 4;
  top: clamp(74px, 10vh, 108px); right: var(--marge);
  margin: 0; padding: 6px 11px;
  border: 1px solid var(--filet-fort); border-radius: 100px;
  background: rgba(7,8,11,.62);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: var(--texte);
  letter-spacing: .16em;
}
/* La traque descend d'un cran pour ne pas passer sous la pastille. */
.ville__traque { top: clamp(122px, 16vh, 168px); }

/* Le bouton d'achat du pied : présent sur chaque page, il mène droit à la
   bande dessinée dans la boutique. C'est la seule action commerciale du site,
   elle ne doit jamais être à plus d'un clic. */
.pied__achat {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 48px; padding: 13px 24px;
  margin-top: clamp(20px, 3vh, 30px);
  border-radius: 100px;
  background: var(--hst-vert-vif); color: #0b0d05;
  font-weight: 600; font-size: 15px; letter-spacing: -.01em;
  transition: translate var(--t-court) var(--sortie), background-color var(--t-court) linear;
}
.pied__achat:hover { background: #d6f45e; translate: 0 -2px; color: #0b0d05; }
.pied__achat::after { content: '→'; font-size: 17px; line-height: 1; }
.atelier-page .pied__achat { color: #0b0d05; }

/* ==========================================================================
   L'ŒUVRE — la bande dessinée elle-même
   Ce n'est pas un article parmi les autres : c'est le produit. Elle passe donc
   AVANT les familles, sur toute la largeur, et les deux façons de l'obtenir
   sont posées côte à côte plutôt que cachées dans un menu.
   ========================================================================== */
.oeuvre {
  display: grid; gap: clamp(24px, 3.6vw, 56px);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  margin-block: clamp(34px, 5vh, 60px) clamp(10px, 2vh, 24px);
  padding: clamp(22px, 3vw, 44px);
  background: color-mix(in oklab, var(--fond-carte) 92%, #000);
  border: 1px solid var(--filet-fort); border-radius: var(--r-l);
  box-shadow: var(--relief-3);
  position: relative; overflow: hidden;
}
@media (min-width: 880px) { .oeuvre { grid-template-columns: .78fr 1.22fr; } }
.oeuvre::after {          /* un halo vert, discret, qui la désigne */
  content: ''; position: absolute; inset: auto -20% -60% auto;
  width: 70%; aspect-ratio: 1; pointer-events: none;
  background: radial-gradient(circle,
      color-mix(in oklab, var(--hst-ciel) 16%, transparent), transparent 68%);
}
.oeuvre > * { position: relative; z-index: 1; }
.oeuvre__couv {
  justify-self: center; width: min(300px, 80%);
  border-radius: var(--r-s); overflow: hidden;
  box-shadow: 0 30px 60px -24px rgba(0,0,0,1);
  rotate: -1.4deg;
  transition: rotate var(--t-moyen) var(--sortie), translate var(--t-moyen) var(--sortie);
}
@media (hover: hover) { .oeuvre__couv:hover { rotate: 0deg; translate: 0 -6px; } }
.oeuvre__couv img { width: 100%; display: block; }

.formats { display: grid; gap: clamp(12px, 1.6vw, 18px); margin-top: clamp(20px, 3vh, 30px);
           grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); }
.format {
  display: grid; gap: 7px; align-content: start;
  padding: clamp(16px, 1.9vw, 22px);
  border: 1px solid var(--filet); border-radius: var(--r-m);
  background: rgba(255,255,255,.03);
}
.format__titre { display: flex; align-items: center; gap: 9px; margin: 0;
                 font-size: 16px; font-weight: 700; font-stretch: 88%; letter-spacing: -.015em; }
.format__titre span {                       /* la pastille de format */
  font-family: 'Plex Mono', monospace; font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: #0b0d05; background: var(--accent);
  border-radius: 3px; padding: 3px 6px; line-height: 1;
}
.format p { margin: 0; color: var(--texte-2); font-size: 14.5px; line-height: 1.55; }

/* ==========================================================================
   LA TRADUCTION DES PLANCHES (version anglaise)
   Les bulles sont dessinées à la main, en français, dans l'image : on ne peut
   pas les traduire sans repeindre l'œuvre. On donne donc le texte À CÔTÉ,
   replié par défaut — qui lit le français n'en a pas besoin, qui ne le lit pas
   y trouve toute la planche.
   ========================================================================== */
.bulles { margin-top: 10px; }
.bulles summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 2px;
  font-family: 'Plex Mono', monospace; font-size: 11px;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent);
}
.bulles summary::-webkit-details-marker { display: none; }
.bulles summary::after {
  content: ''; width: 8px; height: 8px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  rotate: 45deg; translate: 0 -2px;
  transition: rotate var(--t-court) var(--sortie);
}
.bulles[open] summary::after { rotate: -135deg; translate: 0 2px; }
.bulles ol {
  margin: 8px 0 0; padding: 0 0 0 1px; list-style: none;
  display: grid; gap: 7px;
  border-left: 2px solid var(--filet-fort); padding-left: 14px;
}
.bulles li { color: var(--texte-2); font-size: 14.5px; line-height: 1.5; }
.bulles li b { color: var(--texte); font-weight: 600; }

/* ==========================================================================
   LES VILLES SUR TÉLÉPHONE
   Sur un écran étroit, la planche est bornée par la LARGEUR : 359 px de large
   pour 243 de haut, posés au milieu d'un cadre de 844. Les deux tiers de
   l'écran restaient vides. On donne donc toute la largeur à la planche, et on
   remonte le cartouche juste dessous au lieu de le coller en bas de l'écran.
   ========================================================================== */
@media (max-width: 720px) {
  /* Le cartouche remis dans le flux devient un SECOND élément de grille : avec
     « place-items: center », chaque ligne se centre dans sa propre moitié et
     les deux s'écartent. « align-content » regroupe les lignes ensemble au
     centre du cadre — c'est ce qu'on veut. */
  .ville__cadre {
    align-content: center; justify-items: center;
    row-gap: clamp(14px, 2.4vh, 22px);
  }
  .ville__scene { width: 100vw; }
  .ville__piece { border-radius: 0; }
  .ville__planche { border-radius: 0; }

  /* Le cartouche rejoint la planche : il en devient l'étiquette, au lieu de
     flotter seul tout en bas. */
  .ville__cartouche {
    position: static; opacity: 1 !important; translate: none !important;
    width: min(100% - var(--marge) * 2, 560px);
    margin-inline: auto;
    flex-wrap: nowrap; align-items: center;
  }
  /* Le nom de la ville passe DANS le flux, au-dessus de la planche : en fond
     il était masqué par l'image, et l'écran restait vide au-dessus. Ici il
     tient le haut du cadre et annonce la ville avant qu'on la voie. */
  .ville__nom {
    position: static; translate: none !important;
    /* « RÉPUBLIQUE » et « LA DÉFENSE » débordaient des deux côtés : dix signes
       à 19vw demandent 444 px sur un écran de 390. On laisse le nom se replier
       et on borne la taille à la largeur réellement disponible. */
    white-space: normal; text-wrap: balance;
    font-size: clamp(40px, 15vw, 108px);
    -webkit-text-stroke: 1px rgba(244,242,236,.34);
    line-height: .88; text-align: center;
    max-width: calc(100vw - var(--marge));
    overflow-wrap: anywhere;
  }
  .ville__traque { display: none; }      /* trop de textes concurrents ici */
  .ville__apercu { top: clamp(70px, 9vh, 92px); }
}

/* Écrans très étroits : le cartouche passe sur deux lignes plutôt que de
   comprimer le nom du lieu. */
@media (max-width: 430px) {
  .ville__cartouche { flex-wrap: wrap; gap: 10px; }
  .cartouche__lieu { font-size: 17px; }
}

/* ==========================================================================
   LE LIEN D'ÉVITEMENT
   Premier élément atteint au clavier : il permet de sauter la navigation et
   d'aller droit au contenu. Invisible tant qu'on ne l'atteint pas, il ne coûte
   rien à la mise en page — mais sans lui, on retraverse les six entrées du
   menu à chaque page.
   ========================================================================== */
.evitement {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  padding: 12px 20px; min-height: 44px;
  display: inline-flex; align-items: center;
  background: var(--hst-vert-vif); color: #0b0d05;
  border-radius: var(--r-s); font-weight: 600; font-size: 15px;
  translate: 0 -200%;
  transition: translate var(--t-court) var(--sortie);
}
.evitement:focus { translate: 0 0; }

/* ==========================================================================
   VARIER L'ARRIVÉE DES PLANCHES
   Les cinq villes se suivent avec exactement la même approche : on finit par
   voir le mécanisme au lieu de voir les dessins. Chaque planche arrive donc
   d'un angle légèrement différent — l'une penche à gauche, l'autre vient de
   plus loin, la suivante pivote un peu sur elle-même. L'écart reste petit
   (quelques degrés) : il se sent, il ne se remarque pas.
   ========================================================================== */
.ville:nth-of-type(2n) .ville__piece {
  rotate: x calc((1 - var(--pose)) * 6deg) y calc((1 - var(--pose)) * -7deg);
}
.ville:nth-of-type(3n) .ville__piece {
  --z-appro: calc((1 - var(--pose)) * -980);      /* celle-ci vient de plus loin */
  rotate: x calc((1 - var(--pose)) * 9deg) z calc((1 - var(--pose)) * 1.6deg);
}
.ville:nth-of-type(4n) .ville__piece {
  rotate: x calc((1 - var(--pose)) * 5deg) y calc((1 - var(--pose)) * 8deg);
}
/* Le reflet balaie dans l'autre sens une fois sur deux. */
.ville:nth-of-type(2n) .ville__lustre::after {
  background: linear-gradient(254deg,
     transparent 42%, rgba(255,255,255,.40) 50%, transparent 58%);
  translate: calc(60% - var(--p) * 150%) 0;
}

@media (prefers-reduced-motion: reduce) {
  .ville:nth-of-type(2n) .ville__piece,
  .ville:nth-of-type(3n) .ville__piece,
  .ville:nth-of-type(4n) .ville__piece { rotate: none !important; }
}

/* ==========================================================================
   FINITION TYPOGRAPHIQUE
   Ce qui sépare une page correcte d'une page soignée tient à des détails qu'on
   ne nomme pas en les voyant : le crénage des grandes tailles, des chiffres
   qui s'alignent quand ils doivent s'aligner, une césure qui évite les rivières
   sur écran étroit.
   ========================================================================== */
body {
  /* Archivo est VARIABLE : on l'autorise à ajuster son dessin à la taille
     rendue (l'axe optique), ce qu'elle ne fait pas d'elle-même partout. */
  font-optical-sizing: auto;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures contextual;
  /* Chiffres proportionnels dans le texte courant : des chiffres tabulaires
     au milieu d'une phrase laissent des trous. */
  font-variant-numeric: proportional-nums;
}

/* Là où les chiffres doivent S'ALIGNER verticalement, on les rend tabulaires :
   compteurs, codes de planche, relevés, prix. */
.compteur__valeur, .cartouche__code, .code-case, .mono,
.releve__ligne dd, .auteur__ligne dd, .article__prix {
  font-variant-numeric: tabular-nums;
}

/* Les très grands titres ont besoin d'un crénage plus serré que le texte
   courant : à 120 px, l'espacement par défaut d'une grotesque paraît lâche. */
.titre--geant { letter-spacing: -.05em; word-spacing: -.02em; }
.titre--grand { letter-spacing: -.038em; }

/* Sur écran étroit, un texte justifié à gauche en 34 caractères crée des
   fins de ligne très inégales. La césure les rattrape. */
@media (max-width: 620px) {
  .texte, .chapo, .faq__reponse, .produit__desc, .etape p, .bio-texte {
    hyphens: auto;
    -webkit-hyphens: auto;
  }
}

/* Les apostrophes et guillemets ne doivent jamais rester seuls en fin de ligne
   ni casser un mot : on protège les cas fréquents du français. */
.texte, .chapo, .bio-texte { text-wrap: pretty; }

/* ==========================================================================
   LA LUMIÈRE
   Une ombre parfaitement noire n'existe pas : dans une pièce, elle prend la
   couleur de ce qui l'entoure. Sur cette nuit bleutée, les ombres tirent donc
   vers le bleu profond — et les objets cessent de paraître découpés au ciseau.
   ========================================================================== */
:root {
  --ombre-teinte: 224 40% 3%;     /* le bleu de la nuit, en teinte d'ombre */
  --relief-1: 0 1px 0 rgba(244,242,236,.10),
              0 10px 24px -12px hsl(var(--ombre-teinte) / .92);
  --relief-2: 0 1px 0 rgba(244,242,236,.14),
              0 4px 10px -6px hsl(var(--ombre-teinte) / .7),
              0 26px 60px -24px hsl(var(--ombre-teinte) / 1);
  --relief-3: 0 1px 0 rgba(244,242,236,.18),
              0 6px 14px -8px hsl(var(--ombre-teinte) / .75),
              0 60px 120px -40px hsl(var(--ombre-teinte) / 1);
}

/* ==========================================================================
   L'ARRIVÉE DES IMAGES
   Une image différée apparaît d'un coup. Sur une page où tout glisse, c'est
   la seule chose qui claque. On la laisse monter — très vite, sans que ça
   devienne un effet.
   ========================================================================== */
img[loading="lazy"]:not([data-chargee]) { opacity: 0; scale: 1.012; }
img[loading="lazy"] {
  transition: opacity 520ms var(--sortie), scale 620ms var(--sortie);
}
img[data-chargee] { opacity: 1; scale: 1; }

/* ==========================================================================
   LES ÉTATS
   Un bouton qu'on presse doit répondre sous le doigt. Sans état « actif », le
   clic sur mobile ne renvoie rien du tout — l'interface paraît morte.
   ========================================================================== */
.bouton:active, .pied__achat:active, .sommaire__lien:active {
  translate: 0 1px;
  transition-duration: 60ms;
}
.bouton--plein:active { background: #c2e043; }

/* Le liseré spéculaire : une arête de lumière sur le haut des cartes, comme
   sur un objet réel éclairé par le dessus. Elle s'avive au survol. */
.article, .produit, .etape, .format, .oeuvre {
  position: relative;
}
.article::before, .produit::before, .format::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; z-index: 2;
  background: linear-gradient(180deg, rgba(255,255,255,.09), transparent 22%);
  opacity: .8;
  transition: opacity var(--t-moyen) linear;
}
@media (hover: hover) {
  .article:hover::before, .produit:hover::before { opacity: 1; }
}
/* Les cartes d'étape portent leur numéro dans ::before : leur liseré passe
   donc par ::after, qui est libre. */
.etape::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255,255,255,.09), transparent 22%);
}

/* ==========================================================================
   LE FOCUS
   Deux anneaux : un clair sur le bord de l'élément, un sombre juste derrière.
   Le clair seul disparaît sur un fond clair, le sombre seul sur la nuit ;
   ensemble, ils se voient partout.
   ========================================================================== */
:where(button, a, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  box-shadow: 0 0 0 5px rgba(7,8,11,.85);
  border-radius: var(--r-s);
}

/* ==========================================================================
   L'OUVERTURE DES TIROIRS
   Un <details> s'ouvre d'un coup : sur une page où tout le reste glisse, c'est
   le seul geste qui claque. « interpolate-size » permet enfin d'animer une
   hauteur automatique — sous @supports, car les navigateurs qui l'ignorent
   gardent le comportement natif, qui reste parfaitement utilisable.
   ========================================================================== */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }

  .faq details::details-content,
  .bulles::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 420ms var(--sortie),
                opacity 300ms linear,
                content-visibility 420ms allow-discrete;
    opacity: 0;
  }
  .faq details[open]::details-content,
  .bulles[open]::details-content {
    block-size: auto;
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  @supports (interpolate-size: allow-keywords) {
    .faq details::details-content, .bulles::details-content {
      transition: none; opacity: 1;
    }
  }
}

/* ==========================================================================
   LES LIAISONS DE SECTION
   Deux sections de fonds différents se touchent par une arête nette. Un fondu
   de quelques dizaines de pixels suffit à les faire appartenir à la même page.
   ========================================================================== */
.prologue { position: relative; }
.prologue::before, .prologue::after {
  content: ''; position: absolute; inset-inline: 0; height: clamp(60px, 9vh, 120px);
  pointer-events: none; z-index: 1;
}
.prologue::before { top: 0;    background: linear-gradient(to bottom, var(--fond), transparent); }
.prologue::after  { bottom: 0; background: linear-gradient(to top,    var(--fond), transparent); }
.prologue > * { position: relative; z-index: 2; }

/* ==========================================================================
   LES TRANSITIONS ENTRE PAGES
   Passer du récit à la boutique, ou du français à l'anglais, provoque un
   clignotement blanc : le navigateur repeint tout. Une transition de vue
   enchaîne les deux pages en un fondu — la navigation cesse d'être une
   rupture. Réservé aux navigateurs qui la gèrent ; ailleurs, rien ne change.
   ========================================================================== */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation: sortirPage 220ms var(--doux) both; }
::view-transition-new(root) { animation: entrerPage 320ms var(--sortie) both; }
@keyframes sortirPage { to   { opacity: 0; scale: .992; } }
@keyframes entrerPage { from { opacity: 0; scale: 1.006; } }

/* Le menu ne doit pas clignoter d'une page à l'autre : il est identique
   partout, on le fait donc traverser la transition sans être repeint. */
.menu { view-transition-name: menu-fixe; }
::view-transition-group(menu-fixe) { animation-duration: 220ms; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ==========================================================================
   À L'IMPRESSION
   Les mentions légales sont la seule page qu'on imprime vraiment. Sur papier,
   la nuit du site coûte une cartouche entière pour un texte illisible.
   ========================================================================== */
@media print {
  .matiere, .vignette, .menu, .wax-toile, .wax-crayon, .rideau,
  .evitement, .pied__achat, .loupe { display: none !important; }
  html, body { background: #fff !important; color: #111 !important; }
  .legal a { color: #111 !important; }
  .legal a[href^="http"]::after {
    content: " (" attr(href) ")";       /* une URL imprimée doit être lisible */
    font-size: .85em; color: #555; word-break: break-all;
  }
  .legal h2 { break-after: avoid; }
  .legal .ligne { break-inside: avoid; }
  .pied { border-top: 1px solid #ccc; background: none !important; }
  * { box-shadow: none !important; text-shadow: none !important; }
}

/* ==========================================================================
   LE TÉLÉPHONE — retours de Brice, 12/09/2026 (premier tour sur smartphone)
   ========================================================================== */

/* LE BLOC VERT. Ce n'était pas un décor : c'était la SÉLECTION. « ::selection »
   est peint du vert du site ; au doigt, un appui un peu long en déclenche une
   sans le vouloir, et une image sélectionnée se couvre d'un aplat de cette
   couleur — la planche entière devenait un bloc vert. Il n'y a rien à copier
   dans une scène de ville ni dans une image : on les sort de la sélection. */
.ville__cadre, img { -webkit-user-select: none; user-select: none; }

/* La boutique en une image : présente dans la page, montrée sur téléphone seulement. */
.mosaique { display: none; }

@media (max-width: 720px) {
  /* L'OUVERTURE. Sur ordinateur le lettrage fait 880 px et la spirale remplit
     l'écran : c'est une page de titre. À 390 px, 72vw donnait un bandeau de
     64 px de haut perdu au milieu de 844 — ni le titre ni le portail ne se
     voyaient. Le titre prend toute la largeur, la spirale assez de diamètre
     pour tenir l'écran entier autour de lui. */
  .logotype {
    width: 94vw;
    filter:
      drop-shadow(0 0 30px rgba(24,108,168,.75))
      drop-shadow(0 12px 28px rgba(0,0,0,.85));
  }
  .spirale { width: 160vw; opacity: .85; }
  .ouverture__sur  { text-shadow: 0 1px 12px rgba(7,8,11,.95); }
  .ouverture__sous { text-shadow: 0 1px 14px rgba(7,8,11,.95), 0 0 4px rgba(7,8,11,.9); }

  /* SHLAKKK : 13vw faisait 50 px — un murmure pour un coup de hache. */
  .shlakkk__mot { font-size: 23vw; }

  /* « Des siècles plus tard » : l'entrée en profondeur ne se lit pas sur un
     écran étroit, il ne restait qu'une phrase seule sur un écran noir. */
  .portail { display: none; }

  /* La boutique : une image pêle-mêle et UN bouton, au lieu de quatre cartes
     empilées qui faisaient quatre écrans. Le bouton est centré par les marges
     et non par « translate », que .bouton réécrit au survol et à l'appui. */
  .boutique, .merch__actions { display: none !important; }
  .mosaique {
    display: block; position: relative;
    margin-top: clamp(30px, 5vh, 48px);
    border-radius: var(--r-m); overflow: hidden;
    box-shadow: var(--relief-2);
  }
  .mosaique img { display: block; width: 100%; }
  .mosaique::after {
    content: ''; position: absolute; inset: auto 0 0 0; height: 34%;
    background: linear-gradient(to bottom, transparent, rgba(7,8,11,.94) 78%);
    pointer-events: none;
  }
  .mosaique__cta {
    position: absolute; z-index: 1; left: 0; right: 0; bottom: 22px;
    width: max-content; margin-inline: auto;
  }
}

/* ==========================================================================
   LES VILLES SUR TÉLÉPHONE, SANS ANIMATION — 12/09/2026, second retour
   --------------------------------------------------------------------------
   Sur un vrai téléphone, la scène de profondeur (cadre collant, perspective,
   pièce en translateZ, reflet en « overlay », pastilles en backdrop-filter)
   produisait un immense aplat vert qui recouvrait l'écran. Aucun navigateur
   de test ne le reproduit : c'est le GPU du téléphone qui craque sur cet
   empilement. On ne rafistole pas un effet qu'on ne voit pas : sur téléphone
   les planches se posent à plat, dans le flux, et on les regarde. L'ordinateur
   garde l'animation telle quelle.
   ========================================================================== */
@media (max-width: 720px) {
  .ville { height: auto !important; }
  .ville__cadre {
    /* « clip » en largeur seulement : visible, un élément dépassait de 2 px et
       tout l'écran se décalait sous le doigt ; l'ombre de la planche, en
       hauteur, reste visible. */
    position: relative; height: auto; overflow: visible; overflow-x: clip;
    perspective: none;
    padding-block: clamp(40px, 7vh, 72px);
    background: none;
  }
  .ville__ciel, .ville__lustre, .ville__jauge, .ville__traque { display: none; }
  .ville__scene { transform-style: flat; }
  .ville__piece,
  .ville:nth-of-type(n) .ville__piece {
    translate: none !important; scale: none !important; rotate: none !important;
    opacity: 1 !important; will-change: auto;
    box-shadow: 0 1px 0 rgba(255,255,255,.18), 0 18px 40px -18px rgba(0,0,0,.9);
  }
  .ville__apercu {
    position: static; justify-self: center;
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .cartouche__code { backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ==========================================================================
   L'OUVERTURE SUR TÉLÉPHONE — une vraie page de titre, 12/09/2026
   --------------------------------------------------------------------------
   Deux défauts, deux causes. (1) En une ligne, le lettrage ne peut PAS remplir
   un écran vertical : il est quatre fois plus large que haut. On sert donc le
   même dessin, mots empilés (logotype-pile.webp). (2) Ses lettres sont d'un
   bleu marine qui se confond avec la nuit : sur fond sombre, on ne voyait que
   les rayures claires, le titre paraissait transparent. Un liseré clair et un
   halo de portail derrière lui le détachent. Comme pour les villes, la
   profondeur 3D est retirée sur téléphone : le titre est posé, entier, opaque.
   ========================================================================== */
@media (max-width: 720px) {
  .ouverture { height: auto; }
  .ouverture__cadre {
    position: relative; height: 100svh; min-height: 520px;
    perspective: none;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: clamp(16px, 3vh, 28px);
    padding: 72px var(--marge) 70px;
  }
  .ouverture .plan {
    position: static; inset: auto; display: block;
    translate: none !important; scale: none !important; opacity: 1 !important;
    transform-style: flat; will-change: auto;
  }
  .ouverture .plan:has(.spirale) { position: absolute; inset: 0; display: grid; place-content: center; }
  .ouverture .plan:has(.ouverture__sur) { order: -1; }
  .spirale { filter: none; }
  .seuil { display: none; }

  .ouverture .titre { position: relative; isolation: isolate; line-height: 0; }
  .ouverture .titre::before {            /* le cœur lumineux du portail */
    content: ''; position: absolute; inset: -18% -34%; z-index: -1;
    background: radial-gradient(closest-side,
        rgba(206,228,255,.62), rgba(120,170,232,.26) 55%, transparent);
    pointer-events: none;
  }
  .logotype {
    width: min(80vw, calc(52svh * .669));
    filter:
      drop-shadow(0 0 1.5px rgba(238,245,255,.95))
      drop-shadow(0 0 1px rgba(238,245,255,.9))
      drop-shadow(0 14px 26px rgba(0,0,0,.7));
  }
  .ouverture__sur { max-width: none; text-wrap: balance; }
}

/* ==========================================================================
   LIONEL EN PERSONNE — 12/09/2026
   Sa photo se place juste SOUS sa bio (téléphone : bio, photo, puis la fiche).
   Sur ordinateur, elle tient la colonne de droite sur toute la hauteur de la
   section, à côté du texte qu'elle illustre. La couverture qui occupait cette
   place reste présentée dans la boutique, juste en dessous.
   ========================================================================== */
.auteur__photo { width: 100%; }
.auteur__photo img { width: 100%; max-height: none; }
@media (min-width: 900px) {
  .auteur { row-gap: 0; }
  .auteur__bio, .auteur__suite { grid-column: 1; }
  .auteur__photo {
    grid-column: 2; grid-row: 1 / span 2; align-self: start;
    width: fit-content; justify-self: center;
  }
  .auteur__photo img { width: auto; max-width: 100%; max-height: min(82svh, 780px); }
}

/* Accueil : les quatre familles en VITRINE. En format d'origine, les quatre
   cartes avaient quatre hauteurs et la rangée paraissait bancale. Un carré blanc
   identique pour les quatre, et le visuel ENTIER dedans (contain) : la rangée
   s'aligne, rien n'est recadré ni étiré — le blanc autour fait passe-partout. */
.article__vue { aspect-ratio: 1; padding: clamp(10px, 1.2vw, 16px); display: grid; place-items: center; background: #fff; }
.article__vue img { display: block; width: 100%; height: 100%; max-height: none !important; object-fit: contain; filter: none; }

/* ==========================================================================
   LE GRAIN DU PAPIER — 12/09/2026
   Les feuilles de l'atelier (bio.css) et le bandeau de la règle
   (accueil) portaient des PHOTOS de papier de 900 px, tirées en « cover »
   sur des feuilles de 600 px de haut : sur un téléphone (densité 2 à 3), le
   grain était agrandi deux à trois fois — flou, sale, « très zoomé ». Le grain
   est désormais DESSINÉ (bruit SVG), répété à sa taille native : net à toutes
   les densités, et il ne pèse rien. La teinte vient de la couleur de fond.
   ========================================================================== */
:root {
  --grain-papier: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='a' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.24 0 0 0 0 0.2 0 0 0 0 0.15 0 0 0 -0.30 0.156'/%3E%3C/filter%3E%3Cfilter id='b' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.03 .16' numOctaves='2' seed='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.24 0 0 0 0 0.2 0 0 0 0 0.15 0 0 0 -0.15 0.080'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url%28%23a%29'/%3E%3Crect width='260' height='260' filter='url%28%23b%29'/%3E%3C/svg%3E");
  --grain-papier-noir: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='a' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.95 0 0 0 0 0.93 0 0 0 0 0.88 0 0 0 -0.25 0.130'/%3E%3C/filter%3E%3Cfilter id='b' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.03 .16' numOctaves='2' seed='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.95 0 0 0 0 0.93 0 0 0 0 0.88 0 0 0 -0.12 0.064'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url%28%23a%29'/%3E%3Crect width='260' height='260' filter='url%28%23b%29'/%3E%3C/svg%3E");
}

/* ==========================================================================
   NUIT ET PAPIER — 12/09/2026
   Vingt écrans de nuit presque uniforme rendaient l'accueil monotone. La règle
   du jeu et l'atelier passent sur PAPIER — le même grain que la bio : on
   feuillette un carnet entre deux salles obscures. Les jetons sont redéfinis
   ici (encre au lieu de craie), les composants n'ont donc rien à savoir. Bord
   DÉCHIRÉ en haut et en bas : un polygone fixe, calculé une fois (pas de coût
   GPU, rien n'est animé). Contrastes : encre 13:1, texte-2 7:1, accent 6,5:1.
   ========================================================================== */
.bande--papier {
  --texte: #1f1d19;  --texte-2: #4a463e;  --texte-3: #5c574c;
  --filet: rgba(31,29,25,.16);  --filet-fort: rgba(31,29,25,.32);
  --fond-carte: rgba(255,253,247,.62);
  --accent: #1d4f86;                         /* le bleu du ciel, en encre */
  --relief-1: 0 1px 0 rgba(255,255,255,.7), 0 12px 22px -14px rgba(70,52,24,.5);
  --relief-2: 0 1px 0 rgba(255,255,255,.7), 0 22px 40px -22px rgba(70,52,24,.55);
  --relief-3: 0 2px 4px rgba(70,52,24,.16), 0 30px 56px -28px rgba(70,52,24,.6);
  color: var(--texte);
  background: #e8e2d2 var(--grain-papier) 0 0 / 260px 260px;
  color-scheme: light;
  clip-path: polygon(0.0% 6.7px, 2.95% 6.7px, 6.18% 1.4px, 9.45% 8.1px, 10.9% 2.2px, 13.3% 0.1px, 14.98% 8.2px, 17.86% 2.4px, 19.37% 1.1px, 20.57% 1.9px, 22.24% 7.9px, 24.08% 4.9px, 26.77% 2.8px, 29.49% 8.0px, 31.35% 1.5px, 32.87% 0.9px, 35.4% 2.0px, 37.34% 7.4px, 39.6% 4.3px, 42.35% 2.9px, 43.6% 7.6px, 44.84% 3.3px, 47.31% 0.1px, 48.91% 1.8px, 51.77% 8.5px, 53.73% 4.7px, 56.63% 2.2px, 59.59% 0.3px, 62.87% 1.0px, 65.41% 3.1px, 68.65% 2.8px, 70.54% 0.2px, 72.07% 9.0px, 73.63% 3.0px, 76.0% 2.1px, 78.51% 4.1px, 80.63% 2.7px, 81.91% 7.5px, 83.39% 8.5px, 85.98% 1.0px, 88.14% 2.5px, 89.99% 9.0px, 93.06% 4.1px, 95.33% 4.3px, 97.17% 1.3px, 99.2% 8.6px, 100% 3.0px, 100% calc(100% - 3.2px), 99.64% calc(100% - 7.4px), 97.8% calc(100% - 4.0px), 96.33% calc(100% - 4.3px), 94.05% calc(100% - 8.9px), 91.93% calc(100% - 4.5px), 90.16% calc(100% - 5.4px), 86.85% calc(100% - 3.4px), 85.01% calc(100% - 2.0px), 82.98% calc(100% - 8.0px), 81.47% calc(100% - 5.5px), 80.03% calc(100% - 0.0px), 78.06% calc(100% - 5.5px), 74.75% calc(100% - 5.1px), 73.24% calc(100% - 3.3px), 70.25% calc(100% - 3.8px), 66.94% calc(100% - 5.2px), 64.55% calc(100% - 0.9px), 63.27% calc(100% - 3.4px), 61.81% calc(100% - 3.7px), 59.72% calc(100% - 0.0px), 58.0% calc(100% - 5.3px), 55.96% calc(100% - 1.4px), 54.34% calc(100% - 1.6px), 52.84% calc(100% - 0.2px), 50.6% calc(100% - 0.4px), 47.9% calc(100% - 2.0px), 45.73% calc(100% - 0.5px), 42.94% calc(100% - 5.7px), 40.37% calc(100% - 3.8px), 37.89% calc(100% - 2.3px), 35.08% calc(100% - 6.5px), 32.83% calc(100% - 8.4px), 31.27% calc(100% - 4.8px), 27.96% calc(100% - 8.8px), 24.85% calc(100% - 7.6px), 21.83% calc(100% - 6.6px), 19.86% calc(100% - 7.2px), 16.87% calc(100% - 4.2px), 15.11% calc(100% - 1.6px), 12.64% calc(100% - 5.8px), 10.79% calc(100% - 6.2px), 8.52% calc(100% - 2.5px), 6.52% calc(100% - 6.8px), 3.79% calc(100% - 7.0px), 1.8% calc(100% - 7.8px), 0.0% calc(100% - 0.8px));
}
.bande--papier .compteur__valeur { color: var(--texte); }
.bande--papier [data-apparait='detache'] {
  filter: drop-shadow(0 calc(4px + (1 - var(--e)) * 30px) calc(10px + (1 - var(--e)) * 34px)
                      rgba(70,52,24,calc(.16 + (1 - var(--e)) * .2)));
}
/* Le bandeau de la règle devient une feuille posée sur le carnet. */
.bande--papier .regle-carte {
  background: #fbf8f1 var(--grain-papier) 0 0 / 260px 260px;
  rotate: -.6deg;
  box-shadow: 0 2px 4px rgba(70,52,24,.18), 0 22px 40px -22px rgba(70,52,24,.6);
}
.bande--papier .carnet { box-shadow: 0 3px 6px rgba(40,30,15,.25), 0 30px 60px -28px rgba(40,30,15,.7); }

/* ==========================================================================
   LE PROLOGUE EN GRAND — 12/09/2026
   Quatre vignettes de 290 px pour les quatre pages qui racontent toute
   l'histoire : on ne lisait pas les bulles. Deux par rangée. En anglais, chaque
   planche est suivie de sa traduction : on les range en 2×2, chaque traduction
   SOUS sa planche (sans « :has », repli : planche et traduction côte à côte).
   ========================================================================== */
@media (min-width: 760px) {
  .planches-prologue {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 1120px; margin-inline: auto; gap: clamp(22px, 3vw, 44px);
  }
  .planches-prologue:has(> .bulles) { grid-template-areas: "f1 f2" "d1 d2" "f3 f4" "d3 d4"; row-gap: 0; }
  .planches-prologue:has(> .bulles) > :nth-child(1) { grid-area: f1; }
  .planches-prologue:has(> .bulles) > :nth-child(2) { grid-area: d1; }
  .planches-prologue:has(> .bulles) > :nth-child(3) { grid-area: f2; }
  .planches-prologue:has(> .bulles) > :nth-child(4) { grid-area: d2; }
  .planches-prologue:has(> .bulles) > :nth-child(5) { grid-area: f3; }
  .planches-prologue:has(> .bulles) > :nth-child(6) { grid-area: d3; }
  .planches-prologue:has(> .bulles) > :nth-child(7) { grid-area: f4; }
  .planches-prologue:has(> .bulles) > :nth-child(8) { grid-area: d4; }
  .planches-prologue:has(> .bulles) > .bulles { margin: 12px 0 clamp(26px, 3vw, 44px); }
}

/* ==========================================================================
   LES VILLES SUR TÉLÉPHONE, EN PAGES D'EXPOSITION — 12/09/2026
   Le nom en contour d'1 px était presque invisible, et il restait trop d'air
   entre deux villes. Le nom est désormais PLEIN, dans le bleu du ciel de SA
   planche (--teinte, relevée dans chaque dessin et vérifiée à 4,5:1 minimum).
   ========================================================================== */
@media (max-width: 720px) {
  .ville__cadre { padding-block: clamp(26px, 4vh, 44px); row-gap: 12px; }
  .ville__nom {
    color: var(--teinte, var(--hst-ciel));
    -webkit-text-stroke: 0;
    font-size: clamp(44px, 16vw, 112px);
  }
  .ville__apercu { padding: 4px 9px; font-size: 10px; }
}

/* Les légendes du prologue : maintenant que les planches sont grandes, un
   dégradé sur toute la largeur salissait le haut blanc de la page. Une
   pastille sombre, en haut à gauche, suffit à la lire (12/09/2026). */
.planche-carte figcaption {
  inset: 10px auto auto 10px;
  padding: 5px 9px;
  background: rgba(8,9,12,.82);
  border-radius: var(--r-s);
  line-height: 1.3;
}

/* ==========================================================================
   LES PERSONNAGES — 15/09/2026
   Les quatre fiches du storyboard de Lionel. Ses dessins sont sur papier
   BLANC : on ne les détoure pas (les blancs du dessin — yeux, pigeon —
   deviendraient des trous). On les pose sur une carte de papier et l'image s'y
   fond par « multiply » ; « isolation » borne ce mélange à la carte.
   Encre des cartes : les mêmes valeurs que .bande--papier (13:1 et 7:1).
   ========================================================================== */
.persos {
  display: grid; gap: clamp(16px, 2vw, 26px);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  margin-top: clamp(36px, 6vh, 70px);
}
.perso {
  --texte: #1f1d19; --texte-2: #4a463e;
  display: grid; grid-template-rows: auto 1fr; gap: 14px; align-content: start;
  padding: clamp(14px, 1.6vw, 20px) clamp(16px, 1.8vw, 22px) clamp(18px, 2vw, 24px);
  background: var(--papier) var(--grain-papier) 0 0 / 260px 260px;
  border-radius: var(--r-m);
  box-shadow: var(--relief-2);
  color: var(--texte);
  isolation: isolate;
}
/* PIÈGE (vu à l'écran le 15/09) : en grille, le « max-height: 100% » d'une
   image se mesure sur la PISTE, pas sur la boîte. Piste « auto » = hauteur
   indéfinie : la contrainte sautait et le dessin passait sur le texte. La
   boîte a une hauteur fixe, l'image la remplit, « contain » garde le dessin
   entier (jamais rogné). */
.perso__vue { height: clamp(220px, 24vw, 320px); }
.perso__vue picture { display: contents; }
.perso__vue img { display: block; width: 100%; height: 100%; object-fit: contain; mix-blend-mode: multiply; }
.perso__role { margin: 0; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: #1d4f86; }
.perso h3 {
  margin: 5px 0 8px; font-size: clamp(20px, 1.1vw + 14px, 26px); line-height: 1.05;
  font-weight: 800; font-stretch: 84%; letter-spacing: -.025em;
}
.perso__texte { margin: 0; color: var(--texte-2); font-size: 15px; line-height: 1.55; }
/* Sur téléphone : quatre cartes de 600 px empilées faisaient un couloir. Le
   dessin passe à gauche du texte, chaque fiche tient en une hauteur d'image. */
@media (max-width: 560px) {
  .perso { grid-template-columns: 34% minmax(0, 1fr); grid-template-rows: none; align-items: center; }
  .perso__vue { height: 176px; }
  .perso__texte { font-size: 14.5px; }
}

/* ==========================================================================
   LES DOUZE VILLES — 15/09/2026
   La liste de Lionel (fiche technique), dans l'ordre des volumes : c'est le
   premier chiffre du code imprimé sous chaque page (Royat 1.11, La Défense
   3.13, Lyon 5.1 — vérifié sur les planches). Les villes déjà visibles sur la
   page portent une mention, tirée de data-ici pour que l'anglais la traduise.
   ========================================================================== */
.douze { margin-block: clamp(40px, 6vh, 72px) clamp(34px, 5vh, 60px); }
.douze__liste {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: grid; column-gap: clamp(16px, 2vw, 30px);
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
  counter-reset: ville;
}
/* Numéro | nom, et la mention « sur cette page » SOUS le nom : posée à côté,
   elle écrasait « Clermont-Ferrand » sur deux lignes (vu le 15/09). */
.douze__liste li {
  counter-increment: ville;
  display: grid; grid-template-columns: 2.2ch minmax(0, 1fr); column-gap: 10px; align-items: baseline;
  padding: 11px 0; border-top: 1px solid var(--filet);
  font-weight: 700; font-stretch: 88%; letter-spacing: -.015em;
  font-size: clamp(17px, .5vw + 14px, 21px);
}
.douze__liste li::before {
  content: counter(ville); min-width: 2ch;
  font-family: 'Plex Mono', monospace; font-weight: 600; font-size: 13px;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
.douze__liste li[data-ici]::after {
  content: attr(data-ici); grid-column: 2; margin-top: 2px;
  font-family: 'Plex Mono', monospace; font-weight: 400; font-size: 10.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent);
}
.douze__code { margin: clamp(18px, 3vh, 28px) 0 0; max-width: 62ch; }
/* Le lien vers la preuve (le dessin à l'encre, page bio) sous le serment. */
.lien-preuve {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--accent); font-weight: 600;
  text-decoration: underline; text-underline-offset: 3px;
}

/* ==========================================================================
   LA FIN DU PROLOGUE — 15/09/2026
   La dernière page du prologue (la spirale, les mots du sorcier) se pose À
   CÔTÉ de la citation qu'elle contient : on lit le texte, on voit la page.
   ========================================================================== */
.prologue__fin { display: grid; gap: clamp(28px, 4vw, 60px); align-items: center; }
@media (min-width: 900px) { .prologue__fin { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); } }
.prologue__fin .planche-carte { width: min(100%, 440px); justify-self: center; }

/* (La galerie « Toutes les séquences » a été retirée le 15/09 : elle
   dévoilait le livre entier et les cachettes du bourreau. Ses styles aussi.) */

/* ==========================================================================
   LES MOTIFS D'ORIGINE (boutique) — 15/09/2026
   Les dessins que Lionel a tracés pour la marque, sur papier blanc : même
   traitement que les personnages (carte de papier, « multiply »).
   ========================================================================== */
.motifs {
  display: grid; gap: clamp(14px, 1.8vw, 22px);
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
}
.motif {
  margin: 0; display: grid; gap: 12px; align-content: start;
  padding: 14px 14px 16px;
  background: var(--papier) var(--grain-papier) 0 0 / 260px 260px;
  border-radius: var(--r-m); box-shadow: var(--relief-2);
  isolation: isolate;
}
.motif__vue { height: clamp(170px, 19vw, 240px); }
.motif__vue picture { display: contents; }
.motif__vue img { display: block; width: 100%; height: 100%; object-fit: contain; mix-blend-mode: multiply; }
.motif figcaption { color: #4a463e; font-size: 14px; line-height: 1.45; }
.motif figcaption b { display: block; color: #1f1d19; font-size: 15.5px; font-weight: 700; font-stretch: 88%; }
@media (max-width: 480px) {
  .motifs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .motif__vue { height: 140px; }
  .motif figcaption { font-size: 13px; }
}

@media (max-width: 480px) {
  .douze__liste { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .douze__liste li { font-size: 15.5px; gap: 8px; }
  .douze__liste li[data-ici] { color: var(--accent); }
  .douze__liste li[data-ici]::after { content: none; }
}
