/* ==========================================================================
   LUNOR — Feuille de style principale
   Police : système monospace/sans pour l'esthétique cinéma technique.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&family=Playfair+Display:ital@1&display=swap');

:root {
  --bg: #050506;
  --bg-panel: #0a0a0c;
  --fg: #f5f5f3;
  --fg-dim: #9a9a9f;
  --fg-faint: #55555c;
  --line: rgba(255,255,255,0.1);
  --pink: #ff2d78;
  --pink-dim: rgba(255,45,120,0.15);

  /* Couleurs par catégorie (remplacées dynamiquement en JS via data-attribute) */
  --cat-color: var(--pink);
  --cat-color-dim: var(--pink-dim);

  --font-body: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-serif: 'Playfair Display', Georgia, serif;

  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  overflow-x: hidden;
  min-height: 100vh;
}

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

button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
a { color: inherit; text-decoration: none; }
input, textarea { font-family: inherit; }

:focus-visible { outline: 2px solid var(--cat-color, var(--pink)); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

.badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  color: var(--pink);
  border: 1px solid rgba(255,45,120,0.4);
  background: rgba(255,45,120,0.08);
  padding: 5px 12px;
  border-radius: 999px;
  margin-bottom: 20px;
}

.accent { color: var(--pink); }

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 15px 24px;
  white-space: nowrap;
  transition: all 0.25s var(--ease);
}
.btn--white { background: #fff; color: #0a0a0a; }
.btn--white:hover { background: #e8e8e8; }
.btn--outline { border: 1px solid rgba(255,255,255,0.3); color: #fff; }
.btn--outline:hover { border-color: #fff; background: rgba(255,255,255,0.05); }
.btn--accent { background: var(--cat-color, var(--pink)); color: #0a0a0a; }
.btn--accent:hover { filter: brightness(1.1); }
.btn--pill { border-radius: 999px; }
.btn--full { width: 100%; justify-content: center; padding: 18px; font-size: 0.8rem; }

.icon-btn {
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,0.15);
  color: #fff;
  transition: background 0.2s var(--ease);
}
.icon-btn:hover { background: rgba(255,255,255,0.08); }

/* ==========================================================================
   HERO / ACCUEIL
   ========================================================================== */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* padding-bas = hauteur de la bande de l'obturateur (74px) + respiration.
     Tout le texte du hero s'arrete au-dessus de la ligne. */
  padding: 40px 48px 118px;
  overflow: hidden;
}

.hero__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* --------------------------------------------------------------------------
   TRIPTYQUE — fonds verticaux sur écran paysage
   En "cover", un clip 9:16 affiché plein cadre sur un écran 16:9 perd ~70 %
   de sa hauteur : il n'en reste qu'une bande centrale. Trois clips côte à côte
   font 3 x 9:16 = 1.69, contre 1.78 pour l'écran : chaque colonne ne perd que
   ~5 %. Le format vertical devient un parti pris — un mur de moniteurs de
   salle de montage — au lieu d'un accident.
   Posé en JS (.is-triptych) seulement quand la catégorie a au moins 3 clips
   ET que l'écran est paysage. Sur mobile, l'écran est déjà vertical : une
   seule vidéo tombe juste, on ne triptyque rien.
   -------------------------------------------------------------------------- */
.hero__triptych { position: absolute; inset: 0; z-index: 0; display: none; }
.hero.is-triptych .hero__triptych { display: flex; }
.hero.is-triptych .hero__video { display: none; }
.hero__cell { position: relative; flex: 1; overflow: hidden; }
.hero__cell + .hero__cell { border-left: 1px solid rgba(255,255,255,0.14); }
.hero__cell video {
  width: 100%; height: 100%; object-fit: cover;
  transition: filter 1.6s var(--ease), transform 1.6s var(--ease);
}
/* La colonne dont le son est ouvert reste pleine ; les deux autres reculent,
   pour qu'on voie ce qu'on écoute.

   ATTENTION au découpage des règles : le voile est déclaré dès .is-triptych,
   à opacité 0, et non sous .has-sound. Sinon le pseudo-élément n'existerait
   pas avant le clic, serait créé directement à 75 %, et n'aurait aucun état
   précédent à animer — le fondu serait purement décoratif, l'assombrissement
   tomberait d'un coup.

   Deux paliers : 75 % au clic, 90 % quand la souris s'arrête (le hero passe
   en is-idle au bout de 3 s, et seulement si un son est ouvert). La durée est
   lue sur l'état d'arrivée : 1,6 s pour le premier palier, plus posé, et 1 s
   pour le passage en veille.
   La règle .is-live vient en dernier : à spécificité égale elle l'emporte, et
   la colonne écoutée ne s'assombrit jamais, même en mode immersif. */
.hero.is-triptych .hero__cell::after {
  content: ''; position: absolute; inset: 0;
  background: #050506;
  opacity: 0;
  transition: opacity 1.6s var(--ease);
  pointer-events: none;
}
.hero.is-triptych.has-sound .hero__cell::after { opacity: 0.75; }
.hero.is-triptych.has-sound.is-idle .hero__cell::after { opacity: 0.9; transition-duration: 1s; }
.hero.is-triptych.has-sound .hero__cell.is-live::after { opacity: 0; }

/* Le flou décroche les colonnes du point de netteté ; monté sur une échelle
   de 16px : 25 % = 4px, 50 % = 8px.
   Le léger scale n'est pas décoratif — un filtre de flou rend les bords de
   l'image translucides, on verrait le noir du dessous suinter sur les bords
   de chaque colonne. Le grossissement pousse ces bords hors cadre. */
.hero.is-triptych.has-sound .hero__cell video {
  filter: blur(4px);
  transform: scale(1.03);
}
.hero.is-triptych.has-sound.is-idle .hero__cell video {
  filter: blur(8px);
  transform: scale(1.06);
  transition-duration: 1s;
}
.hero.is-triptych.has-sound .hero__cell.is-live video {
  filter: none;
  transform: none;
}

/* --------------------- SÉLECTEUR DE SON (un bouton par clip) --------------
   Il remplace l'ancien bouton blanc "Visionner avec son" : il doit garder le
   même poids visuel, sinon personne ne le voit sur une vidéo. D'où le fond
   blanc plein, et un halo qui bat tant qu'on n'y a pas touché.
   -------------------------------------------------------------------------- */
.hero__sound {
  position: relative;
  display: inline-flex; align-items: center; gap: 14px;
  padding: 0 14px 0 18px; height: 46px;
  background: #fff; color: #0a0a0a;
}
.hero__sound-label {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.06em; color: #0a0a0a; white-space: nowrap;
}
.hero__sound-btns { display: inline-flex; gap: 6px; }
.hero__sound-btn {
  min-width: 34px; height: 32px; padding: 0 8px;
  border: 1px solid rgba(0,0,0,0.28);
  font-family: var(--font-mono); font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.04em; color: #0a0a0a;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.hero__sound-btn:hover { background: rgba(0,0,0,0.08); border-color: #0a0a0a; }
.hero__sound-btn.is-active {
  background: var(--cat-color, var(--pink)); border-color: var(--cat-color, var(--pink)); color: #fff;
}
/* Halo d'appel : il bat jusqu'au premier clic, puis ne revient plus. */
.hero__sound.is-hint::after {
  content: ''; position: absolute; inset: 0;
  border: 1px solid #fff;
  animation: sound-hint 2.6s var(--ease) infinite;
  pointer-events: none;
}
@keyframes sound-hint {
  0%   { transform: scale(1); opacity: 0.8; }
  70%  { transform: scale(1.12); opacity: 0; }
  100% { transform: scale(1.12); opacity: 0; }
}
.hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(5,5,6,0.35) 0%, rgba(5,5,6,0.15) 40%, rgba(5,5,6,0.55) 75%, rgba(5,5,6,0.9) 100%);
  z-index: 1;
  transition: background 0.4s var(--ease);
}
.hero.is-dimmed .hero__scrim { background: rgba(5,5,6,0.75); }

/* --------------------------------------------------------------------------
   AMBILIGHT — variante MARIAGE (data-cat pose par setHeroBackground en JS).
   La video de mariage est VERTICALE (9:16). En cover plein ecran, on perdait
   les deux tiers du plan ; dans un cadre reduit, on obtenait une bande etroite
   cernee de noir. Traitement retenu, celui du vertical en salle :
     - la video monte sur TOUTE LA HAUTEUR, largeur automatique -> plan entier,
       jamais recadre ;
     - .hero__ambilight (le meme fichier, en cover, agrandi et floute) remplit
       tout l'ecran derriere et deborde donc sur les cotes : ce sont ces zones
       laterales qui portent la lueur, et l'ecran reste plein.
   -------------------------------------------------------------------------- */
.hero__ambilight { display: none; }

.hero.is-portrait { background: #050506; }

/* La lueur : plein cadre, floutee, saturee. Elle n'est visible que la ou la
   video ne passe pas, c'est-a-dire les cotes. */
.hero.is-portrait .hero__ambilight {
  display: block;
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scale(1.25);
  filter: blur(80px) saturate(1.9) brightness(1.05);
  opacity: 0.9;
  z-index: 0;
  pointer-events: none;
}
/* Le plan complet, sur toute la hauteur.
   CENTRAGE PAR MARGES, PAS PAR TRANSFORM (v=113). L'ancien centrage
   (top/left 50% + translate(-50%,-50%)) passait par transform — or transform
   est dans la liste de transition des videos du hero (1,6 s, pour le zoom du
   mode son). Au basculement vers un fond vertical, le translate S'ANIMAIT :
   la video glissait depuis la gauche avant de se poser au centre. Safari
   aggravait le cas en peignant d'abord une image non transformee.
   inset:0 + margin:auto centre un element remplace (video, img) dans tous les
   navigateurs, sans transform : plus rien a animer, la video apparait en
   place. Le zoom du triptyque (scale sur .hero__cell video) n'est pas
   concerne et garde sa transition. */
.hero.is-portrait .hero__video {
  position: absolute; inset: 0; margin: auto;
  width: auto; height: 100%; max-width: 100%;
  object-fit: contain;
  z-index: 1;
  box-shadow: 0 0 90px rgba(0,0,0,0.55);
}
/* Le scrim est calibre pour une video plein cadre : on l'allege pour ne pas
   eteindre la lueur sur les cotes. */
.hero.is-portrait .hero__scrim {
  background: linear-gradient(180deg, rgba(5,5,6,0.45) 0%, transparent 28%, transparent 64%, rgba(5,5,6,0.9) 100%);
}
/* --------------------------------------------------------------------------
   LE GLOOM — la lueur qui borde un plan vertical pose seul (v=93).
   Il n'appartient plus au Mariage : il appartient au FORMAT. N'importe quelle
   page dont le fond est une seule video verticale y a droit, et le Mariage
   peut desormais y renoncer — il suffit de lui donner trois clips depuis
   l'intranet.
   Quand le son est ouvert ET que la souris s'arrete (le hero passe en .is-idle
   au bout de 3 s), le Gloom s'eteint en 0,5 s : la piece se fait noire autour
   du plan, il ne reste que le film. Il se rallume des le premier mouvement.
   Deux etats seulement, tout est dans la transition — pas d'animation, donc
   rien qui tourne en boucle sur le GPU pendant qu'on regarde.
   Le triptyque n'est pas concerne : il a deja son propre voile par colonne.
   -------------------------------------------------------------------------- */
.hero.is-portrait .hero__ambilight {
  transition: opacity 0.5s var(--ease), filter 0.5s var(--ease);
}
.hero.is-portrait.has-sound.is-idle .hero__ambilight {
  opacity: 0.22;
  filter: blur(80px) saturate(1.2) brightness(0.4);
}

/* Le triptyque, ce sont 3 clips cote a cote : l'ambilight n'a aucun sens. */
.hero.is-triptych.is-portrait .hero__ambilight { display: none; }
@media (prefers-reduced-motion: reduce) {
  .hero.is-portrait .hero__ambilight { filter: blur(80px) saturate(1.4); opacity: 0.75; }
}

/* Mode immersif : après 3s d'inactivité souris (son activé), tout disparaît sauf la vidéo */
.hero__scrim,
.hover-hint,
.nav-zone,
.hero__content,
.hero__meta {
  transition: opacity 0.6s var(--ease);
}
.hero.is-idle .hero__scrim,
.hero.is-idle .hover-hint,
.hero.is-idle .nav-zone,
.hero.is-idle .hero__content,
.hero.is-idle .hero__meta {
  opacity: 0;
  pointer-events: none;
}

.hover-hint {
  position: absolute; top: 24px; left: 50%; transform: translateX(-50%); z-index: 3;
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.08em;
  color: var(--fg-dim);
  display: flex; align-items: center; gap: 8px;
  /* Absent par defaut : c'est l'animation qui le fait venir, et elle ne se
     declenche qu'une fois la barre repartie (.nav-intro retiree en JS). */
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
/* Deux animations qui se relaient sur la meme propriete : hint-in amene
   l'indice de 0 a 0,45 apres le depart de la barre et s'y tient (forwards),
   puis hint-breathe prend le relais a 1 s — et comme son image a 0 % vaut
   justement 0,45, le passage de l'une a l'autre ne se voit pas. Sans ce
   relais, l'indice serait apparu d'un coup a mi-respiration. */
.hero:not(.nav-intro) .hover-hint {
  animation: hint-in 0.6s var(--ease) 0.45s forwards,
             hint-breathe 3.2s ease-in-out 1.05s infinite;
}
.hover-hint .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--cat-color, var(--pink)); transition: background-color 620ms var(--ease); }
.hero.nav-open .hover-hint { opacity: 0 !important; animation-play-state: paused; }

@keyframes hint-in {
  from { opacity: 0; }
  to   { opacity: 0.45; }
}
@keyframes hint-breathe {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero:not(.nav-intro) .hover-hint { animation: hint-in 0.4s var(--ease) 0.45s forwards; opacity: 0.7; }
}

/* Zone déclencheur de survol (bande invisible en haut de l'écran) */
.nav-zone {
  position: fixed; top: 0; left: 0; right: 0; height: 110px; z-index: 20;
}

.nav {
  position: absolute; top: 20px; left: 24px; right: 24px;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 14px 26px;
  background: rgba(14,14,18,0.42);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid rgba(255,255,255,0.09);
  border-radius: 999px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.25);
  transform: translateY(-140%);
  opacity: 0;
  transition: transform 0.4s var(--ease), opacity 0.4s var(--ease),
              border-color 0.35s var(--ease), box-shadow 0.35s var(--ease);
  pointer-events: none;
}
.nav-zone:hover .nav,
.nav-zone.force-open .nav {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}
/* LE GLOOM SUIT L'AMBIANCE ÉPINGLÉE  (v=98)
   Le rose etait ecrit en dur : la barre restait rose meme quand tout le reste
   de la page — le soulignement, le seuil « ENTRER · AFTERMOVIES », le point du
   panneau — etait passe au vert. C'etait la seule piece qui ne suivait pas, et
   une couleur qui ne suit pas se lit comme une erreur.
   --cat-color est deja pose sur :root par syncSeuil(), d'apres la categorie
   EPINGLEE (et vaut var(--pink) sur Accueil, d'ou le rose conserve la-bas
   sans avoir a le dire). Le gloom n'a donc qu'a le lire : il change au clic,
   pas au survol — comme le fond, comme le seuil.
   color-mix plutot que des rgba() : la couleur arrive en hexa depuis
   content.json, on ne peut pas en composer un rgba() a la main. */
.nav:hover {
  border-color: color-mix(in srgb, var(--cat-color, var(--pink)) 55%, transparent);
  box-shadow:
    0 0 32px color-mix(in srgb, var(--cat-color, var(--pink)) 30%, transparent),
    0 0 60px color-mix(in srgb, var(--cat-color, var(--pink)) 12%, transparent),
    0 10px 40px rgba(0,0,0,0.3);
}

.nav__logo { display: flex; align-items: center; gap: 10px; justify-self: start; }
.nav__logo-badge {
  width: 34px; height: 34px; border-radius: 50%;
  /* suit la couleur de la page epinglee (rose sur l'accueil par defaut) */
  background: var(--cat-color, var(--pink)); color: #0a0a0a;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 0.8rem;
  transition: background-color 620ms var(--ease);
}
.nav__logo-text { font-weight: 700; font-size: 0.85rem; line-height: 1.1; }
.nav__logo-text small { font-weight: 500; color: var(--fg-dim); font-size: 0.6rem; letter-spacing: 0.1em; }

.nav__links { display: flex; align-items: center; gap: 28px; justify-self: center; }
.nav__link {
  position: relative;
  font-size: 0.82rem; color: var(--fg-dim);
  padding: 6px 0;
  transition: color 0.2s var(--ease);
}
.nav__link::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 0; height: 1px;
  /* --link-color est pose sur chaque lien au rendu (renderNav), d'apres sa
     PROPRE categorie — a ne pas confondre avec --cat-color, qui est celle de
     la categorie epinglee. C'est bien celle du lien qu'on veut ici : survoler
     Mariage doit annoncer l'or, meme si Nightlife est epingle.
     Le rose en dur ne se justifiait que pour Accueil, qui n'est pas une
     categorie — et qui retombe justement sur var(--pink) tout seul. */
  background: var(--link-color, var(--pink));
  transition: width 0.3s var(--ease), background 0.25s var(--ease);
}
.nav__link:hover, .nav__link.is-active { color: #fff; }
.nav__link:hover::after, .nav__link.is-active::after { width: 100%; }

.nav__actions { display: flex; align-items: center; gap: 10px; justify-self: end; }
.nav__actions .btn--pill {
  font-size: 0.7rem; padding: 12px 18px;
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease), color 0.3s var(--ease), transform 0.2s var(--ease);
}
/* NOUS CONTACTER suit l'ambiance epinglee  (v=99)
   Le texte reste #0a0a0a : les quatre couleurs de categorie sont toutes assez
   claires pour porter du noir (le bleu #4f8ef7 est la plus sombre des quatre,
   et tient encore un contraste de ~6,7:1). Rien a prevoir de ce cote.
   #mobileContactTrigger suit le meme chemin — il vit dans le menu plein ecran,
   hors du formulaire, donc il herite bien du --cat-color de :root. */
#contactTrigger, #mobileContactTrigger { background: var(--cat-color, var(--pink)); color: #0a0a0a; }
#contactTrigger:hover, #mobileContactTrigger:hover {
  background: var(--cat-color, var(--pink));
  box-shadow:
    0 0 24px color-mix(in srgb, var(--cat-color, var(--pink)) 65%, transparent),
    0 0 0 1px color-mix(in srgb, var(--cat-color, var(--pink)) 40%, transparent);
  transform: translateY(-1px);
}
#muteToggle { transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease), color 0.3s var(--ease); }
#muteToggle.is-active {
  border-color: var(--cat-color, var(--pink)); color: var(--cat-color, var(--pink));
  box-shadow: 0 0 16px color-mix(in srgb, var(--cat-color, var(--pink)) 50%, transparent),
              0 0 0 1px color-mix(in srgb, var(--cat-color, var(--pink)) 25%, transparent);
}

.hero__content { position: relative; z-index: 2; max-width: 900px; width: min(92vw, 900px); }
.hero__title {
  font-family: var(--font-body);
  font-weight: 900;
  line-height: 0.9;
  margin-bottom: 24px;
}
.hero__title-fill {
  display: block;
  font-size: clamp(2.2rem, 5.5vw, 4.3rem);
  color: #fff;
  white-space: nowrap;
}
.hero__title-outline {
  display: block;
  font-size: clamp(2.2rem, 5.5vw, 4.3rem);
  color: transparent;
  /* repli pour les navigateurs sans color-mix */
  -webkit-text-stroke: 1px rgba(255,255,255,0.25);
  /* le contour prend la couleur de la catégorie épinglée (rose sur Accueil) */
  -webkit-text-stroke-color: color-mix(in srgb, var(--cat-color) 62%, transparent);
  transition: -webkit-text-stroke-color 620ms cubic-bezier(0.22, 1, 0.36, 1);
  white-space: nowrap;
}
.hero__desc {
  max-width: 480px;
  color: var(--fg-dim);
  font-size: 1rem;
  line-height: 1.6;
  margin-bottom: 32px;
}
.hero__actions { display: flex; gap: 14px; flex-wrap: wrap; }

.hero__meta {
  position: absolute; bottom: 118px; right: 48px; z-index: 2;
  text-align: right;
}
.hero__meta-info p {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.03em;
  color: var(--fg-dim); margin-bottom: 1px; line-height: 1.5;
}
/* LUNOR COLLECTIVE 2026 suit l'ambiance epinglee  (v=99)
   La regle est bien scopee a .hero__meta-info : la classe .accent generique
   (ligne 66) sert AUSSI aux pieds de panneau et au formulaire — la passer a
   --cat-color aurait repeint « PRODUCTION CINÉMATOGRAPHIQUE » au passage,
   ce qui n'a pas ete demande. Seule la signature du hero bascule. */
.hero__meta-info p.accent {
  color: var(--cat-color, var(--pink));
  transition: color 0.35s var(--ease);
}
.hero__meta-bottom { display: flex; flex-direction: column; align-items: flex-end; gap: 18px; margin-top: 14px; }
/* Le glyphe seul, sans pastille blanche : la pastille servait à garantir la
   lisibilité sur une image claire, on la remplace par une ombre portée douce
   qui détache l'icône sans poser un rond opaque sur la vidéo.
   La zone cliquable reste plus large que le glyphe : confortable au clic sans
   encombrer visuellement. */
.hero__social { display: flex; gap: 12px; }
.hero__social a {
  width: 38px; height: 38px;
  background: none;
  color: rgba(255,255,255,0.92);
  display: flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 1px 7px rgba(0,0,0,0.6));
  transition: color 0.25s var(--ease), transform 0.25s var(--ease);
}
.hero__social a:hover { color: var(--cat-color, var(--pink)); transform: translateY(-2px); }

/* ==========================================================================
   PANNEAUX CATÉGORIE (glissants depuis la droite)
   ========================================================================== */
/* LE MENU RESTE VISIBLE, MAIS IL RECULE  (v=95)
   Un noir a 40% cachait le menu sans le mettre a distance : on le voyait
   aussi net qu'avant, juste plus sombre. Le flou fait le travail que
   l'opacite ne sait pas faire — il dit « ceci est derriere », l'oeil cesse
   d'essayer de le lire et se pose sur le panneau. Le noir descend a 22% en
   echange : c'est le flou qui assombrit, via brightness.
   Meme geste que la porte de l'intranet, en deux fois moins appuye : ici le
   fond doit rester reconnaissable, on n'est pas en train de fermer le site. */
.panel-overlay {
  position: fixed; inset: 0; z-index: 47;
  background: rgba(0,0,0,0.22);
  -webkit-backdrop-filter: blur(9px) brightness(0.5) saturate(0.85);
  backdrop-filter: blur(9px) brightness(0.5) saturate(0.85);
  opacity: 0; pointer-events: none;
  transition: opacity 0.45s var(--ease);
}
.panel-overlay.is-visible { opacity: 1; pointer-events: auto; }
#videoModalOverlay.is-visible { background: rgba(4,4,5,0.94); z-index: 47; }
/* L'Atelier est déjà opaque et plein écran : ce fond n'a jamais eu d'utilité visuelle,
   et son z-index ne doit surtout pas passer au-dessus du contenu de l'Atelier. */
#atelierOverlay { display: none !important; }

.panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 48;
  width: min(78vw, 1600px);
  background: var(--bg-panel);
  border-left: 1px solid var(--line);
  transform: translateX(100%);
  transition: transform 0.45s var(--ease);
  display: flex; flex-direction: column;
  overflow: hidden;
}
/* LE RELIEF DU PANNEAU  (v=95)
   Le panneau ne se posait pas SUR le site, il le remplacait : un simple filet
   a gauche, et rien qui dise l'epaisseur. Trois couches, de la plus serree a
   la plus large :
     1. une arete claire d'un pixel sur le bord gauche — la lumiere qui accroche
        le chant du panneau au moment ou il sort ;
     2. la couleur de la categorie, tres courte, juste derriere cette arete ;
     3. l'ombre portee, large et molle, qui creuse le fond flou.
   L'ombre est declaree ici et non sur .is-open : sinon elle apparaissait d'un
   coup en fin de glissement au lieu d'accompagner le mouvement. */
.panel.is-open {
  transform: translateX(0);
  /* Repli sans color-mix (navigateurs < 2023) : sans lui, TOUTE l'ombre — y
     compris la profondeur noire — tombait d'un coup et le panneau paraissait
     plat. Ici l'ombre teintee est fixee au rose de marque. */
  box-shadow:
    -1px 0 0 rgba(255, 255, 255, 0.14),
    -6px 0 26px -6px rgba(255, 45, 120, 0.30),
    -46px 0 110px rgba(0, 0, 0, 0.82);
  /* Version qui suit la couleur de la categorie (navigateurs recents) : elle
     ecrase le repli ci-dessus la ou color-mix est compris. */
  box-shadow:
    -1px 0 0 rgba(255, 255, 255, 0.14),
    -6px 0 26px -6px color-mix(in srgb, var(--cat-color, var(--pink)) 42%, transparent),
    -46px 0 110px rgba(0, 0, 0, 0.82);
}

.panel__close {
  position: absolute; top: 24px; right: 32px; z-index: 2;
  width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.2);
  display: flex; align-items: center; justify-content: center;
  color: #fff; transition: background 0.2s var(--ease);
}
.panel__close:hover { background: rgba(255,255,255,0.08); }

.panel__eyebrow {
  padding: 32px 48px 0;
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.08em;
  color: var(--fg-dim);
  display: flex; align-items: center; gap: 8px;
}
.panel__eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--cat-color, var(--pink)); }

/* L'ÉQUILIBRE TEXTE / FILMS  (v=95)
   1fr 1fr donnait autant de place a la fiche commerciale qu'aux films. Le
   texte n'a pas besoin de la moitie d'un panneau de 78vw : une colonne de
   lecture confortable plafonne vers 480px, au-dela on ne lit pas mieux, on lit
   moins bien (l'oeil perd sa ligne). Le rab va donc aux films, et la grille de
   droite y gagne une a deux colonnes de cartes en plus — c'est-a-dire des
   films visibles sans avoir a cliquer sur PLUS.
   minmax() et non un pourcentage sec : sur un ecran etroit, la colonne ne
   descend pas sous 300px, sous quoi les puces de services se casseraient. */
.panel__body {
  flex: 1;
  display: grid; grid-template-columns: minmax(300px, 34%) 1fr;
  gap: 44px;
  padding: 20px 48px 24px;
  overflow: hidden;
}
.panel__left { overflow-y: auto; padding-right: 12px; }
.panel__right { display: flex; flex-direction: column; overflow: hidden; }

/* --------------------------------------------------------------------------
   LA COLONNE DE LECTURE  (v=95)
   Moins large ne veut pas dire moins lisible : c'est meme l'inverse. En
   rendant la moitie de sa largeur, cette colonne gagne le droit d'etre lue
   pour de bon — on lui donne donc un interlignage de vrai texte (1.7), une
   couleur qui ne demande plus d'effort (#c8c8cc et non #9a9a9f), et un rythme
   vertical serre pour que la fiche tienne d'un regard.
   -------------------------------------------------------------------------- */
.panel__left h2 {
  font-size: 1.72rem; font-weight: 800; line-height: 1.12;
  letter-spacing: -0.015em; margin-bottom: 12px;
}
.panel__left > p.desc {
  color: #c8c8cc; line-height: 1.7; font-size: 0.94rem; margin-bottom: 22px;
}
/* Un filet a la couleur de la categorie sous le chapeau : il tient la colonne
   et rappelle de quel univers on parle, sans coûter une ligne de texte. */
.panel__left > p.desc::after {
  content: ''; display: block; width: 44px; height: 2px; margin-top: 22px;
  background: var(--cat-color, var(--pink)); opacity: 0.8;
}

.info-box {
  border: 1px solid var(--line);
  padding: 15px 17px;
  margin-bottom: 14px;
  border-radius: 4px;
}
.info-box__label { font-family: var(--font-mono); font-size: 0.64rem; color: var(--fg-dim); letter-spacing: 0.08em; margin-bottom: 11px; }
.info-box .desc { color: #b8b8be; line-height: 1.62; font-size: 0.88rem; }

/* LES SERVICES EN PASTILLES  (v=95)
   La liste en deux colonnes gaspillait : quatre lignes hautes pour quatre
   mots. En pastilles, les memes services tiennent sur deux lignes courtes, se
   lisent d'un coup d'oeil, et cessent de ressembler a un cahier des charges —
   ils ressemblent a une offre. Le ✓ disparait : le cadre le dit deja. */
.check-list { display: flex; flex-wrap: wrap; gap: 7px; list-style: none; }
.check-list li {
  font-size: 0.79rem; line-height: 1;
  padding: 7px 11px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--cat-color, var(--pink)) 34%, transparent);
  background: color-mix(in srgb, var(--cat-color, var(--pink)) 8%, transparent);
  color: #e6e6e8; white-space: nowrap;
}

