/* ============================================================
   MIKE-JEASON LECA — AI-Native Production Studio
   DA : noir intégral + rouge sang (auto-référence Black Blossom)
   ============================================================ */

/* ---------- Fonts (self-hosted) ---------- */
@font-face {
  font-family: 'Big Shoulders';
  src: url('/assets/fonts/big-shoulders-latin.woff2') format('woff2');
  font-weight: 100 900;   /* la variable porte le Black (900) : c'est lui que les titres prennent depuis le 02/09 */
  font-style: normal;
  font-display: block;
  unicode-range: U+0000-00FF, U+0131, U+2013-2014, U+2018-2019, U+201C-201D, U+2022, U+00B7;
}
@font-face {
  font-family: 'Big Shoulders';
  src: url('/assets/fonts/big-shoulders-latin-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
  unicode-range: U+0100-024F, U+0152-0153;
}
/* Rajdhani — la voix « terminal » du HUD (étiquettes, barre, boutons, fil d'Ariane,
   pied de page) : la police du thème Tetsuo, le grain des affiches Ghost in the Shell.
   Plex Mono reste réservée aux relevés (durées, définitions), Plex Sans au corps. */
@font-face {
  font-family: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-500-latin.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  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: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-500-latin-ext.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  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: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-600-latin.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  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: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-600-latin-ext.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  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: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-700-latin.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  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: 'Rajdhani';
  src: url('/assets/fonts/rajdhani-700-latin-ext.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  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: 'IBM Plex Sans';
  src: url('/assets/fonts/ibm-plex-sans-400-latin.woff2') format('woff2');
  /* Police VARIABLE (wght 100–700) : une seule déclaration couvre le gras. Les fichiers
     -600 étaient des copies octet pour octet, téléchargées une seconde fois (audit 01/09). */
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+2013-2014, U+2018-2019, U+201C-201D, U+2022, U+00B7;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/assets/fonts/ibm-plex-sans-400-latin-ext.woff2') format('woff2');
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-024F, U+0152-0153;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+2013-2014, U+2018-2019, U+201C-201D, U+2022, U+00B7;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-024F, U+0152-0153;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+2013-2014, U+2018-2019, U+201C-201D, U+2022, U+00B7;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-024F, U+0152-0153;
}

/* ---------- Tokens ---------- */
:root {
  color-scheme: dark;
  --bg: #0a0a0b;
  --bg2: #0e0e10;
  --panel: #121214;
  --line: #232329;
  --line-soft: #1a1a1f;
  --text: #eceae6;
  --body: #b9b8b4;
  --muted: #8b8b95;
  --red: #e0342b;
  --red-text: #e4423a; /* rouge éclairci pour petit texte (AA 4.5:1 sur --bg/--bg2) */
  /* ---- Surfaces MÉDIA (invariantes au thème) ----
     Une vidéo, une affiche, une planche restent sombres quel que soit le thème. Tout ce qui
     se pose SUR un média — texte du hero, bandes d'univers, spotlight, boutons pause,
     indices, visionneuse — prend ces jetons-ci, jamais --text/--muted : sinon le thème
     clair pose un texte d'encre sur une image noire. */
  --media-bg: #0a0a0b;
  --media-veil: 10, 10, 11;            /* triplet, à écrire rgba(var(--media-veil), .55) */
  --media-fade: var(--bg);             /* fin des dégradés média : sombre = fondu dans la page ; clair = coupe nette */
  --on-media: #eceae6;
  --on-media-dim: #b9b8b4;
  --on-media-muted: #8b8b95;
  --on-media-red: #e4423a;
  --line-on-media: #232329;
  --swatch-line: rgba(255, 255, 255, 0.14);
  --muted-2: #7c7c86;                  /* registre « note », 4,7:1 sur --bg — remplace les opacity:.72 qui tombaient à 3,5:1 */
  --display: 'Big Shoulders', 'Arial Narrow', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  --sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --hud: 'Rajdhani', 'Roboto Condensed', 'Arial Narrow', sans-serif;   /* étiquettes, barre, boutons */
  --pad: clamp(20px, 4vw, 64px);
  --maxw: 1440px;
  /* Échelle typographique — 5 crans de texte + 1 sous-titre. Toute nouvelle
     règle doit piocher ici, jamais inventer une taille intermédiaire. */
  --fs-label: 11px;   /* labels mono capitales (min lisible) */
  --fs-xs: 12px;      /* mono secondaire */
  --fs-sm: 14px;      /* légendes, cellules de tableau */
  --fs-base: 16px;    /* corps de texte */
  --fs-lede: 17px;    /* chapôs et intros */
  --fs-h4: 1.2rem;    /* titres de cartes */
  --fs-h2: clamp(2.6rem, 7vw, 6rem);   /* titres de section — même cran que .section__head h2 (Tetsuo : gros, noir) */
  --fs-hud: 12px;
  /* Tetsuo : courbes et trait (02/09). --ease-cut pour le survol coupé, --ease-wipe pour la
     lame des boutons ; 2 px est le bord interactif par défaut, 1 px reste aux filets. */
  --ease-out: cubic-bezier(0.215, 0.61, 0.355, 1);
  --ease-cut: cubic-bezier(0.86, 0, 0.07, 1);
  --ease-wipe: cubic-bezier(0.76, 0.49, 0.25, 1);
  --stroke: 2px;     /* étiquettes HUD Rajdhani (600, .18em) */
  /* Cran nommé plutôt qu'une valeur en dur ailleurs dans la feuille : le nom du
     studio se cale sur la hauteur du wordmark SVG (20 px), pas sur le corps de
     texte — 14 px le rapetisse à côté, 16 px le fait dépasser. */
  --fs-brand: 15px;   /* nom du studio dans l'en-tête */
  --ls-label: 0.16em; /* interlettrage des labels mono */
  --ls-loose: 0.2em;  /* accent rare */
  /* Échelle d'espacement — base 4 px. Même règle que la typo : on pioche,
     on n'invente pas de valeur intermédiaire. */
  --sp-1: 8px;
  --sp-2: 12px;
  --sp-3: 16px;
  --sp-4: 24px;
  --sp-5: 32px;
  --sp-6: 48px;
  --pad-card: clamp(24px, 3vw, 44px);              /* intérieur des cartes  */
  --pad-pill: 9px 15px;                            /* pastilles et chips    */
  --rhythm: clamp(72px, 11vh, 140px);   /* respiration de section */
}

/* ---------- Thème clair ----------
   Le site est sombre par défaut (identité Black Blossom). Le thème clair est une VERSION,
   pas une préférence système : il s'active par le bouton de la barre (persisté dans
   localStorage['theme'], appliqué avant le premier rendu par le script en tête de page,
   forçable par ?theme=light ou ?theme=dark dans l'URL). Références : la peau claire du
   thème Tetsuo — fond papier, encre, un seul accent, gros titres condensés — et le sceau
   hanko du studio, qui est d'abord une empreinte rouge sur papier.
   Les surfaces média (hero, bandes, spotlight, lecteurs, visionneuse) restent sombres :
   voir les jetons --media-* ci-dessus. Seuls --media-fade et --swatch-line basculent. */
html[data-theme="light"] {
  color-scheme: light;
  --bg: #f7f5f1;
  --bg2: #efece6;
  --panel: #e9e5de;
  --line: #d9d4cc;
  --line-soft: #e6e2da;
  --text: #121214;
  --body: #3d3d42;
  --muted: #66666e;               /* 5,2:1 sur --bg, 4,9:1 sur --bg2 : AA pour les labels 11px */
  --red-text: #c22a22;            /* 5,3:1 sur --bg ; #e4423a ne ferait que 3,9:1 */
  --media-fade: var(--media-bg);  /* le hero et les bandes finissent NOIR, puis la page est claire : coupe nette */
  --swatch-line: rgba(0, 0, 0, 0.14);
  --muted-2: #6e6e76;             /* 4,7:1 sur papier */
}
/* --wa-text : variante TEXTE de l'accent quand l'accent pur passe sous 4,5:1 sur le noir
   (Scarlet #d5182b → #ec3f3f, posée sur <main> de ses pages). Les consommateurs texte
   lisent var(--wa-text, var(--wa, var(--red-text))) ; bordures et fonds gardent --wa. */
/* Les accents d'univers (--wa) sont pensés pour le noir : le jaune Bee Village, le cyan
   VELIS, le rose Glamour State sortent à 1,6-2,5:1 sur papier. En TEXTE, ils sont ramenés
   vers l'encre à lissage perceptuel (oklab) — 4,6:1 au pire, mesuré. Les bordures, fonds,
   filets et barres gardent l'accent pur : ce ne sont pas du texte. */
html[data-theme="light"] .dossier-cta__label,
html[data-theme="light"] .dossier-cta__go,
html[data-theme="light"] .synopsis__part h3,
html[data-theme="light"] .world-story__mark,
html[data-theme="light"] .world-story__title > span:not(.sr),
html[data-theme="light"] .gal-more__btn,
html[data-theme="light"] .synopsis__fold > summary::after,
html[data-theme="light"] a.film-card:hover .world-card__go,
html[data-theme="light"] a.film-card.is-near .world-card__go,
html[data-theme="light"] .world-card:hover .world-card__go,
html[data-theme="light"] .world-card.is-near .world-card__go {
  color: color-mix(in oklab, var(--wa, var(--red)) 52%, #141416);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
/* Filet de sécurité : les règles ciblées ci-dessous couvrent les cas connus,
   celle-ci neutralise tout ce qui aurait pu être ajouté depuis. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
body {
  background: var(--bg);
  color: var(--body);
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
/* Liens dans la prose (intro de page, signature du hero — audit 01/09) : soulignés,
   sinon un lien hérite de la couleur du texte et n'existe pas pour le lecteur. */
.page-head__intro a, .hero__sub a { text-decoration: underline; text-decoration-color: var(--muted); text-underline-offset: 3px; }
.page-head__intro a:hover, .page-head__intro a:focus-visible, .hero__sub a:hover, .hero__sub a:focus-visible { text-decoration-color: var(--red); }
::selection { background: var(--red); color: #fff; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 900;   /* Tetsuo × GITS (02/09) : le Black de Big Shoulders, celui du wordmark poussé à fond */
  color: var(--text);
  text-transform: uppercase;
  line-height: 0.92;
  letter-spacing: -0.005em;
  text-wrap: balance;
}
h3, h4 { font-weight: 700; line-height: 1; letter-spacing: 0.01em; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding-left: var(--pad); padding-right: var(--pad); }

/* ---------- Utility : labels dossier ---------- */
/* ---------- Étiquette HUD (02/09) ----------
   Fini la puce carrée et les ordinaux « 01 · » : une boîte fine à coins cochés, en
   Rajdhani, comme un cartouche de visée. Un CODE (.tag__code, rouge, tiré des données du
   site : W-02, F-07…) puis un LIBELLÉ. Deux coins seulement (haut gauche, bas droite) :
   deux pseudo-éléments, aucun balisage de plus. */
.tag {
  font-family: var(--hud);
  font-size: var(--fs-hud);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  display: inline-flex;
  flex-wrap: wrap; /* une longue étiquette à filets (.sep = un item flex) se replie à 360 px au lieu de déborder */
  align-items: center;
  gap: 10px;
  position: relative;
  padding: 5px 10px 4px;
  border: 1px solid var(--line);
  line-height: 1.2;
  max-width: 100%;
}
.tag .sep { margin: 0; } /* l'écart vient du gap */
.tag::before, .tag::after {
  content: ''; position: absolute; width: 7px; height: 7px; pointer-events: none;
  border: 0 solid var(--red);
}
.tag::before { top: -1px; left: -1px; border-top-width: 2px; border-left-width: 2px; }
.tag::after { bottom: -1px; right: -1px; border-bottom-width: 2px; border-right-width: 2px; }
.tag__code { color: var(--red-text); padding-right: 10px; border-right: 1px solid var(--line); }
.tag--dim { color: var(--muted); }
.tag--dim::before, .tag--dim::after { border-color: var(--muted); }
/* Sur une carte film de l'accueil, l'univers est DESSINÉ comme partout ailleurs. La
   carte est étroite — trois par rangée — donc l'échelle est la plus petite du système ;
   `--mk` reste la propriété commune (voir .plate-mark). La puce carrée de `.tag::before`
   disparaît : elle annonçait une étiquette de texte, elle ferait doublon devant une
   marque. */
/* `width: 100%` n'est pas décoratif. `.tag` est un élément de flex dimensionné par son
   contenu : sans largeur définie, le parent mesure 0, le `max-width: 100%` de l'image
   se résout donc contre 0, et la marque sort à 0×0 — chargée, mais invisible. */
.tag--mark { display: block; width: 100%; margin-bottom: 10px; border: 0; padding: 0; }
.tag--mark::before, .tag--mark::after { display: none; }
.tag--mark img {
  display: block; width: auto; height: auto;
  max-height: calc(clamp(22px, 2.1vw, 30px) * var(--mk, 1)); max-width: 100%;
}

.mono { font-family: var(--mono); font-size: var(--fs-xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }

/* ---------- Header ---------- */
.site-head {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  border-bottom: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.site-head__in {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
/* min-height : seul chemin vers l'accueil sur mobile (le menu plein écran n'a
   pas d'item « Home ») — la cible doit faire 44px comme le burger voisin.
   L'en-tête garde ses 64px, la ligne est déjà centrée. */
.brand { display: flex; align-items: center; gap: 12px; min-height: 44px; }
/* Wordmark lettré (brand-lab, validé 12/08) : JEASON bicolore, O-sceau rouge */
.brand__wordmark { height: 20px; width: auto; display: block; flex: none; }
.brand__name {
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--fs-brand);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text);
  white-space: nowrap;
}
.brand__sub { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); color: var(--muted); text-transform: uppercase; white-space: nowrap; }
@media (max-width: 720px) { .brand__sub { display: none; } }

.nav { display: flex; gap: clamp(16px, 2.5vw, 36px); }
.nav a {
  font-family: var(--hud);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--muted);
  padding: 6px 0;
  border-bottom: 1px solid transparent;
  transition: color .2s, border-color .2s;
}
.nav a:hover { color: var(--text); }
/* [aria-current] sans valeur imposée : les sous-pages marquent leur section
   parente en aria-current="true" (« page » y était sémantiquement faux). */
.nav a[aria-current] { color: var(--text); border-bottom-color: var(--red); }
/* Survol « coupé » (Tetsuo split-trim) : le mot se scinde en deux lames qui glissent en
   sens inverse. Deux spans dans le lien ; la copie basse est absolue, hors lecture, et
   hérite du padding pour tomber pile sur l'original. Souris seulement : au doigt il n'y
   a pas de survol, et dans le menu plein écran la copie se poserait sur le tiret. */
.cut { position: relative; display: inline-block; }
.cut__a { display: block; }
/* La copie porte l'attribut `hidden` : sans feuille elle n'existe pas (display:none du
   navigateur) — sinon la nav lisait « WorldsWorlds ». La règle auteur ci-dessous prime sur
   [hidden] (origine auteur > origine navigateur). user-select: none la tient hors du
   presse-papiers. */
.cut__b { display: block; position: absolute; inset: 0; padding: inherit; opacity: 0; pointer-events: none; user-select: none; }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .cut__a, .cut__b { transition: clip-path .22s var(--ease-cut), transform .22s var(--ease-cut), opacity .15s; }
  .cut:hover .cut__a, .cut:focus-visible .cut__a { clip-path: inset(0 0 50% 0); transform: translate(-2px, -2px); }
  .cut:hover .cut__b, .cut:focus-visible .cut__b { clip-path: inset(50% 0 0 0); transform: translate(2px, 2px); opacity: 1; }
}
body.nav-open .nav .cut__b { display: none; }
/* Bascule de langue — pilule compacte en bout de nav. Spécificité .nav a.… :
   les propriétés partagées de .nav a (padding, bordure basse) doivent céder. */
/* FR/EN et bascule de thème forment une paire (.nav__tools) : deux petites pilules
   côte à côte, en bout de barre comme dans le menu plein écran (demande de Mike, 02/09). */
.nav__tools { display: inline-flex; align-items: center; gap: 6px; margin-left: 6px; }
.nav a.lang-switch {
  border: 1px solid var(--line);
  padding: 5px 10px;
  color: var(--muted);
}
.nav a.lang-switch:hover, .nav a.lang-switch:focus-visible {
  color: var(--text);
  border-color: var(--red);
}
/* Zone tappable étendue sans toucher au soulignement (le padding déplacerait
   la bordure basse) — pas de chevauchement : l'en-tête fait 64px. */
.nav a { position: relative; }
.nav a::after { content: ''; position: absolute; left: -8px; right: -8px; top: -12px; bottom: -12px; }

/* Burger : deux lames qui se croisent en X (rouge) à l'ouverture */
.nav-toggle {
  display: none;
  position: relative;
  z-index: 61; /* au-dessus du voile menu (55), qui est un descendant du même contexte */
  width: 44px;
  height: 44px;
  margin-right: -8px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.nav-toggle__bar {
  position: absolute;
  left: 11px;
  right: 11px;
  height: 2px;
  background: var(--text);
  transition: transform .3s cubic-bezier(.65, 0, .35, 1), background-color .3s;
}
.nav-toggle__bar:nth-child(1) { top: 18px; }
.nav-toggle__bar:nth-child(2) { top: 26px; }
body.nav-open .nav-toggle__bar { background: var(--red-text); }
body.nav-open .nav-toggle__bar:nth-child(1) { transform: translateY(4px) rotate(45deg); }
body.nav-open .nav-toggle__bar:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

@media (max-width: 860px) {
  .nav { display: none; }
  .nav-toggle { display: block; }
  /* Sans JavaScript, le bouton du menu est inerte : les liens restent visibles, en
     rangée compacte sous la marque (audit 01/09). La classe no-js tombe dans le
     script en tête de page, avant le premier rendu — personne ne voit cette rangée
     avec JS. Le pied de page restait le seul chemin, tout en bas de chaque page. */
  html.no-js .site-head__in { height: auto; flex-wrap: wrap; padding-top: 10px; padding-bottom: 10px; }
  html.no-js .nav { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; width: 100%; }
  html.no-js .nav-toggle { display: none; }
  html.no-js .nav__socs { width: auto; margin: 0; padding: 0; border: 0; gap: 14px; }
  html.no-js .nav__soc { width: 34px; height: 34px; }
  html.no-js .nav__soc svg { width: 19px; height: 19px; }

  /* iOS Safari : backdrop-filter fait de .site-head le bloc conteneur de ses
     descendants position:fixed (le menu se retrouvait piégé dans le bandeau) —
     menu ouvert, on neutralise le filtre et on opacifie l'en-tête. */
  body.nav-open .site-head {
    background: var(--bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom-color: transparent;
  }
  body.nav-open .brand { position: relative; z-index: 61; }

  /* Menu plein écran : gros items Big Shoulders numérotés façon générique */
  body.nav-open .nav {
    display: flex;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    height: 100vh;
    height: 100dvh;
    background: var(--bg);
    flex-direction: column;
    /* Centrage « sûr » : justify-content:center distribue le débordement des deux côtés
       et le premier item partait au-dessus de l'écran en paysage (audit 01/09). Marges
       auto sur le premier item et sur un ::after : centré quand ça tient, défilable
       depuis le haut quand ça déborde. */
    justify-content: flex-start;
    padding: 84px var(--pad) calc(28px + env(safe-area-inset-bottom));
    gap: 0;
    z-index: 55;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  body.nav-open .nav a {
    display: flex;
    align-items: baseline;
    gap: 16px;
    font-family: var(--display);
    font-size: clamp(34px, 9vw, 54px);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: 0.03em;
    color: var(--text);
    padding: 12px 0;
    border-bottom: 1px solid var(--line-soft);
  }
  body.nav-open .nav > :first-child { margin-top: auto; }
  body.nav-open .nav::after { content: ''; display: block; flex: none; margin-bottom: auto; }
  /* Plus de « 01 » façon générique (Mike, 02/09) : un trait, rouge sur l'item courant. */
  body.nav-open .nav a::before {
    content: '';
    width: 18px; height: 2px; background: var(--line);
    flex: none; align-self: center;
  }
  body.nav-open .nav a[aria-current] { color: var(--red-text); }
  body.nav-open .nav a[aria-current]::before { background: var(--red); }
  /* Les logos de plateformes échappent aussi à la numérotation : ce sont des liens
     sortants, pas des entrées de navigation — le compteur leur donnait un « 06 » et
     un « 07 » qui les faisaient passer pour des sections du site. */
  body.nav-open .nav a.nav__soc {
    display: inline-flex;
    padding: 0;
    border-bottom: 0;
    color: var(--muted);
  }
  body.nav-open .nav a.nav__soc::before { content: none; }

  /* La bascule de langue échappe à la numérotation et à la typo géante du menu */
  body.nav-open .nav .nav__tools { display: flex; align-items: center; gap: 12px; margin-top: 22px; align-self: flex-start; }
  body.nav-open .nav a.lang-switch {
    font-family: var(--mono);
    font-size: var(--fs-base);
    font-weight: 400;
    letter-spacing: var(--ls-label);
    border: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    padding: 12px 22px;
    margin: 0;
  }
  body.nav-open .nav a.lang-switch::before { content: none; }
  body.nav-open .nav::after {
    content: "AI-Native Production \2014  Paris"; /* deux espaces : la première termine l'échappement */
    margin-top: var(--sp-5);
    font-family: var(--mono);
    font-size: var(--fs-label);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    color: var(--muted);
  }
  body.nav-open { overflow: hidden; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100vh;   /* repli : Safari < 15.4 et Chrome < 108 ignorent svh */
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: clip;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: center 38%; }
.hero__media video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center 42%;
}
/* En plein écran, letterbox natif — jamais de recadrage du film */
.hero__media video:fullscreen { object-fit: contain; }
.hero__media video:-webkit-full-screen { object-fit: contain; }
/* Vidéo engagée hors plein écran (échec/refus) : média au premier plan, contrôles
   utilisables, et plus aucun transform GSAP (respiration/parallaxe) sur le lecteur */
.hero__media.engaged { z-index: 3; transform: none !important; }
.hero__media.engaged::after { display: none; }
.hero__media.engaged video { object-fit: contain; background: var(--media-bg); }
/* Reduced-motion : pas de boucle d'ambiance — mais le film engagé (choix
   volontaire de l'utilisateur) reste visible */
@media (prefers-reduced-motion: reduce) { .hero__media:not(.engaged) video { display: none; } }
/* Sans JS : pas de boucle ni de boutons (l'image d'origine porte le hero) */
html.no-js .hero__media video,
html.no-js .hero__watch,
html.no-js .hero__pause { display: none; }

/* Pause/lecture de la boucle d'ambiance */
/* .loop-pause : même bouton sur les bandes d'univers (.world-teaser), injecté
   par main.js — WCAG 2.2.2, sur le modèle du hero. */
.hero__pause, .loop-pause {
  /* Aligné sur le bord droit de la colonne de contenu, pas sur celui de la fenêtre :
     le conteneur (.hero, .world-teaser) est pleine largeur, donc au-delà de 1440px
     un right fixe faisait dériver le bouton de 284px à 1920. max() garde 20px de
     garde sous 1440px, où (100% - --maxw) devient négatif. */
  /* z-index 3, pas 2 : .hero__in (z-index 2, plus bas dans le DOM) couvre le bas du hero
     de tout son padding — à 2, le bouton était peint DESSOUS et la souris ne l'atteignait
     jamais (Playwright : « .hero__in intercepts pointer events »). Les bandes d'univers
     n'ont pas ce voisin : leur bouton restait cliquable. */
  position: absolute; right: max(20px, calc((100% - var(--maxw)) / 2 + var(--pad))); bottom: 18px; z-index: 3;
  display: inline-flex; align-items: center; min-height: 44px;
  background: rgba(var(--media-veil), 0.55); border: 1px solid var(--line-on-media);
  color: var(--on-media-muted); font-family: var(--mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-label); text-transform: uppercase; padding: var(--pad-pill);
  cursor: pointer; transition: color .2s, border-color .2s;
}
.hero__pause:hover, .hero__pause:focus-visible,
.loop-pause:hover, .loop-pause:focus-visible { color: var(--on-media); border-color: var(--red); }
@media (prefers-reduced-motion: reduce) { .hero__pause, .loop-pause { display: none; } }
/* Téléphone : « Suspendre la boucle » (191 px en FR) recouvrait le logotype de la bande en
   bas à gauche (audit 01/09). Le bouton monte en haut à droite, sous la barre, comme
   la pause du hero. */
@media (max-width: 860px) {
  .world-teaser:not(.world-teaser--strip) .loop-pause { bottom: auto; top: 76px; right: 16px; }
  /* ~200 px de vide entre la bande (ou la pellicule) et l'en-tête de page : la marge de
     170 px est taillée pour l'en-tête fixe, pas pour une bande qui le précède. */
  .world-teaser + .page-head { padding-top: clamp(36px, 6vh, 56px); }
}
.hero__media::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(to top, var(--media-fade) 4%, rgba(var(--media-veil),0.62) 34%, rgba(var(--media-veil),0.18) 62%, rgba(var(--media-veil),0.42) 100%);
}
/* Mobile : le hero mesure ~1000px pour un écran de 740 à 844px, si bien que le
   palier le plus clair (0,18 à 62% du bas) remonte derrière le chapô et la
   ligne de signature au lieu de rester au-dessus du texte. Voile renforcé. */
@media (max-width: 860px) {
  .hero__media::after {
    background:
      linear-gradient(to top, var(--media-fade) 4%, rgba(var(--media-veil),0.88) 30%, rgba(var(--media-veil),0.74) 62%, rgba(var(--media-veil),0.58) 100%);
  }
}
.hero__grain {
  position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.35;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero__in { position: relative; z-index: 2; width: 100%; padding-bottom: clamp(48px, 8vh, 96px); padding-top: 160px; }
/* Mobile : à 160px de retrait haut, le hero déborde du premier écran (956 à
   1001px mesurés pour 740 à 844px de fenêtre) — la ligne de crédibilité et le
   bouton de pause de la boucle passaient sous la ligne de flottaison, ce que
   WCAG 2.2.2 n'admet pas pour une commande de pause. */
@media (max-width: 860px) {
  .hero__in { padding-top: 104px; padding-bottom: 28px; }
  .hero h1 { font-size: clamp(2.6rem, 9.5vw, 8.5rem); }
  .hero__desc { font-size: var(--fs-base); margin-top: var(--sp-3); max-width: 40ch; }
  .hero__cta { margin-top: var(--sp-4); }
  .hero__meta { margin-top: var(--sp-3); gap: 6px 20px; }
  /* Le bouton de pause quitte le bas du premier écran : calé sur 100svh, il se posait
     sur la ligne de chiffres (844 px) ou sur les boutons (740 px) selon la hauteur du
     téléphone. Ancré en haut à droite sous la barre, en icône seule — le libellé reste
     dans le bouton pour les lecteurs d'écran. La ligne de signature lui laisse la place. */
  .hero__pause { top: 76px; bottom: auto; right: 16px; width: 44px; padding: 0; justify-content: center; font-size: 0; letter-spacing: 0; }
  .hero__pause::before { content: ''; width: 10px; height: 12px; border-left: 3px solid currentColor; border-right: 3px solid currentColor; }
  .hero__pause[aria-pressed="true"]::before { width: 0; height: 0; border-left: 10px solid currentColor; border-right: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }
  .hero__in > .hero__sub:first-child { padding-right: 64px; }
}
.hero h1 {
  font-size: clamp(3.6rem, 12.5vw, 12rem);
  line-height: 0.86;
  margin: clamp(20px, 4vh, 40px) 0 20px; /* l'espace qu'occupait la ligne katakana (retirée, Mike 02/09) */
  color: var(--on-media);
}
.hero h1 .accent { color: var(--red); }
.hero__sub {
  font-family: var(--hud);
  font-weight: 600;
  font-size: clamp(12px, 1.4vw, 14px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--on-media);
}
/* Signature et axes : filets fins à la place des points médians (02/09). */
.hero__sub .sep { display: inline-block; width: 16px; height: 1px; background: var(--line-on-media); margin: 0 8px; vertical-align: middle; }
/* Ligne des axes en lettres CREUSES (site officiel GITS 2026 : le logotype techno évidé) —
   Rajdhani 700, trait seul, encre du média. Sans -webkit-text-stroke (moteur ancien), le
   texte reste transparent : on garde donc un fond à 10 % en secours via color-mix. */
.hero__axes { font-family: var(--hud); font-weight: 700; font-size: clamp(22px, 3.2vw, 44px); line-height: 1; letter-spacing: .14em; text-transform: uppercase; margin: 12px 0 0;
  color: color-mix(in srgb, var(--on-media) 12%, transparent); -webkit-text-stroke: var(--hollow-w) var(--on-media); paint-order: stroke fill; }
.hero__axes .sep { width: 22px; background: var(--line-on-media); margin: 0 6px; }
.hero__sub b { color: var(--on-media-red); font-weight: 600; }
.hero__desc { max-width: 560px; margin-top: var(--sp-4); color: var(--on-media-dim); font-size: var(--fs-lede); }
/* Sur le média, un bouton prend les jetons média : en clair, .btn héritait du fond papier. */
.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: var(--sp-5); }
.hero__cta .btn { background: rgba(var(--media-veil), 0.45); border-color: var(--on-media); color: var(--on-media); }
.hero__cta .btn:hover, .hero__cta .btn:focus-visible { color: #fff; border-color: #c22a22; }
.hero__cta .btn--red { background: #c22a22; border-color: #c22a22; color: #fff; }
.hero__cta .btn--red::before { background: var(--on-media); }
.hero__cta .btn--red:hover, .hero__cta .btn--red:focus-visible { color: var(--media-bg); border-color: var(--on-media); }

/* Bouton « watch » du hero : chip mono, triangle de lecture rouge */
.hero__watch {
  display: inline-flex; align-items: center; gap: 10px;
  background: rgba(var(--media-veil), 0.35); border: 1px solid var(--line-on-media); color: var(--on-media);
  font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label);
  text-transform: uppercase; padding: 15px 18px; cursor: pointer;
  transition: border-color .2s, color .2s;
}
.hero__watch::before {
  content: ""; width: 0; height: 0; flex: none;
  border-left: 7px solid var(--on-media-red);
  border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}
.hero__watch:hover, .hero__watch:focus-visible { border-color: var(--red); color: var(--on-media-red); }

/* Boutons (Tetsuo .edgtf-btn) : trait de 2 px dans la couleur du texte, et au survol une
   LAME biaise (pseudo-élément, transform seul) traverse le bouton — rouge sur un bouton
   nu, encre/blanc sur un bouton rouge. #c22a22 et non var(--red) : sous du blanc de
   14 px gras, #e0342b sort à 3,9:1, sous le seuil AA ; #c22a22 tient 4,8:1. */
.btn {
  position: relative; overflow: hidden; isolation: isolate;
  font-family: var(--hud);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.4;
  padding: 12px 30px 11px;
  border: var(--stroke) solid var(--text);
  color: var(--text);
  background: transparent;
  transition: color .3s var(--ease-wipe), border-color .3s;
  display: inline-flex; align-items: center; gap: 12px;
  cursor: pointer;
}
.btn::before {
  content: ''; position: absolute; inset: -1px -24px; z-index: -1;
  background: #c22a22; transform: translateX(-104%) skew(-30deg);
  transition: transform .45s var(--ease-wipe);
}
/* La couleur du texte attend 150 ms que la lame l'ait recouvert : en thème clair, du blanc
   sur papier apparaissait avant la lame (1,97:1 pendant ~150 ms). Le retour est immédiat. */
.btn:hover, .btn:focus-visible { color: #fff; border-color: #c22a22; transition-delay: .15s, 0s; }
.btn:hover::before, .btn:focus-visible::before { transform: translateX(0) skew(-30deg); }
.btn .arrow { display: inline-block; transition: transform .25s var(--ease-out), color .3s; color: var(--red-text); }
.btn:hover .arrow, .btn:focus-visible .arrow { transform: translateX(4px); color: #fff; transition-delay: 0s, .15s; }
.btn--red { background: #c22a22; border-color: #c22a22; color: #fff; }
.btn--red::before { background: var(--text); }
.btn--red:hover, .btn--red:focus-visible { color: var(--bg); border-color: var(--text); }
.btn--red .arrow, .btn--red:hover .arrow, .btn--red:focus-visible .arrow { color: inherit; }
@media (max-width: 860px) { .btn { font-size: 13px; padding: 11px 22px 10px; } }
@media (prefers-reduced-motion: reduce) {
  .btn::before { transition: none; }
  .btn:hover .arrow, .btn:focus-visible .arrow { transform: none; }
  .btn:hover, .btn:focus-visible, .btn:hover .arrow, .btn:focus-visible .arrow { transition-delay: 0s; }
}

.hero__meta {
  margin-top: clamp(36px, 6vh, 64px);
  border-top: 1px solid var(--line-on-media);
  padding-top: 18px;
  display: flex; flex-wrap: wrap; gap: 12px 40px;
}
.hero__meta span { font-family: var(--hud); font-weight: 600; font-size: 13px; letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--on-media-muted); }
.hero__meta b { color: var(--on-media); font-weight: 500; }

/* ---------- Sections ---------- */
.section { padding: var(--rhythm) 0 0; }
.section:last-of-type { padding-bottom: var(--rhythm); }
.section__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: clamp(28px, 4vh, 48px); }
.section__head h2 { font-size: var(--fs-h2); margin-top: var(--sp-2); }
.section__head .more { font-family: var(--hud); font-weight: 600; font-size: 13px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--line); padding-bottom: 4px; transition: color .2s, border-color .2s; position: relative; }
/* Zone tappable ~44px sans déplacer le soulignement (lien isolé, pas de voisin) */
.section__head .more::after { content: ''; position: absolute; left: -8px; right: -8px; top: -12px; bottom: -12px; }
.section__head .more:hover { color: var(--text); border-color: var(--red); }
.section__intro { max-width: 60ch; color: var(--body); font-size: var(--fs-lede); }


/* ---------- Marquee (selected frames) ---------- */
.marquee { position: relative; overflow: hidden; border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); padding: 18px 0; }
.marquee__track { display: flex; gap: 14px; width: max-content; will-change: transform; }
.marquee__track a { flex: none; display: block; }
.marquee__track img { height: clamp(160px, 24vh, 240px); width: auto; filter: saturate(0.92); transition: filter .25s; }
.marquee__track a:hover img { filter: saturate(1.1); }
@media (prefers-reduced-motion: reduce) { .marquee { overflow-x: auto; } }
/* Bouton pause/lecture — même composant que les boucles vidéo (.loop-pause, voir plus
   haut), posé par main.js. La dérive tourne désormais au doigt comme à la souris (05/09) :
   ce contrôle visible est ce qui la rend conforme WCAG 2.2.2 sur tactile. */
/* Le doigt fait glisser la bande (> 8px) : main.js tue la boucle et ajoute cette classe —
   même rendu que l'ancien repli tactile (défilement natif, doublons masqués, plus de
   bouton puisqu'il n'y a plus de dérive automatique à suspendre). */
.marquee.is-native { overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
.marquee.is-native::-webkit-scrollbar { display: none; }
.marquee.is-native .marquee__track a[aria-hidden="true"] { display: none; }
.marquee.is-native .loop-pause { display: none; }

/* ---------- Worlds grid ---------- */
.worlds-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
@media (max-width: 1020px) { .worlds-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .worlds-grid { grid-template-columns: 1fr; } }

.world-card {
  background: var(--bg);
  display: flex;
  flex-direction: column;
  position: relative;
  transition: background .25s;
}
.world-card__media { aspect-ratio: 16 / 10; overflow: hidden; background: var(--bg2); }
.world-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .3s cubic-bezier(.2,.6,.2,1);
}
.world-card:hover .world-card__media img { transform: scale(1.045); }
.world-card__body {
  padding: clamp(20px, 2.4vw, 32px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 24px;
  flex: 1;
  min-height: 200px;
}
.world-card::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--wa, var(--red));
  transform: scaleY(0); transform-origin: top;
  transition: transform .3s cubic-bezier(.2,.6,.2,1);
}
.world-card:hover { background: var(--panel); }
.world-card:hover::before { transform: scaleY(1); }
.world-card__idx { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); color: var(--muted); display: flex; justify-content: space-between; text-transform: uppercase; }
.world-card h3 { font-size: clamp(1.5rem, 2.2vw, 2rem); line-height: 1.04; }
.world-card h3 span { display: block; font-size: 0.55em; font-weight: 400; color: var(--muted); letter-spacing: 0.12em; margin-top: var(--sp-1); }
.world-card__foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.world-card__go { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); transition: color .2s; }
.world-card:hover .world-card__go { color: var(--wa-text, var(--wa, var(--red-text))); }
/* Écrans tactiles : le survol n'existe pas. La carte prend le même état quand elle
   traverse le centre de l'écran — classe posée par main.js sur `(hover: none)`.
   Un seul jeu de styles, deux déclencheurs : rien à maintenir en double. */
.world-card.is-near { background: var(--panel); }
.world-card.is-near::before { transform: scaleY(1); }
.world-card.is-near .world-card__go { color: var(--wa-text, var(--wa, var(--red-text))); }
.world-card.is-near .world-card__media img { transform: scale(1.045); }
/* Même équivalent tactile pour les cartes de dossier, cartes de film, plaques, spotlight et
   accès au dossier (audit 01/09) — sans le translateY du survol. */
.case-card.is-near { border-color: var(--red); }
.case-card.is-near .world-card__go { color: var(--red-text); }
a.film-card.is-near { border-color: var(--wa, var(--red)); }
a.film-card.is-near .world-card__go { color: var(--wa-text, var(--wa, var(--red-text))); }
.world-plate.is-near { border-color: var(--wa, var(--red)); }
.spotlight.is-near { border-color: var(--red); }
.spotlight.is-near .world-card__go { color: var(--on-media-red); }
.dossier-cta.is-near { border-color: var(--wa, var(--red)); }
/* Boucle d'ambiance sur une carte d'univers : la vidéo se pose au-dessus de l'affiche.
   Si la lecture est refusée (iOS en économie d'énergie), main.js la masque et l'image
   reste — c'est pour ça que les deux coexistent au lieu de se remplacer. */
.world-card__media, .world-plate__media { position: relative; }
.world-card__media video, .world-plate__media video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* `.world-card--wide` retiré : l'univers en Spotlight n'a plus sa carte en
   doublon dans la grille, donc il reste six cartes — 2 lignes de 3, puis
   3 lignes de 2, puis une colonne. Plus d'orphelin à rattraper. */

.palette { display: flex; gap: 6px; }
.palette i { width: 14px; height: 14px; border: 1px solid var(--swatch-line); }

/* ---------- Cards (work / films) ---------- */
.cards-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
@media (max-width: 860px) { .cards-2 { grid-template-columns: 1fr; } }
/* Variante à trois : les trois films de l'accueil tiennent sur une ligne pleine
   au lieu de 2 + 1 orpheline. Paliers déclarés APRÈS la base, même spécificité :
   c'est l'ordre source qui tranche, le plus étroit gagne. */
.cards-2--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1100px) { .cards-2--3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 860px) { .cards-2--3 { grid-template-columns: 1fr; } }
/* Le cast Black Blossom compte sept fiches : à 230px de plancher, la grille faisait 5 + 2
   à 1440. Un plancher de 140px tient les sept sur une ligne dès ~1150px de fenêtre. */
