/* ══════════════════════════════════════════════════════════════════════
   Intro — logo, effacement, vidéo slogan, puis le site.

   L'intro repasse en FOND NOIR, alors que le site est clair. Ce n'est pas
   une incohérence : l'ouverture est un plan de présentation, et c'est
   précisément la raison pour laquelle le logo s'y compose en `screen` — un
   mode qui ne fonctionne QUE sur fond noir, et qui avait été abandonné quand
   l'intro était claire.

  Le lockup est désormais PAYSAGE (ratio 1,735, mesuré), contre 1,000
   pour l'ancien logo carré. Le dimensionnement suit : on contraint la
   LARGEUR, la hauteur suit le ratio. Une règle en `vh` donnerait un logo de
   700 px de haut donc 1214 px de large — hors champ sur la plupart des écrans.

   Points de conception :
   - l'overlay est CRÉÉ PAR LE JS. Écrit dans le HTML, il masquerait le
     site pour toujours si le script ne s'exécute pas (cf. bug de page
     blanche corrigé sur les blocs .reveal).
   - fond #020412 : les coins du logo mesurent #03080f / #02070e / #020508,
     soit 2 à 3 niveaux RGB d'écart. En dessous du seuil de perception, et
     `screen` absorbe le reste -> aucune couture autour du logo.
   - la vidéo est encadrée : sur fond sombre, une bordure gris ardoise et
     une ombre portée évitent qu'elle « colle » au plan.
   - tout est piloté par changement de classe + transition CSS : aucune
     boucle d'animation JS à maintenir, et l'intro se met en pause toute
     seule quand l'onglet passe en arrière-plan.
   ══════════════════════════════════════════════════════════════════════ */

.intro {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: #020412;
  overflow: hidden;
  /* La séquence n'est pas une expérience de défilement : on fige. */
}

/* ── Calques ────────────────────────────────────────────────────────── */
.intro__layer {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 1;
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--intro-wipe, 0.85s) cubic-bezier(0.72, 0, 0.2, 1);
}

/**
 * Cadre de la vidéo.
 *
 * La vidéo est en 16/9 (1280×720) et le poster est mesuré bord à bord
 * (0 px de marge) : à `inset: 0` + `object-fit: cover` elle collait aux
 * bords de l'écran, d'où l'impression de « borderline ». On la détache donc
 * du plein écran et on lui donne une vraie marge : elle est centrée dans un
 * cadre, avec de l'air sur les côtés comme sur le haut et le bas.
 *
 * C'est ce cadre, et non la vidéo, qui porte le `clip-path` d'effacement :
 * le front balaie donc toute la fenêtre pendant que la vidéo reste à sa
 * place, cadrée.
 */
.intro__frame {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  /* Révélé depuis la GAUCHE : le logo part vers la droite, derrière lui. */
  clip-path: inset(0 0 0 100%);
  transition: clip-path var(--intro-wipe, 0.85s) cubic-bezier(0.72, 0, 0.2, 1);
}

.intro__video {
  display: block;
  /* Marge d'air : ~8 % de part et d'autre sur un écran 16/9. Le plafond en
     vh évite qu'un écran large ne pousse la vidéo hors de la hauteur. */
  width: min(84vw, 140vh);
  aspect-ratio: 16 / 9;
  object-fit: cover;
  opacity: 0;
  /* Sur fond noir, un liseré froid détache le cadre du plan sans l'éclaircir. */
  border-radius: 6px;
  box-shadow:
    0 0 0 1px rgba(148, 163, 184, 0.22),
    0 34px 80px -26px rgba(0, 0, 0, 0.9);
  transition: opacity 140ms linear;
}

.intro.is-wipe .intro__layer { clip-path: inset(0 0 0 100%); }
.intro.is-wipe .intro__frame { clip-path: inset(0 0 0 0); }
.intro.is-wipe .intro__video { opacity: 1; }

/* ── Logo ───────────────────────────────────────────────────────────── */
/**
 * Le nouveau lockup est PAYSAGE (ratio mesuré 1,735). On contraint donc la
 * LARGEUR et on laisse la hauteur suivre : `min(78vw, 1180px)` donne 1123 px
 * de large sur un écran 1440, soit 72 % de sa hauteur — le logo occupe
 * réellement l'écran sans déborder.
 *
 * `mix-blend-mode: screen` supprime le fond quasi noir baked du logo. Ce mode
 * ne vaut QUE sur fond noir : c'est la raison pour laquelle l'intro est
 * restée noire même après la bascule claire du site.
 */
.intro__logo {
  width: min(78vw, 1180px);
  /* `max-height` + `object-fit` : sans plafond de hauteur, le lockup (ratio
     1,735) mesurait 1065×614 sur un écran 1366×420 et 702×405 sur un
     900×400. Comme `.intro` est en `overflow: hidden`, le logo était
     simplement ROGNÉ en paysage — la marque coupée, sans erreur. */
  max-height: 72vh;
  height: auto;
  object-fit: contain;
  mix-blend-mode: screen;
  opacity: 0;
  transform: translateY(16px) scale(0.97);
  filter: blur(6px);
  transition: opacity 800ms ease, transform 1100ms cubic-bezier(0.22, 1, 0.36, 1),
              filter 800ms ease;
}
.intro.is-logo .intro__logo { opacity: 1; transform: none; filter: blur(0); }