.case-study { border: 1px solid var(--cat-color, var(--pink)); background: color-mix(in srgb, var(--cat-color, var(--pink)) 8%, transparent); }
.case-study .info-box__label { color: var(--cat-color, var(--pink)); }
.case-study h4 { margin-bottom: 8px; }

/* Playfair italique : la DA le reserve aux citations, c'est exactement ici.
   Il tranche avec l'Inter du reste de la colonne et fait respirer la fiche. */
.quote-box {
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 16px 0; margin: 16px 0;
  font-family: var(--font-serif); font-style: italic;
  font-size: 1rem; line-height: 1.55; color: #c8c8cc;
}
.quote-box cite { display: block; margin-top: 9px; font-family: var(--font-mono); font-style: normal; color: var(--fg-dim); font-size: 0.7rem; letter-spacing: 0.04em; }

.cta-row {
  display: block; width: 100%; text-align: center;
  border: 1px solid var(--line); padding: 16px;
  font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.05em;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.cta-row:hover { border-color: var(--cat-color, var(--pink)); background: color-mix(in srgb, var(--cat-color, var(--pink)) 6%, transparent); }

/* ==========================================================================
   LE SÉLECTEUR FILMS / PHOTOS  (v=95)
   Une bascule a deux volets, posee au-dessus de la grille. Le curseur
   (.panel__switch-thumb) glisse d'un volet a l'autre : c'est lui qui porte la
   couleur de la categorie, les deux boutons ne font que s'allumer par-dessus.
   Un fond par bouton aurait clignote a chaque bascule ; un curseur qui glisse
   dit le mouvement, et le mouvement se remarque.
   ========================================================================== */
.panel__switch {
  position: relative; align-self: flex-start;
  display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 2px; padding: 3px; margin-bottom: 16px;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255,255,255,0.03);
}
/* Le curseur est SOUS les boutons (z-index) et se deplace en translateX : la
   largeur d'un volet vaut 50% de la piste, moins les 3px de padding. */
.panel__switch-thumb {
  position: absolute; z-index: 0; top: 3px; bottom: 3px; left: 3px;
  width: calc(50% - 3px); border-radius: 999px;
  background: color-mix(in srgb, var(--cat-color, var(--pink)) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--cat-color, var(--pink)) 55%, transparent);
  transition: transform 0.38s var(--ease), background 0.3s var(--ease);
}
.panel__switch[data-view="photos"] .panel__switch-thumb { transform: translateX(100%); }
/* Une seule collection (categorie sans photo, ou sans film) : le volet restant
   occupe toute la piste. Sans ce cas, le curseur gardait sa demi-largeur et
   flottait a cote du seul bouton, ou partait hors de la piste en translateX. */
.panel__switch--solo .panel__switch-thumb { width: calc(100% - 6px); transform: none; }
.panel__switch--solo .panel__switch-btn { cursor: default; }

.panel__switch-btn {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 26px; border-radius: 999px; border: 1px solid transparent;
  background: none; cursor: pointer;
  font-family: var(--font-mono); font-size: 0.74rem;
  letter-spacing: 0.16em; text-indent: 0.16em;
  color: var(--fg-dim);
  transition: color 0.3s var(--ease);
}
.panel__switch-btn:hover { color: #fff; }
.panel__switch-btn.is-active { color: #fff; }
.panel__switch-count {
  font-size: 0.62rem; letter-spacing: 0.04em; text-indent: 0;
  color: var(--fg-faint); transition: color 0.3s var(--ease);
}
.panel__switch-btn.is-active .panel__switch-count { color: var(--cat-color, var(--pink)); }
.panel__switch-btn:focus-visible { outline: 2px solid var(--cat-color, var(--pink)); outline-offset: 3px; }

/* La lueur d'appel a ete retiree (v=106) : le halo pulsant sous le selecteur
   FILMS/PHOTOS ne tombait jamais juste et n'apportait rien. Le selecteur reste
   parfaitement utilisable sans — le curseur colore et les libelles suffisent. */

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

/* Les deux vues occupent le meme creneau ; une seule est montee a la fois
   (l'autre est retiree du DOM en JS, pas seulement cachee — sinon ses images
   continuaient d'etre chargees et decodees pour rien). */
/* LA MARGE DE LA LUEUR  (v=97)
   overflow-y: auto suffit a rogner AUSSI l'horizontale : des qu'un axe cesse
   d'etre `visible`, l'autre passe en `auto`. Le debordement lateral etait donc
   coupe a zero pixel, et la lueur coloree du survol — qui deborde de 7px —
   se faisait trancher net contre la colonne de texte.
   La reponse n'est pas de retirer le rognage (il tient le defilement) mais de
   lui donner de quoi mordre : ces 16px sont la reserve dans laquelle la lueur
   a le droit de deborder. En haut, 12px couvrent les 7px de lueur plus les 3px
   d'elevation de la carte survolee.
   L'ombre NOIRE, elle, deborde de 22px et reste coupee — mais du noir coupe
   sur du noir ne se voit pas, ces 6px manquants ne coutent rien. */
.panel__cards {
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 16px;
  padding: 12px 16px 24px;
}
.panel__cards--photos .genre-row + .genre-row { margin-top: 30px; }

/* ==========================================================================
   LIGNES PAR GENRE — colonne de droite du panneau categorie
   Cartes verticales, une ligne par genre. Le nombre de colonnes s'adapte a la
   largeur (auto-fill) ; le JS masque ensuite le surplus et pose la carte
   "PLUS" au dernier emplacement. La colonne defile si les lignes debordent.
   ========================================================================== */
.genre-row + .genre-row { margin-top: 34px; }

/* Filet + libelle : le trait file jusqu'au compteur, il structure la ligne
   sans ajouter de bloc. */
.genre-row__head {
  display: flex; align-items: baseline; gap: 14px;
  margin-bottom: 14px;
}
.genre-row__title {
  font-family: var(--font-body);
  font-size: 0.82rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: #fff;
  white-space: nowrap;
}
.genre-row__head::after {
  content: ''; flex: 1; height: 1px; order: 1;
  background: linear-gradient(90deg, var(--cat-color, var(--pink)) 0%, rgba(255,255,255,0.08) 60%, transparent 100%);
  opacity: 0.5;
}
.genre-row__count {
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  color: var(--fg-faint); white-space: nowrap;
}

/* -- RÉDUIRE : le chemin du retour  (v=95) -------------------------------
   PLUS ouvrait une porte sans poignee de l'autre cote : une fois la rangee
   depliee, on ne pouvait plus la replier, et les rangees suivantes partaient
   hors de l'ecran. Le retour se pose dans l'en-tete plutot que dans la grille :
   la ou PLUS occupe une case, REDUIRE ne doit rien deranger — la grille est
   pleine, justement.
   Il n'existe que sur une rangee depliee (pose en JS via .is-expanded). */
.genre-row__less {
  display: none;
  /* order : le filet (::after) est du contenu genere, il vient toujours en
     dernier dans le DOM. Sans ces ordres, le bouton se serait plante entre le
     compteur et le filet, au milieu de la ligne. */
  order: 2;
  align-items: center; gap: 7px;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  color: var(--fg-dim); background: none; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 11px;
  white-space: nowrap;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.genre-row.is-expanded .genre-row__less { display: inline-flex; }
.genre-row__less:hover { color: #fff; border-color: var(--cat-color, var(--pink)); }
.genre-row__less:focus-visible { outline: 2px solid var(--cat-color, var(--pink)); outline-offset: 2px; }
.genre-row__less svg { transition: transform 0.25s var(--ease); }
.genre-row__less:hover svg { transform: translateY(-2px); }

/* -- LES CARTES PHOTO ----------------------------------------------------
   Elles reprennent .card entier — meme forme, meme retrait des voisines, meme
   carte PLUS. Une photo et un film se cliquent de la meme facon dans ce
   panneau ; leur seule difference est ce qui s'ouvre ensuite. */
/* ==========================================================================
   LA MOSAÏQUE PHOTO  (v=97)
   Les photos etaient des vignettes de 128px recadrees en 3/4 : une planche
   contact, pas une galerie. Or une photo recadree n'est plus la photo — le
   cadrage EST le travail, le rogner revient a montrer autre chose.
   Trois colonnes larges, chaque image a son rapport vrai (--ar, pose au rendu
   depuis w/h) : une verticale reste verticale, une horizontale reste
   horizontale, et les hauteurs de la grille varient d'elles-memes. C'est ce
   depaysement des hauteurs qui fait respirer une mosaique — une grille dont
   toutes les cases ont la meme taille se lit comme un tableau Excel.
   align-items: center — la ligne prend la hauteur de sa plus grande photo, les
   autres s'y centrent plutot que de coller en haut : le vide se repartit au
   lieu de tomber tout entier sous l'image.
   ========================================================================== */
.genre-row__grid--photos {
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  align-items: start;
  /* dense : quand une horizontale (2 colonnes) laisse un trou, une carte plus
     petite vient le combler au lieu de laisser un vide — mosaique serree facon
     LunorStills. */
  grid-auto-flow: row dense;
}
/* Une horizontale prend deux colonnes : largeur doublee -> hauteur alignee sur
   une verticale a une colonne. Si la grille n'a qu'UNE colonne (petit ecran),
   le span 2 est ignore par le navigateur (il ne peut pas depasser le nombre de
   colonnes), donc rien ne casse. */
.genre-row__grid--photos .card--wide { grid-column: span 2; }
/* card--tall a ete retire de la carte photo : plus rien ne lui impose 3/4, et
   --ar peut faire son travail. */
.genre-row__grid--photos .card--photo {
  aspect-ratio: var(--ar, 3/4);
  border-radius: 3px;
}
.genre-row__grid--photos .card--more { aspect-ratio: 1/1; }

/* LA LEGENDE NE S'INVITE PLUS
   Les titres viennent des noms de fichiers (« Photo Album 1 00000021 ») : ils
   n'apprennent rien et posaient un bandeau noir en bas de chaque image. Ils
   restent lisibles — au survol, quand on a justement demande a en savoir plus
   sur CETTE photo. Au repos, la mosaique n'est que des photos. */
.genre-row__grid--photos .card__scrim,
.genre-row__grid--photos .card__body {
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}
.genre-row__grid--photos .card--photo:hover .card__scrim,
.genre-row__grid--photos .card--photo:hover .card__body { opacity: 1; }

.genre-row__grid--photos .card--photo .card__title {
  font-size: 0.68rem; font-weight: 500; font-family: var(--font-mono);
  letter-spacing: 0.02em; line-height: 1.35; color: #e2e2e6;
}
.card--photo .card__scrim { background: linear-gradient(180deg, transparent 55%, rgba(0,0,0,0.88) 100%); }

.genre-row__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: 10px;
}
.genre-row__grid .card--tall { aspect-ratio: 3/4; }
.genre-row__grid .card--tall .card__title { font-size: 0.82rem; line-height: 1.25; }
.genre-row__grid .card--tall .card__director,
.genre-row__grid .card--tall .card__year,
.genre-row__grid .card--tall .card__featuring { display: none; }
.genre-row__grid .card--tall .card__body { left: 10px; right: 10px; bottom: 10px; }
.genre-row__grid .card--tall .card__tag { font-size: 0.55rem; padding: 3px 6px; }
/* filter et box-shadow sont repris ici : cette regle est PLUS specifique que
   `.card` et, sans eux, elle effacait la transition du retrait des voisines —
   les cartes de la grille auraient vire au gris sans transition. */
.genre-row__grid .card {
  transition: transform 0.3s var(--ease), filter 0.45s var(--ease),
              box-shadow 0.35s var(--ease);
}
.genre-row__grid .card:hover { transform: translateY(-3px); }

/* -- Carte "PLUS" : le surplus, devine mais pas montre -------------------- */
.card--more {
  position: relative; aspect-ratio: 3/4;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 4px; overflow: hidden;
  background: #0d0d10; cursor: pointer; padding: 0;
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
/* Noir et blanc + floue : on lit qu'il y a des images derriere, sans les lire. */
.card--more .card__img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(1) blur(7px) brightness(0.42) contrast(1.1);
  transform: scale(1.15);
  transition: transform 0.5s var(--ease), filter 0.5s var(--ease);
}
.card--more__veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8,8,10,0.55) 0%, rgba(8,8,10,0.82) 100%);
}
.card--more__inner {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px;
}
.card--more__count {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.08em;
  color: var(--cat-color, var(--pink));
}
.card--more__label {
  font-family: var(--font-body); font-weight: 700;
  font-size: 0.78rem; letter-spacing: 0.28em; text-indent: 0.28em;
  color: #fff;
}
.card--more__arrow { color: rgba(255,255,255,0.55); line-height: 0; }
.card--more__arrow svg { transition: transform 0.35s var(--ease); }
/* Au survol : le trait s'allonge vers la droite, la couleur de la categorie
   prend le dessus. Le mouvement dit "il y a une suite" mieux qu'un libelle. */
.card--more:hover { border-color: var(--cat-color, var(--pink)); transform: translateY(-3px); }
.card--more:hover .card__img { transform: scale(1.22); filter: grayscale(1) blur(5px) brightness(0.5) contrast(1.1); }
.card--more:hover .card--more__arrow { color: var(--cat-color, var(--pink)); }
.card--more:hover .card--more__arrow svg { transform: translateX(5px); }
.card--more:focus-visible { outline: 2px solid var(--cat-color, var(--pink)); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .genre-row__grid .card, .card--more, .card--more .card__img, .card--more__arrow svg { transition: none; }
}

.card {
  position: relative;
  aspect-ratio: 16/9;
  border-radius: 4px;
  overflow: hidden;
  cursor: pointer;
  background: #111;
  /* filter est dans la transition : c'est lui qui porte le retrait des
     voisines, plus bas. Sans lui, elles viraient au gris d'un coup sec. */
  transition: transform 0.35s var(--ease), filter 0.45s var(--ease),
              box-shadow 0.35s var(--ease);
}
.card__img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  /* LA COULEUR AU REPOS  (v=94)
     Le noir et blanc au repos rendait la grille inerte : rien n'y appelait le
     clic, et le survol devait tout faire. La couleur est la des le depart,
     juste retenue d'un cran de lumiere — le survol garde ainsi ou monter. */
  filter: brightness(0.86) saturate(1.04);
  transition: filter 0.5s var(--ease), transform 0.5s var(--ease);
}
.card:hover .card__img { filter: brightness(1.06) saturate(1.16) contrast(1.03); transform: scale(1.04); }

/* --------------------------------------------------------------------------
   LE RETRAIT DES VOISINES — grille du panneau  (v=94)
   Meme geste que dans LunorFilms et dans LunorStills : le relief ne vient pas
   de la carte survolee qui s'allume, il vient des AUTRES qui se retirent —
   noir et blanc, un souffle de flou, un cheveu plus petites. La couleur ne
   subsiste alors qu'a un seul endroit de l'ecran : celui que la souris
   designe. C'est ce contraste qui fait la main, pas la luminosite.
   :has() et non :hover sur la grille : avec :hover, tout s'eteignait des que
   la souris passait dans une gouttiere, sans qu'aucune carte ne s'allume
   (lecon deja payee sur .stills__gallery et .discover-row__track).
   Le :not(:hover) suffit a epargner la carte active : inutile de lui reecrire
   une contre-regle, aucune des deux ne s'applique a elle.
   Deux selecteurs, car les cartes ont deux logements : dans .genre-row__grid
   pour les categories a sous-categories (Nightlife, Brand Content), et a meme
   #panelCards pour les autres (Mariage, Artistes) — d'ou le > .
   -------------------------------------------------------------------------- */
@media (hover: hover) {
  /* Le voile grisant ne vise QUE les cartes video (v=106) : sur les photos, le
     :not(.card--photo) l'exclut. Une galerie photo dont tout s'eteint sauf une
     image faisait "vitrine" ; l'utilisateur veut que les photos restent nettes.
     Les cartes video, elles, gardent l'effet. */
  .genre-row__grid:has(.card:not(.card--photo):hover) .card:not(:hover):not(.card--photo),
  #panelCards:has(> .card:not(.card--photo):hover) > .card:not(:hover):not(.card--photo) {
    filter: grayscale(1) brightness(0.5) blur(1.4px);
    transform: scale(0.985);
  }
  /* La carte survolee se decolle : liseré blanc, nappe a la couleur de la
     categorie, ombre portee. Sobre — la couleur retrouvee fait deja le gros
     du travail. */
  .genre-row__grid:has(.card:hover) .card:hover,
  #panelCards:has(> .card:hover) > .card:hover {
    z-index: 2;
    box-shadow:
      0 0 0 1px rgba(255, 255, 255, 0.4),
      0 0 26px -6px var(--cat-color, var(--pink)),
      0 18px 44px rgba(0, 0, 0, 0.7);
  }
}
@media (prefers-reduced-motion: reduce) {
  .card, .card__img { transition: none; }
}
.card__scrim { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.85) 100%); }
.card__tag {
  position: absolute; top: 14px; left: 14px;
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.06em;
  padding: 4px 10px; background: rgba(0,0,0,0.6);
  border: 1px solid var(--cat-color, var(--pink)); color: var(--cat-color, var(--pink));
}
.card__year { position: absolute; top: 14px; right: 14px; font-family: var(--font-mono); font-size: 0.65rem; color: var(--fg-dim); }
.card__body { position: absolute; left: 16px; right: 16px; bottom: 14px; }
.card__director { font-family: var(--font-mono); font-size: 0.65rem; color: var(--fg-dim); margin-bottom: 4px; }
/* Titres de films : même typo que la bannière LunorFilms — Inter en 800, droit.
   Playfair italique reste réservé aux citations (manifeste, témoignages, intro). */
.card__title {
  font-family: var(--font-body); font-style: normal; font-weight: 800;
  font-size: 1.3rem; line-height: 1.15; letter-spacing: -0.01em;
}
.card__tagline {
  font-size: 0.8rem; line-height: 1.4; color: var(--cat-color, var(--pink)); margin-top: 6px;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height 0.35s var(--ease), opacity 0.35s var(--ease);
}
/* 3 lignes de 0,8rem a 1,4 d'interligne = ~3.4em ; on prend 3.6em pour l'air
   en bas. Au-dela de 3 lignes, line-clamp coupe avec des points de suspension
   plutot qu'en plein milieu d'un mot (v=109). Avant : max-height 40px (~2
   lignes) + overflow hidden -> une 3e ligne etait tranchee net.
   Le clamp s'applique a la tagline entiere : le tiret ::before, etant inline,
   fait partie de la premiere ligne du bloc. */
.card:hover .card__tagline {
  /* 3 lignes x 1,4 d'interligne = 4,2em. L'ancienne valeur 3,6em coupait la 3e
     ligne en deux dans le sens de la HAUTEUR (le line-clamp gere le nombre de
     lignes, mais max-height le rognait avant). 4,5em laisse les 3 lignes
     entieres respirer. (v=110) */
  max-height: 4.5em; opacity: 1;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
}
.card__tagline::before { content: ''; display: inline-block; width: 22px; height: 1px; background: var(--cat-color, var(--pink)); vertical-align: middle; margin-right: 8px; }