.cards-2--cast { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

.case-card {
  border: 1px solid var(--line);
  background: var(--bg2);
  display: flex; flex-direction: column;
  transition: border-color .25s, transform .25s;
}
.case-card__media { aspect-ratio: 16 / 9; overflow: hidden; border-bottom: 1px solid var(--line-soft); }
.case-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .3s cubic-bezier(.2,.6,.2,1);
}
.case-card:hover .case-card__media img { transform: scale(1.04); }
.case-card__body { padding: var(--pad-card); display: flex; flex-direction: column; gap: 20px; flex: 1; }
.case-card:hover { border-color: var(--red); transform: translateY(-3px); }
.case-card h2, .case-card h3 { font-size: clamp(1.8rem, 3vw, 2.6rem); }
.case-card__meta { display: flex; flex-wrap: wrap; gap: 8px 24px; }
.case-card__meta span { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); }
.case-card p { font-size: var(--fs-base); }
.case-card .world-card__go { margin-top: auto; }
.case-card:hover .world-card__go { color: var(--red-text); }

/* ---------- Film cards ---------- */
.film-card {
  border: 1px solid var(--line);
  background: var(--bg2);
  display: flex; flex-direction: column;
  transition: border-color .25s, transform .25s;
}
/* Le survol prend la couleur de l'univers du film quand elle est fournie (--wa),
   le rouge du studio à défaut. */