/* Palier : respiration lente pendant que le moteur 3D compile. Douce, et
   assez lente pour ne pas être lue comme une animation d'attente. */
.intro.is-hold .intro__logo {
  animation: intro-breathe 3.4s ease-in-out infinite;
}
@keyframes intro-breathe {
  0%, 100% { transform: scale(1); filter: blur(0) brightness(1); }
  50% { transform: scale(1.02); filter: blur(0) brightness(1.12); }
}

/* ── Front d'effacement ─────────────────────────────────────────────── */
.intro__edge {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 3;
  width: 2px;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, #0891b2 18%, #22d3ee 50%, #0891b2 82%, transparent);
  box-shadow: 0 0 22px 4px rgba(8, 145, 178, 0.4);
  transform: translateX(-4px);
  transition: transform var(--intro-wipe, 0.85s) cubic-bezier(0.72, 0, 0.2, 1),
              opacity 140ms linear;
}
.intro.is-wipe .intro__edge { opacity: 1; transform: translateX(100vw); }

/* ── Commandes ──────────────────────────────────────────────────────── */
.intro__controls {
  position: absolute;
  z-index: 4;
  right: max(clamp(16px, 3vw, 34px), env(safe-area-inset-right));
  bottom: max(clamp(16px, 3vw, 30px), env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ── DÉFAUT CORRIGÉ : boutons invisibles mais TABBABLES ───────────────────
   Ils étaient masqués avec `opacity: 0`, qui ne retire PAS un élément du
   parcours de tabulation. Mesuré : pendant les ~2,9 s de la phase logo, Tab
   plaçait le focus sur « SON » puis « Passer », tous deux invisibles.
   `visibility: hidden` les retire réellement de l'ordre de tabulation (et
   `transition: visibility` permet la réapparition en fondu, sans casser
   l'animation d'opacité). */
.intro__btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Cible tactile : la hauteur tombait à ~31 px, sous les 40 px confortables. */
  min-height: 40px;
  padding: 10px 16px;
  border: 1px solid rgba(148, 163, 184, 0.26);
  border-radius: 999px;
  background: rgba(9, 14, 26, 0.62);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: #cbd5e1;
  font: 500 12.5px/1 var(--font-mono, monospace);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity 400ms ease, visibility 0s linear 400ms,
              border-color 200ms ease, color 200ms ease;
}
.intro.is-logo .intro__btn,
.intro.is-wipe .intro__btn,
.intro.is-video .intro__btn {
  opacity: 1;
  visibility: visible;
  transition: opacity 400ms ease, visibility 0s linear 0s,
              border-color 200ms ease, color 200ms ease;
}
.intro__btn:hover { color: #fff; border-color: rgba(8, 145, 178, 0.55); }
.intro__btn:focus-visible { outline: 2px solid #22d3ee; outline-offset: 3px; }
.intro__btn--sound { padding: 10px 13px; }

/* ── Filet de sécurité CSS ───────────────────────────────────────────────
   L'overlay est opaque et `html.intro-open` fige le défilement. Si le JS
   échouait entre l'ajout au DOM et la révélation, le visiteur serait bloqué
   SANS échappatoire : `intro.css` n'avait aucune sortie de secours.
   Cette animation retire l'overlay de lui-même, quel que soit l'état du
   script, et rend le défilement (`intro-open` est retiré par `playIntro` en
   temps normal ; ici la classe est neutralisée par `:has`).

   ⚠ Le délai doit DÉPASSER la séquence saine, sinon le filet coupe une intro
   valide. Séquence nominale, mesurée : logoIn 900 + logoHold 2000 + wipe 850
   + videoSettle 350 + durée réelle de `slogan.mp4` (10 006 ms, en-tête mvhd)
   ≈ 14,2 s à partir de l'insertion de l'overlay. Un plafond de 12 s — la
   valeur précédente — masquait donc l'overlay ~2 s AVANT la fin de la vidéo,
   sur un déroulé parfaitement normal : le visiteur ne voyait jamais la fin
   du slogan. 30 s laisse plus du double de marge.

   Pourquoi garder un filet CSS alors qu'il en existe un en JS : si le thread
   principal est réellement bloqué, le minuteur JS ne peut PAS s'exécuter.
   Une animation d'opacité sur un calque composité, elle, peut continuer
   d'avancer hors du thread principal — c'est la seule sortie qui reste. */
@keyframes intro-failsafe {
  0%, 92% { opacity: 1; visibility: visible; }
  100% { opacity: 0; visibility: hidden; }
}
.intro { animation: intro-failsafe 30s linear forwards; }
.intro.is-out { animation: none; }

@media (prefers-reduced-motion: reduce) {
  /* La préférence motion est respectée : l'overlay disparaît d'un coup. */
  .intro { animation: none; }
  .intro__logo { transition: none; }
  .intro.is-hold .intro__logo { animation: none; }
  .intro__edge { display: none; }
}

@media (max-width: 560px) {
  /* En portrait, le logo est limité par la largeur : on autorise un peu
     plus de largeur d'écran. */
  .intro__logo { width: 88vw; }
  .intro__video { width: 92vw; }
  .intro__btn { font-size: 11.5px; padding: 7px 12px; }
}

/* Écran très large et bas (16/10 et au-delà) : la vidéo garde sa marge
   verticale au lieu de coller au bord haut/bas. */
@media (min-aspect-ratio: 16/10) {
  .intro__video { width: min(80vw, 132vh); }
}