.panel__footer {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 48px; border-top: 1px solid var(--line);
  font-family: var(--font-mono); font-size: 0.68rem; color: var(--fg-dim);
}
.panel__footer-social { display: flex; gap: 10px; }
.panel__footer-social a { color: var(--fg-dim); transition: color 0.2s var(--ease); }
.panel__footer-social a:hover { color: #fff; }

/* ==========================================================================
   MODALE VIDÉO (façon Netflix)
   ========================================================================== */
.video-modal {
  position: fixed; top: 50%; left: 50%; z-index: 50;
  width: min(84vw, 1280px);
  height: 92vh;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 40px 100px rgba(0,0,0,0.7);
  opacity: 0; pointer-events: none;
  transform: translate(-50%, -50%) scale(0.96);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.video-modal.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }

.video-modal__close {
  position: absolute; top: 16px; right: 16px; z-index: 30;
  width: 36px; height: 36px; border-radius: 50%;
  background: rgba(10,10,12,0.6); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  border: 1px solid rgba(255,255,255,0.15);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.video-modal__close:hover { background: var(--cat-color, var(--pink)); color: #0a0a0a; transform: rotate(90deg); }

.video-modal__media { position: relative; flex: 0 0 46%; background: #111; overflow: hidden; }
.video-modal__media img { width: 100%; height: 100%; object-fit: cover; }
.video-modal__media video { width: 100%; height: 100%; object-fit: contain; background: #000; }
/* Bouton de sortie du plein écran : superposé à la vidéo, donc visible
   même quand le navigateur masque le reste de la page en plein écran. */
.video-modal__exit-fs {
  position: absolute; top: 20px; right: 20px; z-index: 40;
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(10,10,12,0.45); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,0.16);
  color: #fff;
  cursor: pointer;
  opacity: 0.55;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease),
              opacity 0.2s var(--ease), transform 0.2s var(--ease);
}
/* Discrète au repos, franche au survol : elle ne mange plus l'image. */
.video-modal__exit-fs:hover {
  opacity: 1;
  background: var(--cat-color, var(--pink));
  border-color: var(--cat-color, var(--pink));
  color: #0a0a0a;
  transform: rotate(90deg);
}
.video-modal__exit-fs svg { flex-shrink: 0; }

/* --------------------------------------------------------------------------
   MODE LECTURE "plein écran maison" : le lecteur couvre toute la fenêtre,
   sans passer par le plein écran natif du navigateur (qui superposait sa
   propre interface). is-playing est posée en JS au lancement de la vidéo.

   IMPORTANT — pourquoi c'est la MODALE qui s'agrandit, et pas le média :
   .video-modal est centrée avec transform: translate(-50%,-50%). Or un
   ancêtre porteur d'un transform devient le "containing block" de ses
   descendants en position: fixed. Un enfant en fixed + inset: 0 se calait
   donc sur la boîte de la modale (84vw x 92vh) et non sur la fenêtre :
   le plein écran ne prenait pas tout l'écran. On neutralise donc le
   transform sur .video-modal en mode lecture et c'est elle qui occupe la
   fenêtre ; le média n'a plus qu'à la remplir en absolute.
   -------------------------------------------------------------------------- */
.video-modal.is-playing,
.video-modal.is-open.is-playing,
.video-modal.is-portrait.is-playing,
.video-modal.is-open.is-portrait.is-playing {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100%;
  height: 100vh;      /* repli pour les vieux navigateurs */
  height: 100dvh;     /* tient compte des barres mobiles */
  max-width: none;
  transform: none;
  transition: none;
  flex-direction: column;
  background: #000;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overflow: hidden;
  z-index: 90;
}
/* Le média occupe toute la modale devenue plein écran. */
.video-modal.is-playing.is-portrait .video-modal__media,
.video-modal.is-playing.is-landscape .video-modal__media,
.video-modal.is-playing .video-modal__media {
  position: absolute !important;
  inset: 0 !important;
  z-index: 1 !important;
  flex: none !important;
  width: 100% !important;
  height: 100% !important;
  border: none !important;
  border-right: none !important;
  border-radius: 0 !important;
  background: #000 !important;
}
.video-modal.is-playing .video-modal__media video {
  width: 100% !important; height: 100% !important; object-fit: contain !important; background: #000 !important;
}
/* La fiche info est masquée pendant la lecture (sinon elle garde sa place
   dans le flex et rogne la hauteur du lecteur). */
.video-modal.is-playing .video-modal__info { display: none !important; }
/* Décalé vers le bas : en haut à droite, le bouton recouvrait le curseur de
   volume des contrôles natifs du lecteur (Safari), qui devenait inutilisable. */
/* Descendue : a 24px du haut, la croix recouvrait le curseur de volume des
   controles natifs de Safari, qui devenait inutilisable. */
.video-modal.is-playing .video-modal__exit-fs {
  position: absolute; top: 78px; right: 24px; z-index: 95;
}
/* La croix de fermeture ferait doublon avec "Quitter le plein écran" au même
   endroit : on la masque pendant la lecture. */
.video-modal.is-playing .video-modal__close { display: none; }
/* On masque le dégradé du bas et la vignette en mode lecture. */
.video-modal.is-playing .video-modal__media::after { display: none; }
.video-modal__loading {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: rgba(5,5,6,0.55);
}
.video-modal__spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.2);
  border-top-color: var(--cat-color, var(--pink));
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.video-modal__media::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgba(10,10,12,0.9) 100%);
  pointer-events: none;
}
.video-modal__tags {
  position: absolute; bottom: 18px; right: 20px; z-index: 1;
  display: inline-flex; align-items: center; gap: 10px;
  width: fit-content;
  background: rgba(10,10,12,0.5); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  border: 1px solid rgba(255,255,255,0.12);
  padding: 6px 12px; border-radius: 3px;
  font-family: var(--font-mono); font-size: 0.66rem;
  color: var(--fg-dim);
}
.video-modal__tags .tag-item { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.video-modal__tags .tag-sep { width: 1px; height: 11px; background: rgba(255,255,255,0.15); }
.video-modal__tags .tag-icon { color: var(--cat-color, var(--pink)); flex-shrink: 0; }
/* --------------------------------------------------------------------------
   BOUTON LECTURE : anneau circulaire en verre dépoli + libellé en dessous.
   Pas de cadre autour du texte (l'ancienne boîte rectangulaire faisait
   "lecteur générique") : le cercle porte l'action, le libellé accompagne.
   -------------------------------------------------------------------------- */
.video-modal__play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  background: none; border: none; padding: 0;
}
.video-modal__play-ring {
  position: relative;
  width: 78px; height: 78px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(10,10,12,0.3);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.55);
  color: #fff;
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease),
              color 0.35s var(--ease), transform 0.35s var(--ease);
}
/* Le triangle est optiquement décentré : on le rattrape de 2px. */
.video-modal__play-ring svg { margin-left: 3px; }
/* Halo qui respire, pour attirer l'œil sans clignoter. */
.video-modal__play-ring::before {
  content: ''; position: absolute; inset: -1px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.35);
  animation: play-halo 2.8s var(--ease) infinite;
  pointer-events: none;
}
@keyframes play-halo {
  0%   { transform: scale(1);    opacity: 0.7; }
  70%  { transform: scale(1.45); opacity: 0; }
  100% { transform: scale(1.45); opacity: 0; }
}
.video-modal__play-label {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.72);
  white-space: nowrap;
  /* L'interlettrage ajoute une espace après la dernière lettre : le mot
     paraîtrait décalé à gauche. On le recentre optiquement. */
  margin-left: 0.34em;
  transition: color 0.35s var(--ease);
}
.video-modal__play:hover .video-modal__play-ring {
  background: var(--cat-color, var(--pink));
  border-color: var(--cat-color, var(--pink));
  color: #0a0a0a;
  transform: scale(1.06);
}
.video-modal__play:hover .video-modal__play-ring::before { animation: none; opacity: 0; }
.video-modal__play:hover .video-modal__play-label { color: #fff; }
.video-modal__play:focus-visible { outline: none; }
.video-modal__play:focus-visible .video-modal__play-ring {
  outline: 2px solid var(--cat-color, var(--pink)); outline-offset: 4px;
}

.video-modal__info {
  flex: 1; overflow-y: auto;
  display: grid; grid-template-columns: 2fr 1fr; gap: 48px;
  padding: 36px 44px 44px;
}
.video-modal__breadcrumb { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.06em; color: var(--cat-color, var(--pink)); margin-bottom: 8px; }
.video-modal__title {
  font-family: var(--font-body); font-style: normal; font-weight: 800;
  font-size: 2.3rem; line-height: 1.05; letter-spacing: -0.015em;
  margin-bottom: 22px;
}
.video-modal__main h4 { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.08em; color: var(--fg-dim); margin-bottom: 10px; }
.video-modal__synopsis { font-size: 0.92rem; line-height: 1.7; color: var(--fg-dim); white-space: pre-line; }

.video-modal__tech h4 { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.08em; color: var(--fg-dim); margin-bottom: 16px; }
/* Chaque paire libelle+valeur est un bloc : la valeur est TOUJOURS sous son
   libelle (v=107). Avant, label et value etaient des freres directs qui
   tombaient dans les cases successives de la grille 2 colonnes — le libelle a
   gauche, sa valeur a droite, au lieu de dessous. Le regroupement corrige ca
   sans dependre du sens de lecture de la grille. */
.video-modal__tech .video-modal__spec { margin-top: 14px; }
.video-modal__tech .label { font-family: var(--font-mono); font-size: 0.62rem; color: var(--fg-faint); }
.video-modal__tech .value { font-size: 0.9rem; margin-top: 4px; }
.video-modal__swatches { display: flex; gap: 8px; margin-top: 8px; }
.video-modal__swatches span { width: 30px; height: 30px; border-radius: 4px; border: 1px solid var(--line); }
.video-modal__lut-desc { font-size: 0.76rem; font-style: italic; color: var(--fg-dim); margin-top: 12px; line-height: 1.5; }

/* --------------------------------------------------------------------------
   VARIANTE PORTRAIT (vidéos 9:16) — média vertical à gauche, infos à droite.
   Sélectionnée automatiquement en JS via aspectRatio (classe .is-portrait).
   La disposition par défaut (média large en haut) reste celle du paysage.
   -------------------------------------------------------------------------- */
.video-modal.is-portrait {
  width: min(78vw, 880px);
  height: 88vh;
  flex-direction: row;
}
.video-modal.is-portrait .video-modal__media {
  flex: 0 0 46%;
  height: 100%;
  border-right: 1px solid var(--line);
}
/* En portrait, le visuel remplit toute la colonne média (cadrage plein,
   sans bande vide autour). */
.video-modal.is-portrait .video-modal__media img { object-fit: cover; background: #0a0a0c; }
.video-modal.is-portrait .video-modal__media::after {
  background: linear-gradient(180deg, transparent 70%, rgba(10,10,12,0.85) 100%);
}
.video-modal.is-portrait .video-modal__info {
  flex: 1;
  display: block;
  padding: 40px 42px;
}
.video-modal.is-portrait .video-modal__title { font-size: 1.9rem; }
.video-modal.is-portrait .video-modal__main { margin-bottom: 32px; }
/* La fiche technique passe sous le synopsis, separee par un filet. */
.video-modal.is-portrait .video-modal__tech {
  border-top: 1px solid var(--line);
  padding-top: 28px;
}
/* En portrait la colonne est plus etroite : on aere la fiche technique
   sur deux colonnes pour eviter une longue liste verticale. */
.video-modal.is-portrait .video-modal__tech {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 32px;
}
.video-modal.is-portrait .video-modal__tech h4 { grid-column: 1 / -1; }
.video-modal.is-portrait .video-modal__tech .video-modal__spec { margin-top: 0; }
.video-modal.is-portrait .video-modal__tech .video-modal__lut-block { grid-column: 1 / -1; margin-top: 8px; }

/* --------------------------------------------------------------------------
   VARIANTE PAYSAGE (16:9, Univisium…) — média large en haut.
   Avant : largeur min(84vw,1280px) + object-fit:cover -> la boîte média était
   bien plus large que haute, donc la vignette 16:9 était rognée en haut/bas.
   Maintenant : la largeur est bornée par la hauteur de la fenêtre (92vh) pour
   que le média garde son ratio 16/9 sans jamais déborder, et l'image est en
   object-fit:contain -> on voit la totalité du visuel.
   :not(.is-playing) : en lecture plein écran, les règles .is-playing priment.
   -------------------------------------------------------------------------- */
.video-modal.is-landscape:not(.is-playing) {
  width: min(72vw, 1080px, 92vh);
  height: auto;
  max-height: 92vh;
}
.video-modal.is-landscape:not(.is-playing) .video-modal__media {
  flex: 0 0 auto;
  width: 100%;
  aspect-ratio: 16 / 9;
}
/* Visuel entier, jamais recadré ; le fond comble les éventuelles bandes. */
.video-modal.is-landscape:not(.is-playing) .video-modal__media img {
  object-fit: contain;
  background: #0a0a0c;
}
/* La fiche info prend le reste et défile si le synopsis est long. */
.video-modal.is-landscape:not(.is-playing) .video-modal__info {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* ==========================================================================
   L'ATELIER (page à onglets)
   ========================================================================== */
.atelier {
  position: fixed; inset: 0; z-index: 45;
  background: var(--bg);
  display: flex; flex-direction: column;
  opacity: 0; pointer-events: none; transform: translateY(12px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
.atelier.is-open { opacity: 1; pointer-events: auto; transform: translateY(0); }

.atelier__header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 48px; border-bottom: 1px solid var(--line);
}
.atelier__title-block { display: flex; align-items: center; gap: 20px; }
.panel__close--inline { position: static; }
.atelier__eyebrow { font-family: var(--font-mono); font-size: 0.7rem; color: var(--pink); letter-spacing: 0.06em; margin-bottom: 4px; }
.atelier__title-block h2 { font-size: 1rem; font-weight: 700; }

.atelier__tabs { display: flex; gap: 4px; }
.atelier__tab {
  font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.04em;
  color: var(--fg-dim); padding: 12px 18px;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}
.atelier__tab.is-active { background: var(--pink); color: #0a0a0a; font-weight: 600; }
.atelier__tab:not(.is-active):hover { color: #fff; }

.atelier__body { flex: 1; overflow-y: auto; padding: 88px 72px; }
.atelier__panel { display: none; max-width: 1400px; margin: 0 auto; }
.atelier__panel.is-active { display: block; }

/* -- Onglet Manifeste (traitement premium : espace généreux, hairlines, serif discret) -- */
.manifeste { display: grid; grid-template-columns: 1.5fr 1fr; gap: 100px; align-items: start; }

.eyebrow-mark {
  display: inline-flex; align-items: center; gap: 14px;
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--fg-dim); margin-bottom: 48px;
}
.eyebrow-mark::before { content: ''; width: 28px; height: 1px; background: var(--pink); }

.manifeste h3 {
  font-family: var(--font-body); font-weight: 800; font-style: normal;
  font-size: clamp(1.9rem, 2.4vw, 2.5rem);
  line-height: 1.2; letter-spacing: -0.005em;
  max-width: 16ch;
  color: #fff;
  margin-bottom: 36px;
}
.manifeste .quote {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  color: var(--fg-dim); font-size: 1.05rem; line-height: 1.6;
  max-width: 42ch;
  padding-left: 22px; border-left: 1px solid rgba(255,45,120,0.35);
  margin-bottom: 48px;
}
.manifeste p {
  color: var(--fg-dim); font-size: 0.94rem; line-height: 1.9;
  max-width: 58ch; letter-spacing: 0.001em;
  margin-bottom: 26px;
}

.manifeste .side-card { border: 1px solid var(--line); overflow: hidden; }
.manifeste .side-card__media { aspect-ratio: 4/3; background: linear-gradient(135deg, #14141a, #1c1420); position: relative; overflow: hidden; }
/* Le visuel remplit le cadre 4:3 ; le voile assure la lisibilite du libelle. */
.manifeste .side-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.manifeste .side-card__media::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 55%, rgba(10,10,12,0.7) 100%);
}
.manifeste .side-card__media .label { z-index: 2; 
  position: absolute; bottom: 20px; left: 20px;
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--fg-dim);
}
.manifeste .side-card__body { padding: 36px 32px; }
.manifeste .side-card__body h4 {
  font-family: var(--font-body); font-weight: 700; font-size: 1.2rem;
  line-height: 1.4; margin-bottom: 16px; color: #fff;
}
.manifeste .side-card__body p { font-size: 0.86rem; line-height: 1.75; color: var(--fg-dim); max-width: none; margin-bottom: 0; }
.manifeste .side-card .cta-row {
  margin-top: 28px; border: none; border-top: 1px solid var(--line);
  padding: 20px 0 0; text-align: left; color: var(--pink);
  font-size: 0.68rem; letter-spacing: 0.1em;
}
.manifeste .side-card .cta-row:hover { background: none; border-top-color: var(--pink); }

.day-night { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; margin: 56px 0; padding-top: 40px; border-top: 1px solid var(--line); }
.day-night > div { padding: 0; }
.day-night h5 { font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 16px; color: var(--fg-faint); }
.day-night p { color: var(--fg-dim); font-size: 0.88rem; line-height: 1.75; margin: 0; max-width: 32ch; }

.manifeste .info-box__label {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fg-faint); margin-bottom: 28px;
}
.team-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px; margin-top: 0; }
.team-grid > div { padding: 24px 0 0; border-top: 1px solid var(--line); }
.team-grid .team-label { font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 14px; color: var(--fg-faint); }
.team-grid h5 { font-family: var(--font-body); font-weight: 700; font-size: 1.15rem; margin-bottom: 12px; color: #fff; }
.team-grid p { color: var(--fg-dim); font-size: 0.85rem; line-height: 1.7; margin-bottom: 20px; max-width: none; }
.team-grid .loc { font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.06em; color: var(--fg-faint); }
.manifeste-footer-note {
  margin-top: 64px; padding-top: 28px; border-top: 1px solid var(--line);
  display: flex; align-items: center; gap: 10px; color: var(--fg-faint); font-size: 0.72rem; letter-spacing: 0.04em;
}

/* -- Onglet Méthode -- */
.methode { text-align: center; }
.methode .eyebrow { font-family: var(--font-mono); font-size: 0.75rem; color: var(--pink); letter-spacing: 0.08em; margin-bottom: 12px; }
.methode h3 { font-size: 2.2rem; font-weight: 800; margin-bottom: 14px; }
.methode > p { color: var(--fg-dim); max-width: 700px; margin: 0 auto 40px; }
.steps { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; text-align: left; }
.steps > div {
  border: 1px solid var(--line); padding: 22px;
  position: relative;
  background: rgba(255,255,255,0.012);
  /* 0.55s : assez lent pour que le relief se pose au lieu de claquer. */
  transition: transform 0.55s var(--ease), border-color 0.55s var(--ease),
              background 0.55s var(--ease), box-shadow 0.55s var(--ease),
              opacity 0.55s var(--ease), filter 0.55s var(--ease);
}
/* Filet a la couleur de l'etape, qui se trace de gauche a droite au survol. */
.steps > div::after {
  content: ''; position: absolute; left: -1px; right: -1px; top: -1px; height: 2px;
  background: var(--step-color, var(--pink));
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.55s var(--ease);
}

/* Le relief ne se joue qu'a la souris : sur ecran tactile, :hover reste colle
   apres le tap et donnerait une carte figee en surbrillance. */
@media (hover: hover) {
  /* Des qu'une carte est survolee, les AUTRES reculent : c'est le contraste
     qui cree le relief, pas seulement l'elevation de la carte active. */
  .steps:hover > div {
    opacity: 0.4;
    filter: saturate(0.35);
    transform: scale(0.985);
  }
  .steps > div:hover {
    opacity: 1;
    filter: none;
    transform: translateY(-8px) scale(1.015);
    border-color: rgba(255,255,255,0.16);
    background: rgba(255,255,255,0.03);
    box-shadow: 0 22px 50px rgba(0,0,0,0.6), 0 0 45px -18px var(--step-color, var(--pink));
    z-index: 2;
  }
  .steps > div:hover::after { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .steps > div, .steps > div::after { transition: none; }
  .steps:hover > div { opacity: 1; filter: none; transform: none; }
  .steps > div:hover { transform: none; }
}
.steps .num { font-family: var(--font-mono); font-size: 1.3rem; font-weight: 700; margin-bottom: 14px; }
.steps h5 { margin-bottom: 10px; font-size: 1rem; }
.steps p { color: var(--fg-dim); font-size: 0.85rem; line-height: 1.5; }

/* -- Onglet Réalisateurs -- */
/* Les cartes tenaient sur 640px fixes + 40px de marge : ca depassait la zone
   visible et forcait le scroll. On leur fait remplir la hauteur disponible. */
.atelier__panel[data-panel="realisateurs"].is-active {
  height: 100%;
  display: flex; align-items: stretch;
}
/* 88px de marge haute + basse mangeaient la hauteur utile : sur cet onglet on
   les reduit, ce qui rend les cartes plus grandes sans faire apparaitre de
   scroll. overflow:hidden verrouille la promesse "pas de scroll". */
.atelier__body[data-tab="realisateurs"] { padding: 28px 72px; overflow: hidden; }
.realisateurs {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 22px;
  max-width: 1180px; margin: 0 auto;
  width: 100%; height: 100%;
}
.director-card {
  border: 1px solid var(--line);
  min-height: 0;           /* sans ca, le contenu impose sa hauteur et deborde */
  display: flex; flex-direction: column;
  background: rgba(255,255,255,0.012);
  transition: transform 0.55s var(--ease), border-color 0.55s var(--ease),
              background 0.55s var(--ease), box-shadow 0.55s var(--ease),
              opacity 0.55s var(--ease), filter 0.55s var(--ease);
}

/* Meme relief que les etapes de la Methode : la carte survolee monte, l'autre
   recule. Souris uniquement : au tactile, :hover resterait colle apres le tap. */
@media (hover: hover) {
  .realisateurs:hover .director-card {
    opacity: 0.42;
    filter: saturate(0.3);
    transform: scale(0.985);
  }
  .realisateurs .director-card:hover {
    opacity: 1;
    filter: none;
    transform: translateY(-8px) scale(1.015);
    border-color: rgba(255,255,255,0.18);
    background: rgba(255,255,255,0.03);
    box-shadow: 0 24px 55px rgba(0,0,0,0.62), 0 0 45px -18px var(--pink);
    z-index: 2;
  }
}
@media (prefers-reduced-motion: reduce) {
  .director-card { transition: none; }
  .realisateurs:hover .director-card { opacity: 1; filter: none; transform: none; }
  .realisateurs .director-card:hover { transform: none; }
}
.director-card__photo { flex: 1; background: #111; position: relative; overflow: hidden; }
/* Le portrait remplit la carte ; le dégradé du bas assure la lisibilité si la
   photo est claire à cet endroit. */
/* object-position est pose en inline depuis content.json (champ photoPosition) :
   chaque portrait a son propre point d'ancrage, sinon cover recadre au centre
   et coupe les tetes des que la carte se raccourcit. */
.director-card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.director-card__photo::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(10,10,12,0.35) 0%, transparent 30%, transparent 70%, rgba(10,10,12,0.55) 100%);
}
.director-card__badge { position: absolute; top: 16px; left: 16px; z-index: 2; font-family: var(--font-mono); font-size: 0.62rem; background: var(--pink); color: #0a0a0a; padding: 4px 10px; }
.director-card__body { padding: 20px; flex: 0 0 auto; }
.director-card__role { font-family: var(--font-mono); font-size: 0.65rem; color: var(--fg-dim); letter-spacing: 0.05em; margin-bottom: 8px; }
.director-card__body h4 { font-size: 1.4rem; margin-bottom: 8px; }
.director-card__body p { color: var(--fg-dim); font-size: 0.88rem; }

/* -- Onglet Expertise -- */
.expertise { display: grid; grid-template-columns: 1.4fr 1fr; gap: 50px; align-items: start; }
.expertise__list { max-height: 640px; overflow-y: auto; padding-right: 12px; }
.expertise__item { border: 1px solid var(--line); padding: 22px; margin-bottom: 16px; }
.expertise__item .num { color: var(--pink); font-family: var(--font-mono); font-weight: 700; margin-right: 10px; }
.expertise__item h5 { display: inline; font-size: 1.05rem; }
.expertise__item p { color: var(--fg-dim); margin: 12px 0 14px; line-height: 1.6; }
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-list span { font-family: var(--font-mono); font-size: 0.68rem; border: 1px solid var(--line); padding: 5px 10px; color: var(--fg-dim); }

.testimonials-label { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.06em; color: #a679f0; margin-bottom: 18px; }
.testimonial-card { border: 1px solid var(--line); padding: 24px; margin-bottom: 16px; position: relative; }
.testimonial-card::before { content: '"'; font-family: var(--font-serif); font-size: 3rem; color: var(--line); position: absolute; top: 4px; left: 16px; }
.testimonial-card p.q { font-style: italic; color: var(--fg-dim); line-height: 1.6; margin-bottom: 14px; padding-left: 8px; }
.testimonial-card cite { font-size: 0.82rem; color: #a679f0; font-style: normal; }

/* ==========================================================================
   FORMULAIRE DE CONTACT / BRIEF
   ========================================================================== */
.panel--form { width: min(58vw, 1040px); --cat-color: var(--pink); --cat-color-dim: rgba(255,45,120,0.13); }
.brief-form { flex: 1; min-height: 0; overflow-y: auto; padding: 80px 60px 0; max-width: 760px; }
.brief-form h2 { font-size: 2rem; font-weight: 700; margin-bottom: 20px; letter-spacing: -0.005em; }
.brief-form__intro { color: var(--fg-dim); max-width: 540px; line-height: 1.8; font-size: 0.88rem; margin-bottom: 8px; }

.brief-form__section-num {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.16em;
  color: var(--pink); margin: 56px 0 24px; padding-bottom: 14px;
  border-bottom: 1px solid rgba(255,45,120,0.25);
}
.brief-form .req { color: var(--pink); }

.brief-form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; margin-bottom: 44px; }
.brief-form label {
  display: block; font-family: var(--font-mono); font-size: 0.62rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); margin-bottom: 12px;
}
.brief-form input, .brief-form textarea {
  width: 100%; margin-top: 12px;
  background: rgba(255,255,255,0.02); border: 1px solid var(--line);
  color: #fff; padding: 17px 18px; font-size: 0.82rem; border-radius: 2px;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
.brief-form input:focus, .brief-form textarea:focus { border-color: var(--pink); background: rgba(255,45,120,0.03); }
.brief-form textarea { min-height: 120px; resize: vertical; }

.brief-form__label { font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); margin: 36px 0 18px; }
.brief-form__choices { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 8px; }
.brief-form__choices--row { grid-template-columns: repeat(3, 1fr); }
.choice-btn {
  border: 1px solid var(--line); padding: 18px 20px; text-align: left;
  font-size: 0.82rem; color: var(--fg-dim); background: transparent;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
}
.brief-form__choices--row .choice-btn { text-align: center; font-family: var(--font-mono); font-size: 0.72rem; }
.choice-btn:hover { border-color: rgba(255,45,120,0.5); transform: translateY(-1px); }
.choice-btn.is-selected { border-color: var(--pink); background: rgba(255,45,120,0.08); color: var(--pink); font-weight: 600; }

.brief-form__message { margin: 52px 0; }
.brief-form__note { font-size: 0.7rem; color: var(--fg-faint); text-align: center; margin: 20px 0 72px; }

.brief-form .btn--full {
  padding: 22px; font-size: 0.82rem; letter-spacing: 0.08em;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), filter 0.25s var(--ease);
}
.brief-form .btn--full:hover { transform: translateY(-2px); box-shadow: 0 16px 40px rgba(255,45,120,0.25); filter: brightness(1.08); }

/* ==========================================================================
   ESPACE DÉCOUVERTE "LUNORFILMS" — vitrine libre, distincte du site commercial
   ========================================================================== */
.discover {
  position: fixed; inset: 0; z-index: 46;
  background: var(--bg);
  display: flex; flex-direction: column;
  opacity: 0; pointer-events: none; transform: translateY(14px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.discover.is-open { opacity: 1; pointer-events: auto; transform: translateY(0); }

.discover__header {
  position: absolute; top: 0; left: 0; right: 0; z-index: 5;
  display: flex; align-items: center; justify-content: space-between;
  padding: 26px 48px;
  background: linear-gradient(180deg, rgba(5,5,6,0.85) 0%, transparent 100%);
}
.discover__back {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em;
  color: var(--fg-dim); transition: color 0.2s var(--ease);
}
.discover__back:hover { color: #fff; }
.discover__kicker {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.16em;
  color: var(--cat-color, var(--pink)); text-transform: uppercase;
  transition: color 620ms var(--ease);
}
/* Rose fixe dans les DEUX univers (v=108) : « GALERIE DES CRÉATIONS » comme
   « GALERIE PHOTOGRAPHIQUE » restent à la marque quelle que soit la catégorie.
   La règle de base (--cat-color) ne sert plus qu'au cas par défaut ; les deux
   pages la surchargent. */
#discoverPage .discover__kicker,
#stillsPage .discover__kicker { color: var(--pink); }
.discover__header-actions { display: flex; align-items: center; gap: 12px; }
.discover__contact-btn { font-size: 0.66rem; padding: 11px 18px; }

.discover__body { flex: 1; overflow-y: auto; }

.discover__hero {
  position: relative; height: 68vh; min-height: 480px;
  display: flex; align-items: flex-end;
  padding: 0 64px 56px;
  background-size: cover; background-position: center;
  overflow: hidden;
  transition: height 2.2s var(--ease), padding-bottom 2.2s var(--ease);
}
.discover__hero-video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.discover__hero::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(5,5,6,0.15) 0%, rgba(5,5,6,0.35) 55%, var(--bg) 100%),
    linear-gradient(90deg, rgba(5,5,6,0.75) 0%, transparent 45%);
  transition: opacity 1.4s var(--ease);
}

/* --------------------------------------------------------------------------
   MODE CINÉMA — bannière d'exploration
   Quand le son est ouvert et que la souris s'arrête 1,5 s, la bannière prend
   tout l'écran : le dégradé, les textes et les rangées s'effacent, il ne reste
   que l'image et le son. Le moindre mouvement de souris ramène tout en place.
   La montée dure 2,2 s pour que ce soit un glissement, pas un basculement.
   -------------------------------------------------------------------------- */
.discover.is-cinema .discover__body { overflow: hidden; }
.discover.is-cinema .discover__hero { height: 100vh; padding-bottom: 0; }
.discover.is-cinema .discover__hero::before { opacity: 0; }
.discover__header,
.discover__brand,
.discover__hero-content,
.discover__rows,
.discover__intro {
  transition: opacity 1.4s var(--ease);
}
.discover.is-cinema .discover__header,
.discover.is-cinema .discover__brand,
.discover.is-cinema .discover__hero-content,
.discover.is-cinema .discover__rows,
.discover.is-cinema .discover__intro {
  opacity: 0; pointer-events: none;
}
.discover__hero-content { position: relative; z-index: 2; max-width: 640px; }
.discover__hero-tag {
  display: inline-block; font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.1em;
  /* Rose, et non la couleur de la categorie (v=106) : dans LunorFilms le badge
     doit rester a la marque. Le contour de "FILMS" et le kicker, eux, suivent
     toujours la categorie de la banniere — c'est --cat-color qui les porte. */
  color: var(--pink); border: 1px solid var(--pink);
  padding: 5px 12px; margin-bottom: 20px;
}
.discover__hero-title {
  font-family: var(--font-body); font-style: normal; font-weight: 700;
  font-size: clamp(1.7rem, 2.9vw, 2.8rem); line-height: 1.12;
  letter-spacing: -0.015em; color: #fff; margin-bottom: 16px;
}
.discover__hero-desc { color: var(--fg-dim); font-size: 0.95rem; line-height: 1.7; max-width: 46ch; margin-bottom: 28px; }
.discover__hero-actions { display: flex; gap: 14px; }

/* Le logotype de LunorFilms : meme construction que LunorStills (.brandmark),
   un cran plus petit — il est pose sur la banniere video, pas sur du noir, et
   il ne doit pas ecraser l'image. */
.discover__brand {
  position: absolute; top: 84px; left: 64px; z-index: 2;
}
.discover__wordmark { font-size: clamp(1.8rem, 4.4vw, 3.6rem); }
/* Le contour de « FILMS » repasse rose (v=107), quelle que soit la catégorie
   de la bannière — même logique que le kicker : la marque prime ici. Borné a
   #discoverPage pour ne pas toucher LunorStills, qui partage .brandmark. */
#discoverPage .discover__wordmark .m-line {
  -webkit-text-stroke-color: color-mix(in srgb, var(--pink) 62%, transparent);
}
/* STILLS aussi en rose fixe (v=109) : le contour de LunorStills ne suit plus la
   catégorie épinglée. Même logique que FILMS — la marque prime. Le contour de
   STUDIO CRÉATIF sur l'accueil, lui, garde --cat-color (autre élément). */
#stillsPage .stills__brand-mark .m-line {
  -webkit-text-stroke-color: color-mix(in srgb, var(--pink) 62%, transparent);
}

.discover__rows { padding: 12px 0 8px; }
.discover-row { position: relative; margin-bottom: 52px; }
.discover-row__title {
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--fg-dim); margin: 0 64px 16px;
}
/* position: relative — c'est le repere de la carte qui s'agrandit. Elle passe
   en position: absolute SANS quitter sa piste, donc elle suit le defilement
   horizontal de la rangee au lieu d'etre collee a l'ecran.

   La generosite verticale du padding n'est pas decorative : une piste qui
   defile horizontalement rogne aussi ce qui deborde en HAUT et en BAS (des
   qu'un overflow n'est pas "visible", l'autre axe cesse de l'etre). Sans cette
   marge interieure, la carte basculee serait coupee. Les marges negatives
   rendent a la mise en page les pixels que le padding lui prend : a l'oeil,
   l'espacement des rangees ne bouge pas d'un poil. */
.discover-row__track {
  position: relative;
  display: flex; gap: 14px; overflow-x: auto; scroll-behavior: smooth;
  /* LA RESERVE. Une piste qui defile horizontalement rogne aussi ce qui
     deborde en HAUT et en BAS : des qu'un overflow n'est pas "visible",
     l'autre axe cesse de l'etre. Tout ce qui sort de ce cadre est coupe NET —
     c'est ce qui tranchait la lueur a l'horizontale.
     Le compte, pour une carte basculee de 336px sur une place de 158 :
       en haut  : (336-158)/2 = 89, plus 26 de lueur       -> 115 minimum
       en bas   : 89, plus 61 d'ombre portee (26 de decalage + 35 de flou) -> 150
     D'ou 130px de rab de chaque cote, avec de la marge. Les marges negatives
     rendent ces pixels a la mise en page : a l'oeil, l'espacement des rangees
     ne bouge pas d'un cheveu. */
  padding: 136px 64px 160px; scrollbar-width: none;
  margin: -130px 0;

  /* LA PISTE NE CAPTE PLUS RIEN, SEULES LES CARTES LE FONT.
     Sans ca, cette reserve serait un piege : les pistes voisines, agrandies de
     130px chacune, se chevauchent largement, et celle du dessous — plus loin
     dans le HTML — volerait le survol des cartes de celle du dessus. Un cadre
     transparent au pointeur ne vole rien a personne.
     Le defilement a la molette continue de fonctionner : l'evenement vise la
     carte, puis remonte jusqu'a la piste qui defile. Les fleches < > sont
     soeurs de la piste, elles ne sont pas concernees. */
  pointer-events: none;
}
.discover-row__track::-webkit-scrollbar { display: none; }

.discover-row__card {
  position: relative; flex: 0 0 280px; aspect-ratio: 16/9;
  border-radius: 4px; overflow: hidden; cursor: pointer;
  /* filter fait partie de la transition : sans lui, les voisines s'eteignaient
     d'un coup sec au lieu de se retirer. Il est volontairement plus lent que
     le reste (0.5s contre 0.35s) : la couleur doit se retirer plus lentement
     que la carte ne monte, sinon le noir et blanc arrive avant le relief et
     on voit deux gestes au lieu d'un. */
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease),
              filter 0.5s var(--ease);
  background: #111;
}
.discover-row__card:hover { transform: scale(1.09); z-index: 3; }

/* Seules les cartes captent la souris ; leur piste, elle, est transparente au
   pointeur (voir .discover-row__track).
   IMPERATIF : ce "auto" est cantonne a .discover.is-open. Ecrit tel quel sur
   la carte, il RALLUMERAIT les clics alors meme que la galerie est fermee :
   .discover est en pointer-events:none au repos pour laisser passer les clics
   vers l'accueil, mais un descendant qui redit "auto" reprend la main sur cet
   ancetre. Les cartes de la galerie fermee interceptaient alors le bouton
   LunorFilms de la page d'accueil, par-dessus tout le reste. */
.discover.is-open .discover-row__card { pointer-events: auto; }

/* --------------------------------------------------------------------------
   LA LUEUR — survol d'une carte  (v=90)
   Quatre couches d'ombre, et l'ordre compte : la plus serree en premier.
     1. un liseré blanc d'un pixel, colle au cadre : c'est l'arete eclairee ;
     2. un halo blanc court, qui fait deborder cette lumiere sur le noir ;
     3. une nappe rose courte et discrete — la lueur qu'on croit voir passer
        DERRIERE la carte, comme un projecteur cache dessous. Elle se tient
        pres du cadre : sa portee compte autant que son intensite, une nappe
        large finit par teinter tout le noir de la rangee au lieu de designer
        une carte ;
     4. l'ombre portee, qui la decolle du fond.
   Tout est en box-shadow, jamais en pseudo-element : la carte est en
   overflow:hidden (les coins arrondis rognent la video), et un ::before aurait
   ete coupe net par ce meme rognage. Une ombre, elle, se peint dehors.
   -------------------------------------------------------------------------- */