a.film-card:hover { border-color: var(--wa, var(--red)); transform: translateY(-3px); }
a.film-card:hover .world-card__go { color: var(--wa-text, var(--wa, var(--red-text))); }
.film-card__media {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-bottom: 1px solid var(--line-soft);
  background: var(--media-bg);
}
/* La règle couvre l'IMAGE autant que la vidéo : quand main.js remplace la boucle
   par son affiche (refus de lecture), l'image doit remplir la boîte 16/9 comme
   la vidéo — sinon elle retombe sur le img{max-width:100%} du reset et laisse
   une bande noire sous elle. Même leçon que les bandes .world-teaser. */
.film-card__media img, .film-card__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.film-card__body { padding: var(--pad-card); display: flex; flex-direction: column; gap: 18px; flex: 1; }
/* Cartes du cast Black Blossom : reveals 3:4 en fiche compacte. Remplace les
   styles inline dupliqués sur les 8 cartes de la page. */
.cast-card .film-card__media { aspect-ratio: 832 / 1114; }
.cast-card .film-card__body { gap: 6px; }
/* 13px : les glyphes JP sont optiquement plus gros — exception d'échelle assumée,
   même dérogation que .roster__cell .jp */
.cast-card .jp { font-size: 13px; color: var(--red-text); font-family: var(--mono); letter-spacing: var(--ls-loose); }
/* `.cast-card h3` seul est ecrase par `.film-card h2, .film-card h3` (meme specificite,
   plus bas dans le fichier) : les noms sortaient a 33,6 px dans une boite de 80 px, et
   trois a cinq des sept debordaient de leur carte selon la largeur. Le selecteur double
   passe devant sans !important. Le rembourrage de --pad-card (44 px) est dimensionne pour
   des cartes a deux par rangee, pas pour des tuiles de 169 px : resserre ici, la boite de
   texte passe de 69-80 px a 118-139 px. Surtout PAS d'overflow-wrap: anywhere, mesure le
   02/09 : il coupe KUROHIM/E et KAGESAK/URA en plein milieu. */
.film-card.cast-card h3 { font-size: clamp(1.25rem, 1.6vw, 1.75rem); }
.cast-card .film-card__body { padding: 18px 14px 22px; }
.cast-card .film-card__body p { font-size: var(--fs-sm); color: var(--muted); }
.film-card__bar { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.film-card h2, .film-card h3 { font-size: clamp(1.5rem, 2.4vw, 2.1rem); }
.film-card__specs { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.film-card__specs span { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); }

/* ---------- Reels ---------- */
.reels-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
@media (max-width: 860px) { .reels-grid { grid-template-columns: 1fr; } }
/* ShowGens : deux formats 2:1 empilés à gauche, le carré à droite sur les deux rangées.
   Sans ce calage, le 1:1 tombait seul sur la deuxième rangée et laissait une demi-page
   vide (mesuré le 02/09). Sous media query et par classe, jamais en ligne : à 860 px la
   grille repasse à une colonne, et un grid-row en ligne y ferait chevaucher le carré sur
   les deux autres. */
@media (min-width: 861px) {
  .reels-grid--showgens > :nth-child(1) { grid-column: 1; grid-row: 1; }
  .reels-grid--showgens > :nth-child(2) { grid-column: 1; grid-row: 2; }
  .reels-grid--showgens > :nth-child(3) { grid-column: 2; grid-row: 1 / span 2; }
}
.reels-grid--solo { grid-template-columns: 1fr; }

/* Teaser d'univers — bande vidéo en tête de la page du monde en Spotlight */
.world-teaser { position: relative; height: clamp(300px, 56vh, 600px);
  overflow: hidden; border-bottom: 1px solid var(--line); }
/* L'affiche et la vidéo se superposent et se cadrent pareil. La règle doit couvrir
   l'IMAGE autant que la vidéo : quand iOS refuse la lecture, main.js masque la vidéo
   et affiche l'affiche à sa place. Sans règle sur `img`, celle-ci retombait à sa
   taille native (1280×720) dans une bande de 390 px — on ne voyait plus qu'un coin
   du poster, à l'échelle 1:1. C'était le défaut intermittent des bandes Glamour
   State et Rabbit Butchers. */
.world-teaser img, .world-teaser video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* pointer-events:none : sans ça le voile, peint en dernier, avale tout le pointeur
   et le survol des lames de la bande pellicule (/films/) ne se déclenche jamais. */
.world-teaser::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, var(--media-fade) 2%, rgba(var(--media-veil),.22) 42%, rgba(var(--media-veil),.38) 100%); }
/* Grain de pellicule sur toutes les surfaces animées (même bruit que le hero).
   ::before : au-dessus du média, sous le fondu ::after et le bouton pause. */
.world-teaser::before, .spotlight__media::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.3;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Mouvement réduit : les bandes d'univers SANS titre disparaissent — elles
   n'existent que pour leur boucle. La bande pellicule de /films/, elle, RESTE : main.js y
   remplace chaque vidéo par son affiche, la bande devient une planche contact
   fixe, c'est le sommaire visuel de la page et non du mouvement.
   La bande TITRÉE des pages d'univers reste aussi (audit 01/09) : elle porte le
   <h1> — le logotype de l'univers — depuis que la page-head ne répète plus le nom.
   La masquer effaçait le titre de quatorze pages pour qui refuse le mouvement.
   Seule sa boucle part ; l'affiche <img> dessous couvre le cadre.
   Le :not() est explicite parce que la règle sans lui était inerte sur la
   variante : .world-teaser--strip pèse la même spécificité (0,1,0) et vient
   plus bas — elle gagnait en silence. */
@media (prefers-reduced-motion: reduce) {
  .world-teaser:not(.world-teaser--strip):not(.world-teaser--titled) { display: none; }
  .world-teaser--titled video { display: none; }
}
html.no-js .world-teaser video { display: none; }
/* Bande pellicule (/films/) : une case par univers animé, boucles d'aperçu. Chaque lame
   est un LIEN vers son film sur la page (audit 01/09 : elles avaient l'air tappables et
   ne faisaient rien) — l'anneau de focus se dessine dans la lame, pas autour du biseau.
   Les cases sont taillées EN BISEAU — le motif lame du studio (sceau coupé,
   transition coup de lame) — et s'élargissent au survol. Le média est
   contre-incliné et surdimensionné pour couvrir les coins du biseau.
   Le fondu ::after de .world-teaser couvre toute la bande. */
.world-teaser--strip {
  display: flex; gap: 2px;
  /* Pleine tête d'écran : la bande descend jusqu'au titre de la page,
     qui affleure en bas du premier écran (choix Mike, 12/08) */
  height: clamp(440px, 60vh, 680px);
}
.world-teaser--strip > a {
  position: relative; overflow: hidden; flex: 1 1 0;
  transform: skewX(-7deg);
  transition: flex-grow .45s ease;
}
/* Les cases de bord débordent du cadre : pas de triangle vide aux extrémités */
.world-teaser--strip > a:first-child { margin-left: -4%; flex-grow: 1.18; }
/* :last-of-type, pas :last-child — main.js appendChild le bouton .loop-pause
   en dernier enfant du conteneur, ce qui volait son débord à la dernière case. */
.world-teaser--strip > a:last-of-type { margin-right: -4%; flex-grow: 1.18; }
/* Contre-inclinaison : l'échelle doit couvrir les coins du biseau, qui grandissent
   avec la hauteur (décalage ≈ hauteur × tan 7°) — 1,8 couvre le pire cas 680px. */