.discover-row__card:hover,
.discover-row__card.is-expanded {
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.55),
    0 0 20px 1px rgba(255, 255, 255, 0.22),
    0 0 46px 3px rgba(255, 45, 120, 0.34),
    0 26px 70px rgba(0, 0, 0, 0.75);
}

/* --------------------------------------------------------------------------
   LE RETRAIT DES VOISINES
   L'assombrissement ne vient pas d'un voile pose sur la page : ce sont les
   AUTRES cartes de la rangee qui s'effacent et se troublent. Le noir se creuse
   donc autour de la carte survolee, et nulle part ailleurs — le reste de la
   page, le heros, les autres rangees ne bougent pas.
   :has() et non :hover sur la piste : avec :hover, tout s'eteignait des que la
   souris passait dans une gouttiere, sans qu'aucune carte ne s'allume.
   -------------------------------------------------------------------------- */
@media (hover: hover) {
  .discover-row__track:has(.discover-row__card:hover) .discover-row__card:not(:hover) {
    /* Les voisines se retirent, elles ne s'eteignent pas : on doit continuer a
       les lire pour savoir ou aller ensuite. Le noir et blanc est franc — le
       saturate(0.75) d'avant laissait une couleur tiede qui ne tranchait avec
       rien, alors que c'est precisement ce tranchage qui designe la carte
       vivante. Le flou reste sous la barre du lisible : un souffle, pas un
       voile. */
    filter: grayscale(1) brightness(0.52) blur(1.4px);
    transform: scale(0.98);
  }
}
.discover-row__card.is-expanded {
  /* transform fait partie de la transition. Sans lui, le scale(1.09) du survol
     retombait a 1 d'un seul coup au moment ou la bascule commencait : c'etait
     le a-coup qu'on voyait au demarrage. Les quatre cotes sont animes
     ensemble, sinon la largeur sautait pendant que la hauteur glissait. */
  transform: none !important;
  z-index: 5;
  border-radius: 6px;
  transition: left 0.45s var(--ease), top 0.45s var(--ease),
              width 0.45s var(--ease), height 0.45s var(--ease),
              transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
  will-change: width, height;
}
/* Le fantome garde la place de la carte dans la file pendant sa bascule. Ses
   dimensions sont posees en JS, d'apres la carte elle-meme. */
.discover-row__card-placeholder { flex: 0 0 280px; visibility: hidden; }
.discover-row__card img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  /* LA COULEUR AU REPOS  (v=94) — voir .card__img.
     Une rangee grise se parcourt des yeux ; une rangee en couleur se clique.
     Le noir et blanc n'a pas disparu pour autant : il est passe du repos au
     retrait, sur les voisines de la carte survolee (regle plus haut). */
  filter: brightness(0.88) saturate(1.04);
  transition: filter 0.5s var(--ease);
}
.discover-row__card:hover img { filter: brightness(1.06) saturate(1.16); }
.discover-row__preview {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 0.4s var(--ease); pointer-events: none;
  /* z-index explicite BAS (v=110). Une <video> en lecture est souvent promue
     sur sa propre couche de composition par le navigateur, et cette couche
     passe alors DEVANT des freres en z-index:1 qui, eux, ne sont pas promus —
     d'ou le tag et les infos qui disparaissaient une fraction de seconde au
     demarrage puis revenaient. On fixe la video sous les surcouches. */
  z-index: 0;
}
/* Tag, degrade et infos promus sur leur propre couche et places au-dessus de
   la video : ils ne peuvent plus etre recouverts, meme pendant la lecture. */
.discover-row__card .scrim,
.discover-row__card .tag,
.discover-row__card .info,
.discover-row__card .discover-row__hover-actions {
  z-index: 2;
  transform: translateZ(0);
}
.discover-row__card .scrim { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,0.9) 100%); }
.discover-row__card .tag {
  position: absolute; top: 10px; left: 10px;
  font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.05em;
  padding: 3px 9px; background: rgba(0,0,0,0.6); color: var(--cat-color-item, var(--pink));
  border: 1px solid var(--cat-color-item, var(--pink));
}
.discover-row__card .info { position: absolute; left: 14px; right: 14px; bottom: 12px; z-index: 1; }
.discover-row__card .director { font-family: var(--font-mono); font-size: 0.6rem; color: var(--fg-dim); margin-bottom: 3px; }
.discover-row__card .title { font-family: var(--font-body); font-weight: 700; font-size: 1rem; color: #fff; }

.discover-row__hover-actions {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  display: flex; gap: 8px;
  opacity: 0; transform: translateY(-4px);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.discover-row__card:hover .discover-row__hover-actions { opacity: 1; transform: translateY(0); }
.dr-icon-btn {
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(10,10,12,0.7); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  border: 1px solid rgba(255,255,255,0.25); color: #fff;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.dr-icon-btn:hover { border-color: var(--cat-color-item, var(--pink)); color: var(--cat-color-item, var(--pink)); transform: scale(1.1); }
.dr-icon-btn--play svg { margin-left: 1px; }

.discover-row__arrow {
  position: absolute; top: 0; bottom: 30px; width: 60px; z-index: 4;
  display: flex; align-items: center; justify-content: center;
  color: #fff; opacity: 0; transition: opacity 0.25s var(--ease);
}
.discover-row:hover .discover-row__arrow { opacity: 1; }
.discover-row__arrow--left { left: 0; background: linear-gradient(90deg, var(--bg) 15%, transparent 100%); }
.discover-row__arrow--right { right: 0; background: linear-gradient(270deg, var(--bg) 15%, transparent 100%); }

.discover__intro {
  max-width: 56ch; margin: 20px auto 90px; padding: 0 64px;
  text-align: center; font-style: italic; font-family: var(--font-serif);
  color: var(--fg-faint); font-size: 0.9rem; line-height: 1.7;
}

/* ==========================================================================
   MENU MOBILE / TABLETTE
   ========================================================================== */
.mobile-menu-btn { display: none; }

.mobile-nav {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(8,8,10,0.85);
  backdrop-filter: blur(24px) saturate(160%);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 40px;
  opacity: 0; pointer-events: none; transform: translateY(-12px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
.mobile-nav.is-open { opacity: 1; pointer-events: auto; transform: translateY(0); }
.mobile-nav__close {
  position: absolute; top: 24px; right: 24px;
  width: 42px; height: 42px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.15);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
}
.mobile-nav__links { display: flex; flex-direction: column; align-items: center; gap: 28px; }
.mobile-nav__links a {
  font-family: var(--font-body); font-weight: 700; font-size: 1.6rem; color: var(--fg-dim);
  transition: color 0.2s var(--ease);
}
.mobile-nav__links a:hover, .mobile-nav__links a.is-active { color: #fff; }
.mobile-nav__contact { font-size: 0.75rem; padding: 16px 28px; }

/* ==========================================================================
   RESPONSIVE — tablette & téléphone
   ========================================================================== */
@media (max-width: 1100px) {
  .panel__body, .video-modal__info, .manifeste, .expertise { grid-template-columns: 1fr; }
  /* LE VRAI PROBLEME : en une seule colonne, .panel__left et .panel__right
     restent chacun un bloc a defilement INDEPENDANT (overflow-y:auto sur
     l'un, sur l'autre) empile dans une grille dont le PARENT est lui-meme en
     overflow:hidden. Resultat : la fiche texte se comporte comme sa propre
     fenetre a hauteur figee, avec son propre ascenseur, AVANT meme d'arriver
     aux films — d'ou l'impression que "le texte prend toute la place".
     On n'imbrique plus qu'un seul defilement naturel, celui de .panel__body
     lui-meme : le texte, puis la grille de films, se lisent d'affilee. */
  .panel__body { overflow-y: auto; overflow-x: hidden; display: block; }
  .panel__left, .panel__right, .panel__cards { overflow: visible; }
  .panel__left { padding-right: 0; margin-bottom: 8px; }
  /* Sur petit écran, la modale portrait repasse en pile verticale (média en haut). */
  .video-modal.is-portrait { flex-direction: column; }
  .video-modal.is-portrait .video-modal__media { flex: 0 0 46%; border-right: none; }
  .video-modal.is-portrait .video-modal__tech { grid-template-columns: 1fr; }
  .steps, .team-grid, .realisateurs { grid-template-columns: repeat(2, 1fr); }
  .panel, .panel--form { width: 92vw; }

  .nav { grid-template-columns: auto auto; left: 16px; right: 16px; padding: 12px 18px; }
  .nav__links { display: none; }
  .nav__actions .icon-btn:not(.mobile-menu-btn):not(#muteToggle) { display: none; }
  .mobile-menu-btn { display: flex; }
  .nav__actions #contactTrigger { padding: 12px 14px; font-size: 0.62rem; }

  /* Sur mobile/tablette, le menu doit rester accessible sans avoir à survoler */
  .nav-zone { height: auto; }
  .nav { transform: translateY(0); opacity: 1; pointer-events: auto; position: relative; top: 16px; margin: 0 16px; left: 0; right: 0; }
  .hover-hint { display: none; }
}

/* ==========================================================================
   NOUVEAUX ÉLÉMENTS : badge Instagram, galerie photo, sous-groupe de portfolio
   ========================================================================== */
.ig-badge {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--cat-color, var(--pink)); font-size: 0.72rem;
}
.card__featuring { font-size: 0.78rem; color: var(--fg-dim); margin-top: 4px; }

.panel__group-label {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--cat-color, var(--pink)); margin: 28px 0 14px; padding-top: 20px; border-top: 1px solid var(--line);
}

.photo-gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.photo-gallery__item { position: relative; border-radius: 4px; overflow: hidden; aspect-ratio: 3/4; }
.photo-gallery__item img { width: 100%; height: 100%; object-fit: cover; }
/* Le bloc entier est cale en bas, pas chaque paragraphe : sinon le titre et la
   description se superposeraient au meme endroit. */
.photo-gallery__cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 10px;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.9));
}
.photo-gallery__cap p {
  font-size: 0.68rem; color: #fff; font-family: var(--font-mono);
}
.photo-gallery__desc {
  margin-top: 4px;
  font-family: var(--font-body) !important; font-size: 0.66rem !important;
  font-weight: 300; line-height: 1.55; color: rgba(255,255,255,0.62) !important;
}

.video-modal__featuring { margin: -10px 0 24px; }
.video-modal__featuring .featuring-name { font-weight: 700; font-size: 1rem; margin-right: 14px; }
.video-modal__featuring .ig-followers { color: var(--fg-dim); }
.video-modal__featuring .featuring-note { color: var(--fg-dim); font-size: 0.82rem; margin-top: 10px; line-height: 1.5; }

/* --------------------------------------------------------------------------
   À L'AFFICHE — fiche Instagram des artistes
   Une fiche par personne : un film peut en réunir deux (CLS & Heatfool,
   Wazy & Seyes). La couleur de catégorie tient tout : glyphe, pseudo, bordure
   au survol. Pas de logo Instagram en couleurs — le dégradé de la marque
   jurerait avec la DA, le glyphe seul suffit à se faire reconnaître.
   -------------------------------------------------------------------------- */
.talents { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.talent {
  display: flex; align-items: center; gap: 16px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  background: rgba(255,255,255,0.02);
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
.talent:hover { border-color: var(--cat-color, var(--pink)); background: rgba(255,255,255,0.05); }
.talent__ig {
  flex-shrink: 0;
  width: 42px; height: 42px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--cat-color, var(--pink));
  color: var(--cat-color, var(--pink));
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.talent:hover .talent__ig { background: var(--cat-color, var(--pink)); color: #0a0a0a; }
.talent__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.talent__top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.talent__name { font-weight: 700; font-size: 0.95rem; color: #fff; }
.talent__handle {
  font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.02em;
  color: var(--cat-color, var(--pink));
}
.talent__meta {
  font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.06em;
  color: var(--fg-dim);
}
.talent__note { font-size: 0.76rem; font-style: italic; color: var(--fg-dim); line-height: 1.45; }
.talent__arrow {
  flex-shrink: 0; margin-left: auto; color: var(--fg-faint);
  transition: color 0.25s var(--ease), transform 0.25s var(--ease);
}
.talent:hover .talent__arrow { color: var(--cat-color, var(--pink)); transform: translate(2px, -2px); }

#videoModalInstagram { margin-top: 24px; }
.ig-cta {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 13px 20px; border-radius: 3px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--cat-color, var(--pink)) 18%, transparent), transparent);
  border: 1px solid var(--cat-color, var(--pink));
  color: #fff; font-family: var(--font-mono); font-size: 0.78rem;
  transition: background 0.25s var(--ease), transform 0.25s var(--ease);
}
.ig-cta svg { color: var(--cat-color, var(--pink)); flex-shrink: 0; }
.ig-cta strong { color: var(--cat-color, var(--pink)); font-weight: 600; }
.ig-cta em { color: var(--fg-dim); font-style: normal; }
.ig-cta .ig-cta__arrow { opacity: 0; margin-left: -6px; transition: opacity 0.25s var(--ease), margin 0.25s var(--ease); }
.ig-cta:hover { background: color-mix(in srgb, var(--cat-color, var(--pink)) 20%, transparent); transform: translateX(2px); }
.ig-cta:hover .ig-cta__arrow { opacity: 1; margin-left: 0; }

/* ==========================================================================
   ÉPINGLAGE, SEUIL & OBTURATEUR  (refonte navigation)
   ========================================================================== */

/* --- 1. MENU : survolé = éclairci, épinglé = soulignement à la couleur --- */
/* LE POINT EST PARTI  (v=98)
   Il disait la meme chose que le soulignement, 7px plus bas et en plus petit.
   Deux marques pour un seul etat, c'est une de trop : le trait suffit — il
   reste allume sur le lien epingle quand la souris est ailleurs, ce qui est
   exactement l'information que le point apportait.
   La regle .is-active::after part avec lui : le soulignement lit maintenant
   --link-color des le repos (voir .nav__link::after), elle ne faisait plus que
   redire ce qui etait deja vrai. */

/* --- 2. LE SEUIL --- */
.seuil {
  position: relative; display: inline-flex; align-items: center; gap: 16px;
  padding: 15px 26px 15px 22px; cursor: pointer; overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--cat-color) 45%, transparent);
  background: rgba(0,0,0,0.28);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  color: #fff; font-family: var(--font-mono);
  font-size: 0.63rem; letter-spacing: 0.2em; text-transform: uppercase;
  transition: border-color 0.45s var(--ease), background 0.45s var(--ease), transform 0.45s var(--ease);
}
.seuil::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--cat-color); box-shadow: 0 0 14px var(--cat-color);
  transform: scaleY(0.25); transition: transform 0.5s var(--ease);
}
.seuil:hover { background: rgba(0,0,0,0.5); transform: translateX(4px);
  border-color: color-mix(in srgb, var(--cat-color) 85%, transparent); }
.seuil:hover::before { transform: scaleY(1); }
.seuil__arrow { display: flex; align-items: center; gap: 3px; }
.seuil__arrow i { width: 5px; height: 1px; background: var(--cat-color); transition: width 0.45s var(--ease); }
.seuil:hover .seuil__arrow i { width: 16px; }

/* Le coucou : le panneau sort de 34px, tranche allumée à la couleur de la catégorie */
.panel.is-peek {
  transform: translateX(calc(100% - 34px));
  box-shadow: -14px 0 60px rgba(0,0,0,0.8),
              -1px 0 0 color-mix(in srgb, var(--cat-color) 70%, transparent),
              0 0 44px color-mix(in srgb, var(--cat-color) 22%, transparent);
}
/* L'Atelier est plein écran : son coucou est un fondu fantôme, pas un glissement */
.atelier.is-peek { opacity: 0.16; transform: translateY(6px); pointer-events: none; }

/* --- 3. L'OBTURATEUR --- */
.shutter-rule {
  position: absolute; left: 0; right: 0; bottom: 74px; height: 1px; z-index: 24;
  background: rgba(255,255,255,0.16); pointer-events: none;
}
.shutter-rule::before, .shutter-rule::after {
  content: ''; position: absolute; top: -1px; height: 3px; width: 0;
  transition: width 0.55s var(--ease);
}
/* La tranche LunorFilms prend la couleur de la catégorie épinglée (rose sur
   Accueil). La tranche LunorStills garde son blanc cassé : c'est l'argentique,
   il ne suit pas l'ambiance. */
.shutter-rule::before {
  left: 0; background: var(--cat-color, var(--pink));
  box-shadow: 0 0 12px var(--cat-color, var(--pink));
  transition: width 0.55s var(--ease), background-color 620ms var(--ease), box-shadow 620ms var(--ease);
}
.shutter-rule::after { right: 0; background: #e8e2d4; box-shadow: 0 0 12px rgba(232,226,212,0.7); }
.hero.hov-films .shutter-rule::before { width: 50%; }
.hero.hov-stills .shutter-rule::after { width: 50%; }
.shutter-rule i {
  position: absolute; left: 50%; top: 50%; width: 7px; height: 7px;
  border: 1px solid rgba(255,255,255,0.45); background: var(--bg);
  transform: translate(-50%,-50%) rotate(45deg); transition: transform 0.5s var(--ease), border-color 0.5s var(--ease);
}
.hero.hov-films .shutter-rule i, .hero.hov-stills .shutter-rule i {
  transform: translate(-50%,-50%) rotate(135deg) scale(1.5); border-color: #fff;
}

.shutter { position: absolute; left: 0; right: 0; bottom: 0; height: 74px; z-index: 25; display: flex; }
.shutter__half {
  flex: 1; display: flex; align-items: center; padding: 0 48px;
  background: none; border: 0; cursor: pointer;
}
.shutter__half--films { justify-content: flex-start; }
.shutter__half--stills { justify-content: flex-end; }
.shutter__label {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: rgba(255,255,255,0.42);
  transition: color 0.45s var(--ease), transform 0.45s var(--ease), letter-spacing 0.45s var(--ease);
}
.shutter__label b { font-weight: 500; color: rgba(255,255,255,0.72); transition: color 0.45s var(--ease); }
.shutter__label em { font-style: normal; opacity: 0.4; }
.shutter__half:hover .shutter__label { color: #fff; transform: translateY(-5px); letter-spacing: 0.3em; }
.shutter__half--films:hover .shutter__label b { color: var(--cat-color, var(--pink)); }
.shutter__half--stills:hover .shutter__label b { color: #e8e2d4; }

/* L'image arrêtée : le hero se fige réellement (les vidéos sont mises en pause en JS) */
/* transform 1.6s AJOUTE (v=112) : cette regle, plus bas dans la cascade que
   celle de .hero__cell video (« filter/transform 1.6s »), l'ecrasait et
   OUBLIAIT transform. Du coup le scale(1.03) des colonnes voisines, applique a
   l'ouverture du son (.has-sound), sautait d'un coup au lieu de glisser : un
   micro-zoom/dezoom instantane a chaque clic sur un bouton de son. Retabli ici
   a 1,6 s, comme prevu par la regle du triptyque. Le palier immersif (is-idle)
   garde son transition-duration: 1s propre. */
.hero__video, .hero__cell video, .hero__ambilight { transition: filter 0.5s var(--ease), opacity 0.7s var(--ease), transform 1.6s var(--ease); }
.hero.is-frozen .hero__video, .hero.is-frozen .hero__cell video {
  filter: contrast(1.12) saturate(0.55) brightness(0.9);
}
.hero__grain {
  position: absolute; inset: 0; z-index: 3; opacity: 0; pointer-events: none;
  transition: opacity 0.45s var(--ease); mix-blend-mode: overlay;
  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='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.hero.is-frozen .hero__grain { opacity: 0.5; }
/* Le cadre de visee doit rester dans l'IMAGE. Son bas s'arrete au-dessus de la
   bande de l'obturateur (74px) : sinon les coins du bas tombent a cheval sur la
   ligne et sur les libelles LunorFilms / LunorStills. */
.hero__frame { position: absolute; inset: 6vh 5vw 100px; z-index: 4; opacity: 0; pointer-events: none; transition: opacity 0.45s var(--ease); }
.hero.is-frozen .hero__frame { opacity: 1; }
.hero__frame span { position: absolute; width: 28px; height: 28px; border: 1px solid rgba(255,255,255,0.7); }
.hero__frame span:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.hero__frame span:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
.hero__frame span:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
.hero__frame span:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }

/* --- ESPACE LUNORSTILLS --- */
.stills {
  position: fixed; inset: 0; z-index: 46; background: var(--bg);
  display: flex; flex-direction: column;
  opacity: 0; pointer-events: none; transform: translateY(14px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.stills.is-open { opacity: 1; pointer-events: auto; transform: translateY(0); }
/* 6vw et non 5vw : la marge doit valoir la gouttiere, sinon les photos de bord
   sont plus serrees contre l'ecran qu'elles ne le sont entre elles, et la
   galerie parait poussee vers l'exterieur. */
.stills__body { flex: 1; overflow-y: auto; padding: 120px 6vw 90px; }
.stills__intro { max-width: 980px; margin-bottom: 60px; }

/* --------------------------------------------------------------------------
   LE LOGOTYPE DES DEUX UNIVERS  (v=103)
   LunorStills et LunorFilms sont deux entites, pas deux sous-pages : elles ont
   le meme logotype, construit comme le hero d'accueil — LUNOR plein, le reste
   en contour a la couleur de la categorie epinglee. Meme regle pour les deux,
   une seule classe : si l'une bouge, l'autre suit.
   -------------------------------------------------------------------------- */
.brandmark {
  display: block;
  font-family: var(--font-body); font-weight: 900;
  line-height: 0.86; letter-spacing: -0.03em; text-transform: uppercase;
  white-space: nowrap;
}
.brandmark .m-fill { color: #fff; }
.brandmark .m-line {
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(255,255,255,0.3);
  -webkit-text-stroke-color: color-mix(in srgb, var(--cat-color, var(--pink)) 62%, transparent);
  transition: -webkit-text-stroke-color 620ms var(--ease);
}
.brandmark__tag {
  display: flex; align-items: center; gap: 10px; margin-top: 18px;
  font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.26em;
  text-transform: uppercase; color: var(--fg-faint);
}
.brandmark__tag::before {
  content: ''; width: 26px; height: 1px; background: var(--accent); flex: none;
}

.stills__brand { margin-bottom: 26px; }
.stills__brand-mark { font-size: clamp(2.6rem, 7vw, 6rem); }

/* Le titre passe en Inter : c'est la typo du site. Le serif italique en faisait
   une citation posee sur la page, pas le titre de la maison. */
.stills__title {
  font-family: var(--font-body); font-style: normal; font-weight: 800;
  font-size: clamp(1.5rem, 2.4vw, 2.1rem); line-height: 1.15;
  letter-spacing: -0.01em; color: #fff; margin: 30px 0 16px;
}
.stills__desc { color: var(--fg-dim); font-size: 0.95rem; line-height: 1.75; font-weight: 300; max-width: 620px; }

/* --------------------------------------------------------------------------
   PHOTOS VEDETTES
   Une rangee justifiee : hauteur commune, largeurs proportionnelles au ratio.
   flex-grow: var(--r) sur une base 0 -> les largeurs sont dans le rapport exact
   des proportions, donc aucune photo n'est recadree et toutes ont EXACTEMENT
   la meme hauteur. Les hauts sont alignes par construction, pas par chance.
   -------------------------------------------------------------------------- */
.stills__featured { margin-bottom: 74px; }
.stills__head {
  display: flex; align-items: center; gap: 18px;
  margin-bottom: 22px;
}
.stills__head-title {
  font-family: var(--font-body); font-weight: 700; font-size: 0.95rem;
  letter-spacing: 0.02em; color: #fff; flex: none;
}
.stills__head-rule { flex: 1; height: 1px; background: rgba(255,255,255,0.1); }
.stills__head-num {
  font-family: var(--font-mono); font-size: 0.55rem; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--fg-faint); flex: none;
}
.stills__featured-row {
  display: flex; align-items: flex-start; gap: 14px;
  height: clamp(300px, 46vh, 560px);
}
.still--feat {
  --r: 1.5;
  flex: var(--r) 1 0; min-width: 0; width: auto; height: 100%;
  aspect-ratio: auto;
  position: relative; padding: 0; border: 0; background: #0d0d0f;
  cursor: zoom-in; overflow: hidden;
  opacity: 0; transition: opacity 0.7s var(--ease), transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
.still--feat.is-loaded { opacity: 1; }
.still--feat.is-dead { display: none; }
.still--feat img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 0.9s var(--ease), filter 0.5s var(--ease); }
.still--feat .still__caption { padding: 30px 12px 10px; }
@media (hover: hover) {
  .stills__featured-row:has(.still--feat:hover) .still--feat img { filter: brightness(0.38) saturate(0.75); }
  .stills__featured-row:has(.still--feat:hover) .still--feat:hover img { filter: none; transform: scale(1.03); }
  .still--feat.is-loaded:hover {
    z-index: 2;
    box-shadow: 0 28px 64px -14px rgba(0,0,0,0.9), 0 0 0 1px rgba(255,255,255,0.16);
  }
}
.still--feat:hover .still__caption, .still--feat:focus-visible .still__caption { opacity: 1; transform: none; }
.still--feat:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
@media (max-width: 900px) {
  .stills__featured-row { height: 34vh; gap: 8px; }
}

/* ==========================================================================
   GALERIE LUNORSTILLS  (v=81)
   Une seule suite continue, sans categorie. Mise en page "justifiee" : chaque
   photo garde ses proportions exactes et les rangees se calent pile sur la
   largeur — comme une planche-contact, pas une grille qui recadre tout au
   carre.

   Comment ca tient sans une ligne de JS de mise en page :
     flex-basis: --r x hauteur-cible   -> la place demandee, proportionnelle
     flex-grow:  --r                    -> le rab se repartit dans la meme
                                           proportion
   Resultat : dans une rangee, toutes les photos sont etirees du meme facteur,
   donc elles ont toutes la meme hauteur, et aucune n'est deformee. La rangee
   se recalcule seule au redimensionnement, sans ResizeObserver.
   --r est pose en JS depuis content.json (ou lu sur l'image si l'intranet ne
   l'a pas encore mesure).
   ========================================================================== */
.stills__count {
  font-family: var(--font-mono); font-size: 0.55rem; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--fg-faint);
  padding-bottom: 14px; margin-bottom: 26px;
  border-bottom: 1px solid rgba(255,255,255,0.07);
}

/* Mosaique en colonnes. Chaque photo garde ses proportions exactes et remplit
   la largeur de sa colonne ; les colonnes s'equilibrent toutes seules en
   hauteur. Aucun recadrage, aucun trou a droite, aucune ligne de JS.

   Le multi-colonnes CSS fait tout : column-count pose le nombre de colonnes,
   break-inside: avoid interdit de couper une photo en deux, et le navigateur
   repartit la pile pour que les colonnes finissent a la meme hauteur.
   --r (le ratio, pose en JS depuis content.json) sert a reserver la hauteur
   AVANT que l'image arrive : c'est ce qui supprime les sauts au chargement.

   A savoir : la lecture se fait en colonnes, de haut en bas, puis colonne
   suivante — et non de gauche a droite. Pour une galerie d'ambiance, sans
   ordre narratif, c'est sans consequence ; c'est le prix de la mosaique. */
/* LES PROPORTIONS DE LA RÉFÉRENCE  (v=100)
   Mesure faite sur les captures, les deux a l'echelle 1:1 (fenetre 2380px) :
     thewildstrawberry -> 3 colonnes de 602px, gouttieres ET marges de 143px
     LunorStills       -> 4 colonnes de 524px, gouttieres de 14px
   143/2380 = 6.0vw exactement, pour la gouttiere comme pour la marge : chez
   eux, le vide entre deux photos vaut le vide qui borde la page.
   On ne fige donc pas des pixels mais le RAPPORT.

   POURQUOI PLUS DE column-count  (v=101)
   Le multi-colonnes CSS ne garantit pas que les colonnes DEMARRENT a la meme
   ligne : des qu'un element ne tient pas dans la colonne courante, le
   navigateur reporte le fragment — et la marge du report se retrouve en haut
   de la colonne suivante. C'est le decalage visible sur la capture : la
   colonne du milieu commencait 6vw plus bas que ses voisines.
   Les colonnes sont maintenant de vraies piles (une <div> par colonne), et
   `align-items: flex-start` les fait toutes partir de la meme ligne. Le
   remplissage se fait en JS, photo par photo, dans la colonne la plus courte
   a cet instant : les colonnes finissent a des hauteurs proches, comme avant,
   mais les hauts sont alignes par construction. */
.stills__gallery {
  display: flex;
  align-items: flex-start;
  gap: 6vw;
}
.stills__col {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
  gap: 6vw;
}
@media (max-width: 1100px) { .stills__gallery, .stills__col { gap: 5vw; } }
@media (max-width: 700px)  { .stills__gallery { gap: 0; } .stills__col { gap: 7vw; } }

.still {
  --r: 1.5;
  position: relative; display: block; padding: 0;
  /* La gouttiere verticale vaut l'horizontale : c'est le `gap` de la pile qui
     s'en charge maintenant, plus une marge sur la carte. */
  width: 100%; margin: 0;
  border: 0; background: #0d0d0f; cursor: zoom-in; overflow: hidden;
  aspect-ratio: var(--r);
  opacity: 0; transform: translateY(10px);
  /* opacite lente : c'est l'arrivee de la photo dans la mosaique.
     transform/ombre rapides : c'est le relief au survol, il doit repondre. */
  transition: opacity 0.7s var(--ease), transform 0.45s var(--ease),
              box-shadow 0.45s var(--ease);
}
.still.is-loaded { opacity: 1; transform: none; }
.still.is-dead { display: none; }   /* image absente : on ne laisse pas de trou */

/* --------------------------------------------------------------------------
   LA PHOTO EST INTACTE AU REPOS  (v=88)
   Plus aucun filtre par defaut : ce que l'oeil voit, c'est l'etalonnage du
   photographe, pas une couche de contraste posee par le site. Une galerie qui
   n'ose montrer ses photos qu'au survol ne sert a rien.
   Le contraste ne vient plus de la photo survolee : il vient des AUTRES, qui
   s'effacent autour d'elle. C'est le principe de la table lumineuse — on
   n'eclaire pas le tirage qu'on regarde, on eteint ceux d'a cote.
   -------------------------------------------------------------------------- */
.still img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 0.9s var(--ease), filter 0.5s var(--ease);
}

.still__caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 34px 14px 12px; text-align: left;
  background: linear-gradient(transparent, rgba(0,0,0,0.88));
  font-family: var(--font-mono); font-size: 0.55rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(255,255,255,0.8);
  opacity: 0; transform: translateY(6px); pointer-events: none;
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.still:hover .still__caption, .still:focus-visible .still__caption { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   LE RELIEF — survol d'une photo
   Trois choses ensemble, sinon ca ne decolle pas :
     1. les VOISINES tombent a 38 % de luminosite et se desaturent un peu ;
     2. la photo survolee monte de 6px, grandit de 2 % et passe en z-index 2 —
        elle sort donc de la mosaique au lieu de pousser ses voisines (une
        transformation ne deplace rien dans la mise en page) ;
     3. une ombre portee large et une fine arete claire sur le pourtour : c'est
        elle qui donne l'epaisseur, l'ombre seule ne fait que salir le noir.
   Le tout est verrouille sur (hover: hover) : au doigt, il n'y a pas de survol,
   et une photo resterait allumee apres le tap.
   -------------------------------------------------------------------------- */
@media (hover: hover) {
  /* Les autres photos ne s'assombrissent plus au survol (v=106) : dans la
     mosaique, une photo mise en avant en eteignant toutes les autres faisait
     "vitrine", pas galerie. Seule la photo survolee reagit, par son propre
     relief plus bas. Les VEDETTES gardent l'effet (regle .stills__featured-row
     plus haut) : la, l'assombrissement des voisines est voulu. */
  .stills__gallery .still:hover img {
    transform: scale(1.03);
  }
  .still.is-loaded:hover,
  .still.is-loaded:focus-visible {
    z-index: 2;
    transform: translateY(-6px) scale(1.02);
    box-shadow:
      0 28px 64px -14px rgba(0,0,0,0.9),
      0 6px 18px rgba(0,0,0,0.5),
      0 0 0 1px rgba(255,255,255,0.16);
  }
}
/* Au clavier, la photo focalisee se detache aussi, meme sans souris. */
.still.is-loaded:focus-visible img { filter: none; }

/* Le liseré rose ne sert qu'au clavier : a la souris, la photo se suffit. */
.still::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border: 1px solid transparent; transition: border-color 0.35s var(--ease);
}
.still:focus-visible { outline: none; }
.still:focus-visible::after { border-color: var(--accent); }

/* ==========================================================================
   VISIONNEUSE LUNORSTILLS  (v=81)
   ========================================================================== */
.stills__lightbox {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 0.3s var(--ease);
}
.stills__lightbox.is-open { opacity: 1; pointer-events: auto; }
.stills__lightbox[hidden] { display: none; }

/* Fond separe du conteneur : c'est LUI qui recoit le clic "je veux sortir".
   Un clic sur la photo ne doit pas fermer — on peut vouloir la regarder.
   (v=106) On ne masque plus le site : on le LAISSE VOIR, floute et assombri
   derriere la photo. L'ancien rgba(4,4,5,0.96) etait un cache quasi opaque —
   autant dire du noir. Ici 0.62 laisse transparaitre la page, et le blur en
   fait un arriere-plan sourd. C'est une photo posee SUR le site, pas un plein
   ecran deguise (le vrai plein ecran est un bouton, plus bas). */
.stills__lightbox-bg {
  position: absolute; inset: 0; background: rgba(4,4,5,0.62);
  -webkit-backdrop-filter: blur(20px) saturate(0.9) brightness(0.55);
  backdrop-filter: blur(20px) saturate(0.9) brightness(0.55);
  cursor: zoom-out;
}
.stills__lightbox-figure {
  position: relative; margin: 0; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
}
.stills__lightbox-stage { position: relative; display: flex; align-items: center; justify-content: center; }
/* --------------------------------------------------------------------------
   LA PHOTO PREND L'ECRAN  (v=101)
   Avant : 88vw x 76vh, et la legende, posee SOUS l'image dans la meme colonne,
   lui prenait encore sa hauteur. La photo occupait la moitie de l'ecran.
   Maintenant la legende est en surimpression (HUD) : elle ne coute plus un
   pixel a l'image, qui monte a 96vw x 94vh. En plein ecran reel, elle prend
   tout. Le HUD dort tant que la souris ne bouge pas.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   LA PHOTO EST GRANDE, PAS PLEIN ECRAN  (v=106)
   Par defaut elle occupe 88vw x 82vh : large, mais posee sur le site qu'on
   voit floute derriere. Le vrai plein ecran (100vw/100vh, site masque) est un
   BOUTON dans le HUD — l'utilisateur choisit. Avant, l'ouverture basculait
   d'office en plein ecran reel ; ce n'est plus le cas.

   LE SELECTEUR EST BORNE A LA SCENE
   Il visait avant TOUTES les images de la visionneuse et leur imposait
   opacity:0 (que seule .is-in de la grande image levait) — les vignettes du
   mini-carrousel restaient donc noires. Borne a .stills__lightbox-stage img.
   Ne pas reelargir. */
.stills__lightbox-stage img {
  max-width: 88vw; max-height: 82vh; object-fit: contain; display: block;
  box-shadow: 0 40px 120px rgba(0,0,0,0.7);
  opacity: 0; transform: scale(0.985);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.stills__lightbox-stage img.is-in { opacity: 1; transform: none; }

/* Attente : un simple anneau qui tourne, visible seulement si le reseau traine. */
.stills__lightbox-spin {
  position: absolute; width: 26px; height: 26px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.15); border-top-color: var(--accent);
  opacity: 0; transition: opacity 0.2s var(--ease) 0.25s;
}
.stills__lightbox-spin.is-on { opacity: 1; animation: stills-spin 0.8s linear infinite; }
@keyframes stills-spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   LE HUD — legende + mini-carrousel
   Il ne s'affiche que quand la souris bouge, et se retire seul apres 2,4 s
   d'immobilite : au repos, il n'y a que la photo. La remontee se fait en
   translation + fondu, sur la meme courbe que le reste du site.
   ========================================================================== */
.stills__hud {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  padding: 70px 24px 26px;
  background: linear-gradient(transparent, rgba(0,0,0,0.55) 42%, rgba(0,0,0,0.86));
  opacity: 0; transform: translateY(22px); pointer-events: none;
  transition: opacity 0.5s var(--ease), transform 0.6s var(--ease);
}
.stills__hud.is-awake { opacity: 1; transform: none; pointer-events: auto; }

.stills__lightbox-bar {
  display: flex; align-items: center; justify-content: center;
  gap: 18px; flex-wrap: wrap; max-width: 60ch; text-align: center;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.18em;
  text-transform: uppercase;
}
/* La description sort du mono en capitales : c'est une phrase, elle doit se
   lire comme une phrase — d'ou le retour a la ligne force (flex-basis 100%). */
.stills__lightbox-desc {
  flex-basis: 100%;
  font-family: var(--font-body, inherit); font-size: 0.85rem; font-weight: 300;
  letter-spacing: 0; text-transform: none; line-height: 1.7;
  color: var(--fg-dim); margin-top: 4px;
}
.stills__lightbox-desc[hidden] { display: none; }
.stills__lightbox-caption { color: var(--fg-dim); }
.stills__lightbox-counter { color: var(--fg-faint); }
.stills__lightbox-counter::before { content: '— '; }

/* Le mini-carrousel : la photo ouverte, puis les suivantes. Aucune barre de
   defilement — la fenetre glisse toute seule avec la lecture. */
.stills__strip { display: flex; align-items: center; gap: 8px; }
.stills__strip-item {
  --r: 1.5;
  position: relative; flex: none; padding: 0; border: 0; background: #0d0d0f;
  height: 64px; width: calc(64px * var(--r));
  overflow: hidden; cursor: pointer; opacity: 0.42;
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease),
              box-shadow 0.35s var(--ease), height 0.35s var(--ease), width 0.35s var(--ease);
}
.stills__strip-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stills__strip-item:hover { opacity: 0.85; transform: translateY(-3px); }
.stills__strip-item:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent); opacity: 1; }
/* La photo en cours : plus haute, pleine opacite, liseree a la couleur de
   marque. On voit d'un coup d'oeil ou on en est dans la serie. */