.world-teaser--strip img, .world-teaser--strip video { transform: skewX(7deg) scale(1.8); }
@media (hover: hover) {
  .world-teaser--strip > a:hover, .world-teaser--strip > a:focus-visible { flex-grow: 1.7; }
}
.world-teaser--strip > a:focus-visible { outline-offset: -4px; }
@media (max-width: 860px) {
  /* Variante B (choix Mike) : grille 3×2, les six univers visibles d'un coup,
     assez haute pour tenir l'écran d'entrée — le titre affleure dessous. */
  .world-teaser--strip {
    display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr 1fr 0.72fr;
    height: clamp(470px, 62vh, 620px);
  }
  .world-teaser--strip > a { flex: none; margin: 0; }
  /* Les cases de bord de chaque rangée débordent : pas de triangle vide */
  .world-teaser--strip > a:nth-child(3n+1) { margin-left: -5vw; }
  .world-teaser--strip > a:nth-child(3n) { margin-right: -5vw; }
  /* Septième lame (The Bird) : bande pleine largeur qui ferme la grille —
     deux rangées de trois lames, puis un plan large. */
  .world-teaser--strip > a:nth-child(7) { grid-column: 1 / -1; margin: 0 -5vw; }
  /* La grille n'offre que sept emplacements. La huitième lame
     (preview-velis-03, doublon du plan VELIS-03 déjà servi par strip-velis-03
     en 3e position) tombait dans une 4e rangée implicite de hauteur nulle :
     invisible, mais elle ajoutait une bande vide sous la dernière rangée. */
  .world-teaser--strip > a:nth-child(8) { display: none; }
  .world-teaser--strip img, .world-teaser--strip video { transform: skewX(7deg) scale(1.6); }
}

/* ---------- Spotlight — univers mis en avant (accueil + worlds) ---------- */
.spotlight { position: relative; display: block; overflow: hidden;
  border: 1px solid var(--line); margin-bottom: 26px; transition: border-color .25s; }
.spotlight:hover, .spotlight:focus-visible { border-color: var(--red); }
.spotlight__media { position: absolute; inset: 0; }
.spotlight__media img, .spotlight__media video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.spotlight__media::after { content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(var(--media-veil),.9) 6%, rgba(var(--media-veil),.34) 52%, rgba(var(--media-veil),.12) 100%); }
@media (prefers-reduced-motion: reduce) { .spotlight__media video { display: none; } }
.spotlight__tag { position: absolute; top: 0; left: 0; z-index: 2;
  /* #c22a22, pas var(--red) : #e0342b sous #eceae6 en 11px sort à 3,72:1,
     sous le seuil AA de 4,5:1 pour du petit texte. Ce rouge-ci donne 4,79:1. */
  background: #c22a22; color: #fff; font-family: var(--hud); font-weight: 700; font-size: 12px;
  letter-spacing: 0.18em; text-transform: uppercase; line-height: 1; display: inline-flex; align-items: stretch; }
/* Étiquette PV (visuel-clé GITS 2026, sans son mot japonais) : le CODE de l'univers puis le
   libellé, deux cellules séparées d'un filet. */
.spotlight__tag > span { display: inline-flex; align-items: center; padding: 8px 12px 7px; }
.spotlight__tag .pv__code { border-right: 1px solid rgba(255, 255, 255, .35); }
.spotlight__in { position: relative; z-index: 1;
  padding: clamp(150px, 24vw, 250px) clamp(18px, 3.4vw, 40px) clamp(20px, 3vw, 32px);
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 18px; }
.spotlight__meta { font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--on-media-muted); margin: 0 0 8px; }
.spotlight__title { font-family: var(--display); font-weight: 600;
  font-size: clamp(32px, 5vw, 54px); line-height: .95; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--on-media); margin: 0 0 8px; }
/* L'univers en Spotlight était le SEUL de la landing sans son logotype — et pour une
   raison mécanique : celui qu'on met en avant sort de la grille des cartes, et c'est
   la carte qui portait la marque. Le mettre en avant le débrandait. */
.spotlight__title img {
  display: block; width: auto; height: auto;
  max-height: calc(clamp(38px, 5.2vw, 58px) * var(--mk, 1)); max-width: 100%;
}
.spotlight__desc { max-width: 58ch; color: var(--on-media-dim); margin: 0; }
.spotlight .world-card__go { flex: none; color: var(--on-media-muted); }
.spotlight:hover .world-card__go, .spotlight:focus-visible .world-card__go { color: var(--on-media-red); }
/* Intertitre entre un film et sa masonry (pages univers) */
.reel-followup { margin: var(--sp-6) 0 var(--sp-3); }
.reel { margin: 0; position: relative; scroll-margin-top: 90px; }
.reel video { width: 100%; height: auto; display: block; background: var(--media-bg); border: 1px solid var(--line); cursor: pointer; }
.reel__hint {
  position: absolute; top: 12px; right: 12px;
  font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--on-media); background: rgba(var(--media-veil),0.74); border: 1px solid var(--line-on-media);
  padding: 6px 11px; opacity: 0; transition: opacity .25s; pointer-events: none;
}
.reel:hover .reel__hint,
.reel:focus-within .reel__hint { opacity: 1; }
/* Écrans tactiles : ni survol ni focus n'arrivent avant le tap. L'indice reste
   affiché tant que le lecteur n'est pas engagé — sinon treize boucles muettes
   sans le moindre signe qu'on peut les lire. */
@media (hover: none) {
  .reel:not(.engaged) .reel__hint { opacity: 1; }
}
.reel.engaged .reel__hint { display: none; }
/* Bouton sous-titres (injecté par main.js sur les lecteurs à piste captions).
   Toujours visible — un réglage d'accessibilité ne se découvre pas au survol. */
.cc-toggle {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label);
  color: var(--on-media-muted); background: rgba(var(--media-veil),0.74); border: 1px solid var(--line-on-media);
  cursor: pointer; transition: color .2s, border-color .2s;
}
.cc-toggle:hover, .cc-toggle:focus-visible { color: var(--on-media); border-color: var(--red); }
.cc-toggle[aria-pressed="true"] { color: var(--on-media); border-color: var(--red); background: rgba(var(--media-veil),0.9); }
.reel.engaged .cc-toggle { display: none; }
.reel.engaged .loop-pause { display: none; }
.reel figcaption { display: flex; gap: 16px; justify-content: space-between; align-items: baseline; padding: 10px 2px 0; font-size: var(--fs-sm); color: var(--body); flex-wrap: wrap; }
.reel__title { font-family: var(--display); font-weight: 600; font-size: clamp(19px, 2.4vw, 26px); letter-spacing: 0.04em; text-transform: uppercase; color: var(--text); margin: 0 0 10px; }
/* LE MONDE EST DESSINÉ, LE FILM RESTE ÉCRIT. Au-dessus d'un lecteur, la ligne disait
   « Glamour State — Teaser » : deux choses de nature différente dans une seule
   typographie. L'univers a une marque, il la porte ; « Teaser » est un format, il
   n'en a pas et n'en aura jamais. Les séparer rend aussi service aux films dont le
   titre ne nomme pas leur monde — « The Empty Throne » n'annonçait Scarlet Protocol
   nulle part.
   Les ShowGens n'ont volontairement pas de marque ici : ce ne sont pas des univers.
   `--mk` est la propriété commune (voir .plate-mark) ; seule l'échelle est locale. */
/* UN FILM NE SE JOUE QU'À UN ENDROIT. Sur une page d'univers, un film qui vit déjà sur
   /films/ devient une affiche qui y mène — au lieu d'un troisième lecteur pour le même
   fichier. Le contenu ne quitte pas le site, il cesse d'être instancié trois fois. */
.reel-link { display: block; position: relative; overflow: hidden; }
.reel-link img { width: 100%; height: auto; display: block; }
.reel-link__go {
  position: absolute; right: 14px; bottom: 14px;
  font-family: var(--mono); font-size: 12px; letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--text);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  padding: 9px 13px; backdrop-filter: blur(6px);
}
.reel-link:hover .reel-link__go { background: var(--wa, var(--red)); color: #fff; }
/* LE DÉPLIAGE D'UNE PLANCHE-CONTACT, ET POURQUOI IL NE PART PAS AILLEURS.
   La première version SUPPRIMAIT les planches au-delà de la douzième et renvoyait vers
   /gallery/. Faux deux fois : la galerie n'a aucune image en commun avec les pages
   d'univers (0 sur 102, mesuré), et les planches retirées n'existaient plus nulle part.
   Elles sont maintenant TOUTES dans la page, masquées, et le bouton les révèle sur
   place. `content-visibility: hidden` fait que le navigateur ne les met pas en page tant
   qu'elles sont repliées, et `loading="lazy"` fait qu'il ne les télécharge pas : le gain
   de poids est le même qu'en les supprimant, sans la perte. */
.masonry figure[data-repli] { display: none; content-visibility: hidden; }
.masonry.est-deplie figure[data-repli] { display: block; content-visibility: visible; }
.gal-more { margin-top: 18px; }
.gal-more__btn {
  font-family: var(--mono); font-size: 12px; letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--wa-text, var(--wa, var(--red-text)));
  background: none; border: 1px solid var(--line); padding: 13px 16px; min-height: 44px;
  cursor: pointer; transition: border-color .2s, color .2s;
}
.gal-more__btn:hover { border-color: var(--wa, var(--red)); }
.gal-more__btn:focus-visible { outline: 2px solid var(--wa, var(--red)); outline-offset: 3px; }
/* Sans JS le bouton ne peut rien : on le cache et on montre tout, plutôt que de laisser
   un bouton mort et des planches inatteignables. */
html.no-js .gal-more { display: none; }
html.no-js .masonry figure[data-repli] { display: block; content-visibility: visible; }
/* LE SYNOPSIS SE REPLIE DERRIÈRE SON PROPRE AVERTISSEMENT. Il annonce « l'intrigue
   entière, fin comprise » : le replier ne l'allège pas seulement, ça le rend honnête —
   avant, la mise en garde surplombait les spoilers au lieu d'en protéger. */
.synopsis__fold > summary { cursor: pointer; list-style: none; display: block; }
.synopsis__fold > summary::-webkit-details-marker { display: none; }
.synopsis__fold > summary::after {
  content: 'Read the synopsis  ▾'; display: inline-block; margin-top: 10px;
  font-family: var(--mono); font-size: 12px; letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--wa-text, var(--wa, var(--red-text)));
}
.synopsis__fold[open] > summary::after { content: 'Close  ▴'; }
[lang="fr"] .synopsis__fold > summary::after { content: 'Lire le synopsis  ▾'; }
[lang="fr"] .synopsis__fold[open] > summary::after { content: 'Replier  ▴'; }
.synopsis__fold > summary:focus-visible { outline: 2px solid var(--wa, var(--red)); outline-offset: 4px; }

.reel__title--mark { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.reel__title--mark img {
  display: block; width: auto; height: auto;
  max-height: calc(clamp(34px, 4.4vw, 50px) * var(--mk, 1)); max-width: 100%;
}
.reel__title--mark span { font-size: 0.78em; letter-spacing: 0.06em; color: var(--muted); }
.reel figcaption .mono { white-space: nowrap; flex: none; }
/* L'override mobile doit VENIR APRÈS la règle de base : même sélecteur, même
   spécificité — placé avant, il perdait à toutes les largeurs. */
/* flex:none interdit le rétrécissement : sans shrink, white-space:normal ne
   peut rien replier — le mono FR (plus long) débordait de 47px à 390px. */
@media (max-width: 860px) { .reel figcaption .mono { white-space: normal; flex: 0 1 auto; min-width: 0; } }
/* La date de sortie ne se coupe jamais en deux. `white-space: normal` ci-dessus rend au
   mono le droit de se replier — nécessaire, le mono français débordait sans lui — mais il
   le rendait AUSSI à l'intérieur de la date : à 390 px « 2026-08-10 » se lisait « 2026- »
   puis « 08-10 » sur la ligne suivante (mesuré le 03/09/2026). Le repli reste entre les
   éléments de la bande, jamais dans un nombre. */
.reel figcaption .mono time { white-space: nowrap; }

/* ---------- Method ---------- */
.method-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--line-soft); border: 1px solid var(--line-soft); }
@media (max-width: 860px) { .method-grid { grid-template-columns: 1fr; } }
/* Variante à quatre (les quatre biomes de VELIS-03) : une ligne pleine, sinon la
   cellule manquante rend un panneau gris uni — le fond du conteneur — et se lit
   comme un bug d'affichage. Palier mobile aligné sur celui de .method-grid. */
.method-grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1020px) { .method-grid--4 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 860px) { .method-grid--4 { grid-template-columns: 1fr; } }
.method-cell { background: var(--bg); padding: var(--pad-card); }
.method-cell h3 { font-size: var(--fs-h4); margin: 14px 0 12px; }
.method-cell p { font-size: var(--fs-sm); }

/* ---------- Gallery (masonry) ---------- */
.masonry { columns: 3 340px; column-gap: 14px; }
/* Trois colonnes pour deux ou quatre planches, c'est 1/1/0 ou 2/2/0 : un trou d'environ
   440 x 500 px a droite (mesure du 02/09 sur les dossiers de production). Les sections qui
   ont de la reserve depliente six planches ; celles qui n'ont que deux ou quatre planches en
   tout passent a deux colonnes. Le :has() lit le nombre reel de figures plutot qu'une classe
   posee a la main : rien a maintenir si une planche est ajoutee plus tard, et aucun balisage
   nouveau, donc aucun jeton ?v a rejetonner. Sans support de :has(), on retombe simplement
   sur l'affichage d'aujourd'hui. */
.masonry:has(> figure:nth-child(2):last-child),
.masonry:has(> figure:nth-child(4):last-child) { columns: 2 340px; }
/* Le chapo touchait la premiere planche au pixel pres (476,84 contre 476,84), et une planche
   inclinee le recouvrait sur 16 px pendant un defilement rapide. */
.section__intro + .masonry { margin-top: var(--sp-4); }
.masonry figure { break-inside: avoid; margin: 0 0 14px; position: relative; }
.masonry a { display: block; border: 1px solid var(--line-soft); transition: border-color .2s; overflow: hidden; }
.masonry a:hover { border-color: var(--wa, var(--red)); }
.masonry img { width: 100%; transition: transform .3s cubic-bezier(.2,.6,.2,1); }
.masonry figure:hover img { transform: scale(1.03); }
@media (prefers-reduced-motion: reduce) {
  .masonry figure:hover img,
  .world-card:hover .world-card__media img,
  .world-card.is-near .world-card__media img,
  .case-card:hover .case-card__media img { transform: none; }
}
.masonry figcaption { position: absolute; left: 0; bottom: 0; padding: 10px 12px; font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: transparent; transition: color .2s; pointer-events: none; }
.masonry figure:hover figcaption,
.masonry figure:focus-within figcaption { color: var(--on-media); text-shadow: 0 1px 8px rgba(0,0,0,0.9); }
/* Écrans tactiles : ni survol ni focus (le tap ouvre la visionneuse). La légende
   quitte l'image et se pose sous la vignette, lisible en permanence — sinon 59
   images défilent sans un titre. */
/* /gallery/ sur téléphone : une seule colonne faisait 15 000 px de long. Deux colonnes
   pour les images (paysage pour la plupart) ; les planches d'univers, en portrait,
   gardent la colonne unique. */
@media (max-width: 620px) {
  .masonry--frames { columns: 2 150px; column-gap: 10px; }
  .masonry--frames figure { margin-bottom: 10px; }
}
@media (hover: none) {
  .masonry figcaption {
    position: static; padding: 8px 2px 0; color: var(--muted); text-shadow: none;
  }
}