.stills__strip-item.is-current {
  opacity: 1; height: 82px; width: calc(82px * var(--r));
  box-shadow: 0 0 0 1px var(--accent), 0 10px 26px rgba(0,0,0,0.6);
}

@media (max-width: 700px) {
  .stills__strip { display: none; }   /* au doigt, le balayage suffit */
  .stills__hud { padding: 50px 16px 18px; }
}

.stills__lightbox-close, .stills__lightbox-nav, .stills__lightbox-fs {
  position: absolute; z-index: 2; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(12,12,14,0.55);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  color: #fff; display: flex; align-items: center; justify-content: center;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease), transform 0.3s var(--ease), opacity 0.3s var(--ease);
}
.stills__lightbox-close:hover, .stills__lightbox-nav:hover, .stills__lightbox-fs:hover {
  background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.45);
}
.stills__lightbox-close:focus-visible, .stills__lightbox-nav:focus-visible, .stills__lightbox-fs:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}
.stills__lightbox-close { top: 26px; right: 32px; width: 40px; height: 40px; }
.stills__lightbox-close:hover { transform: rotate(90deg); }
/* Le bouton plein ecran, sous la croix. Il montre l'icone "entrer" par defaut,
   et "sortir" quand on EST en plein ecran (la classe .is-fs est posee en JS au
   fullscreenchange). */
.stills__lightbox-fs { top: 74px; right: 32px; width: 40px; height: 40px; }
.stills__lightbox-fs .ic-exit { display: none; }
.stills__lightbox.is-fs .stills__lightbox-fs .ic-enter { display: none; }
.stills__lightbox.is-fs .stills__lightbox-fs .ic-exit { display: block; }
.stills__lightbox-nav { top: 50%; margin-top: -25px; width: 50px; height: 50px; }
.stills__lightbox-nav--prev { left: 24px; }
.stills__lightbox-nav--next { right: 24px; }
.stills__lightbox-nav:hover { transform: scale(1.08); }
.stills__lightbox-nav[hidden] { display: none; }
/* Croix, fleches et plein ecran suivent le HUD : au repos, il ne reste que la photo. */
.stills__lightbox.is-idle .stills__lightbox-close,
.stills__lightbox.is-idle .stills__lightbox-fs,
.stills__lightbox.is-idle .stills__lightbox-nav { opacity: 0; pointer-events: none; }

/* Plein ecran reel : plus rien derriere, la photo prend tout le cadre. */
.stills__lightbox:fullscreen .stills__lightbox-bg,
.stills__lightbox:-webkit-full-screen .stills__lightbox-bg { background: #050506; }
.stills__lightbox:fullscreen .stills__lightbox-stage img { max-width: 100vw; max-height: 100vh; box-shadow: none; }
.stills__lightbox:-webkit-full-screen .stills__lightbox-stage img { max-width: 100vw; max-height: 100vh; box-shadow: none; }

/* Sur telephone il n'y a pas de survol : les fleches encombrent, et le
   balayage fait deja le travail. On ne garde que la croix. */
@media (max-width: 700px) {
  .stills__lightbox-nav { display: none; }
  .stills__lightbox-stage img { max-width: 96vw; max-height: 86vh; }
  .stills__lightbox-close { top: 16px; right: 16px; }
  .stills__lightbox-bar { flex-direction: column; gap: 6px; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .still, .still img, .stills__lightbox-stage img, .stills__lightbox-close, .stills__lightbox-nav {
    transition-duration: 0.01ms !important;
  }
  .still { opacity: 1; transform: none; }
  .stills__lightbox-spin.is-on { animation: none; }
}

@media (max-width: 900px) {
  .hero { padding-bottom: 40px; }
  .hero__meta { position: static; text-align: left; margin-top: 28px; }
  .shutter { height: auto; position: static; padding: 22px 0 8px; }
  /* LE BUG : flex:1 ne force pas un flex-item a retrecir sous la largeur de
     son propre contenu (min-width:auto par defaut) — sur un ecran etroit,
     chaque moitie gardait donc sa largeur "naturelle" (calculee sur un texte
     qui ne passe jamais a la ligne), et les deux moities cumulees debordaient
     largement du cadre. min-width:0 autorise enfin le retrecissement ; le
     texte, lui, est deja en white-space normal et peut passer a la ligne. */
  .shutter__half { padding: 10px 20px; min-width: 0; }
  .shutter__half--stills { justify-content: flex-start; }
  .shutter-rule { display: none; }
  .stills__body { padding: 100px 6vw 60px; }

  /* Le label ne repose plus sur un survol pour devenir lisible (il n'y en a
     pas au doigt) : la couleur de repos doit donc suffire toute seule. Le
     flex d'origine (gap entre 3 segments inline) devient un simple bloc qui
     laisse le texte s'enrouler naturellement dans la largeur disponible. */
  .shutter__label {
    display: block;
    color: rgba(255,255,255,0.7);
    letter-spacing: 0.12em;
    line-height: 1.5;
  }
  .shutter__label b { color: #fff; }
  .shutter__label em { margin: 0 5px; opacity: 0.55; }
}
@media (prefers-reduced-motion: reduce) {
  .seuil, .panel, .shutter__label, .still img { transition-duration: 0.01ms !important; }
}

/* ==========================================================================
   FONDS VIDEO : aucun bouton de lecture, jamais
   Safari/iOS affiche un gros bouton play par-dessus une video quand la lecture
   automatique est refusee ou que le fichier ne se charge pas. Sur un fond,
   ce bouton n'a aucun sens : on le supprime. Le lecteur de la modale video
   (#videoModalPlayer) n'est pas concerne, lui garde ses controles.
   ========================================================================== */
.hero__video,
.hero__ambilight,
.hero__cell video,
.discover__hero-video {
  pointer-events: none;
}
/* Un seul pseudo-element inconnu dans une liste separee par des virgules
   invalide TOUTE la regle. Chaque pseudo-element a donc sa propre regle :
   si le navigateur n'en connait pas un, les autres s'appliquent quand meme. */
.hero__video::-webkit-media-controls,
.hero__ambilight::-webkit-media-controls,
.hero__cell video::-webkit-media-controls,
.discover__hero-video::-webkit-media-controls {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-enclosure,
.hero__ambilight::-webkit-media-controls-enclosure,
.hero__cell video::-webkit-media-controls-enclosure,
.discover__hero-video::-webkit-media-controls-enclosure {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-panel,
.hero__ambilight::-webkit-media-controls-panel,
.hero__cell video::-webkit-media-controls-panel,
.discover__hero-video::-webkit-media-controls-panel {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-panel-container,
.hero__ambilight::-webkit-media-controls-panel-container,
.hero__cell video::-webkit-media-controls-panel-container,
.discover__hero-video::-webkit-media-controls-panel-container {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-container,
.hero__ambilight::-webkit-media-controls-container,
.hero__cell video::-webkit-media-controls-container,
.discover__hero-video::-webkit-media-controls-container {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-overlay-enclosure,
.hero__ambilight::-webkit-media-controls-overlay-enclosure,
.hero__cell video::-webkit-media-controls-overlay-enclosure,
.discover__hero-video::-webkit-media-controls-overlay-enclosure {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-overlay-play-button,
.hero__ambilight::-webkit-media-controls-overlay-play-button,
.hero__cell video::-webkit-media-controls-overlay-play-button,
.discover__hero-video::-webkit-media-controls-overlay-play-button {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-start-playback-button,
.hero__ambilight::-webkit-media-controls-start-playback-button,
.hero__cell video::-webkit-media-controls-start-playback-button,
.discover__hero-video::-webkit-media-controls-start-playback-button {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-webkit-media-controls-play-button,
.hero__ambilight::-webkit-media-controls-play-button,
.hero__cell video::-webkit-media-controls-play-button,
.discover__hero-video::-webkit-media-controls-play-button {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero__video::-internal-media-controls-overlay-cast-button,
.hero__ambilight::-internal-media-controls-overlay-cast-button,
.hero__cell video::-internal-media-controls-overlay-cast-button,
.discover__hero-video::-internal-media-controls-overlay-cast-button {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0 !important;
  pointer-events: none !important;
}

/* Le poster de repli doit cadrer comme la video, pas s'etirer. */
.hero__video, .hero__cell video { object-fit: cover; }

/* ==========================================================================
   IMAGE DE REPLI DU FOND  (v=72)
   L'attribut "poster" d'une <video> est ce qui autorise Safari a afficher son
   bouton play par-dessus. On ne s'en sert plus : le repli est une couche CSS
   posee sous la video, visible seulement si la video manque.
   Aujourd'hui : noir uni. Pour y remettre une image, ajouter dans la regle
   ci-dessous : url('../assets/images/LE-FICHIER.jpg?v=1') center / cover no-repeat
   ========================================================================== */
.hero__fallback {
  position: absolute; inset: 0; z-index: 0;
  background: var(--bg);
  background-size: cover; background-position: center;
}
/* Image figee volee a la video (voir freezeFrame en JS) : elle doit cadrer
   exactement comme la video qu'elle remplace, jamais s'etirer. */
.hero__cell.has-still { background-size: cover; background-position: center; }
/* Une video qui n'a pas pu se charger est retiree : rien a dessiner, donc
   aucun bouton play, dans aucun navigateur. */
.hero__video.is-dead,
.hero__cell video.is-dead,
.discover__hero-video.is-dead { display: none !important; }

/* ==========================================================================
   EN-TETE DES GALERIES (LunorFilms / LunorStills)  (v=75)
   ========================================================================== */

/* Le titre est cale au centre exact de l'ecran, et non plus au milieu de ce
   que veut bien laisser le flux entre "Accueil" et les boutons de droite.
   On le sort du flex : "Accueil" et les actions se repartissent librement. */
.discover__kicker {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  pointer-events: none;
}

/* Bouton son : au repos son contour est a 0.15, presque invisible. Quand le
   son est actif, il monte a 0.30 — la valeur exacte du contour de NOUS
   CONTACTER juste a sa droite — et une onde discrete pulse autour pour dire
   que ca joue. La classe .is-active est deja posee par le JS. */
.discover__header-actions .icon-btn {
  position: relative;
  transition: background 0.2s var(--ease), border-color 0.45s var(--ease);
}
.discover__header-actions .icon-btn.is-active {
  /* Rose fixe (v=108) : le bouton son actif reste a la marque, quelle que soit
     la categorie de la page. */
  border-color: color-mix(in srgb, var(--pink) 55%, transparent);
  color: var(--pink);
}
.discover__header-actions .icon-btn.is-active::after {
  content: '';
  position: absolute; inset: -1px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--pink) 55%, transparent);
  pointer-events: none;
  animation: soundPulse 2.4s var(--ease) infinite;
}
@keyframes soundPulse {
  0%   { transform: scale(1);    opacity: 0.75; }
  70%  { transform: scale(1.55); opacity: 0; }
  100% { transform: scale(1.55); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .discover__header-actions .icon-btn.is-active::after { animation: none; opacity: 0; }
}

/* ==========================================================================
   PORTE DE L'INTRANET  (v=78)
   Le site passe derriere un verre depoli sombre, le formulaire reste seul net.
   --------------------------------------------------------------------------
   POURQUOI CE N'EST PAS UN SIMPLE FONDU
   Faire varier l'opacite d'un element qui porte un backdrop-filter ne fonctionne
   pas : le navigateur compose le flou d'un bloc, puis fait varier l'opacite du
   resultat. Le fond apparaissait donc deja floute et deja assombri — d'ou la
   sensation de coupure nette.
   Ici on ne touche plus a l'opacite du voile : on anime le flou et la teinte
   EUX-MEMES, de 0 a leur valeur finale. Le fond s'enfonce progressivement.
   La visibilite prend le relais de pointer-events et attend la fin de la
   transition pour se couper (d'ou le transition-delay a la fermeture).
   ========================================================================== */
.gate {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center;
  visibility: hidden; pointer-events: none;
  transition: visibility 0s linear 0.65s;
}
.gate.is-open { visibility: visible; pointer-events: auto; transition-delay: 0s; }

.gate__backdrop {
  position: absolute; inset: 0;
  background: rgba(4, 4, 5, 0);
  -webkit-backdrop-filter: blur(0px) saturate(1) brightness(1);
  backdrop-filter: blur(0px) saturate(1) brightness(1);
  transition: background 0.65s var(--ease),
              -webkit-backdrop-filter 0.65s var(--ease),
              backdrop-filter 0.65s var(--ease);
}
.gate.is-open .gate__backdrop {
  background: rgba(4, 4, 5, 0.72);
  -webkit-backdrop-filter: blur(22px) saturate(0.6) brightness(0.55);
  backdrop-filter: blur(22px) saturate(0.6) brightness(0.55);
}

.gate__box {
  position: relative;
  width: min(92vw, 400px);
  padding: 44px 40px 36px;
  background: rgba(12, 12, 14, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.75), 0 0 60px rgba(255, 45, 120, 0.06);
  text-align: center;
  /* Le formulaire entre APRES le fond : le regard suit le mouvement au lieu de
     recevoir les deux d'un coup. */
  opacity: 0;
  transform: translateY(16px) scale(0.98);
  transition: opacity 0.35s var(--ease), transform 0.45s var(--ease);
}
.gate.is-open .gate__box {
  opacity: 1;
  transform: none;
  transition: opacity 0.4s var(--ease) 0.18s, transform 0.55s var(--ease) 0.12s;
}

/* Fondu au noir avant de partir vers l'intranet : la page suivante remonte du
   noir de son cote, les deux se rejoignent sans a-coup. */
.gate.is-leaving .gate__backdrop {
  background: rgba(4, 4, 5, 1);
  -webkit-backdrop-filter: blur(30px) saturate(0.3) brightness(0.2);
  backdrop-filter: blur(30px) saturate(0.3) brightness(0.2);
  transition-duration: 0.5s;
}
.gate.is-leaving .gate__box {
  opacity: 0;
  transform: translateY(-8px) scale(0.99);
  transition: opacity 0.35s var(--ease), transform 0.5s var(--ease);
}

.gate__close {
  position: absolute; top: 14px; right: 14px;
  width: 32px; height: 32px; border-radius: 50%;
  border: 0; background: none; cursor: pointer;
  color: var(--fg-faint);
  display: flex; align-items: center; justify-content: center;
  transition: color 0.25s var(--ease);
}
.gate__close:hover { color: #fff; }

.gate__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-bottom: 18px;
  border-radius: 50%;
  border: 1px solid rgba(255, 45, 120, 0.35);
  background: rgba(255, 45, 120, 0.1);
  color: var(--pink);
}
.gate__kicker {
  display: block;
  font-family: var(--font-mono); font-size: 0.55rem; letter-spacing: 0.26em;
  color: var(--fg-faint);
}
.gate__title { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.01em; margin: 10px 0 6px; }
.gate__desc { color: var(--fg-faint); font-size: 0.8rem; font-weight: 300; margin-bottom: 26px; }

.gate__field { position: relative; display: flex; }
.gate__field input {
  flex: 1;
  padding: 14px 52px 14px 16px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  color: #fff;
  font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.14em;
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.gate__field input:focus {
  outline: none;
  border-color: rgba(255, 45, 120, 0.65);
  background: rgba(255, 255, 255, 0.06);
}
.gate__field input::placeholder { color: rgba(255, 255, 255, 0.22); letter-spacing: 0.06em; }

.gate__submit {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 36px; height: 36px; border-radius: 6px;
  border: 0; background: var(--pink); color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: filter 0.25s var(--ease), opacity 0.25s var(--ease);
}
.gate__submit:hover { filter: brightness(1.15); }
.gate__submit:disabled { opacity: 0.45; cursor: wait; }

.gate__msg {
  min-height: 18px; margin-top: 14px;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.08em;
  color: var(--fg-faint);
}
.gate__msg.is-err { color: #ff5a5a; }
.gate__msg.is-ok { color: #3ddc84; }

/* Secousse breve sur mot de passe refuse. */
.gate__box.is-wrong { animation: gateShake 0.4s var(--ease); }
@keyframes gateShake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}
@media (prefers-reduced-motion: reduce) {
  .gate__box.is-wrong { animation: none; }
  .gate, .gate__backdrop, .gate__box { transition-duration: 0.01ms; transition-delay: 0s; }
}


/* ==========================================================================
   AFFICHES DES FONDS (posters)  (v=85)
   L'image d'abord, la video ensuite. L'affiche est un <img> pose juste sous la
   video, avec EXACTEMENT le meme cadrage qu'elle : plein cadre en cover
   partout, et sur toute la hauteur en contain pour le Mariage (dont le plan
   est vertical). Le raccord est donc invisible quand la video prend le relais.

   Les videos de fond partent a 0 d'opacite et ne montent qu'une fois qu'elles
   JOUENT (.is-shown, pose en JS sur l'evenement "playing"). Une video refusee
   par le navigateur reste donc invisible, et l'affiche tient le fond : pas de
   noir, pas de bouton play, personne ne voit qu'il manque quelque chose.
   ========================================================================== */
.hero__poster {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
  opacity: 0;
  transition: opacity 0.45s var(--ease);
  pointer-events: none;
  user-select: none;
}
.hero__poster.is-shown { opacity: 1; }
/* La video joue : l'affiche s'en va. */
.hero__poster.is-gone  { opacity: 0; }
.hero__poster.is-dead  { display: none; }   /* affiche absente : le noir du repli reprend */
/* En triptyque, l'affiche du fond unique n'a plus lieu d'etre : chaque colonne
   porte la sienne. Sans ca, celle du Mariage (z-index 1) passerait devant. */
.hero.is-triptych > .hero__poster { display: none; }

/* Mariage : l'affiche epouse la video contenue, jamais le plein cadre.
   z-index 1 comme la video, et posee apres elle dans le HTML : elle la couvre
   exactement, sans jamais passer devant le scrim. */
.hero.is-portrait .hero__poster {
  /* Meme centrage que la video (inset:0 + margin:auto, voir plus haut) : les
     deux couches doivent se superposer exactement, et aucune ne doit glisser. */
  inset: 0; margin: auto;
  width: auto; height: 100%; max-width: 100%;
  object-fit: contain;
  z-index: 1;
}

/* LES VIDEOS DE FOND NE SONT JAMAIS MASQUEES.
   Safari refuse de lancer tout seul une video a opacite 0 : il la juge
   invisible. C'est l'affiche, posee par-dessus, qui s'efface quand la video
   demarre — l'effet est le meme a l'ecran, sans le blocage. */

@media (prefers-reduced-motion: reduce) {
  .hero__poster { transition-duration: 0.01ms; }
}

/* ==========================================================================
   ADAPTATION MOBILE & TABLETTE  (v=115)
   --------------------------------------------------------------------------
   Tout ce qui suit est ADDITIF : rien au-dessus n'est modifié, et chaque
   règle ici vise explicitement le tactile et/ou les petits écrans. Le
   desktop (≥1024px, souris) reste rigoureusement identique à avant — cette
   section n'existant que sous des media queries qui ne le concernent pas.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. LA PAGE NE SE LIT QUE VERTICALEMENT
   Plusieurs panneaux (contact, catégorie, atelier...) restent en permanence
   dans le DOM en position:fixed, translatés hors-cadre pour se cacher
   (translateX(100%)) plutôt que retirés. C'est invisible et sans
   conséquence... sauf sur Safari iOS, où le rebond élastique ("rubber-band")
   du geste de balayage prend en compte cette géométrie hors-écran même à
   travers overflow-x:hidden, et autorise donc un léger glissement latéral de
   toute la page. touch-action:pan-y est la réponse standard : elle indique
   au moteur de gestes de ne reconnaître QUE le geste vertical sur la page,
   quelle qu'en soit la cause — indépendant de tout calcul de largeur.
   On restaure explicitement pan-x sur les DEUX carrousels horizontaux
   volontaires (rangées façon Netflix, onglets de L'Atelier) : sans ça,
   l'interdiction posée sur la page s'appliquerait aussi à eux et bloquerait
   leur propre balayage.
   -------------------------------------------------------------------------- */
html, body { touch-action: pan-y; }
.discover-row__track, .atelier__tabs { touch-action: pan-x pan-y; }

/* --------------------------------------------------------------------------
   1. LE TOUCHER REMPLACE LE SURVOL
   Deux familles de cas :
     a) de l'info qui n'existait qu'au survol (légendes, actions rapides) et
        qui, sans rien faire, ne serait plus JAMAIS visible au doigt — on la
        rend permanente ;
     b) des effets décoratifs (grossissement, halo, assombrissement des
        voisines) qui, eux, ont un vrai risque de rester "collés" après un
        tap sur mobile (le point:hover peut s'activer sans jamais se
        désactiver) — on les neutralise pour retomber sur l'état de repos.
   -------------------------------------------------------------------------- */
@media (hover: none) {
  /* a) info qui doit rester visible */
  .still__caption,
  .still--feat .still__caption {
    opacity: 1;
    transform: none;
  }
  .genre-row__grid--photos .card--photo .card__scrim,
  .genre-row__grid--photos .card--photo .card__body {
    opacity: 1;
  }
  .discover-row__hover-actions {
    opacity: 1;
    transform: none;
  }
  /* Les flèches de défilement de rangée n'ont pas de sens au doigt (le
     balayage natif les remplace) : autant ne jamais les afficher. */
  .discover-row__arrow { display: none; }

  /* b) survols décoratifs à neutraliser (retour à l'état de repos, déjà
     pensé pour rester lisible seul — voir les commentaires v=94 plus haut) */
  .discover-row__card:hover:not(.is-expanded) { transform: none; box-shadow: none; }
  .discover-row__card:hover img { filter: brightness(0.88) saturate(1.04); }
  .card:hover .card__img { filter: none; transform: none; }
  .card--more:hover { border-color: var(--line); transform: none; }
  .card--more:hover .card__img { transform: none; filter: none; }
  .card--more:hover .card--more__arrow { color: rgba(255,255,255,0.55); }
  .card--more:hover .card--more__arrow svg { transform: none; }
  .genre-row__grid .card:hover { transform: none; }
  .still--feat:hover .still__caption { transform: none; }
}

/* --------------------------------------------------------------------------
   2. CIBLES TACTILES PLUS GÉNÉREUSES
   34px/30px se visent bien à la souris ; au doigt, la zone confortable
   commence vers 40-44px (repère courant : Apple HIG / Material Design).
   On agrandit seulement les boutons ronds les plus sollicités, sans toucher
   à leur mise en page desktop (aucune de ces règles n'entre en jeu à la
   souris, pointer:coarse ne matchant jamais un pointeur de précision).
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  .icon-btn { width: 40px; height: 40px; }
  .panel__close, .video-modal__close, .mobile-nav__close { width: 42px; height: 42px; }
  .dr-icon-btn { width: 34px; height: 34px; }
  .stills__lightbox-close, .stills__lightbox-fs { min-width: 42px; min-height: 42px; }
  .stills__lightbox-nav { min-width: 46px; min-height: 46px; }
  .panel__switch-btn { padding: 11px 22px; }
  .choice-btn { padding: 13px 14px; }

  /* Pas de halo bleu ni de fond gris rémanent au tap sur iOS/Android : la
     rétroaction vient déjà des transitions propres à chaque élément. */
  a, button, .card, .discover-row__card, .still, .choice-btn, .genre-row__less {
    -webkit-tap-highlight-color: transparent;
  }
}

/* --------------------------------------------------------------------------
   3. ZONES SÛRES (encoches / barres système)
   Les téléphones récents ont une encoche ou une île dynamique en haut, et
   une barre de gestes en bas. env() vaut 0 sur les écrans qui n'en ont pas :
   ces ajouts sont sans risque et ne concernent de toute façon que des
   éléments déjà masqués au-dessus de 1100px.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .mobile-nav__close { top: calc(24px + env(safe-area-inset-top)); right: calc(24px + env(safe-area-inset-right)); }
  .mobile-nav { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }
  .panel__close { top: calc(24px + env(safe-area-inset-top)); right: calc(20px + env(safe-area-inset-right)); }
  .video-modal__close { top: calc(16px + env(safe-area-inset-top)); }
  .video-modal__exit-fs { top: calc(16px + env(safe-area-inset-top)); }
  .atelier__header { padding-top: env(safe-area-inset-top); }
  .discover__header, #stillsPage .discover__header { padding-top: calc(26px + env(safe-area-inset-top)); }
}

/* --------------------------------------------------------------------------
   4. PALIER TÉLÉPHONE (≤ 600px)
   Les marges fixes (48-72px) pensées pour un grand panneau desktop
   deviennent disproportionnées sur un écran de poche : on les resserre, sans
   toucher aux mêmes propriétés au-dessus de ce seuil.
   -------------------------------------------------------------------------- */
@media (max-width: 600px) {
  .hero { padding: 24px 20px 118px; }
  .hero__meta { right: 20px; }
  .hero__frame { inset: 4vh 4vw 100px; }
  .hero__title-fill, .hero__title-outline { white-space: normal; }

  .panel__eyebrow { padding: calc(26px + env(safe-area-inset-top)) 20px 0; }
  .panel__body { padding: 16px 20px 20px; gap: 24px; }
  .panel__cards { padding: 12px 4px 24px; }
  .panel__footer { padding-left: 20px; padding-right: 20px; }

  /* LA FICHE CATÉGORIE, EN PLUS DENSE
     1.72rem/1.7 de hauteur de ligne, pensés pour une colonne de 34% d'un
     panneau de 1600px, occupent une portion disproportionnée d'un écran de
     poche — plusieurs lignes de titre, puis un pavé de texte entier, avant
     même d'apercevoir un film. On resserre le rythme vertical (titres,
     marges, boîtes) sans rien retirer : la même information tient dans
     nettement moins de hauteur, la grille de films arrive plus vite. */
  .panel__left h2 { font-size: 1.32rem; line-height: 1.2; margin-bottom: 8px; }
  .panel__left > p.desc { font-size: 0.85rem; line-height: 1.55; margin-bottom: 16px; }
  .panel__left > p.desc::after { margin-top: 14px; }
  .info-box { padding: 12px 14px; margin-bottom: 10px; }
  .info-box .desc { font-size: 0.82rem; line-height: 1.5; }
  .quote-box { padding: 12px 0; margin: 12px 0; font-size: 0.9rem; }
  .cta-row { padding: 13px; font-size: 0.7rem; }
  .check-list li { font-size: 0.74rem; padding: 6px 10px; }
  .panel__switch-btn { padding: 9px 16px; }

  .brief-form { padding: 56px 20px 0; }
  .brief-form__row { grid-template-columns: 1fr; gap: 18px; margin-bottom: 32px; }
  .brief-form__choices { grid-template-columns: 1fr 1fr; }
  .brief-form__choices--row { grid-template-columns: repeat(2, 1fr); }
  .brief-form h2 { font-size: 1.5rem; }

  .atelier__header { padding: 16px 20px; flex-wrap: wrap; gap: 12px; }
  .atelier__tabs { overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none; width: 100%; }
  .atelier__tabs::-webkit-scrollbar { display: none; }
  .atelier__tab { white-space: nowrap; }
  .atelier__body { padding: 32px 20px; }
  .atelier__body[data-tab="realisateurs"] { padding: 20px 20px; }
  .manifeste, .expertise, .day-night, .steps, .team-grid, .realisateurs { grid-template-columns: 1fr; }
  .manifeste h3 { font-size: 1.5rem; }

  .video-modal__info { padding: 24px 20px 28px; gap: 28px; }
  .video-modal__title { font-size: 1.5rem; }
  .video-modal__synopsis { font-size: 0.85rem; line-height: 1.6; }

  .discover__header, #stillsPage .discover__header { padding-left: 20px; padding-right: 20px; }
  .discover__intro { padding: 0 24px; }
  .discover__hero-content { max-width: 100%; }

  .stills__body { padding: 100px 5vw 70px; }
  .stills__intro { margin-bottom: 40px; }
}

/* --------------------------------------------------------------------------
   5. GALERIES : DÉFILEMENT AU DOIGT PLUS FRANC
   overflow-x:auto suffisait déjà pour le balayage ; scroll-snap donne en plus
   ce petit "clic" qui cale une carte bien en place à la fin du geste — un
   confort tactile que la souris n'attend pas (elle, elle a les flèches).
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  .discover-row__track { scroll-snap-type: x proximity; }
  .discover-row__card, .discover-row__card-placeholder { scroll-snap-align: start; }
}

/* --------------------------------------------------------------------------
   6. CHARGE ALLÉGÉE SUR MOBILE
   Un grand rayon de flou (backdrop-filter) coûte cher à recalculer sur un
   GPU de téléphone, surtout sur un élément qui occupe tout l'écran (le menu
   plein écran). On réduit le rayon uniquement quand souris ET grand écran
   sont tous deux absents — jamais sur desktop, même en fenêtre resserrée
   avec une souris.
   -------------------------------------------------------------------------- */
@media (pointer: coarse) and (max-width: 1100px) {
  .mobile-nav {
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    backdrop-filter: blur(14px) saturate(140%);
  }
  .panel-overlay {
    -webkit-backdrop-filter: blur(6px) brightness(0.55) saturate(0.9);
    backdrop-filter: blur(6px) brightness(0.55) saturate(0.9);
  }
}

/* --------------------------------------------------------------------------
   7. FORMULAIRE : BOUTON D'ENVOI TOUJOURS ACCESSIBLE
   Sur un écran de poche, le formulaire est long : on colle le bouton d'envoi
   en bas de la zone de défilement plutôt que de le laisser se perdre après
   le pavé RGPD. position:sticky sur le conteneur qui défile déjà (le
   <form>) — aucun JS, aucun risque de superposition ailleurs.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .brief-form__note { margin-bottom: 14px; }
  #formSubmit {
    position: sticky;
    bottom: 0;
    box-shadow: 0 -18px 24px 6px var(--bg-panel);
  }
}

/* --------------------------------------------------------------------------
   8. MOSAÏQUE / GRILLES PHOTO : COLONNES SERRÉES SUR TRÈS PETIT ÉCRAN
   auto-fill + minmax(280px, 1fr) laisse déjà le nombre de colonnes se
   calculer tout seul ; sous 480px, une seule colonne large est plus
   confortable qu'une colonne de 280px comprimée dans une marge étroite.
   -------------------------------------------------------------------------- */
@media (max-width: 480px) {
  .genre-row__grid--photos { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
}

/* --------------------------------------------------------------------------
   9. PANNEAU CATÉGORIE EN PLEIN ÉCRAN  (v=117)
   Sur téléphone / tablette portrait, le panneau qui glisse depuis la droite
   (Nightlife, Mariage, Brand Content, Artistes) prend TOUTE la largeur au lieu
   de laisser un liseré du fond à gauche : sur un écran de poche, ces quelques
   pour-cent visibles n'apportent rien et le contenu du panneau gagne de la
   place. Le mouvement ne change pas — il entre toujours de la droite
   (translateX(100%) → 0), simplement le point d'arrivée couvre l'écran entier.

   SEUIL VOLONTAIREMENT CALÉ SUR LA PAUSE (< 1024px).
   suspendHeroSound() met les vidéos de fond du hero EN PAUSE dès que
   innerWidth < 1024 (isMobileTier), parce que le fond est alors entièrement
   recouvert. On borne donc ce plein écran au même seuil (max-width:1023.98px,
   soit « < 1024 » côté pixel) : partout où le panneau couvre tout l'écran, la
   vidéo derrière est déjà à l'arrêt — les deux comportements sont synchrones,
   et le desktop (≥ 1024px) n'est jamais concerné.

   Le palier 1024–1100px conserve son comportement actuel (largeur 92vw, fond
   toujours visible en liseré et donc toujours en lecture) : rien n'y change.

   Scopé à #categoryPanel : le formulaire (.panel--form) garde sa largeur.
   La bordure gauche du panneau n'a plus de fond à révéler en plein écran : on
   la retire pour un bord franc.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  #categoryPanel { width: 100vw; border-left: 0; }
}

/* --------------------------------------------------------------------------
   10. BARRE D'ONGLETS DU BAS  (v=118)
   Architecture mobile façon application : les cinq destinations principales
   (Accueil · LunorFilms · LunorStills · L'Atelier · Contact) deviennent
   accessibles en permanence depuis une barre basse, au lieu d'être dispersées
   entre le bouton du header, la bande de l'obturateur et le menu burger.
   Le burger du header, lui, ne change pas de rôle : il reste le choix de la
   CATÉGORIE (Nightlife, Mariage, Brand Content, Artistes).

   .tabbar est une classe NEUVE : aucune règle existante n'est réécrite, et
   l'élément est en display:none tant qu'on n'est pas passé sous 1100px — le
   desktop ne la voit jamais.

   Seuil 1100px : le même que celui où le header bascule déjà en mode mobile
   (liens masqués, burger affiché). Les deux barres apparaissent donc ensemble.
   -------------------------------------------------------------------------- */
.tabbar { display: none; }

@media (max-width: 1100px) {
  /* Hauteur de la barre + son décollement du bas, exposés en variables : tout
     ce qui doit lui laisser la place s'y réfère, une seule valeur à ajuster. */
  :root {
    --tabbar-h: 60px;
    --tabbar-gap: 10px;
    /* Espace total à réserver sous un contenu pour qu'il ne finisse pas
       masqué par la barre (encoche du bas des téléphones comprise). */
    --tabbar-space: calc(var(--tabbar-h) + var(--tabbar-gap) * 2 + env(safe-area-inset-bottom));
  }

  /* LA BARRE
     z-index 49 : au-dessus des panneaux et des pages (45 à 48) pour rester
     atteignable partout, mais SOUS la modale vidéo (50), le menu burger et la
     visionneuse photo (60) et la porte intranet (70) — ces trois-là sont des
     moments pleins écran qui doivent la recouvrir. */
  .tabbar {
    position: fixed; z-index: 49;
    left: 12px; right: 12px;
    bottom: calc(var(--tabbar-gap) + env(safe-area-inset-bottom));
    height: var(--tabbar-h);
    display: flex; align-items: stretch;
    padding: 6px;
    border-radius: calc(var(--tabbar-h) / 2);
    background: rgba(14, 14, 16, 0.72);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, 0.09);
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
    /* Le glissement horizontal sur la barre nous appartient : sans ça, le
       moteur de gestes happerait le mouvement avant nos écouteurs et le
       balayage d'un onglet à l'autre resterait sans effet. */
    touch-action: none;
    /* Un balayage prolongé sur du texte déclenche la sélection (et la loupe
       sur iOS) : le geste finissait en surlignage au lieu de changer d'onglet. */
    -webkit-user-select: none;
    user-select: none;
  }

  /* LE CURSEUR GLISSANT
     Sa position tient dans une seule variable, --tab-i (l'index de l'onglet,
     0 à 4), posée en JS. Le déplacement passe par translate3d : c'est une
     composition GPU, sans recalcul de mise en page — c'est ce qui rend le
     mouvement fluide même sur un téléphone modeste.
     La courbe est volontairement "sortie rapide" (0.22, 1, 0.36, 1) : le
     curseur part vite et se pose en douceur, au lieu d'un glissement mou. */
  .tabbar__thumb {
    position: absolute; z-index: 0;
    top: 6px; bottom: 6px; left: 6px;
    width: calc((100% - 12px) / 5);
    border-radius: calc((var(--tabbar-h) - 12px) / 2);
    background: rgba(255, 255, 255, 0.085);
    transform: translate3d(calc(var(--tab-i, 0) * 100%), 0, 0);
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1),
                opacity 0.2s var(--ease);
    will-change: transform;
    pointer-events: none;
  }
  /* Pendant le balayage, le curseur colle au doigt : aucune transition, sinon
     il traînerait derrière le geste et donnerait une impression de latence. */
  .tabbar.is-dragging .tabbar__thumb { transition: opacity 0.2s var(--ease); }
  /* Panneau catégorie ouvert : aucun onglet ne correspond, le curseur s'efface. */
  .tabbar.is-none .tabbar__thumb { opacity: 0; }

  .tabbar__btn {
    position: relative; z-index: 1;
    flex: 1; min-width: 0;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 3px;
    border: 0; background: none; cursor: pointer;
    color: rgba(255, 255, 255, 0.52);
    transition: color 0.16s var(--ease);
    -webkit-tap-highlight-color: transparent;
  }
  .tabbar__icon {
    display: flex; align-items: center; justify-content: center;
    transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .tabbar__label {
    font-family: var(--font-mono);
    font-size: 0.42rem; letter-spacing: 0.13em;
    white-space: nowrap;
  }
  /* Réponse immédiate au doigt, avant même que la page ne s'ouvre : l'icône
     s'enfonce légèrement. C'est ce micro-retour qui fait qu'une barre paraît
     "réactive" plutôt que lente. */
  .tabbar__btn:active .tabbar__icon { transform: scale(0.88); }

  /* ONGLET ACTIF
     La couleur suit --cat-color : la barre reste accordée à l'ambiance en
     cours (verte en Nightlife, or en Mariage...). La pastille, elle, est
     portée par le curseur glissant ci-dessus. */
  .tabbar__btn.is-active {
    color: var(--cat-color, var(--pink));
  }
  .tabbar__btn.is-active .tabbar__icon { transform: scale(1.06); }
  .tabbar__btn.is-active:active .tabbar__icon { transform: scale(0.94); }

  /* Le header perd son bouton NOUS CONTACTER : la destination vit maintenant
     dans la barre du bas, en cinquième onglet. On ne retire rien au-dessus de
     1100px — la règle est bornée à ce media query. */
  .nav__actions #contactTrigger { display: none; }

  /* LA BANDE DE L'OBTURATEUR EST REDONDANTE EN MOBILE
     .shutter occupe les 74px du bas de l'accueil et n'y propose que
     LunorFilms et LunorStills — exactement les onglets 2 et 3. Deux barres
     empilées mangeraient ~135px d'un écran de poche pour les mêmes deux
     liens. On masque donc l'obturateur SOUS 1100px seulement ; il reste
     intact sur desktop. (Pour le rétablir : supprimer ces deux lignes.) */
  .shutter { display: none; }

  /* DE LA PLACE SOUS CHAQUE CONTENU
     Sans ça, la barre recouvrirait la fin des zones qui défilent (dernière
     rangée de la galerie, pied de panneau, bouton d'envoi du formulaire). */
  .hero { padding-bottom: calc(20px + var(--tabbar-space)); }
  .panel { padding-bottom: var(--tabbar-space); }
  .discover__body { padding-bottom: var(--tabbar-space); }
  .stills__body { padding-bottom: calc(40px + var(--tabbar-space)); }
  .atelier__body { padding-bottom: calc(32px + var(--tabbar-space)); }
}

@media (max-width: 360px) {
  /* Cinq libellés sur un très petit écran : on garde les icônes lisibles et
     on laisse les mots se resserrer plutôt que se chevaucher. */
  .tabbar__label { font-size: 0.38rem; letter-spacing: 0.08em; }
}

@media (prefers-reduced-motion: reduce) {
  .tabbar__btn { transition-duration: 0.01ms; }
  .tabbar__thumb, .tabbar__icon { transition-duration: 0.01ms; }
}

/* --------------------------------------------------------------------------
   11. ACCUEIL ÉPURÉ SUR MOBILE  (v=119)
   Sur un écran de poche, l'accueil portait trois blocs devenus superflus :
     - le paragraphe de présentation (« Atelier de production exécutive
       basé à Rennes... ») : long, il repoussait le titre et le bouton vers
       le haut alors que c'est l'image qui doit parler ;
     - le sélecteur ÉCOUTER : il sert à choisir LEQUEL des trois clips parle.
       Sur téléphone il n'y a qu'un seul fond à l'écran — le choix n'a plus
       d'objet, et l'icône son du header suffit à couper ou rendre le son ;
     - le bloc de coordonnées (ATELIER / COORDS / LUNOR COLLECTIVE) : de
       l'habillage, qui encombre une colonne étroite.

   Ce qui reste se réorganise : le titre descend naturellement (il n'a plus
   le paragraphe sous lui), le bouton ENTRER se place juste dessous, et le
   logo Instagram vient lui faire face à droite, sur la même ligne.

   Aucun de ces éléments n'est retiré du DOM : ils restent en display:none,
   donc le JS qui les manipule (son du hero, coordonnées) continue de
   fonctionner sans le moindre garde-fou supplémentaire. Au-dessus de
   1100px, tout réapparaît à l'identique.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .hero__desc { display: none; }
  .hero__sound { display: none; }
  .hero__meta-info { display: none; }
  /* LE BOUTON ENTRER EST DE RETOUR  (v=123)
     Il avait été masqué au profit d'un balayage vers le haut qui ouvrait la
     page. Le balayage vertical sert désormais à AUTRE CHOSE — il change
     d'ambiance (section 14) — donc l'entrée redevient un geste explicite,
     comme sur desktop : on choisit du pouce, puis on entre d'un appui.
     .hero__sound reste masqué juste au-dessus : le sélecteur ÉCOUTER n'a
     toujours pas d'objet avec un seul fond à l'écran. */

  /* Un balayage qui part sur le titre ne doit pas le surligner. */
  .hero__title { -webkit-user-select: none; user-select: none; }
}

/* --------------------------------------------------------------------------
   12. UN ÉCRAN, PAS UNE PAGE QUI DÉFILE  (v=120)
   L'accueil mobile devient un plan fixe, façon application : plus aucun
   défilement vertical, donc plus jamais d'ascenseur sur le bord droit.

   POURQUOI 100dvh ET PAS 100vh
   Sur iOS, 100vh vaut la hauteur du navigateur BARRES MASQUÉES — soit plus
   que la surface réellement visible tant qu'elles sont là. Le hero dépassait
   donc du cadre, et c'est ce dépassement qui créait l'ascenseur. dvh mesure
   la hauteur réellement disponible à l'instant T. On garde 100vh juste
   au-dessus comme repli pour les navigateurs qui ignorent dvh (ils
   appliqueront l'ancienne valeur, sans régression).

   min-height est remis à 0 : sans ça la règle d'origine (min-height:100vh)
   l'aurait emporté sur la hauteur en dvh.

   Rien de tout ceci ne gêne les pages qui se superposent (galerie, Stills,
   L'Atelier, panneaux) : elles sont en position:fixed et défilent à
   l'intérieur d'elles-mêmes, pas dans le document.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  html, body {
    height: 100%;
    min-height: 0;
    overflow: hidden;
    /* Coupe aussi le rebond élastique de Safari, qui laissait entrevoir du
       vide au-dessus et en dessous du plan. */
    overscroll-behavior: none;
  }
  .hero {
    min-height: 0;
    height: 100vh;
    height: 100dvh;
  }
}

/* --------------------------------------------------------------------------
   13. LE RAIL D'AMBIANCES  (v=123)
   L'incitation d'avant disait « balayez vers le haut pour entrer », avec le
   nom de la destination recopié du bouton. Le geste ayant changé de sens, ce
   message serait devenu faux : le balayage vertical ne fait plus entrer, il
   fait DÉFILER les ambiances (Accueil, Nightlife, Mariage, Brand Content,
   Artistes). Et le bouton ENTRER, revenu, dit déjà où l'on va.

   Ce qu'il reste à dire est donc d'une autre nature : non plus « fais ce
   geste » mais « tu es ici, sur cinq ». C'est exactement ce qu'un rail de
   points raconte sans un mot — et il donne au passage la seule chose qui
   manquait vraiment : la certitude que le geste a été pris en compte, avant
   même que la vidéo de fond n'ait changé.

   Bord droit, à mi-hauteur : il ne croise ni le titre (aligné en bas à
   gauche), ni le logo Instagram, ni la barre d'onglets. Le point actif
   s'allonge et prend la couleur de la catégorie ; les autres restent des
   points. Purement décoratif — le geste se fait sur tout l'écran.

   .hero-pager est une classe NEUVE, en display:none par défaut : le desktop
   ne la voit jamais.
   -------------------------------------------------------------------------- */
.hero-pager { display: none; }

@media (max-width: 1100px) {
  .hero-pager {
    position: absolute; z-index: 3;
    right: 9px; top: 50%; transform: translateY(-50%);
    display: flex; flex-direction: column; align-items: center; gap: 7px;
    pointer-events: none;
    -webkit-user-select: none; user-select: none;
  }
  .hero-pager__dot {
    width: 3px; height: 3px; border-radius: 2px;
    background: rgba(255, 255, 255, 0.34);
    /* La hauteur ET la couleur bougent : deux propriétés bon marché, aucune
       mise en page à recalculer ailleurs — le rail garde sa largeur. */
    transition: height 0.32s var(--ease), background 0.32s var(--ease);
  }
  .hero-pager__dot.is-on {
    height: 17px;
    background: var(--cat-color, var(--pink));
  }

  /* Écran au repos (inactivité) : le rail s'efface avec le reste. */
  .hero.is-idle .hero-pager { opacity: 0; transition: opacity 0.6s var(--ease); }

  /* LE BAS DE L'ÉCRAN, DE BAS EN HAUT : barre d'onglets, puis la ligne
     ENTRER / Instagram, puis le titre. L'ancien étage « incitation » (54px)
     disparaît avec elle : c'est ce qu'on rend au bouton.
     ATTENTION : un palier plus haut (≤900px) repasse .hero__meta en
     position:static — bottom/right n'auraient alors plus aucun effet. On
     rétablit donc explicitement le positionnement absolu ici. */
  .hero__meta {
    position: absolute;
    bottom: calc(var(--tabbar-space) + 19px);
    right: 20px;
    margin-top: 0;
    text-align: right;
  }
  .hero__meta-bottom { margin-top: 0; gap: 0; }

  /* Le hero aligne son contenu en bas (justify-content:flex-end) : c'est son
     padding bas qui décide où se pose le bouton ENTRER, dernier élément du
     flux. On y réserve la barre d'onglets et rien de plus. */
  .hero {
    padding-bottom: calc(var(--tabbar-space) + 14px);
  }
  /* Le bouton ne doit pas s'étirer sur toute la largeur ni buter contre le
     logo Instagram qui lui fait face, à 20px du bord droit. */
  .hero .seuil { max-width: calc(100% - 62px); }
  .hero .seuil__label { overflow: hidden; text-overflow: ellipsis; }
}

/* --------------------------------------------------------------------------
   14. LE BALAYAGE VERTICAL CHANGE D'AMBIANCE  (v=123)
   REMPLACE l'ancien pager vertical, qui faisait monter la page d'entrée
   depuis le bas. Nouveau partage des gestes, calqué sur le modèle desktop :
     - balayage vertical = on parcourt les ambiances, sans quitter l'accueil ;
     - appui sur ENTRER   = on ouvre celle qu'on a choisie ;
   et la page qui s'ouvre revient donc par la DROITE, avec sa croix, comme
   partout ailleurs. Tout ce que la section 14 précédente redéfinissait
   (arrivée par le bas, croix masquée, voile supprimé) a été retiré : les
   règles d'origine reprennent la main d'elles-mêmes, sans surcharge à
   entretenir.

   POURQUOI UN FONDU AU NOIR ET PAS UN GLISSEMENT
   Une page qui suit le doigt suppose de pouvoir afficher les deux états à la
   fois. Ici l'état, c'est une VIDÉO — parfois trois. Les empiler pour les
   faire glisser côte à côte reviendrait à décoder deux fonds simultanément
   sur un téléphone : le geste serait fluide en théorie et saccadé en
   pratique. On fait donc ce que fait le cinéma quand il change de plan : un
   fondu court par le noir. Le fond sortant s'efface, le fond entrant arrive,
   et le montage se fait dans l'intervalle où il n'y a rien à décoder.

   UNE SEULE ANIMATION, DEUX MOITIÉS. La classe est posée une fois, le JS
   change la catégorie au point mort (~45%) — quand tout est déjà à opacité 0
   — puis la retire à la fin. Aucune synchronisation à tenir entre deux
   animations séparées : c'est la même image-clé qui garantit que le
   changement ne se voit pas.

   Le contenu de premier plan (titre, bouton) part dans le SENS du geste et
   revient de l'autre côté : c'est ce décalage, et lui seul, qui donne la
   sensation d'avoir tourné une page plutôt que rechargé la même.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  :root { --swap-time: 0.40s; }

  /* LE COMBINATEUR « > » N'EST PAS UN DÉTAIL DE STYLE.
     renderHeroClips reconstruit le contenu du triptyque (tri.innerHTML) au
     point mort du fondu. Les cellules qu'il crée contiennent chacune une
     <video> et une .hero__poster NEUVES. Avec un sélecteur descendant, ces
     éléments nés à 45% se mettraient à jouer l'animation depuis son début :
     le fond entrant repartirait à zéro, disparaîtrait une seconde fois, puis
     reviendrait — un double clignotement. En ne visant que les ENFANTS
     DIRECTS du hero, on n'anime que des éléments stables, qui traversent le
     changement de fond sans être remplacés ; le sous-arbre suit
     l'opacité de son conteneur, ce qui est exactement l'effet voulu. */
  .hero.is-swap > .hero__triptych,
  .hero.is-swap > .hero__video,
  .hero.is-swap > .hero__poster,
  .hero.is-swap > .hero__fallback,
  .hero.is-swap > .hero__ambilight {
    animation: heroSwapFade var(--swap-time) var(--ease);
  }

  .hero.is-swap-up .hero__content,
  .hero.is-swap-up .hero__meta { animation: heroSwapUp var(--swap-time) var(--ease); }
  .hero.is-swap-down .hero__content,
  .hero.is-swap-down .hero__meta { animation: heroSwapDown var(--swap-time) var(--ease); }

  /* Pendant le fondu, plus rien n'est cliquable : un appui à l'aveugle sur un
     bouton à opacité 0, qui appartient déjà à l'ambiance suivante, ouvrirait
     une page que l'utilisateur n'a pas choisie. */
  .hero.is-swap .hero__content,
  .hero.is-swap .hero__meta { pointer-events: none; }

  /* Le rail, lui, ne clignote PAS : il bascule immédiatement sur le nouveau
     point. C'est la seule confirmation disponible pendant les 200 ms où
     l'écran est au noir — la retarder rendrait le geste incertain. */
}

/* Le palier à zéro est volontairement COURT (42% → 52%, soit ~40 ms) : il
   n'a qu'à couvrir l'instant où le fond est remplacé. Plus long, il devient
   un noir que l'on regarde ; plus court, le montage se voit. */
@keyframes heroSwapFade {
  0%   { opacity: 1; }
  42%  { opacity: 0; }
  52%  { opacity: 0; }
  100% { opacity: 1; }
}
@keyframes heroSwapUp {
  0%   { transform: translateY(0);     opacity: 1; }
  42%  { transform: translateY(-24px); opacity: 0; }
  52%  { transform: translateY(24px);  opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}
@keyframes heroSwapDown {
  0%   { transform: translateY(0);     opacity: 1; }
  42%  { transform: translateY(24px);  opacity: 0; }
  52%  { transform: translateY(-24px); opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero.is-swap > .hero__triptych,
  .hero.is-swap > .hero__video,
  .hero.is-swap > .hero__poster,
  .hero.is-swap > .hero__fallback,
  .hero.is-swap > .hero__ambilight,
  .hero.is-swap-up .hero__content,   .hero.is-swap-up .hero__meta,
  .hero.is-swap-down .hero__content, .hero.is-swap-down .hero__meta {
    animation-duration: 0.01ms;
  }
}

/* --------------------------------------------------------------------------
   15. LE DÉFILEMENT DU PANNEAU RESTE DANS LE PANNEAU  (v=122, revu v=123)
   Cette section portait aussi une transform neutre permanente sur le hero :
   elle évitait un basculement de « bloc conteneur » au moment où l'ancien
   pager poussait l'accueil hors cadre. Ce pager n'existe plus (section 14),
   le hero ne reçoit donc plus jamais de transform — la règle est retirée
   plutôt que laissée à ne rien faire, un calque promu en permanence pour
   personne étant un coût sans contrepartie.

   Ce qui reste est indépendant du geste et vaut toujours.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  /* Le défilement du panneau ne remonte plus au document : sans ça, arriver
     en bas de la liste des films relançait le rebond élastique de la page
     entière — un à-coup au milieu d'un geste qui devait être continu. */
  .panel__body { overscroll-behavior: contain; }
}

/* --------------------------------------------------------------------------
   16. L'EN-TÊTE DE CATÉGORIE, ET LE PIED DE PAGE QUI S'EN VA  (v=122)

   LE PIED DE PAGE PART
   « © 2026 LUNOR RENNES · Instagram · PRODUCTION CINÉMATOGRAPHIQUE » occupe
   une bande pleine largeur, bordure comprise, pour trois informations dont
   aucune n'est une destination : le contact a son onglet, l'Instagram est
   déjà sur l'accueil, et la mention légale n'a pas à coûter un huitième d'un
   écran de poche. Sur desktop il reste, il y a la place.

   L'EN-TÊTE PREND FORME
   Le libellé « CATÉGORIE : AFTERMOVIES » était une ligne de texte flottante
   à 16px du bloc suivant : rien ne disait où finissait l'en-tête et où
   commençait la fiche, d'où l'impression qu'il « collait » au bloc du bas.
   On en fait une vraie barre de titre — respiration au-dessus, filet en
   dessous, léger dégradé qui la détache du fond — et le point de couleur
   gagne un halo à la teinte de la catégorie : c'est lui qui porte
   l'identification, le texte n'a plus à grossir pour se faire voir.

   LA POIGNÉE A ÉTÉ RETIRÉE (v=123)
   Elle annonçait un geste vers le bas, du temps où la page arrivait du bas.
   La page revient maintenant par la droite et la croix est de retour : une
   poignée en haut au centre désignerait un geste qui n'existe plus.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  #categoryPanel .panel__footer { display: none; }

  /* LA CROIX REJOINT L'EN-TÊTE  (v=123)
     Elle est revenue en haut à droite (le panneau entre de nouveau par la
     droite). À sa position d'origine — 24px du haut, 42px de haut au doigt —
     elle flottait au-dessus du filet de l'en-tête et le traversait. On la
     remonte pour que son centre tombe sur celui du libellé : les deux ne sont
     plus deux éléments superposés mais une seule barre de titre. */
  #categoryPanel .panel__close {
    /* La taille est FIXÉE ici, et pas laissée aux règles de cible tactile
       (42px en pointer:coarse, 38px sinon) : tout l'alignement ci-dessous
       repose sur elle, et un écart de 4px selon l'appareil décalerait la
       croix par rapport au libellé sans que rien ne l'explique. */
    width: 42px; height: 42px;
    top: calc(12px + env(safe-area-inset-top));
    right: calc(16px + env(safe-area-inset-right));
  }

  /* L'ID l'emporte volontairement sur la règle ≤600px (section 4) : un seul
     en-tête pour toute la plage mobile, au lieu de deux réglages à suivre.
     Le padding droit réserve la place de la croix — sans lui, un libellé long
     (« CATÉGORIE : BRAND CONTENT ») passerait dessous. */
  #categoryPanel .panel__eyebrow {
    /* ALIGNEMENT DE LA CROIX SUR LE LIBELLÉ, SANS ARITHMÉTIQUE DE POLICE
       (v=126). La croix flottait plus bas que le texte et touchait le filet.
       On aurait pu la remonter d'un nombre de pixels trouvé à l'œil, mais ce
       nombre aurait dépendu de la taille de police, de sa hauteur de ligne et
       du rendu de chaque navigateur — donc juste ici et faux ailleurs.

       On donne plutôt à l'en-tête une BANDE de la hauteur exacte de la croix
       (42px), et on centre le texte dedans : .panel__eyebrow est déjà en
       display:flex avec align-items:center, il n'y a rien à ajouter. La croix
       partant du même bord haut (12px) et faisant la même hauteur, les deux
       centres tombent au même endroit par construction, quelle que soit la
       police.

       box-sizing:border-box (règle globale, ligne 29) : min-height comprend
       le padding ET la bordure. La bande utile vaut donc
       min-height − padding-haut − padding-bas − filet, soit
       (67 + encoche) − (12 + encoche) − 12 − 1 = 42px. L'encoche apparaît des
       deux côtés de la soustraction et s'annule : la bande fait 42px sur tous
       les téléphones, et la croix reste alignée.

       LE RETRAIT DE TÊTE EST UN PADDING, PAS UNE MARGE (v=128). Avec une
       marge, la barre commençait 12px + encoche PLUS BAS que le haut de
       l'écran, et cet espace laissait voir le fond nu du panneau : c'est la
       « bande noire » au-dessus de CATÉGORIE. En padding, le fond de la barre
       — son dégradé et son filet — part du bord réel de l'écran et passe SOUS
       la barre d'état, tandis que le texte reste exactement où il était.
       C'est déjà le procédé des en-têtes de LunorFilms, LunorStills et
       L'Atelier (section 3) : cet en-tête-ci était le seul à faire autrement. */
    padding: calc(12px + env(safe-area-inset-top)) 62px 12px 20px;
    min-height: calc(67px + env(safe-area-inset-top));
    gap: 10px;
    font-size: 0.66rem; letter-spacing: 0.18em;
    color: #f0f0f2;
    border-bottom: 1px solid var(--line);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent);
  }
  #categoryPanel .panel__eyebrow .dot {
    width: 8px; height: 8px; flex: 0 0 auto;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--cat-color, var(--pink)) 20%, transparent);
  }

  /* Le filet ci-dessus sépare déjà : l'air qui suit n'a plus à faire ce
     travail, il n'a qu'à laisser respirer la fiche. */
  #categoryPanel .panel__body { padding-top: 20px; }
}

/* --------------------------------------------------------------------------
   17. PLUS DE FILMS À L'ÉCRAN, D'UN SEUL REGARD  (v=122)
   minmax(128px) a été calibré pour une colonne de droite de panneau desktop.
   Sur un téléphone, la même valeur ne laisse tenir que DEUX cartes par ligne,
   hautes de 220px : une rangée de genre remplit la moitié de l'écran pour y
   montrer deux films sur vingt.

   96px descend le calcul d'un cran partout : trois cartes sur un téléphone au
   lieu de deux, six sur une tablette au lieu de quatre — soit une carte
   30 à 35% plus étroite, ce qui était la demande. La grille reste en
   auto-fill : rien n'est figé, c'est toujours la largeur disponible qui
   décide du nombre de colonnes, et clampGenreRows() mesure ce nombre au lieu
   de le supposer (gridColumnCount) — aucun JS à retoucher.

   Tout ce qui est POSÉ SUR la carte suit la même réduction : à 100px de
   large, un titre de 0.82rem et un badge de catégorie de 0.55rem occupent
   l'image entière. Le titre est en plus borné à deux lignes : au-delà il
   débordait sous la carte suivante.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .genre-row__grid {
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 8px;
  }

  .genre-row__grid .card--tall .card__title {
    font-size: 0.66rem; line-height: 1.2;
    /* Deux lignes maximum, coupées par des points de suspension plutôt qu'au
       milieu d'un mot — même traitement que les taglines (v=109). */
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden;
  }
  .genre-row__grid .card--tall .card__body { left: 7px; right: 7px; bottom: 7px; }
  .genre-row__grid .card--tall .card__tag {
    top: 7px; left: 7px;
    font-size: 0.46rem; letter-spacing: 0.04em; padding: 2px 5px;
  }

  /* La carte PLUS partage la grille : elle rétrécit avec les autres. Son
     libellé espacé de 0.28em ne tenait plus sur 100px. */
  .card--more__inner { gap: 4px; }
  .card--more__count { font-size: 0.52rem; }
  .card--more__label { font-size: 0.6rem; letter-spacing: 0.16em; text-indent: 0.16em; }
  .card--more__arrow svg { width: 16px; height: 16px; }

  /* Des cartes plus basses rendent l'ancien interligne de rangée
     disproportionné : il annonçait des blocs deux fois plus hauts. */
  .genre-row + .genre-row { margin-top: 24px; }
  .genre-row__head { margin-bottom: 10px; gap: 10px; }
  .genre-row__title { font-size: 0.72rem; letter-spacing: 0.1em; }
}