/* ---------- Lightbox ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(6,6,7,0.96);
  color: var(--on-media);   /* la visionneuse reste une salle noire, quel que soit le thème */
  display: none;
  align-items: center; justify-content: center;
  padding: clamp(16px, 4vw, 56px);
}
.lightbox.open { display: flex; }
.lightbox img { max-width: 100%; max-height: calc(100vh - 130px); width: auto; height: auto; object-fit: contain; }
@media (max-width: 620px) { .lightbox img { max-height: calc(100svh - 150px); } }  /* la légende (bottom 110px) chevauchait l'image à 360 px */
.lightbox__caption { position: absolute; left: 0; right: 0; bottom: 22px; text-align: center; font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--on-media-muted); padding: 0 70px; }
.lightbox button {
  position: absolute; background: none; border: 1px solid var(--line-on-media); color: var(--on-media);
  font-family: var(--mono); font-size: var(--fs-sm); padding: var(--pad-pill); cursor: pointer; z-index: 2;
  transition: border-color .2s;
  /* Commandes principales d'une UI plein écran : cible tactile 44px garantie */
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}
.lightbox button:hover { border-color: var(--red); }
.lightbox__close { top: 18px; right: 18px; }
.lightbox__prev { left: 14px; top: 50%; transform: translateY(-50%); }
.lightbox__next { right: 14px; top: 50%; transform: translateY(-50%); }
@media (max-width: 620px) {
  .lightbox__prev, .lightbox__next { top: auto; bottom: 60px; transform: none; }
  .lightbox__caption { bottom: 110px; padding: 0 16px; }
}

/* ---------- Page hero (inner pages) ---------- */
.page-head { position: relative; padding-top: 170px; padding-bottom: clamp(40px, 6vh, 72px); border-bottom: 1px solid var(--line-soft); } /* relative : hôte de la dissolution .pix */
.page-head h1 { font-size: clamp(3.2rem, 10vw, 9rem); margin-top: var(--sp-3); }
.page-head__intro { max-width: 62ch; margin-top: var(--sp-4); font-size: var(--fs-lede); color: var(--body); }
.page-head__meta { display: flex; flex-wrap: wrap; gap: 10px 36px; margin-top: var(--sp-5); }
.page-head__meta span { font-family: var(--hud); font-weight: 600; font-size: 13px; letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); }
.page-head__meta b { color: var(--text); font-weight: 500; }

/* Fil d'Ariane — présent visuellement parce que le balisage BreadcrumbList
   l'exige : Google demande que le fil déclaré corresponde à un fil réel. */
.crumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1);
  /* Le fil passe à deux lignes sur mobile (dossiers de production) : 8px
     d'interligne laissaient la zone tappable du haut recouvrir le texte du bas
     de 5px — un appui sur « Production » atteignait « BLACK-BLOSSOM ». */
  row-gap: 16px;
  margin-bottom: var(--sp-2);
  font-family: var(--hud); font-size: var(--fs-hud); font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
/* padding/marge opposés : la zone tappable atteint 44px sans décaler la mise en page */
.crumb a { color: var(--muted); text-decoration: none; border-bottom: 1px solid transparent;
  padding-block: 13px; margin-block: -13px; position: relative; }
/* Le padding règle la hauteur, rien n'élargissait la cible : « Home » et
   « Work » ne font que 33,5px de large. Extension par pseudo-élément, comme
   `.site-foot__legal a::after`, pour ne pas allonger le soulignement au survol.
   Les liens sont séparés d'au moins 16px (gap + chevron) : pas de recouvrement. */
.crumb a::after { content: ''; position: absolute; left: -6px; right: -6px; top: 0; bottom: 0; }
.crumb a:hover, .crumb a:focus-visible { color: var(--text); border-bottom-color: var(--line); }
.crumb [aria-current="page"] { color: var(--text); }
/* Le séparateur (posé par inject_schema.py) devient un filet, pas une barre oblique */
.crumb [aria-hidden="true"] { color: var(--line); font-size: 0; width: 14px; height: 1px; background: var(--line); display: inline-block; }

/* ---------- Journal de production ---------- */
.log-list { border-top: 1px solid var(--line-soft); }
.log-entry { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 12px clamp(24px, 4vw, 64px); padding: clamp(26px, 4vh, 42px) 0; border-bottom: 1px solid var(--line-soft); }
.log-entry > .mono { padding-top: 5px; color: var(--red-text); }
.log-entry h3 { font-size: var(--fs-h4); }
.log-entry div p { margin-top: var(--sp-2); max-width: 62ch; color: var(--body); font-size: var(--fs-base); }
@media (max-width: 700px) {
  .log-entry { grid-template-columns: 1fr; gap: 8px; padding: 24px 0; }
  .log-entry > .mono { padding-top: 0; }
}

/* ---------- About : portrait ---------- */
.about-head { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: clamp(28px, 5vw, 72px); align-items: end; }
/* En-tête de hub en deux colonnes : le titre à gauche, le chapô et la méta à droite. Les
   deux hubs ouvraient sur 747 et 775 px de texte, avec la moitié droite vide et 7 à 12 %
   seulement de la première image au-dessus de la ligne de flottaison. On ne remet PAS
   d'image ici : le spotlight et les cartes d'étude de cas arrivent juste en dessous, avec
   les leurs. */
.page-head--split { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); align-items: end; }
@media (max-width: 900px) { .page-head--split { grid-template-columns: 1fr; } }
@media (max-width: 900px) {
  .about-head { grid-template-columns: 1fr; }
  .about-head .about-head__photo { max-width: min(400px, 100%); }
}
.about-head__photo { border: 1px solid var(--line); }

/* ---------- Prose / dossier blocks ---------- */
.prose { max-width: 68ch; }
.prose p { margin-bottom: 1.2em; font-size: var(--fs-base); }
.prose p:last-child { margin-bottom: 0; }
/* Une note en mono DANS un bloc de prose : `.prose p` (même spécificité que `.mono`,
   déclarée plus bas) l'emportait et rendait ces notes en 16px capitales espacées —
   énormes à côté du corps qu'elles annotent. Elles reprennent leur taille de note. */
.prose p.mono, .log-entry p.mono, .method-cell p.mono, p.mono { font-size: var(--fs-xs); }
/* Note en retrait sous un bloc : un cran plus sourd que --muted, mais encore AA. Remplace
   les 50 `style="opacity:.72"` des dossiers de production, qui passaient sous 4,5:1. */
.mono--note { color: var(--muted-2); margin-top: var(--sp-2); }
.prose strong { color: var(--text); font-weight: 600; }

.spec-table { width: 100%; border-collapse: collapse; }
.spec-table th, .spec-table td { text-align: left; padding: 14px 18px 14px 0; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.spec-table th { font-family: var(--mono); font-size: var(--fs-label); font-weight: 500; letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); width: 190px; white-space: nowrap; }
.spec-table td { color: var(--body); font-size: var(--fs-base); }
.spec-table td b { color: var(--text); font-weight: 600; }
/* Écrans étroits (360 px) : la colonne d'intitulés en `nowrap` de 190 px poussait la
   table à 370 px de large — la page débordait. Les intitulés passent à la ligne et
   la table s'empile en deux temps. */
@media (max-width: 430px) {
  .spec-table th { width: 40%; white-space: normal; padding-right: 12px; }
  .spec-table td { font-size: var(--fs-sm); }
}

/* minmax(400px) et non 250px : à 250px auto-fill tient 5 colonnes pour les six
   factions de Glamour State — la 6e reste seule et le fond du conteneur rend un
   panneau gris. À 400px : 3 colonnes en desktop, 2 vers 900px, 1 en dessous —
   toujours des lignes pleines. .roster ne sert que sur Glamour State. */
.roster { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(400px, 100%), 1fr)); gap: 1px; background: var(--line-soft); border: 1px solid var(--line-soft); }
.roster__cell { background: var(--bg); padding: 22px 24px; }
.roster__cell .jp { font-size: 13px; color: var(--red-text); font-family: var(--mono); letter-spacing: var(--ls-loose); }
.roster__cell h3 { font-size: var(--fs-h4); margin: 6px 0 6px; }
.roster__cell p { font-size: var(--fs-sm); color: var(--muted); }

.note-box { border: 1px solid var(--line); border-left: 3px solid var(--red); background: var(--bg2); padding: 22px 26px; max-width: 64ch; }
.note-box p { font-size: var(--fs-sm); }
.note-box .mono { color: var(--red-text); display: block; margin-bottom: var(--sp-1); }

/* ---------- Plaques univers (page Worlds) ---------- */
.world-plate {
  /* display:block obligatoire : la plaque est un <a>, qui retomberait en inline. */
  display: block;
  position: relative;
  border: 1px solid var(--line);
  background: linear-gradient(150deg, color-mix(in srgb, var(--wa, var(--red)) 13%, var(--bg)) 0%, var(--bg) 58%);
  padding: var(--pad-card);
  overflow: hidden;
}
.world-plate h2 { font-size: clamp(1.8rem, 3.4vw, 2.8rem); margin-top: var(--sp-2); }
/* La place rendue par l'ordinal (112 px -> 48 px) va à l'image : le rapport passe de
   16/8 à 16/10, soit 434 x 271 au lieu de 434 x 217 à 1440. L'image reste en cover,
   donc rien à recadrer à la main. */
.world-plate__media { margin-top: var(--sp-4); aspect-ratio: 16 / 10; overflow: hidden; border: 1px solid var(--line-soft); }
.world-plate__media img { width: 100%; height: 100%; object-fit: cover;
  transition: transform .3s cubic-bezier(.2,.6,.2,1); }
.world-plate .palette { margin-top: var(--sp-3); }
.world-plate .mono { margin-top: var(--sp-2); }
/* La plaque est un lien : même grammaire de survol que .world-card sur l'accueil —
   bordure à l'accent de l'univers et léger zoom de la couverture. */
.world-plate:hover, .world-plate:focus-visible { border-color: var(--wa, var(--red)); }
.world-plate:hover .world-plate__media img,
.world-plate:focus-visible .world-plate__media img { transform: scale(1.045); }

/* ---------- Two-column layout ---------- */
.cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: clamp(28px, 5vw, 80px); align-items: start; }
@media (max-width: 900px) { .cols { grid-template-columns: 1fr; } }
.cols__sticky { position: sticky; top: 96px; }
@media (max-width: 900px) { .cols__sticky { position: static; } }

/* ---------- Timeline ---------- */
.timeline { border-left: 1px solid var(--line); padding-left: 32px; display: flex; flex-direction: column; gap: 34px; max-width: 720px; }
.timeline__item { position: relative; }
.timeline__item::before { content: ''; position: absolute; left: -37px; top: 6px; width: 9px; height: 9px; background: var(--red); }
.timeline__item .mono { display: block; margin-bottom: 6px; color: var(--red-text); }
.timeline__item h3 { font-size: var(--fs-h4); margin-bottom: 6px; }
.timeline__item p { font-size: var(--fs-sm); }

/* ---------- Footer ---------- */
/* ---------- Plaque rouge de contact (02/09) ----------
   L'affiche rouge monochrome de Ghost in the Shell : le bloc contact devient une plaque
   pleine largeur au rouge du studio, titre noir Black, texte encre — dans les deux
   thèmes (une plaque se lit comme un média : ses couleurs sont les siennes). Le débord
   se fait par pseudo-élément, le conteneur reste dans la grille. */
.site-foot { margin-top: var(--rhythm); background: var(--bg2); overflow: clip; }
.site-foot__cta { position: relative; isolation: isolate; padding-top: clamp(56px, 9vh, 110px); padding-bottom: clamp(56px, 9vh, 110px); }
/* isolation : sans contexte d'empilement, le ::before en z-index:-1 passe DERRIERE le
   fond de .site-foot. La plaque ne tenait que par la transformation residuelle du tween
   GSAP : en reduced-motion et sans JS elle disparaissait, encre noire sur fond noir. */
/* #e4423a et non var(--red) : l'encre #0a0a0b sort à 4,42:1 sur #e0342b (sous AA pour
   l'étiquette de 12 px), à 4,85:1 sur #e4423a — le rouge texte du studio, même famille. */