/* --------------------------------------------------------------------------
   18. LA RÉPONSE AU DOIGT DOIT ÊTRE IMMÉDIATE  (v=122)
   Sur mobile, deux règles se sont additionnées sans qu'on y prenne garde :
   la section 1 neutralise les effets de survol (ils resteraient collés après
   un tap), et la section 2 supprime le halo gris natif d'iOS/Android
   (-webkit-tap-highlight-color). Résultat : un appui sur une carte ne
   produisait plus RIEN tant que la fiche ne s'ouvrait pas. Or ce délai — le
   temps de charger une vignette, de monter une modale — est justement celui
   pendant lequel l'utilisateur se demande si son geste a été pris.

   :active se déclenche à la pression, avant tout code : c'est la seule
   rétroaction qui ne peut pas être en retard. 0.12s et un enfoncement de 4%,
   assez pour être senti, trop court pour être vu comme une animation.
   Borné à (hover: none) : la souris, elle, a déjà son survol.
   -------------------------------------------------------------------------- */
@media (hover: none) {
  /* Chaque transition existante est RECOPIEE telle quelle, seule celle de
     transform est raccourcie : remplacer la propriete entiere aurait supprime
     au passage les fondus de couleur et de bordure de ces memes boutons. */
  .genre-row__grid .card {
    transition: transform 0.12s cubic-bezier(0.2, 0, 0, 1),
                filter 0.45s var(--ease), box-shadow 0.35s var(--ease);
  }
  .card--more {
    transition: border-color 0.3s var(--ease),
                transform 0.12s cubic-bezier(0.2, 0, 0, 1);
  }
  .panel__switch-btn {
    transition: color 0.3s var(--ease),
                transform 0.12s cubic-bezier(0.2, 0, 0, 1);
  }
  .genre-row__less {
    transition: color 0.2s var(--ease), border-color 0.2s var(--ease),
                transform 0.12s cubic-bezier(0.2, 0, 0, 1);
  }

  .genre-row__grid .card:active,
  .card--more:active,
  .genre-row__less:active { transform: scale(0.96); }
  .panel__switch-btn:active { transform: scale(0.94); }
  /* La barre d'onglets est deja servie : .tabbar__btn:active enfonce son
     icone (section 10). Rien a ajouter ici, sous peine de cumuler les deux
     reductions d'echelle sur le meme appui. */
}

/* « and (hover: none) » n'est pas decoratif : sans lui, un poste de travail
   ayant coche « reduire les animations » aurait vu ces regles :active
   l'emporter sur le survol des memes cartes — soit une modification du
   desktop, ce que cette section entiere s'interdit. */
@media (prefers-reduced-motion: reduce) and (hover: none) {
  .genre-row__grid .card:active,
  .card--more:active,
  .panel__switch-btn:active,
  .genre-row__less:active { transform: none; }
}

/* --------------------------------------------------------------------------
   19. LA BARRE SE FAIT OUBLIER  (v=124)
   Deux moments, et deux seulement, où la barre d'onglets n'est plus le sujet :

     1. LE PANNEAU CATÉGORIE est ouvert (Nightlife, Mariage, Brand Content,
        Artistes). On y est entré par le bouton ENTRER, et cette destination
        n'appartient PAS à la barre : aucun onglet ne s'y allume. La barre n'a
        donc plus rien à annoncer, elle se réduit à des raccourcis.
     2. LE MODE IMMERSIF (son ouvert, écran au repos : .hero.is-idle). Tout
        l'accueil s'efface pour ne laisser que la vidéo. Une barre restée à
        pleine taille était alors le seul élément d'interface encore présent
        — donc le seul à regarder, exactement l'inverse de l'effet cherché.

   L'ATELIER, LUNORFILMS, LUNORSTILLS ET LE CONTACT GARDENT LA BARRE PLEINE.
   Ce sont les destinations de la barre elle-même : un onglet y est allumé, et
   c'est précisément là qu'elle sert de repérage. Replier ses libellés à ce
   moment-là reviendrait à lui retirer sa fonction au moment où elle est
   utile. Le critère n'est donc pas « une page est ouverte » mais « la barre
   a-t-elle encore quelque chose à dire » — et la réponse se lit dans
   currentTabbarTarget(), qui renvoie null pour le seul panneau catégorie.

   CE QUI CHANGE : les libellés se replient (opacité ET hauteur, sinon ils
   laisseraient leur place vide), la barre perd 14px de haut, se resserre
   latéralement et descend à 4px du bord. Le principe est celui des barres
   d'onglets d'applications : les pictogrammes suffisent une fois que
   l'utilisateur sait où il est.

   POURQUOI --tabbar-space N'EST PAS TOUCHÉ
   Cette variable est définie sur :root et sert de réserve de place à tout ce
   qui doit rester au-dessus de la barre (hero, panneaux, galeries). On
   redéfinit --tabbar-h sur la BARRE seulement : les valeurs internes suivent
   (hauteur, arrondis, curseur), mais la réserve de place ne bouge pas. Sans
   cette précaution, chaque passage en mode compact ferait se recalculer et
   remonter le contenu de toutes les pages — un reflux général pour gagner
   14px, au pire moment.

   Le rembourrage de 6px reste inchangé de propos délibéré : idxFromX(), qui
   traduit une abscisse en numéro d'onglet pendant le balayage de la barre,
   raisonne sur cette valeur. La modifier ici décalerait les zones tactiles
   sans que rien ne le signale.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .tabbar {
    transition: height 0.34s var(--ease), bottom 0.34s var(--ease),
                left 0.34s var(--ease), right 0.34s var(--ease),
                border-radius 0.34s var(--ease), background 0.34s var(--ease);
  }
  /* max-height plutôt que display:none : on veut voir le libellé se replier,
     et une hauteur nulle rend vraiment la place à l'icône, qui se recentre. */
  .tabbar__label {
    max-height: 1.4em; opacity: 1; overflow: hidden;
    transition: opacity 0.18s var(--ease), max-height 0.3s var(--ease);
  }
  /* La couleur est recopiée : remplacer la transition entière l'aurait
     supprimée au passage (voir .tabbar__btn, section 10). */
  .tabbar__btn { transition: color 0.16s var(--ease), gap 0.3s var(--ease); }

  .tabbar.is-compact {
    --tabbar-h: 46px;
    height: 46px;
    border-radius: 23px;
    bottom: calc(4px + env(safe-area-inset-bottom));
    left: 30px; right: 30px;
    /* Un peu plus transparente : moins de matière, moins de présence. */
    background: rgba(14, 14, 16, 0.60);
  }
  .tabbar.is-compact .tabbar__label { opacity: 0; max-height: 0; }
  .tabbar.is-compact .tabbar__btn { gap: 0; }
}

/* Borné à la plage mobile ET complété de .tabbar__btn : la règle de la
   section 10 qui neutralisait déjà sa durée est plus haut dans le fichier,
   celle que l'on vient d'écrire l'aurait donc emporté sur elle. */
@media (prefers-reduced-motion: reduce) and (max-width: 1100px) {
  .tabbar, .tabbar__label, .tabbar__btn { transition-duration: 0.01ms; }
}

/* --------------------------------------------------------------------------
   20. LA PAGE VA JUSQU'AU BAS DE L'ÉCRAN  (v=126)
   La barre d'onglets réservait sa place en poussant le bas du panneau vers le
   haut (.panel { padding-bottom: var(--tabbar-space) }, section 10) : une
   bande morte de ~74px, sur laquelle il n'y avait rien à voir, et un filet
   qui dessinait une fin de page là où l'écran continuait.

   C'est un contresens sur ce qu'est cette barre. Une pilule translucide au
   verre dépoli n'est pas une bordure de mise en page : elle est FAITE pour
   flotter au-dessus du contenu, et c'est ce contenu qui passe dessous — en
   partie visible à travers elle, entièrement visible de part et d'autre
   puisqu'elle ne touche pas les bords. Lui réserver un couloir vide revient
   à payer le prix d'une barre opaque tout en affichant une barre translucide.

   RÉSERVER LA PLACE AU BOUT DU DÉFILEMENT, PAS DANS LA MISE EN PAGE.
   Le padding quitte le panneau (qui reprend donc toute la hauteur de l'écran,
   soit une rangée de films de plus visible d'un coup) et se pose en bas de la
   zone qui DÉFILE. Effet : le contenu passe librement sous la barre pendant
   la lecture, et en fin de course la dernière rangée peut quand même remonter
   au-dessus d'elle. On ne perd rien, on ne cache rien.

   La réserve est calculée sur la barre COMPACTE (46px + 4px de décollement),
   la seule que l'on voie dans ce panneau — voir section 19. Utiliser
   --tabbar-space, taillé pour la barre pleine, aurait rendu 20px de trop.

   Borné à #categoryPanel : le formulaire de contact garde son comportement,
   la barre y est pleine.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  :root {
    /* Bas de la barre compacte + sa hauteur + un peu d'air. */
    --tabbar-space-compact: calc(46px + 4px + 12px + env(safe-area-inset-bottom));
  }

  #categoryPanel { padding-bottom: 0; }

  /* L'ID l'emporte sur le padding raccourci de la section 4 (≤600px), quel
     que soit l'ordre des deux règles dans le fichier. */
  #categoryPanel .panel__body { padding-bottom: var(--tabbar-space-compact); }
}

/* --------------------------------------------------------------------------
   21. LA PAGE OCCUPE TOUT L'ÉCRAN  (v=127)
   Deux bandes noires encadraient le site sur téléphone : l'une au-dessus de
   l'en-tête de catégorie, l'autre sous la barre d'onglets. Ce n'était pas du
   vide laissé par une marge — c'était le hors-champ. Sans viewport-fit=cover,
   iOS retire les zones de sécurité (encoche, barre de gestes) de la fenêtre
   et les remplit avec le fond du document (--bg #050506), pendant que le
   panneau, lui, est en --bg-panel #0a0a0c. Deux noirs voisins mais distincts :
   d'où la couture visible. La balise est désormais posée dans index.html.

   CE QUE ÇA NE DÉPLACE PAS
   Tout ce qui se cale déjà sur env(safe-area-inset-*) reste EXACTEMENT où il
   est : la barre d'onglets, l'en-tête de catégorie, la croix, les en-têtes
   des univers. C'est le principe même de ces variables — elles valaient zéro
   tant que la fenêtre s'arrêtait au bord sûr, elles valent maintenant la
   hauteur de l'encoche, et la somme ne change pas. La barre du bas ne bouge
   donc pas d'un pixel ; c'est seulement le FOND qui s'étend derrière elle,
   jusqu'au bas réel de l'écran — et donc sous la barre de Safari, qui est
   translucide et laisse voir le site au travers.

   CE QUE ÇA DÉPLACE, ET QU'IL FAUT RATTRAPER
   À l'inverse, ce qui touchait un bord SANS passer par env() se retrouve
   maintenant sous l'encoche ou sous la barre de gestes. C'est ce que cette
   section rattrape, élément par élément. Les valeurs d'origine sont
   conservées telles quelles et simplement augmentées de l'inset : à l'écran,
   rien ne change par rapport à avant.

   Les insets GAUCHE et DROITE ne valent zéro qu'en portrait ; en paysage sur
   un téléphone à encoche ils atteignent une quarantaine de pixels. D'où leur
   présence sur les éléments ancrés à un bord latéral.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  /* L'en-tête de l'accueil : .nav y est en position relative (top:16px), donc
     c'est à la zone qui le contient de descendre sous l'encoche. */
  .nav-zone { padding-top: env(safe-area-inset-top); }

  .tabbar {
    left: calc(12px + env(safe-area-inset-left));
    right: calc(12px + env(safe-area-inset-right));
  }
  .tabbar.is-compact {
    left: calc(30px + env(safe-area-inset-left));
    right: calc(30px + env(safe-area-inset-right));
  }

  /* Le rail d'ambiances et le logo Instagram sont ancrés au bord droit. */
  .hero-pager { right: calc(9px + env(safe-area-inset-right)); }
  .hero__meta { right: calc(20px + env(safe-area-inset-right)); }

  /* Le titre de l'accueil ne doit pas passer sous l'encoche en paysage. */
  .hero {
    padding-left: calc(20px + env(safe-area-inset-left));
    padding-right: calc(20px + env(safe-area-inset-right));
  }

  /* Visionneuse photo : ses commandes sont posées en absolu sur les bords. */
  .stills__lightbox-close,
  .stills__lightbox-fs { top: calc(26px + env(safe-area-inset-top)); }

  /* Formulaire de contact : sa croix se cale déjà sur l'inset (section 3), le
     texte doit suivre, sinon les deux se chevauchent. */
  .brief-form { padding-top: calc(80px + env(safe-area-inset-top)); }
}

/* Le palier téléphone garde son propre retrait de tête, augmenté de l'inset.
   Placé APRÈS le bloc ci-dessus pour l'emporter dessus sur petit écran. */
@media (max-width: 600px) {
  .brief-form { padding-top: calc(56px + env(safe-area-inset-top)); }
}

/* --------------------------------------------------------------------------
   22. LA MÊME CHOSE POUR TOUT LE SITE  (v=127)
   La section 20 a rendu au panneau catégorie toute la hauteur de l'écran, en
   déplaçant la réserve de la mise en page vers le bout du défilement. Restait
   à généraliser — et l'inventaire est plus court qu'il n'y paraît :

     · LunorFilms, LunorStills, L'Atelier : DÉJÀ CORRECTS. Leur réserve était
       posée sur .discover__body, .stills__body et .atelier__body, c'est-à-dire
       sur les blocs qui défilent eux-mêmes. Ces blocs occupent déjà toute la
       hauteur restante ; le rembourrage n'y a jamais été qu'une fin de course.
       Rien à changer, et surtout rien à toucher pour rien.

     · L'accueil : son fond est la vidéo, qui couvre tout le cadre. Le
       rembourrage n'y sert qu'à poser le titre et le bouton au-dessus de la
       barre. Rien à changer non plus.

     · Le FORMULAIRE DE CONTACT : seul cas restant. Comme le panneau
       catégorie, il portait sa réserve sur .panel — une bande morte sous son
       pied de page. On la déplace sur le pied de page lui-même : son fond
       descend alors jusqu'au bas réel de l'écran (donc sous la barre de
       Safari, qui le laisse voir) pendant que son texte reste au-dessus de la
       barre d'onglets.

   La barre y est PLEINE (voir section 19) : la réserve se calcule donc sur
   --tabbar-space, et non sur la variante compacte de la section 20.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  #contactPanel { padding-bottom: 0; }
  #contactPanel .panel__footer {
    padding-bottom: calc(18px + var(--tabbar-space));
  }
}

/* ==========================================================================
   AFFINAGE MOBILE  (v=129) — sections 23 à 28
   --------------------------------------------------------------------------
   NOTE DE MÉTHODE, POUR PLUS TARD.
   Une piste a été tentée puis ABANDONNÉE : faire défiler les pages dans le
   DOCUMENT (au lieu de leur boîte interne) pour que Safari replie sa barre
   d'adresse. Ça marchait — la barre se repliait — mais ce n'était pas le
   besoin : le besoin, c'est que les vignettes remplissent l'écran jusqu'en
   bas et se voient DERRIÈRE la barre. Or l'architecture d'origine le fait
   déjà : les pages sont en position:fixed et, depuis viewport-fit=cover
   (section 21), leur cadre descend jusqu'au bas physique de l'écran, sous une
   barre translucide. Deux choses seulement s'y opposaient, et ce sont elles
   qu'on corrige ici — pas l'architecture.
   ========================================================================== */

/* --------------------------------------------------------------------------
   23. LA SÉLECTION DE FILMS VA JUSQU'AU BAS DE L'ÉCRAN  (v=129)
   Section 20 posait une réserve de fin de défilement (~96px) sous la grille de
   films, pour que la dernière rangée puisse remonter au-dessus de la barre
   d'onglets. C'est ELLE, le « bloc noir gris » : un aplat de --bg-panel,
   sans rien dedans, occupant exactement la bande où l'on veut voir passer les
   vignettes derrière le verre de Safari.

   Le raisonnement de la section 20 vaut pour une barre d'onglets OPAQUE. La
   nôtre est une pilule translucide qui ne touche même pas les bords : ce qui
   passe dessous reste largement lisible, et de part et d'autre entièrement.
   La réserve payait donc le prix d'une barre pleine tout en en affichant une
   flottante — exactement le contresens que la section 20 dénonçait pour la
   mise en page, et qu'elle a reproduit en fin de défilement.

   On la retire. La sélection remplit l'écran jusqu'au bord physique, elle
   passe sous la barre d'onglets ET sous celle de Safari. C'est le rendu des
   catalogues mobiles (Netflix, Prime, Amazon) et c'est ce qui était demandé.

   scroll-padding-bottom prend le relais pour ce qui compte vraiment : quand
   le navigateur amène lui-même un élément à l'écran (ancre, focus clavier),
   il ne le colle plus sous la barre.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  #categoryPanel .panel__body {
    padding-bottom: 0;
    scroll-padding-bottom: var(--tabbar-space-compact);
  }
  #discoverPage .discover__body,
  #stillsPage .stills__body {
    padding-bottom: 0;
    scroll-padding-bottom: var(--tabbar-space);
  }
}

/* --------------------------------------------------------------------------
   24. LE PIED DE L'ACCUEIL REDESCEND JUSQU'EN BAS  (v=129)
   L'accueil ne défile pas (section 12) : sa hauteur est bornée à 100dvh,
   c'est-à-dire la surface visible BARRE COMPRISE. Le dégradé du .hero__scrim,
   qui court en inset:0, s'arrête donc au ras de cette barre — et la bande
   qu'on aperçoit derrière elle est de la vidéo brute, coupée net.

   ON NE TOUCHE PAS À LA HAUTEUR DU HERO : la borner en dvh est précisément ce
   qui a supprimé l'ascenseur de l'accueil. On superpose un voile en
   position:fixed — pour un élément fixe, bottom:0 désigne le bas du viewport
   de mise en page, qui descend SOUS la barre. Le dégradé va donc chercher le
   bas physique de l'écran sans que la page ne gagne un pixel de hauteur.

   ::after sur .hero plutôt qu'un élément de plus dans index.html : le voile
   est purement décoratif. z-index 1, comme le scrim : étant le dernier
   enfant, il se peint par-dessus lui, mais reste sous le titre et le bouton
   ENTRER (2), sous le rail d'ambiances (3), l'en-tête (20) et la barre (49).
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .hero::after {
    content: '';
    position: fixed;
    left: 0; right: 0; bottom: 0;
    height: calc(190px + env(safe-area-inset-bottom));
    background: linear-gradient(180deg,
      rgba(5, 5, 6, 0) 0%,
      rgba(5, 5, 6, 0.42) 44%,
      rgba(5, 5, 6, 0.86) 78%,
      #050506 100%);
    z-index: 1;
    pointer-events: none;
    transition: opacity 0.6s var(--ease);
  }
  .hero.is-idle::after { opacity: 0; }
}

/* --------------------------------------------------------------------------
   25. LES FILMS EN RANGÉES D'AFFICHES  (v=129)
   La grille de films de la fiche catégorie devient une rangée HORIZONTALE par
   genre, au format affiche 2/3, qui déborde des marges et se balaie au doigt :
   la grammaire exacte des catalogues de streaming. Deux gains, pas un :
     · on voit d'un coup d'œil qu'un genre contient plus que ce qui tient en
       largeur — une grille, elle, se referme sur « +6 PLUS » ;
     · une rangée occupe une hauteur fixe et modeste, donc plusieurs genres
       cohabitent à l'écran au lieu d'un seul.
   Les PHOTOS gardent leur grille : une mosaïque se parcourt verticalement,
   c'est sa nature.

   scroll-snap en proximity et non mandatory : on cale sans emprisonner le
   doigt. touch-action:pan-x pan-y rend à la rangée le geste horizontal que la
   section 0 avait retiré à toute la page.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  #categoryPanel .genre-row__grid:not(.genre-row__grid--photos) {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    /* Débordement des marges du panneau (20px, section 4) : les affiches
       filent jusqu'au bord de l'écran, et la rangée dit d'elle-même qu'elle
       continue au-delà. */
    margin-left: -20px; margin-right: -20px;
    padding: 2px 20px 8px;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    touch-action: pan-x pan-y;
  }
  #categoryPanel .genre-row__grid:not(.genre-row__grid--photos)::-webkit-scrollbar { display: none; }

  #categoryPanel .genre-row__grid:not(.genre-row__grid--photos) > .card {
    flex: 0 0 clamp(118px, 30vw, 162px);
    scroll-snap-align: start;
  }
  #categoryPanel .genre-row__grid:not(.genre-row__grid--photos) .card__img {
    width: 100%; height: auto;
    aspect-ratio: 2 / 3;
    object-fit: cover;
  }
  #categoryPanel .genre-row__grid:not(.genre-row__grid--photos) .card--more {
    aspect-ratio: 2 / 3; height: auto;
  }

  /* LUNORFILMS : les rangées existaient déjà, on règle le geste. Une carte
     pleine plus l'amorce de la suivante — l'invitation à balayer — et des
     marges qui saignent au bord de l'écran. */
  .discover-row__track {
    gap: 10px;
    padding-left: 20px; padding-right: 28px;
    scrollbar-width: none;
  }
  .discover-row__track::-webkit-scrollbar { display: none; }
  .discover-row__card,
  .discover-row__card-placeholder {
    flex-basis: clamp(228px, 66vw, 300px);
  }
}

/* --------------------------------------------------------------------------
   26. LA FICHE CATÉGORIE PREND DE L'ALLURE  (v=129)
   Un bandeau teinté à la couleur de l'univers en tête de fiche — le geste des
   fiches de titres — posé en IMAGE DE FOND sur .panel__body : pas de calque
   supplémentaire, pas de pseudo-élément à glisser au bon niveau d'empilement,
   et il défile naturellement avec le contenu. Le titre prend enfin la taille
   d'un titre, et l'appel à projet la couleur de l'univers : c'est LE bouton
   de la fiche, il n'a pas à rester gris.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  #categoryPanel .panel__body {
    background-image: linear-gradient(180deg, var(--cat-color-dim, rgba(255,45,120,0.13)), rgba(10,10,12,0) 82%);
    background-image: linear-gradient(180deg,
      color-mix(in srgb, var(--cat-color, var(--pink)) 20%, transparent),
      rgba(10, 10, 12, 0) 82%);
    background-repeat: no-repeat;
    background-size: 100% 320px;
  }
  #categoryPanel .panel__left h2 {
    font-size: 1.9rem; line-height: 1.08; letter-spacing: -0.02em;
    margin-bottom: 10px;
  }
  #categoryPanel .cta-row {
    border-color: var(--cat-color, var(--pink));
    background: var(--cat-color-dim, rgba(255, 45, 120, 0.13));
  }
  /* Les onglets Films/Photos restent sous la main pendant toute la lecture.
     .panel__body est le conteneur qui défile : un sticky top:0 s'y colle. */
  #categoryPanel .panel__switch {
    position: sticky; top: 0; z-index: 4;
    padding: 10px 0 12px;
    background: linear-gradient(180deg, var(--bg-panel) 62%, rgba(10, 10, 12, 0));
  }
}

/* --------------------------------------------------------------------------
   27. CE QUI COÛTE CHER ET NE SE VOIT PAS  (v=129)

   A. LE FLOU DU VOILE, DERRIÈRE UN PANNEAU OPAQUE.
   .panel-overlay est un backdrop-filter plein écran (flou 9px + luminosité +
   saturation). Sur desktop il a un sens : le panneau ne fait que 78vw, on voit
   le site flouté à côté. Sous 1024px le panneau couvre TOUT l'écran
   (section 9) : ce flou est recalculé sur la surface entière, à chaque image,
   pour quelque chose que personne ne voit. C'est l'un des postes les plus
   coûteux d'un GPU de téléphone, et il tombait pile pendant le défilement.
   On coupe le filtre, PAS l'élément : il reste la cible de fermeture au clic
   et son opacité pilote toujours la transition. Rien ne change à l'écran.

   B. LE GRAIN, QUI FORCE UN CALQUE POUR RIEN.
   mix-blend-mode oblige le navigateur à composer le calque séparément, même à
   opacité nulle. Le grain ne s'allume qu'au SURVOL de LunorStills
   (.hero.is-frozen) : sur un écran tactile, jamais.

   C. UN FOND DERRIÈRE CHAQUE VIGNETTE.
   Avec le chargement différé ajouté en v=115, une carte pas encore chargée
   serait un trou noir. Un fond légèrement plus clair en fait une carte qui
   attend son image — la différence entre « ça charge » et « c'est cassé ».

   D. LA RÉPONSE AU DOIGT.
   Un léger enfoncement à l'appui, en transform pure : le compositeur seul,
   aucune mise en page recalculée.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {
  .panel-overlay {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .card__img,
  .discover-row__thumb {
    background-color: #101013;
  }
}

@media (hover: none) {
  .hero__grain { display: none; }

  .card:active,
  .discover-row__card:active,
  .still:active {
    transform: scale(0.965);
    transition: transform 0.12s var(--ease);
  }
}

/* --------------------------------------------------------------------------
   28. LA BARRE D'ONGLETS, FINITION  (v=129)
   Une arête de lumière d'un pixel sur son bord haut et une ombre portée qui
   la décolle du contenu qui passe désormais dessous : c'est ce qui fait
   qu'une pilule FLOTTE au lieu d'être posée. Aucune géométrie ne change —
   idxFromX() et les zones tactiles restent exactement ce qu'elles étaient.
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .tabbar {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.07),
      0 12px 32px rgba(0, 0, 0, 0.5);
  }
}

/* --------------------------------------------------------------------------
   29. LE DOCUMENT QUI DÉFILE — POUR PASSER SOUS LE VERRE DE SAFARI  (v=130)

   POURQUOI ON Y REVIENT, ET POURQUOI C'EST LA SEULE VOIE.
   Comparaison faite sur téléphone entre ce site et Amazon : chez eux, les
   vignettes passent derrière l'interface en verre de Safari ; chez nous, un
   bloc noir coupe la page net. La différence n'est ni une couleur ni une
   marge : chez eux LA PAGE EST LE DOCUMENT QUI DÉFILE, et Safari l'étend
   alors jusqu'au bas physique de l'écran, son verre posé par-dessus. Une page
   figée (position:fixed, défilement interne) ne défile pas AUX YEUX de
   Safari : il la cantonne au-dessus de sa barre, et la zone de la barre n'a
   rien à laisser voir. C'est tout ou rien — donc c'est tout.

   CE QUI AVAIT FAIT ÉCHOUER LA PREMIÈRE TENTATIVE, ET LA CORRECTION.
   La généralisation posait en flux TOUTES les pages, y compris les FERMÉES
   (invisibles mais hautes de tout leur contenu). Le document devenait plus
   haut que la page ouverte : en défilant au-delà de sa fin, elle sortait par
   le haut et l'accueil — peint en fixe derrière — apparaissait. D'où le
   « retour à l'accueil au défilement ». Correction de racine : les règles de
   flux ne s'appliquent qu'à .is-docpage, une classe que le script pose sur LA
   SEULE page active, après sa glissade d'entrée, et retire après sa sortie.
   Une page fermée ne pèse jamais dans le document.

   L'ordre d'empilement ne change pas : la page garde son z-index, l'accueil
   fixé derrière n'a pas le sien, la barre d'onglets reste au-dessus.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {

  /* La fenêtre redevient la surface qui défile. overflow-x en CLIP et non en
     hidden : hidden sur la racine est un tueur connu du traitement « page qui
     défile » chez Safari (il fabrique un conteneur de défilement là où on
     veut la fenêtre). clip rogne pareil — la glissade de sortie vers la
     droite ne crée pas d'ascenseur horizontal — sans rien fabriquer. hidden
     reste en repli pour les Safari d'avant clip. */
  html.doc-scroll {
    height: auto; min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overflow-x: clip;
    background: var(--bg);
    overscroll-behavior: auto;
    /* scroll-behavior:smooth global : les remises à zéro du script
       s'animeraient, on verrait la page se rembobiner. */
    scroll-behavior: auto;
  }
  html.doc-scroll body {
    height: auto; min-height: 0;
    overflow: visible;
    overscroll-behavior: auto;
  }

  /* L'accueil ne pèse pas dans la hauteur du document ; entièrement recouvert,
     ses vidéos déjà en pause à ce palier (isMobileTier). Son voile de bas de
     page (section 24), arrimé à la fenêtre, n'a rien à faire par-dessus une
     page ouverte. */
  html.doc-scroll .hero { position: fixed; top: 0; left: 0; right: 0; }
  html.doc-scroll .hero::after { display: none; }

  /* Le voile de fermeture ne prend plus le tap : pendant un rebond élastique
     en bout de course, un doigt posé au-delà du bord de la page tombait sur
     lui — et fermait tout. La croix, le balayage et la barre suffisent. */
  html.doc-scroll .panel-overlay { pointer-events: none; }

  /* LA PAGE ACTIVE, ET ELLE SEULE. Quand le document est en haut, cette boîte
     est PIXEL POUR PIXEL celle du position:fixed qu'elle remplace : rien ne
     bouge à l'écran, seul le destinataire du geste change. lvh et non svh ni
     dvh : la hauteur barres masquées, la seule qui couvre le bas physique de
     l'écran et ne change pas de valeur pendant que la barre se replie. */
  html.doc-scroll .is-docpage {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: auto;
    height: auto;
    min-height: 100vh;
    min-height: 100lvh;
    overflow: visible;
  }
  /* translateX(0)/translateY(0) n'est pas `none` : une transform, même
     neutre, fait de la page le repère de ses position:fixed internes (croix,
     en-têtes), qui seraient emportés par le défilement. Visuellement, c'est
     le même point d'arrivée. */
  html.doc-scroll .is-docpage.is-open { transform: none; }

  /* ---- PANNEAU CATÉGORIE ---- */
  html.doc-scroll #categoryPanel.is-docpage .panel__body {
    overflow: visible;
    overscroll-behavior: auto;
  }
  /* L'en-tête tenait en place parce que seul .panel__body défilait ; sticky
     le fait tenir pareil quand c'est la fenêtre. Fond rendu opaque : son
     dégradé s'éteint en transparent et la grille défilerait au travers. */
  html.doc-scroll #categoryPanel.is-docpage .panel__eyebrow {
    position: sticky; top: 0; z-index: 5;
    background-color: var(--bg-panel);
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent);
  }
  html.doc-scroll #categoryPanel.is-docpage .panel__close { position: fixed; }
  /* Les onglets Films/Photos (collants, section 26) changent de référence :
     leur conteneur de défilement est maintenant la fenêtre, ils doivent donc
     s'arrêter SOUS l'en-tête et non à 0. Même bande de 67px + encoche que lui
     (voir le calcul de la section 16). */
  html.doc-scroll #categoryPanel.is-docpage .panel__switch {
    top: calc(67px + env(safe-area-inset-top));
  }

  /* ---- LUNORFILMS · LUNORSTILLS · L'ATELIER ---- */
  /* display:block : leur colonne flex ne servait qu'à répartir une hauteur
     figée ; en flux, la hauteur vient du contenu. */
  html.doc-scroll #discoverPage.is-docpage,
  html.doc-scroll #stillsPage.is-docpage,
  html.doc-scroll #atelierPanel.is-docpage { display: block; }

  html.doc-scroll #discoverPage.is-docpage .discover__body,
  html.doc-scroll #stillsPage.is-docpage .stills__body,
  html.doc-scroll #atelierPanel.is-docpage .atelier__body {
    overflow: visible;
    flex: none;
  }
  /* Les en-têtes de LunorFilms et LunorStills sont en absolute top:0 dans la
     page : dans une page qui défile, ils partiraient avec elle. fixed les
     rend à la fenêtre (transform retirée plus haut), au même endroit. Celui
     de L'Atelier est dans le flux : sticky, avec un fond. */
  html.doc-scroll #discoverPage.is-docpage .discover__header,
  html.doc-scroll #stillsPage.is-docpage .discover__header { position: fixed; }
  html.doc-scroll #atelierPanel.is-docpage .atelier__header {
    position: sticky; top: 0; z-index: 6;
    background: var(--bg);
  }

  /* Le MODE CINÉMA de LunorFilms (bannière plein écran, défilement gelé) est
     un recouvrement total : il reprend le contrat d'une page fixe le temps
     qu'il dure. Le gel du document est posé par le script (doc-lock). */
  html.doc-scroll #discoverPage.is-cinema {
    position: fixed; top: 0; left: 0; right: 0; bottom: 0;
    height: auto; min-height: 0; overflow: hidden;
  }
  html.doc-scroll #discoverPage.is-cinema .discover__hero { height: 100lvh; }

  /* ---- FORMULAIRE DE CONTACT ---- */
  /* Pleine largeur, comme la catégorie : le liseré de fond à gauche
     n'apporte rien sous 1024px. */
  html.doc-scroll #contactPanel.is-docpage { width: 100vw; border-left: 0; }
  html.doc-scroll #contactPanel.is-docpage .brief-form {
    overflow: visible; flex: none; min-height: 0;
  }
  html.doc-scroll #contactPanel.is-docpage .panel__close { position: fixed; }
  /* Le bouton d'envoi collait au bas de la zone de défilement du formulaire
     (section 7). Cette zone est désormais la fenêtre : sans retrait, il se
     poserait SOUS la barre d'onglets, qui est pleine sur cette page. */
  html.doc-scroll #contactPanel.is-docpage #formSubmit { bottom: var(--tabbar-space); }

  /* ---- LE VERROU DE LECTURE (doc-lock) ---- */
  /* Une surface modale (visionneuse vidéo, visionneuse photo, mode cinéma)
     recouvre une page qui défile dans le document : celui-ci ne doit plus
     bouger derrière — SANS perdre la position. Le script fige body à
     l'endroit exact (top négatif en ligne) et restaure au déverrouillage. */
  html.doc-lock body {
    position: fixed;
    left: 0; right: 0; width: 100%;
  }
}

/* --------------------------------------------------------------------------
   30. LES FANTÔMES QUI TEIGNAIENT LE VERRE  (v=131)

   LE VRAI COUPABLE DU BLOC NOIR, DOCUMENTÉ.
   Safari 26 (Liquid Glass) ne lit plus theme-color. Pour teinter sa barre, il
   ÉCHANTILLONNE les éléments en position fixed ou sticky proches des bords de
   l'écran, et en lit deux propriétés : background-color et backdrop-filter —
   Y COMPRIS sur les éléments à opacity:0 et pointer-events:none. Invisible ne
   veut pas dire ignoré : tant que l'élément est dans l'arbre de rendu, il est
   échantillonné. (Règles non documentées par Apple, établies par
   rétro-ingénierie communautaire — voir 1ar.io/updates/safari-26-liquid-glass
   -web et github.com/andesco/safari-color-tinting : zones d'échantillonnage
   ~4px du haut, ~3px du bas, ≥80% de largeur.)

   Or ce site garde en permanence dans le DOM, cachés mais peints, des écrans
   FIXES QUASI NOIRS qui touchent le bord bas : LunorFilms, LunorStills et
   L'Atelier (opacity:0, translateY(14px) — donc DANS la fenêtre), le menu
   plein écran (fond sombre + flou de 24px), les deux voiles, les panneaux.
   Safari les échantillonne, conclut « cette page est noire jusqu'en bas », et
   peint sa barre en aplat sombre — LE bloc qui masquait les vignettes, quoi
   qu'on fasse par ailleurs.

   LA CORRECTION : UN ÉLÉMENT CACHÉ NE DOIT PLUS RIEN DONNER À ÉCHANTILLONNER.
   On retire background-color et backdrop-filter de chaque surface fixe TANT
   QU'ELLE EST FERMÉE. C'est visuellement neutre par construction : ces
   surfaces sont à opacity:0. À l'ouverture, le fond revient à la frame zéro,
   sous une opacité qui part de zéro — aucune couture visible, aucune
   animation perdue, aucun JS.

   DEUX SURFACES SONT DÉJÀ SAINES, ET C'EST INSTRUCTIF : la visionneuse photo
   (son fond sombre est sur un enfant en position:absolute, que
   l'échantillonneur ignore) et la porte intranet (visibility:hidden : non
   peinte, donc non lue).

   Le fond de secours, lui, doit être EXPLICITE : quand aucun élément fixe ne
   qualifie, Safari retombe sur le fond de <html>/<body> — et un fond
   transparent retombe sur du BLANC. On le déclare donc sur <html> aussi.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {

  /* Le fond de secours du verre : explicite, jamais transparent. */
  html { background-color: var(--bg); }

  /* Les trois univers plein écran, fermés : rien à échantillonner. */
  .discover:not(.is-open),
  .stills:not(.is-open),
  .atelier:not(.is-open) { background: transparent; }

  /* Les panneaux fermés sont déjà hors cadre (translateX(100%)) mais un
     échantillonneur à géométrie près n'a pas à trancher : on retire aussi. */
  .panel:not(.is-open):not(.is-peek) { background: transparent; }

  /* La visionneuse vidéo fermée. */
  .video-modal:not(.is-open) { background: transparent; }

  /* Le menu plein écran fermé : son fond ET son flou — le backdrop-filter
     est échantillonné au même titre que la couleur. */
  .mobile-nav:not(.is-open) {
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* Les voiles fermés. */
  .panel-overlay:not(.is-visible) { background: transparent; }

  /* Pendant qu'une page défile dans le document, le voile — même visible —
     est un plein écran sombre plaqué sur le bord bas, DERRIÈRE une page
     opaque : personne ne le voit, mais l'échantillonneur si, et il peut
     l'emporter sur les vrais pixels de la page. Il ne sert plus à rien là
     (le tap y est déjà désactivé, section 29) : on le sort de l'arbre. */
  html.doc-scroll .panel-overlay { display: none; }
}

/* --------------------------------------------------------------------------
   31. LUNORFILMS EN FORMAT POCHE — EN-TÊTE DE MARQUE, AFFICHES VERTICALES  (v=134)

   Adaptation de la page « galerie des créations » (LunorFilms) au téléphone.
   Tout est sous le seuil tactile — le desktop ne voit rien de tout ça.

   A. L'EN-TÊTE. Le kicker « GALERIE DES CRÉATIONS » est calé au centre EXACT
      (réglage global desktop) et passait SOUS « NOUS CONTACTER » : masqué. Le
      bouton SON, sans objet (la vidéo de bannière est retirée), est masqué aussi.
      Restent « Accueil » à gauche, « NOUS CONTACTER » à droite.

   B. LA BANNIÈRE = LA MARQUE, RIEN D'AUTRE. Plus de vidéo (retirée du DOM en JS),
      et on retire aussi TOUT ce qui l'accompagnait : le badge, le titre, le pitch,
      les boutons LECTURE / PLUS D'INFOS. Il ne reste que le logotype LUNORFILMS,
      AGRANDI, et sa ligne « GALERIE DES CRÉATIONS · RENNES, FR » — un en-tête de
      marque sobre, puis les rangées.

   C. LES RANGÉES, DENSES ET CLIQUABLES.
      · Affiches verticales 2:3, trois de front plus l'amorce (recadrage central
        de la vignette 16:9, comme la fiche catégorie, section 25).
      · AUCUN bouton sur la carte (ni play ni flèche, retirés du DOM en JS) : c'est
        la carte ENTIÈRE qui est cliquable et ouvre la fiche du film. Sur mobile on
        n'attache plus la machinerie de survol (aperçu vidéo + agrandissement),
        qui, émulée au doigt, interférait avec le tap et alourdissait la page.
      · Rythme resserré façon catalogue mobile : on annule la grande RÉSERVE
        verticale de la piste (136/160px, prévue pour la lueur du survol desktop,
        inutile ici) et on rapproche les rangées.
   -------------------------------------------------------------------------- */
@media (max-width: 1023.98px) {

  /* A. En-tête : kicker centré + bouton son retirés (bornés à #discoverPage :
     LunorStills partage l'en-tête, non touché ici). */
  #discoverPage .discover__kicker { display: none; }
  #discoverPage .discover__header-actions .icon-btn { display: none; }

  /* B. Bannière réduite à la marque. La vidéo est retirée en JS ; on masque en
     plus, par sécurité, la vidéo (si elle subsistait) ET tout le bloc de contenu
     (badge / titre / pitch / boutons). */
  #discoverPage:not(.is-cinema) .discover__hero {
    height: auto; min-height: 0;
    display: block;
    /* réserve la hauteur de l'en-tête absolu (top:0) sous lequel la marque
       ne doit pas passer */
    padding: calc(78px + env(safe-area-inset-top)) 0 22px;
    background-image: none !important;   /* !important : bat le style inline du JS */
  }
  #discoverPage:not(.is-cinema) .discover__hero::before { display: none; }
  #discoverPage:not(.is-cinema) .discover__hero-video { display: none; }
  #discoverPage:not(.is-cinema) .discover__hero-content { display: none; }

  /* La marque passe dans le flux ; le logotype est agrandi, la ligne
     « GALERIE DES CRÉATIONS · RENNES, FR » reste dessous. */
  #discoverPage:not(.is-cinema) .discover__brand {
    position: static;
    padding: 0 20px;
  }
  #discoverPage:not(.is-cinema) .discover__wordmark {
    font-size: clamp(2.6rem, 12vw, 3.4rem);
  }
  #discoverPage:not(.is-cinema) .discover__brand .brandmark__tag {
    margin-top: 18px; font-size: 0.62rem;
  }

  /* C. Rangées façon Netflix mobile : affiches verticales 2:3, resserrées. */
  #discoverPage .discover__rows { padding-top: 6px; }
  #discoverPage .discover-row { margin-bottom: 16px; }
  #discoverPage .discover-row__title {
    margin-left: 20px; margin-right: 20px; margin-bottom: 8px;
  }

  /* La piste : on annule la grande réserve verticale du survol desktop
     (padding 136/160 + margin -130) — inutile sans agrandissement, elle gonflait
     la hauteur et le coût de peinture. Les marges latérales viennent de la
     section 25. */
  #discoverPage .discover-row__track {
    padding-top: 0; padding-bottom: 0;
    margin-top: 0; margin-bottom: 0;
  }

  #discoverPage .discover-row__card,
  #discoverPage .discover-row__card-placeholder {
    flex-basis: clamp(112px, 30vw, 150px);
    aspect-ratio: 2 / 3;
  }
  #discoverPage .discover-row__card .tag {
    top: 7px; left: 7px; font-size: 0.5rem; padding: 2px 6px;
  }
  #discoverPage .discover-row__card .info { left: 9px; right: 9px; bottom: 9px; }
  #discoverPage .discover-row__card .director { font-size: 0.55rem; }
  #discoverPage .discover-row__card .title {
    font-size: 0.8rem; line-height: 1.2;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden;
  }

  /* ---- D. Tags & textes qui s'affichent partout (et pas qu'en haut) --------
     Au repos, .scrim/.tag/.info sont promus sur leur propre couche GPU
     (translateZ(0)) pour rester au-dessus de la vidéo d'aperçu au démarrage.
     Cette vidéo n'existe plus sur mobile — la promotion ne sert donc à rien, et
     iOS Safari, quand les couches se comptent par dizaines, en LÂCHE certaines :
     les cartes arrivées en cours de défilement perdaient leur tag et leur titre.
     On retire la promotion : mêmes plans (le z-index suffit), mais tout se peint
     dans la couche de la carte, de façon fiable — et c'est plus léger. */
  #discoverPage .discover-row__card .scrim,
  #discoverPage .discover-row__card .tag,
  #discoverPage .discover-row__card .info {
    transform: none;
  }
  #discoverPage .discover-row__card .tag,
  #discoverPage .discover-row__card .info { opacity: 1; }

  /* ---- E. Le tap ouvre la fiche, et la rangée défile au doigt --------------
     La piste était en pointer-events:none (héritage desktop : elle laissait
     passer le survol vers les cartes agrandies, qui se chevauchaient). Sur
     mobile il n'y a plus ni agrandissement ni chevauchement — cette valeur
     empêchait juste d'attraper la piste pour la faire défiler. On la rétablit,
     avec l'inertie iOS. Et on rend les enfants de la carte transparents au
     doigt : chaque tap va DIRECTEMENT à la carte, qui ouvre la fiche — plus rien
     n'intercepte. */
  #discoverPage .discover-row__track {
    pointer-events: auto;
    -webkit-overflow-scrolling: touch;
  }
  #discoverPage .discover-row__card { pointer-events: auto; cursor: pointer; }
  #discoverPage .discover-row__card * { pointer-events: none; }
}