.site-foot__cta::before { content: ''; position: absolute; top: 0; bottom: 0; left: -50vw; right: -50vw; background: #e4423a; z-index: -1; }
.site-foot__cta .tag { color: #0a0a0b; border-color: rgba(10, 10, 11, .45); }
.site-foot__cta .tag::before, .site-foot__cta .tag::after { border-color: #0a0a0b; }
.site-foot__cta p { color: #1c0c0b; }
.site-foot__cta .btn--red { background: #0a0a0b; border-color: #0a0a0b; color: #fff; }
.site-foot__cta .btn--red::before { background: #fff; }
.site-foot__cta .btn--red:hover, .site-foot__cta .btn--red:focus-visible { color: #0a0a0b; border-color: #0a0a0b; }
.site-foot__cta .follow, .site-foot__cta .follow a, .site-foot__cta .follow__label { color: #0a0a0b; }
.site-foot__cta .follow a { border-color: rgba(10, 10, 11, .45); }
.site-foot__cta h2 { font-size: clamp(3rem, 9vw, 8rem); margin: 16px 0 10px; color: #0a0a0b; }
.site-foot__cta p { max-width: 560px; font-size: var(--fs-base); }
.site-foot__cta .btn { margin-top: var(--sp-5); }
/* Bouton de copie de l'adresse : secondaire à côté du lien courriel, sur la plaque rouge.
   Il est posé `hidden` dans le balisage et révélé par le script — sans JavaScript, ou si
   l'API presse-papiers manque, personne ne voit un bouton qui ne fait rien. */
.site-foot__cta .btn--copy { background: transparent; border-color: rgba(10, 10, 11, .55); color: #0a0a0b; margin-left: 12px; }
.site-foot__cta .btn--copy::before { background: #0a0a0b; }
.site-foot__cta .btn--copy:hover, .site-foot__cta .btn--copy:focus-visible { color: #e4423a; border-color: #0a0a0b; }
@media (max-width: 560px) { .site-foot__cta .btn--copy { margin-left: 0; } }
.site-foot__grid { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 24px 40px; padding-top: 34px; padding-bottom: 34px; position: relative; margin-top: clamp(28px, 4vh, 48px); }
/* Filet hachuré (les bandes de danger du visuel-clé, en rouge sur le fond du pied — jamais
   de jaune) à la place du trait plein. */
.site-foot__grid::before { content: ''; position: absolute; left: 0; right: 0; top: 0; height: 6px; background: repeating-linear-gradient(-45deg, var(--red) 0 8px, transparent 8px 16px); }
/* gap 24px + padding compensé : cibles de 44px sans chevauchement et sans
   toucher au dessin — 12 + 20,4 + 12 = 44,4px de haut, et 24 − 12 − 12 = 0 de
   séparation. Seule la hauteur du pied de page bouge (+12px par intervalle). */
.site-foot__col { display: flex; flex-direction: column; gap: 24px; }
.site-foot__col a { font-family: var(--hud); font-weight: 600; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); transition: color .2s; padding-block: 12px; margin-block: -12px; }
.site-foot__col a:hover { color: var(--red-text); }
.site-foot__legal { padding-bottom: 34px; display: flex; flex-wrap: wrap; gap: 8px 32px; }
.site-foot__legal span, .site-foot__legal a { font-family: var(--hud); font-weight: 500; font-size: 11px; letter-spacing: 0.14em; color: var(--muted); text-transform: uppercase; }
/* Cible de 44 px par padding compensé (comme les colonnes) : la zone ::after ne compte
   pas pour les outils qui mesurent la boîte du lien, et sur un lien tenant en une ligne
   la boîte faisait 20 px (checkup_rendu, 02/09). */
.site-foot__legal a { border-bottom: 1px solid var(--line); transition: color .2s; position: relative; display: inline-block; padding-block: 12px; margin-block: -12px; }
/* Extension de cible sans toucher au soulignement ; voisins non interactifs */
.site-foot__legal a::after { content: ''; position: absolute; left: -6px; right: -6px; top: -14px; bottom: -14px; }
.site-foot__legal a:hover { color: var(--red-text); }

/* ---------- Page légale ---------- */
/* Le bloc garde la grille du site (donc le bord gauche du h1 de .page-head) ;
   la mesure de lecture de 72ch est portée par les enfants, sinon le padding de
   .wrap était compté dedans et la colonne ne faisait que ~60ch. */
.legal-prose { max-width: var(--maxw); }
.legal-prose > * { max-width: 64ch; }
.legal-prose h2 { font-size: clamp(1.35rem, 2.2vw, 1.8rem); margin: 44px 0 14px; }
.legal-prose h2:first-child { margin-top: 0; }
.legal-prose p, .legal-prose li { font-size: var(--fs-base); color: var(--body); margin-bottom: var(--sp-2); }
.legal-prose ul { padding-left: 20px; margin-bottom: var(--sp-2); }
.legal-prose li { margin-bottom: var(--sp-1); }
.legal-prose a { color: var(--text); border-bottom: 1px solid var(--line); transition: color .2s; }
.legal-prose a:hover { color: var(--red-text); }

/* ---------- Reveal (JS hook) ----------
   Masqué uniquement quand les animations vont réellement tourner
   (classe posée par main.js si GSAP est chargé et motion OK). */
html.js-anim .reveal { opacity: 0; }

/* ---------- Transition lame entre pages ---------- */

/* ---------- Intro : le sceau se coupe et se ressoude ---------- */
.seal-intro {
  position: fixed; inset: 0; z-index: 200;
  background: var(--bg);
  display: none;
  flex-direction: column; gap: 22px;
  align-items: center; justify-content: center;
  cursor: pointer;
}
/* Amorce (écran « now loading » du site officiel, sans son clignotement à 10 Hz) : une bande
   vivante sous le sceau, un relevé HUD, un curseur à 1 Hz ; sortie en coupe (clip-path). */
.seal-intro .vband { width: clamp(110px, 16vw, 170px); }
.boot__readout { font-family: var(--hud); font-weight: 600; font-size: var(--fs-hud); letter-spacing: .24em; text-transform: uppercase; color: var(--muted); display: flex; gap: 10px; align-items: center; margin: 0; }
.boot__readout .sep { margin: 0; }
.boot__readout i { width: 8px; height: 12px; background: var(--red); }
html.js-anim .boot__readout i { animation: caret 1s steps(2, end) infinite; }
@keyframes caret { 50% { opacity: 0; } }
.seal-intro.on { display: flex; }
.seal-intro svg { width: clamp(110px, 16vw, 170px); height: auto; }
/* La lame prend la couleur d'encre du thème : l'attribut stroke du SVG est ivoire,
   invisible sur papier. Le sceau lui-même (rouge, gravure sombre) tient sur les deux fonds. */
.seal-intro .si-blade { stroke: var(--text); }

/* ---------- Focus ---------- */
/* [tabindex="0"] : tout ce que le site rend focusable sans être <a> ni <button>
   — les lecteurs .reel (main.js leur pose tabindex/role=button) et la région
   défilable .marquee. `role="button"` ne change pas le sélecteur de type : sans
   cette troisième branche, ces éléments retombaient sur l'anneau par défaut de
   Chromium, rgb(16,16,16), invisible sur le fond du site. */
a:focus-visible, button:focus-visible, [tabindex="0"]:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
/* Sur la plaque de contact, l'anneau rouge tombe sur #e4423a : 1,09:1, invisible,
   dans les deux themes. Il reprend donc l'encre de la plaque, deja a 4,85:1 pour le
   texte. Meme chose pour le soulignement de focus des liens « Follow ». */
.site-foot__cta a:focus-visible, .site-foot__cta button:focus-visible { outline-color: #0a0a0b; }
.site-foot__cta .follow a:focus-visible { border-bottom-color: #0a0a0b; }

/* ---------- Skip link ---------- */
.skip {
  position: absolute; left: 16px; top: 12px; z-index: 200;
  background: #c22a22; color: #fff; font-family: var(--mono); font-size: var(--fs-xs);
  padding: var(--pad-pill); letter-spacing: 0.12em;
  /* transform plutôt que `top` : composité par le GPU, aucun recalcul de mise en page */
  transform: translateY(-100px); transition: transform .2s;
}
.skip:focus { transform: translateY(0); }


/* ---------- Transition « signal » entre les pages (02/09) ----------
   La page quittée PERD LE SIGNAL : 220 ms de bandes tranchées (clip-path + translateX, par
   paliers) puis coupe nette sur le fond ; la page arrivée fait son propre beat — dissolution en
   pixels de l'en-tête et de la bande d'univers, salve du titre (main.js). Téléphone : coupe
   simple de 140 ms, sans bandes (profil calme). Joué sur <body> par main.js (html.signal-out)
   AVANT la navigation, chez tout le monde : la View Transition inter-documents native était
   abandonnée dans ~9 navigations sur 10 sous Chrome 141 (première navigation = rien), elle est
   donc désactivée. Reduced motion : main.js ne pose jamais la classe. */
@view-transition { navigation: none; }
html.signal-out body { pointer-events: none; }
/* Le départ s'applique au CONTENU, pas au document : la barre et sa bande restent
   peintes pendant toute la sortie. Sinon le site s'éteint entièrement au moment où il
   dit perdre le signal, et le visiteur regarde un écran noir sans repère (mesuré le
   02/09 : 240 ms sans barre en local, bien plus sur une ligne lente). */
html.signal-out main, html.signal-out .site-foot { animation: vt-signal-out 0.14s steps(1, end) both; }
@media (max-width: 860px) {
  html.signal-out body { animation: none; }
  html.signal-out main, html.signal-out .site-foot { animation: vt-cut-out 0.14s steps(1, end) both; }
}
@keyframes vt-signal-out {
  0%   { clip-path: inset(0); transform: none; opacity: 1; }
  15%  { clip-path: inset(6% 0 58% 0); transform: translateX(-14px); }
  30%  { clip-path: inset(38% 0 24% 0); transform: translateX(18px); }
  45%  { clip-path: inset(0 0 100% 0); transform: none; }
  55%  { clip-path: inset(62% 0 8% 0); transform: translateX(-10px); }
  70%  { clip-path: inset(18% 0 46% 0); transform: translateX(8px); }
  85%, 100% { clip-path: inset(0 0 100% 0); transform: none; opacity: 0; }
}
@keyframes vt-cut-out { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  html.signal-out body,
  html.signal-out main, html.signal-out .site-foot { animation: none; }
}

/* ---------- Accès au dossier de production (pages d'univers) ----------
   Le bouton avait été inséré HORS du `.wrap` : il s'alignait sur le bord de page,
   décalé du reste. Bloc dédié, dans le flux, et coloré à l'accent de l'univers
   (--wa) pour qu'on le voie sans qu'il crie. */
.dossier-cta {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px clamp(20px, 3vw, 48px);
  margin-top: clamp(32px, 5vh, 56px);
  padding: clamp(20px, 2.6vw, 30px) clamp(20px, 3vw, 34px);
  border: 1px solid color-mix(in srgb, var(--wa, var(--red)) 42%, transparent);
  background:
    linear-gradient(100deg, color-mix(in srgb, var(--wa, var(--red)) 13%, transparent) 0%, transparent 62%),
    var(--bg2);
  transition: border-color .25s ease, background-color .25s ease;
}
.dossier-cta:hover, .dossier-cta:focus-visible {
  border-color: var(--wa, var(--red));
  background:
    linear-gradient(100deg, color-mix(in srgb, var(--wa, var(--red)) 22%, transparent) 0%, transparent 62%),
    var(--panel);
}
.dossier-cta__label {
  grid-column: 1; font-family: var(--mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--wa-text, var(--wa, var(--red-text)));
  display: flex; align-items: center; gap: 10px;
}
.dossier-cta__label::before {
  content: ''; width: 8px; height: 8px; flex: none;
  background: var(--wa, var(--red));
  clip-path: polygon(0 0, 100% 0, 100% 66%, 66% 100%, 0 100%);
}
.dossier-cta__title {
  grid-column: 1; font-family: var(--display); font-weight: 600;
  font-size: clamp(1.15rem, 2.2vw, 1.6rem); line-height: 1.05;
  text-transform: uppercase; color: var(--text); letter-spacing: 0.01em;
}
.dossier-cta__go {
  grid-column: 2; grid-row: 1 / 3; align-self: center; white-space: nowrap;
  font-family: var(--mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--wa-text, var(--wa, var(--red-text)));
  border-bottom: 1px solid color-mix(in srgb, var(--wa, var(--red)) 55%, transparent);
  padding-bottom: 4px;
}
.dossier-cta:hover .dossier-cta__go { border-bottom-color: var(--wa, var(--red)); }
.dossier-cta .arrow { transition: transform .25s ease; display: inline-block; }
.dossier-cta:hover .arrow { transform: translateX(4px); }
@media (max-width: 640px) {
  .dossier-cta { grid-template-columns: 1fr; gap: 10px; }
  .dossier-cta__go { grid-column: 1; grid-row: auto; justify-self: start; margin-top: 4px; }
}

/* ---------- Titre d'univers posé sur la boucle hero ----------
   Le logotype EST le titre : il porte le h1, et le nom reste en texte pour les lecteurs
   d'écran et les moteurs. Le voile est peint dans le MÉDIA, jamais sous le lettrage —
   une lueur autour d'un titre clair réduit le contraste local qui fait la netteté du
   bord ; c'est le défaut de tutoriel par excellence, et les vrais titrages traitent le
   fond, pas le texte.
   L'état complet cède la place à l'état réduit sous 720 px : à cette largeur un pétale
   fait deux pixels et ne fait plus que salir le bord. */
/* Le voile est plus dense EN BAS À GAUCHE, là où le titre se pose — un voile uniforme
   assombrirait le plan sans rien gagner sur le contraste local. */
/* Le voile du coin doit couvrir l'EMPRISE RÉELLE du logotype, pas son ancrage : une
   marque va jusqu'à 48 % de la largeur en grand écran. Avec l'ancien voile, SCARLET —
   rouge sur sa propre salle du trône rouge — se dissolvait dans le décor : seul le
   fanion violet tenait. Le noyau descend à .93 et la retombée est repoussée. */
.world-teaser--titled::after {
  background:
    radial-gradient(135% 118% at 4% 104%, rgba(3,3,6,.93) 0%, rgba(3,3,6,.66) 32%,
                    rgba(3,3,6,.26) 58%, transparent 78%),
    linear-gradient(to bottom, rgba(6,6,10,.42) 0%, transparent 34%,
                    rgba(6,6,10,.30) 74%, var(--media-fade) 100%);
}
/* Ancré en BAS À GAUCHE, et calé sur la HAUTEUR, pas sur la largeur : un logotype
   dont le rapport va de 2:1 à 10:1 ne peut pas être dimensionné en largeur sans que
   les plus longs écrasent la boucle et que les plus carrés disparaissent. On borne
   donc la hauteur — c'est ce que fait un carton de titre. */
.world-mark {
  position: absolute; z-index: 2; left: var(--sp-5); bottom: var(--sp-4);
  right: var(--sp-5); display: flex; align-items: flex-end; margin: 0;
  pointer-events: none;
}
/* Cette surcharge est indispensable : la règle de cadrage du hero
   (.world-teaser img { position:absolute; inset:0; object-fit:cover }) attrape aussi
   le logotype, puisqu'il est dans le même conteneur — il se retrouvait recadré en
   fond, et on n'en voyait qu'un fragment. */
/* <picture> choisit la variante (-min sous 720 px) : une seule marque téléchargée au lieu
   de deux dont une en display:none (audit 01/09). display:contents laisse l'<img> être
   l'item flex du titre, comme avant. Même chose sous le hero. */
.world-mark picture, .hero__media picture { display: contents; }
.world-teaser .world-mark img {
  position: static; inset: auto; object-fit: initial;
  width: auto; height: auto;
  /* La borne est en HAUTEUR, et elle se règle par univers. Les sept logotypes
     vont de 1:1 (The Bird, empilé comme sa référence) à 6:1 (Black Blossom) :
     une borne unique donne au plus carré le sixième de la surface du plus long,
     et il disparaît. `--mark-h` est posée sur la page, pas ici. */
  max-height: var(--mark-h, clamp(78px, 15vh, 150px)); max-width: min(620px, 56%);
}
/* La classe de lecteur d'écran est GLOBALE. Portée par `.world-mark` seule, elle
   ne s'appliquait pas au même motif recopié dans les plaques de landing : le nom
   s'y affichait en pleine taille de <h2>, par-dessus le logotype et débordant de
   la plaque. Un utilitaire d'accessibilité ne se scope pas à un composant. */
.sr, .world-mark .sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
@media (max-width: 720px) {
  .world-mark { left: var(--sp-3); right: var(--sp-3); }
  .world-mark img { max-width: 100%; }
}
/* LA PLAQUE DE LANDING porte le même logotype que la page, en variante RÉDUITE.
   La complète y serait illisible : une plaque fait 380 px de large et le bloc complet
   embarque emblème, filets et sous-titre, qui tombent tous sous le pixel. La réduite
   ne garde que ce qui survit à cette taille — c'est exactement ce pour quoi elle
   existe. La hauteur est bornée, jamais la largeur : les sept vont de 1:1 à 6:1. */
/* LA HAUTEUR SE CORRIGE PAR L'AIRE D'ENCRE. Borner la seule hauteur donnait à The Bird
   (un bloc empilé presque carré) 66 px de large là où Glamour State (une ligne de 8,5:1)
   en prenait 500 : un rapport de 1 à 8 sur la même grille. Corriger par l'aire de la
   BOÎTE règle ça, mais pas complètement — une boîte compte le blanc comme de l'encre.
   Mesuré sur les sept aplats : Rabbit Butchers remplit sa boîte à 43 %, Scarlet Protocol
   à 16 %. À boîtes de même aire, le dessin de Scarlet est donc presque trois fois moins
   présent que celui de Rabbit — ce qui se voyait, et qu'aucun réglage de hauteur ne
   rattrape.

   `--mk` = √( (ratio_réf × encre_réf) / (ratio × encre) ), la référence restant
   Bee Village. L'aire d'ENCRE rendue est alors la même pour les sept, et c'est elle que
   l'œil pèse. Les couvertures sont mesurées sur le rendu en aplat des variantes `-min`,
   pas estimées : les rejouer après toute retouche d'un logotype.

   Un seul principe pour les trois endroits où les sept marques cohabitent — les plaques,
   les cartes de la home, et le titre du bloc d'histoire. */
.plate-mark { margin: 6px 0 10px; display: flex; }
.plate-mark img {
  width: auto; height: auto;
  max-height: calc(clamp(34px, 5.4vw, 60px) * var(--mk, 1)); max-width: 100%;
}
@media (max-width: 720px) { .plate-mark img { max-height: calc(42px * var(--mk, 1)); } }

/* Sur une carte de la home, la marque remplace le titre : le nom part en `.sr`, et le
   sous-titre qui vivait dans le <h3> devient sa propre ligne. */
.plate-mark--card { margin: 0 0 8px; }
.plate-mark--card img { max-height: calc(clamp(30px, 3.4vw, 48px) * var(--mk, 1)); }
.world-card__sub {
  font-size: clamp(0.82rem, 1.05vw, 0.95rem); font-weight: 400; color: var(--muted);
  letter-spacing: 0.12em; line-height: 1.35; margin: 0;
}

/* LE SYNOPSIS — l'autre texte de l'univers, et il ne remplace pas le premier. Le bloc
   d'immersion fait ENTRER : une scène, quelqu'un à suivre, aucun spoiler. Celui-ci
   RACONTE : présent de narration, point de vue surplombant, l'intrigue entière, fin
   comprise. Deux registres, donc deux traitements — l'immersion garde son image et sa
   colonne étroite, le synopsis se pose en deux colonnes de texte nu, comme un résumé
   de cycle. Les mettre au même format ferait lire le second comme la suite du premier.
   Réf. de manière donnée par Mike : blog.belial.fr, résumé du cycle de Dune. */
.synopsis { margin-top: clamp(40px, 7vh, 84px); }
.synopsis__title { font-size: var(--fs-h2); margin: 6px 0 4px; }
/* <span> (un <p> n'a pas sa place dans <summary>) posé en bloc, comme le <p> d'avant. */
.synopsis__warn {
  display: block;
  font-family: var(--mono); font-size: 12px; letter-spacing: var(--ls-loose);
  text-transform: uppercase; color: var(--muted); margin-bottom: clamp(20px, 3vh, 34px);
}
.synopsis__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: clamp(24px, 3vw, 46px); }
.synopsis__part h3 {
  font-family: var(--mono); font-size: 13px; letter-spacing: var(--ls-loose);
  text-transform: uppercase; color: var(--wa-text, var(--wa, var(--red-text)));
  padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--line-soft);
}
.synopsis__part p { font-size: var(--fs-sm); color: var(--muted); line-height: 1.72; max-width: 58ch; }

/* .page-head--titled : la page-head ne répète plus le nom, monté dans la bande
   titrée (h1.world-mark). Aucune règle propre — la classe reste comme repère. */

/* ---------- Couverture narrative d'univers ----------
   Le seul endroit du site où l'on raconte : un visuel de couverture qui plonge dans
   le fond, le titre de l'univers traité à sa couleur, et trois mouvements de texte
   (ancrage · règle du monde · faille). Voir la skill jeason-storyteller. */
.world-story { margin-top: clamp(36px, 6vh, 72px); }
.world-story__art {
  position: relative; overflow: hidden;
  aspect-ratio: 21 / 9; background: var(--bg2);
}
.world-story__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Le dégradé plongeant : l'image se dissout dans la page, sans coupure franche. */
.world-story__art::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to bottom, transparent 34%, color-mix(in srgb, var(--bg) 72%, transparent) 74%, var(--bg) 100%),
    linear-gradient(to right, color-mix(in srgb, var(--wa, var(--red)) 16%, transparent) 0%, transparent 55%);
}
.world-story__in {
  position: relative; z-index: 1;
  margin-top: clamp(-140px, -11vw, -72px);
  max-width: 64ch; padding-right: var(--sp-3);
}
.world-story__mark {
  font-family: var(--mono); font-size: var(--fs-label); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: var(--wa-text, var(--wa, var(--red-text)));
  display: flex; align-items: center; gap: 10px; margin-bottom: var(--sp-3);
}
.world-story__mark::before {
  content: ''; width: 26px; height: 2px; background: var(--wa, var(--red)); flex: none;
}
.world-story__title {
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(2.1rem, 5.2vw, 3.6rem); line-height: 0.95; color: var(--text);
  margin-bottom: var(--sp-4);
}
/* LE NOM DE L'UNIVERS EST DESSINÉ, PAS ÉCRIT. Il l'était partout ailleurs — dans le
   hero, sur la plaque de landing — et ici seulement il retombait dans la police du
   site : le seul endroit de la page où l'univers parlait avec la voix de la maison.

   C'est la variante RÉDUITE, pas la complète. Le hero porte déjà le bloc entier
   quelques centaines de pixels plus haut ; le répéter ici n'ajouterait rien et ferait
   deux fois la même annonce sur un écran. La réduite donne le mot-symbole seul, ce qui
   est l'autre registre — un titre de section, pas une enseigne — et elle n'embarque pas
   de sous-titre, donc elle ne double pas celui que le bloc porte déjà.

   LA TAILLE SE PREND À AIRE CONSTANTE, JAMAIS À HAUTEUR CONSTANTE — et c'est `--mk`,
   la MÊME propriété que les plaques et les cartes de la home, pas une seconde variable
   qui dirait la même chose. Le principe et son calcul sont documentés en tête de
   `.plate-mark` : ne les changer qu'à un seul endroit. Ici seule l'échelle diffère, le
   bloc d'histoire donnant plus d'air qu'une plaque de 380 px.

   Mesuré au rendu sur les sept : 22 000 px² à 1440 px, 11 000 px² à 390 px. */
.world-story__title img {
  display: block; width: auto; height: auto;
  max-height: calc(clamp(50px, 6.8vw, 71px) * var(--mk, 1));
  max-width: 100%;
}
/* `:not(.sr)` par précaution de spécificité : `.world-story__title span` (0,1,1) bat
   `.sr` (0,1,0), donc un jour où l'on ajouterait ici un intitulé pour lecteur d'écran,
   il hériterait de `display: block` et sortirait à l'écran. Le sous-titre se désigne
   explicitement plutôt que « tout span ». */
.world-story__title > span:not(.sr) {
  display: block; font-size: 0.42em; letter-spacing: var(--ls-label);
  color: var(--wa-text, var(--wa, var(--red-text))); margin-top: 10px; font-weight: 500;
}
.world-story__lede { font-size: var(--fs-lede); color: var(--text); margin-bottom: 1.1em; }
.world-story p { max-width: 62ch; color: var(--body); margin-bottom: 1.1em; }
.world-story p:last-child { margin-bottom: 0; }
@media (max-width: 760px) {
  .world-story__art { aspect-ratio: 4 / 3; }
  .world-story__in { margin-top: -56px; max-width: none; padding-right: 0; }
}

/* ---------- Sous-titres (pistes WebVTT, rendu natif du lecteur) ----------
   Jaune, plus petits que le défaut du navigateur, et posés plus bas : le réglage
   d'usine est blanc, gros, et mange le tiers bas de l'image — soit exactement la
   zone où le cadre place ses personnages. Le fond est un voile, pas une boîte
   opaque : on doit voir l'image derrière la ligne.
   ::cue ne prend qu'un jeu restreint de propriétés — ni marge, ni positionnement.
   Le calage vertical vient de l'attribut « line » des repères, pas d'ici. */
::cue {
  color: #f5c518;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 78%;
  font-weight: 500;
  line-height: 1.3;
  background: transparent;
  text-shadow: 0 1px 4px rgba(0, 0, 0, .95), 0 0 10px rgba(0, 0, 0, .7);
}

/* ---------- /films/ : cadre nu, invite et pause SOUS le cadre ----------
   Demande de Mike (14/08) : rien par-dessus l'image. La note d'alors croyait les
   contrôles natifs présents ; ils ne le sont pas — main.js les retire pour faire des
   lecteurs des boucles muettes (clic = plein écran + son). Sans invite ni pause, treize
   films tournaient sans qu'un visiteur sache qu'un clic donne le son, et sans moyen de
   les arrêter (WCAG 2.2.2). Audit du 01/09. Compromis : le cadre reste nu, l'invite
   passe en ligne sous la légende, la pause vit dans la légende comme partout ailleurs.
   Le bouton CC survit : c'est lui qui donne accès aux sous-titres. */
.page-films .reel .reel__hint {
  position: static; display: inline-block; opacity: 1; margin: 8px 14px 0 2px; padding: 0;
  background: none; border: 0; color: var(--muted); pointer-events: none; vertical-align: middle;
}
/* main.js pose le bouton pause en absolu sur le bas du cadre (placeCC) : ici il revient
   dans le flux, à côté de l'invite, sous la légende. `position: static` neutralise le
   `top` calculé par le script. */
.page-films .reel .loop-pause {
  position: static; display: inline-flex; margin: 8px 0 0; vertical-align: middle;
  background: var(--bg2); border-color: var(--line); color: var(--muted);
}
.page-films .reel .loop-pause:hover, .page-films .reel .loop-pause:focus-visible { color: var(--text); }
.page-films .reel.engaged .reel__hint, .page-films .reel.engaged .loop-pause { display: none; }
/* Une ancre de section (#showgens) doit atterrir sous l'en-tête fixe, comme .reel */
.section[id] { scroll-margin-top: 72px; }

/* ---------- Repère d'univers en coin de lecteur ----------
   Posé en haut à droite du cadre, jamais cliquable : le clic doit atteindre la vidéo,
   qui sert elle-même de bouton lecture. Retiré quand le mouvement est refusé, où il
   n'apporte rien à une image fixe. */
.reel__wm {
  position: absolute; top: 14px; right: 14px; z-index: 3;  /* top réécrit par main.js */
  pointer-events: none; opacity: .82;
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, .85));
}
.reel__wm img {
  display: block;
  height: calc(clamp(15px, 2.4vw, 26px) * var(--mk, 1));
  width: auto;
}
@media (max-width: 520px) { .reel__wm { right: 9px; opacity: .74; } }
@media (prefers-reduced-motion: reduce) { .reel__wm { display: none; } }

/* ---------- Ligne « suivre » du bloc contact ----------
   Les réseaux ne vivaient jusqu'ici que dans le JSON-LD : lisibles par une machine,
   invisibles pour un visiteur, qui ne trouvait que X au fond du pied de page. La ligne
   est posée sous le bouton e-mail, seul endroit de la page où l'on regarde déjà quand
   on cherche à joindre quelqu'un. */
.follow {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px 22px;
  margin-top: var(--sp-4);
  font-family: var(--mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-label); text-transform: uppercase;
}
.follow__label { color: var(--muted); }
.follow a { color: var(--text); text-decoration: none; border-bottom: 1px solid var(--line); padding-bottom: 3px; transition: color .2s, border-color .2s; }
.follow a:hover, .follow a:focus-visible { color: var(--text); border-color: var(--red); }

/* ---------- Logos de plateformes dans la barre ----------
   Placés en bout de barre plutôt qu'au pied de page : visibles sur les 46 pages sans
   défiler, alors que le bloc contact est le dernier écran de la page — un visiteur qui
   ne déroule pas jusqu'au bout ne le voyait jamais.
   Les marques sont tracées en SVG (aucune requête, nettes à toute taille) et prennent
   `currentColor`, ce qui permet la transition de couleur.
   REPOS : gris sourd, pour ne pas concurrencer le sceau ni la navigation.
   SURVOL : la couleur officielle de chaque marque — rouge YouTube, blanc pur pour X,
   qui est sa couleur sur fond sombre. On ne recolore pas un logo, on le passe de sa
   version monochrome (autorisée par les deux chartes) à sa couleur de marque. */
.nav__socs { display: inline-flex; align-items: center; gap: 14px; }
.nav__soc {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  color: var(--muted); border-bottom: 0;
  transition: color .2s, transform .2s;
}
.nav__soc svg { width: 19px; height: 19px; display: block; }
.nav__soc--yt:hover, .nav__soc--yt:focus-visible { color: #ff0000; }
.nav__soc--x:hover,  .nav__soc--x:focus-visible  { color: var(--text); }  /* X : blanc sur noir, noir sur papier — sa couleur de marque suit le fond */
.nav__soc:hover, .nav__soc:focus-visible { transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .nav__soc:hover { transform: none; } }

/* ---------- Bascule de thème ----------
   Un bouton, pas un lien : il ne mène nulle part. Petit bouton-logo (sceau à encoche, à
   moitié plein — la moitié pleine se retourne quand le thème change) collé à la pilule
   FR/EN, même bordure, même hauteur, sur ordinateur comme dans le menu plein écran
   (Mike, 02/09 : « un logo discret à côté du bouton FR/EN »). Jamais de libellé visible.
   `width`/`height` sont en dur sur le <svg> : la page doit rester présentable sans feuille. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 31px; height: 31px; padding: 0; margin: 0;   /* = hauteur de la pilule FR/EN */
  background: none; border: 1px solid var(--line); color: var(--muted); cursor: pointer;
  font: inherit; -webkit-tap-highlight-color: transparent;
  transition: color .2s, border-color .2s;
}
.theme-toggle:hover, .theme-toggle:focus-visible { color: var(--text); border-color: var(--red); }
.theme-toggle[aria-pressed="true"] { color: var(--text); }
.theme-toggle__ico { display: block; width: 15px; height: 15px; flex: none; }
.theme-toggle__fill { transform-origin: 50% 50%; transition: transform .3s cubic-bezier(.65, 0, .35, 1); }
html[data-theme="light"] .theme-toggle__fill { transform: scaleX(-1); }
.theme-toggle__txt { display: none; }
html.no-js .theme-toggle { display: none; }
@media (max-width: 860px) {
  /* Dans le menu : même hauteur que la pilule FR/EN (44 px), logo un peu plus grand. */
  body.nav-open .nav .theme-toggle { width: 44px; height: 44px; margin: 0; }
  body.nav-open .nav .theme-toggle__ico { width: 20px; height: 20px; }
}

/* Menu plein écran : les items y sont de grandes lignes empilées. Les logos y forment
   une rangée à part, plus grands — c'est le seul endroit où ils sont la dernière chose
   qu'on voit avant de fermer le menu. */
@media (max-width: 860px) {
  .nav__socs { width: 100%; justify-content: center; gap: 28px; margin-top: 22px; padding-top: 22px; border-top: 1px solid var(--line-soft); }
  .nav__soc { width: 48px; height: 48px; }
  .nav__soc svg { width: 26px; height: 26px; }
}

/* ---------- Repli sans color-mix() ----------
   Safari < 16.2, Chrome < 111, Firefox < 113 jettent toute déclaration qui contient
   color-mix() : l'en-tête fixe perdait son fond, les plaques d'univers leur teinte et
   les index géants sortaient en couleur pleine (audit 01/09). Couleurs pleines en
   repli, sur les jetons du thème — les deux thèmes sont couverts. */
@supports not (color: color-mix(in srgb, red, blue)) {
  .site-head, .reel-link__go { background: var(--bg); }
  .btn { background: var(--bg2); }
  .world-plate { background: var(--bg2); }
  .dossier-cta { border-color: var(--line); background: var(--bg2); }
  .dossier-cta:hover, .dossier-cta:focus-visible { background: var(--panel); }
  .dossier-cta__go { border-bottom-color: var(--line); }
  .world-story__art::after { background: linear-gradient(to bottom, transparent 34%, var(--bg) 100%); }
}

/* Téléphone en paysage : les items du menu à 54 px ne tiennent pas dans 390 px de haut. */
@media (max-width: 860px) and (max-height: 500px) {
  body.nav-open .nav a { font-size: 34px; padding: 8px 0; }
}


/* ==================== Tetsuo × Ghost in the Shell — vague 2 (02/09/2026) ====================
   Tout ce qui bouge ici tient en transform / opacity / clip-path, sans filtre ni flou.
   Les déclencheurs sont des classes posées par main.js : .is-glitching (une salve, puis
   propre), .is-in (réticules). Sous prefers-reduced-motion, chaque effet est son état final. */

/* Glitch de titre : deux copies en pseudo-éléments — content: attr(data-text), l'attribut
   étant posé par main.js depuis le texte (<br> → saut de ligne, white-space: pre-line).
   Rien dans le DOM pour Google ; pour les lecteurs d'écran, le texte alternatif VIDE
   (`/ ""`) retire le texte généré de l'arbre d'accessibilité — sans lui, chaque titre
   était annoncé trois fois (vérification adversariale, 02/09 ; Chromium 77+, Safari 17.4+,
   Firefox 128+, les autres gardent l'ancien comportement). Tranchées au clip-path,
   décalées en transform, steps(1) : quatre salves en 650 ms. Rouge + encre. */
.glitch { position: relative; }
.glitch::before, .glitch::after {
  content: attr(data-text) / ""; position: absolute; inset: 0; white-space: pre-line;
  color: inherit; clip-path: inset(0 0 100% 0); opacity: 0; pointer-events: none; user-select: none;
}
.glitch::before { color: var(--red); }
.glitch::after { color: var(--text); }
.hero .glitch::after { color: var(--on-media); }
.site-foot__cta .glitch::before { color: #fff; }
.site-foot__cta .glitch::after { color: #0a0a0b; }
@media (prefers-reduced-motion: no-preference) {
  .glitch.is-glitching { animation: gl-base .65s steps(1, end) 1 both; }
  .glitch.is-glitching::before { animation: gl-a .65s steps(1, end) 1 both; }
  .glitch.is-glitching::after { animation: gl-b .65s steps(1, end) 1 both; }
}
@keyframes gl-a {
  0%   { clip-path: inset(12% 0 62% 0); transform: translateX(-14px); opacity: 1; }
  6%   { clip-path: inset(44% 0 34% 0); transform: translateX(10px); opacity: 1; }
  12%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  26%  { clip-path: inset(70% 0 8% 0);  transform: translateX(18px); opacity: 1; }
  33%  { clip-path: inset(22% 0 58% 0); transform: translateX(-8px); opacity: 1; }
  40%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  55%  { clip-path: inset(4% 0 80% 0);  transform: translateX(-20px); opacity: 1; }
  61%  { clip-path: inset(52% 0 28% 0); transform: translateX(12px); opacity: 1; }
  68%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  82%  { clip-path: inset(30% 0 45% 0); transform: translateX(8px); opacity: 1; }
  90%, 100% { clip-path: inset(0 0 100% 0); transform: none; opacity: 0; }
}
@keyframes gl-b {
  0%   { clip-path: inset(58% 0 20% 0); transform: translateX(12px); opacity: 1; }
  6%   { clip-path: inset(6% 0 76% 0);  transform: translateX(-16px); opacity: 1; }
  12%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  26%  { clip-path: inset(36% 0 48% 0); transform: translateX(-20px); opacity: 1; }
  33%  { clip-path: inset(80% 0 2% 0);  transform: translateX(9px); opacity: 1; }
  40%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  55%  { clip-path: inset(66% 0 14% 0); transform: translateX(16px); opacity: 1; }
  61%  { clip-path: inset(18% 0 66% 0); transform: translateX(-10px); opacity: 1; }
  68%  { clip-path: inset(0 0 100% 0);  transform: none; opacity: 0; }
  82%  { clip-path: inset(50% 0 30% 0); transform: translateX(-8px); opacity: 1; }
  90%, 100% { clip-path: inset(0 0 100% 0); transform: none; opacity: 0; }
}
/* la plaque entière saute de 2–3 px à la première image de chaque salve */
@keyframes gl-base {
  0%   { transform: translateX(-3px); }
  6%   { transform: translateX(2px); }
  12%  { transform: none; }
  26%  { transform: translateX(3px); }
  33%  { transform: none; }
  55%  { transform: translateX(-2px); }
  61%  { transform: translateX(2px); }
  68%  { transform: none; }
  82%  { transform: translateX(-3px); }
  90%, 100% { transform: none; }
}

/* Bandes d'image (affiche GITS 1995) : huit bandes clonées du visuel de la carte,
   tranchées au clip-path et décalées ; trois portent une séparation de canaux — deux
   copies multipliées par #f00 et #0ff, écran l'une sur l'autre, écartées de quelques px :
   où elles se recouvrent l'image est exacte, les bords frangent rouge / cyan. Au repos
   visibility: hidden, coût nul. main.js construit les bandes (clones lazy, même URL que
   la vignette : un seul téléchargement) et lance la salve à la révélation et au survol. */
.plate { position: relative; }
.plate__strips { position: absolute; inset: 0; pointer-events: none; z-index: 1; overflow: hidden; }
.plate__s { position: absolute; inset: 0; isolation: isolate; visibility: hidden; opacity: 0; clip-path: var(--clip); }
.plate__s img, .plate__ch img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; transition: none; }
.plate__ch { position: absolute; inset: 0; isolation: isolate; }
.plate__ch::after { content: ''; position: absolute; inset: 0; mix-blend-mode: multiply; }
.plate__ch--r::after { background: #f00; }
.plate__ch--r { transform: translateX(calc(var(--split, 7px) * -1)); }
.plate__ch--gb::after { background: #0ff; }
.plate__ch--gb { mix-blend-mode: screen; transform: translateX(var(--split, 7px)); }
.plate__s:nth-child(1) { --clip: inset(4% 0 88% 0);  --dx: -16px; --dx2: 9px;   --d: 0s; }
.plate__s:nth-child(2) { --clip: inset(14% 0 80% 0); --dx: 22px;  --dx2: -12px; --d: .03s; --split: 6px; }
.plate__s:nth-child(3) { --clip: inset(27% 0 67% 0); --dx: -9px;  --dx2: 18px;  --d: .01s; }
.plate__s:nth-child(4) { --clip: inset(36% 0 59% 0); --dx: 30px;  --dx2: -14px; --d: .05s; }
.plate__s:nth-child(5) { --clip: inset(48% 0 47% 0); --dx: -24px; --dx2: 8px;   --d: 0s;   --split: 9px; }
.plate__s:nth-child(6) { --clip: inset(58% 0 37% 0); --dx: 12px;  --dx2: -26px; --d: .04s; }
.plate__s:nth-child(7) { --clip: inset(71% 0 25% 0); --dx: -30px; --dx2: 14px;  --d: .02s; --split: 5px; }
.plate__s:nth-child(8) { --clip: inset(83% 0 10% 0); --dx: 18px;  --dx2: -10px; --d: .06s; }
@media (prefers-reduced-motion: no-preference) {
  .plate.is-glitching .plate__s { visibility: visible; animation: gl-strip .6s steps(1, end) 1 both; animation-delay: var(--d); }
}
@keyframes gl-strip {
  0%   { transform: none; opacity: 0; }
  5%   { transform: translateX(var(--dx)); opacity: 1; }
  14%  { transform: translateX(calc(var(--dx) * -.6)); opacity: 1; }
  20%  { transform: none; opacity: 0; }
  34%  { transform: translateX(var(--dx2)); opacity: 1; }
  44%  { transform: none; opacity: 0; }
  62%  { transform: translateX(calc(var(--dx) * 1.3)); opacity: 1; }
  70%  { transform: translateX(var(--dx2)); opacity: 1; }
  76%, 100% { transform: none; opacity: 0; }
}

/* Réticules (le HUD de Ghost in the Shell, en rouge du studio) : quatre croix creuses aux
   coins du hero et du spotlight, dessinées en dégradés — aucun SVG. Elles entrent à la
   révélation (échelle 1,5 → 1) ; sans JS ou sous animations réduites, elles sont là. */
.hud__mark { --c: var(--on-media-red); position: absolute; z-index: 3; width: 20px; height: 20px; pointer-events: none; opacity: .85;
  background: linear-gradient(var(--c), var(--c)) left center / 7px 1px no-repeat,
              linear-gradient(var(--c), var(--c)) right center / 7px 1px no-repeat,
              linear-gradient(var(--c), var(--c)) center top / 1px 7px no-repeat,
              linear-gradient(var(--c), var(--c)) center bottom / 1px 7px no-repeat;
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out); }
html.js-anim .hero:not(.is-in) .hud__mark, html.js-anim .spotlight:not(.is-in) .hud__mark { opacity: 0; transform: scale(1.5); }
.hud__mark--tl { top: 14px; left: 14px; }
.hud__mark--tr { top: 14px; right: 14px; }
.hud__mark--bl { bottom: 14px; left: 14px; }
.hud__mark--br { bottom: 14px; right: 14px; }
.spotlight .hud__mark--tl { top: 48px; }
/* Hero : calés sur la colonne de contenu, comme le bouton de pause — qui occupe le coin
   bas droit sur grand écran et le coin haut droit sur téléphone : ce coin-là se tait. */
.hero .hud__mark { --x: max(20px, calc((100% - var(--maxw)) / 2 + var(--pad))); }
.hero .hud__mark--tl, .hero .hud__mark--bl { left: var(--x); }
.hero .hud__mark--tr, .hero .hud__mark--br { right: var(--x); }
.hero .hud__mark--tl, .hero .hud__mark--tr { top: 84px; }
.hero .hud__mark--bl, .hero .hud__mark--br { bottom: 30px; }
.hero .hud__mark--br { display: none; }
@media (max-width: 860px) {
  .hero .hud__mark--tl, .hero .hud__mark--tr { top: 78px; }
  .hero .hud__mark--bl, .hero .hud__mark--br { bottom: 10px; }
  .hero .hud__mark--tr { display: none; }
  .hero .hud__mark--br { display: block; }
  /* Spotlight : en bas, la ligne « Open the file » touche les coins sur téléphone. */
  .spotlight .hud__mark--bl, .spotlight .hud__mark--br { display: none; }
}

/* Trame de balayage : lignes d'un pixel tous les 3 px, opacité seule, posées sur le hero
   (élément .scan, hors du média qui, lui, respire en scale) et sur le spotlight. */
.scan, .spotlight__media::before { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: .15;
  background: repeating-linear-gradient(to bottom, rgba(var(--media-veil), .9) 0 1px, transparent 1px 3px); }

/* Cartes : un filet de 2 px à la couleur de l'univers court sur le bord haut au survol
   (Tetsuo, scaleX seul) — en plus du zoom et du cadre déjà en place. */
.case-card, .film-card { position: relative; }
.world-card::after, .case-card::after, .film-card::after {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: var(--stroke); z-index: 2; pointer-events: none;
  background: var(--wa, var(--red)); transform: scaleX(0); transform-origin: left; transition: transform .35s var(--ease-out);
}
.world-card:hover::after, .world-card.is-near::after, .world-card:focus-visible::after,
.case-card:hover::after, .case-card:focus-visible::after,
.film-card:hover::after, .film-card.is-near::after, .film-card:focus-visible::after { transform: scaleX(1); }

/* Pied de page : la marque en géant (l'Anton 80 px du pied Tetsuo, ici Big Shoulders 900
   à l'échelle de l'écran) entre la plaque rouge et les colonnes ; chaque colonne a son
   titre en Rajdhani rouge souligné d'un tiret 30 × 3 (séparateur de widget Tetsuo). */
/* Marque du pied en Big Shoulders 900 CREUX (3 px, 10 % de fond) — le katakana au-dessus a été
   retiré à la demande de Mike (02/09 : « pas professionnel ») : aucun lettrage japonais. */
.site-foot__mark { font-family: var(--display); font-weight: 900; font-size: clamp(2.6rem, 13.4vw, 13rem); line-height: .85; letter-spacing: -.01em; text-transform: uppercase; color: var(--text); margin: 0; padding-top: clamp(40px, 6vh, 72px); white-space: nowrap; display: grid; justify-items: start; gap: .06em; }
.lockup__la { font-size: 1em; line-height: .85; position: relative; --hollow-w: 3px; }
@media (max-width: 480px) { .lockup__la { --hollow-w: 2px; } }
.site-foot__col h3 { font-family: var(--hud); font-weight: 600; font-size: var(--fs-hud); letter-spacing: 0.18em; text-transform: uppercase; color: var(--red-text); line-height: 1.2; margin: 0 0 -6px; padding-bottom: 8px; position: relative; }
.site-foot__col h3::after { content: ''; position: absolute; left: 0; bottom: 0; width: 30px; height: 3px; background: var(--red); }

/* Compteurs (hero, en-têtes de pages) : libellé fin au-dessus, nombre en Big Shoulders 900
   dessous — les colonnes de crédits d'une affiche, le compteur Tetsuo. */
.hero__meta > span, .page-head__meta > span { display: flex; flex-direction: column; gap: 6px; } /* enfant direct : un .sep imbriqué garde sa règle */
.hero__meta b, .page-head__meta b { font-family: var(--display); font-weight: 900; font-size: clamp(1.9rem, 3.2vw, 3rem); line-height: .9; letter-spacing: -.01em; }
/* Chiffres gros, mots petits (02/09). La valeur de méta sortait à 46-48 px en Big
   Shoulders 900 contre 17 px pour l'intro, sur des pages dont le h1 est un logo : la
   ligne la plus forte de la page était « IMPERIAL COURT SCI-FI ». Les nombres gardent
   leur corps, les mots reprennent celui du libellé qui les surmonte. Le em suit donc
   la valeur : rien à re-régler si le clamp bouge. */
.hero__meta b .wd, .page-head__meta b .wd {
  font-family: var(--hud); font-weight: 600; font-size: 0.30em;
  letter-spacing: var(--ls-label); vertical-align: 0.16em;
}

/* Filets .sep à la place des points médians « · » (légendes, méta, sous-titres) : un trait
   de 14 px, muet pour les lecteurs d'écran (aria-hidden), sur média en --line-on-media.
   Le balisage garde UNE ESPACE de chaque côté du span : sans elle, les noms accessibles,
   le presse-papiers et le rendu sans feuille collaient les mots (« cinema1 short film ») —
   d'où des marges de 5 px, l'espace faisant le reste (vérification adversariale, 02/09). */
.sep { display: inline-block; width: 14px; height: 1px; background: var(--line); margin: 0 5px; vertical-align: middle; }
.spotlight__meta .sep, .reel__wm .sep, .hero__meta .sep, .lightbox__caption .sep { background: var(--line-on-media); }
.spotlight__meta { font-family: var(--hud); font-weight: 600; font-size: 13px; letter-spacing: 0.16em; }

@media (prefers-reduced-motion: reduce) {
  .hud__mark, .cut__a, .cut__b { transition: none; }
  .glitch::before, .glitch::after, .plate__s { animation: none !important; opacity: 0 !important; visibility: hidden !important; }
}


/* ==================== Vague 3 — le site officiel GITS 2026, porté (02/09/2026) ====================
   theghostintheshell-anime.jp : lettres creuses, kanji sur latin, dissolution en pixels des
   images, bande de couleur vivante, étiquette PV, écran d'amorce. Mécanismes repris, palette
   NON reprise (leur spectre cyan/bleu/violet cède au rouge / braise / papier du studio). */
:root { --hollow-w: 2px; --vband-lo: #3a0d0b; --vband-hi: #f4efe8; }
html[data-theme="light"] { --vband-lo: #e9d9d4; --vband-hi: #121214; }
@media (max-width: 480px) { :root { --hollow-w: 1.5px; } }

/* C1 — lettres creuses : trait seul dans la couleur du texte, fond à 10 % pour la profondeur ;
   les copies de glitch gardent le trait (rouge, puis encre). */
.hollow { color: transparent; -webkit-text-stroke: var(--hollow-w) var(--hollow-c, var(--text)); paint-order: stroke fill; }
.hollow--fill { color: color-mix(in srgb, var(--hollow-c, var(--text)) 10%, transparent); }
.hollow.glitch::before { color: transparent; -webkit-text-stroke-color: var(--red); }
.hollow.glitch::after { color: transparent; -webkit-text-stroke-color: var(--hollow-c, var(--text)); }

/* C4 — dissolution en pixels (leur shader WebGL, ici des cellules DOM en opacity seule) :
   main.js bâtit la grille SUR le média (couleur --media-bg, noire dans les deux thèmes), chaque
   cellule part avec son délai --d = base + durée·√aléa. Jamais dans le HTML : sans JS, pas de
   voile ; sans feuille, la grille n'est pas bâtie (garde STYLED). */
.pix { position: absolute; inset: 0; z-index: 2; display: grid; grid-template-columns: repeat(var(--pc, 32), 1fr); grid-auto-rows: 1fr; pointer-events: none; contain: strict; }
.pix i { display: block; background: var(--media-bg); opacity: 1; }
.pix--bg i { background: var(--bg); } /* hôte sans média (en-tête de page) : cellules couleur de fond, le texte se matérialise */
/* La dissolution passe par background-color, PAS par opacity, et c'est une mesure, pas un
   goût : une cellule à `opacity` sort du flux de peinture normal et Chromium la traite en
   couche à part — 640 cellules, 640 couches à recomposer à chaque image du doigt. Le fond
   transparent donne EXACTEMENT le même pixel (0 écart sur 2 962 440, 28 paires, les deux
   thèmes, accueil et /work/, y compris en damier à mi-dissolution) et rend 52 % du temps
   perdu au défilement. `steps(1, end)` garde la bascule franche : rien ne se fond. */
html.js-anim .pix.is-in i { background-color: transparent; transition: background-color .05s steps(1, end) var(--d, 0s); }
html:not(.js-anim) .pix { display: none; }

/* C5 — bande vivante (leur « gradation dance », en transform seul) : un enfant trois fois trop
   large dérive en translateX, 9 s aller-retour. 2 px sous la barre, 4 px × 260 sous les h2 de
   l'accueil (posés par main.js, entrée en clip-path), 14 px en bord droit du menu plein écran. */
.vband { position: relative; display: block; overflow: hidden; height: var(--vband-h, 2px); pointer-events: none; }
.vband::before { content: ''; position: absolute; top: 0; bottom: 0; left: -100%; width: 300%;
  background: linear-gradient(90deg, var(--vband-lo) 0%, var(--red) 12%, var(--vband-hi) 22%, var(--red) 33%, var(--vband-lo) 45%, var(--red) 55%, var(--vband-hi) 66%, var(--red) 78%, var(--vband-lo) 90%, var(--red) 100%); }
html.js-anim .vband::before { animation: vband-drift 9s ease-in-out infinite alternate; }
@keyframes vband-drift { from { transform: translateX(0); } to { transform: translateX(33.333%); } }
.vband--head { position: absolute; left: 0; right: 0; bottom: -1px; }
.vband--h4 { --vband-h: 4px; width: 260px; max-width: 100%; margin-top: 14px; }
html.js-anim .vband--h4 { clip-path: inset(0 100% 0 0); }
html.js-anim .vband--h4.is-in { clip-path: inset(0); transition: clip-path .4s cubic-bezier(.4, 0, 1, 1) .35s; }
body.nav-open .nav::before { content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 14px; pointer-events: none;
  background: linear-gradient(180deg, var(--vband-lo) 0%, var(--red) 35%, var(--vband-hi) 55%, var(--red) 75%, var(--vband-lo) 100%); }

@media (prefers-reduced-motion: reduce) {
  .vband::before, .boot__readout i { animation: none !important; }
  .vband--h4 { clip-path: none !important; transition: none !important; }
  .pix { display: none !important; }
}
