/* ============================================================================
   VOLTYR LABS — vnext V2 · SECTION « LE MOTEUR »
   Couche 3D TEMPS RÉEL (three.js r169, WebGL2) pilotée au scroll.
   C'est la technique d'Armory et de Trionn — pas notre scrub de séquence.

   ⚠️ PIÈGES TENUS ICI (cf. reference-scroll-3d.md § 5) :
   1. AUCUN `will-change` sur .scene3d-pin ni sur un de ses ancêtres. Un will-change
      persistant sur un élément épinglé sort le bloc du viewport → écran blanc pendant
      TOUT le pin, invisible autrement qu'au vrai scroll molette (piège déjà payé, aa13a8d).
   2. AUCUN `transform` / `filter` / `.reveal` sur .scene3d-scroll ni sur un ancêtre :
      ça crée un bloc conteneur pour le position:fixed du pin GSAP → le pin se cale sur
      l'ancêtre au lieu du viewport.
   3. AUCUN `mix-blend-mode` nulle part dans cette section. La couche additive porte son
      propre alpha (écrit dans le shader) : la page doit rester juste sans aucun blend CSS.
      C'est le contrôle de solidité de la référence.
   ============================================================================ */

.scene3d-section{
  /* pas de padding-bottom : le bloc épinglé enchaîne directement */
  padding:100px 0 0;
}
.scene3d-head{text-align:center;max-width:760px;margin:0 auto;}
.scene3d-head .eyebrow{justify-content:center;display:inline-flex;}
.scene3d-head .section-sub{margin:0 auto 0;text-align:center;}

/* --- piste de scroll : le pin consomme cette hauteur ---------------------- */
.scene3d-scroll{position:relative;margin-top:44px;}

/* --- le bloc épinglé ------------------------------------------------------ */
/* L'objet occupe les deux tiers hauts, le texte vit dans le tiers bas.
   Un objet plein cadre avale la page — la composition compte autant que le shader. */
.scene3d-pin{
  position:relative;
  min-height:100svh;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  padding-bottom:clamp(64px,11vh,120px);
  overflow:hidden;
}
/* voile bas : rend le texte lisible quelle que soit la maille derrière lui,
   sans mix-blend-mode (interdit ici) et sans toucher au canvas. */
.scene3d-pin::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:46%;z-index:1;
  background:linear-gradient(180deg,transparent,rgba(8,9,12,.55) 42%,rgba(8,9,12,.92));
  pointer-events:none;
}

/* --- la scène : plein cadre, derrière le HUD ------------------------------ */
.scene3d-stage{
  position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
}
.scene3d-canvas{
  display:block;width:100%;height:100%;
  /* pas de filter/backdrop-filter : le glow est fait dans le shader */
}

/* repli quand la scène est coupée (touche 0), refusée (reduced-motion,
   pas de WebGL2, économiseur de données) ou pas encore montée. */
.scene3d-fallback{
  position:absolute;inset:0;opacity:0;transition:opacity .5s ease;pointer-events:none;
  background:
    radial-gradient(38% 44% at 50% 50%, rgba(77,171,255,.20), transparent 68%),
    radial-gradient(72% 58% at 50% 52%, rgba(31,143,255,.07), transparent 72%);
}
.scene3d-fallback::after{
  content:"";position:absolute;left:50%;top:50%;width:min(46vmin,420px);aspect-ratio:1;
  transform:translate(-50%,-50%);border-radius:50%;
  border:1px solid rgba(77,171,255,.22);
  box-shadow:0 0 60px rgba(31,143,255,.18) inset, 0 0 80px rgba(31,143,255,.10);
}
.scene3d-section.is-fallback .scene3d-fallback{opacity:1;}
.scene3d-section.is-fallback .scene3d-canvas{opacity:0;}
/* Sans scène, plus rien ne fait avancer les étapes : empilées et toutes
   lisibles, sinon deux tiers du texte de la section sont perdus pour de bon
   (pas de WebGL2, économiseur de données, contexte perdu, touche 0). */
.scene3d-section.is-fallback .scene3d-steps{height:auto;display:flex;flex-direction:column;gap:26px;}
.scene3d-section.is-fallback .scene3d-step{opacity:1;position:relative;transform:none;}

/* --- HUD : les trois temps de la scène ------------------------------------ */
.scene3d-hud{
  position:relative;z-index:2;width:100%;max-width:1180px;padding:0 32px;
  display:flex;flex-direction:column;align-items:center;gap:26px;
  pointer-events:none;
}
.scene3d-steps{
  position:relative;height:112px;width:100%;max-width:560px;
}
.scene3d-step{
  position:absolute;inset:0;text-align:center;
  opacity:0;transform:translateY(12px);
  transition:opacity .55s cubic-bezier(.16,1,.3,1),transform .55s cubic-bezier(.16,1,.3,1);
}
.scene3d-step.on{opacity:1;transform:none;}
.scene3d-step b{
  display:block;font-family:var(--font-display);font-weight:700;
  font-size:clamp(21px,2.6vw,29px);letter-spacing:-.02em;line-height:1.16;margin-bottom:10px;
  text-shadow:0 0 24px rgba(31,143,255,.35);
}
.scene3d-step span{
  display:block;font-size:15.5px;color:var(--text-muted);max-width:46ch;margin:0 auto;
}

/* barre de progression de la scène (même grammaire que .chain-progress) */
.scene3d-progress{
  width:min(320px,58vw);height:2px;border-radius:2px;background:rgba(255,255,255,.09);overflow:hidden;
}
.scene3d-progress i{
  display:block;width:100%;height:100%;transform-origin:0 50%;transform:scaleX(0);
  background:linear-gradient(90deg,var(--electric-bright),var(--electric-deep));
  box-shadow:0 0 12px var(--glow);
}

/* légende clavier — OUTIL DE RÉGLAGE, PAS UN ÉLÉMENT DU SITE.
   🔴 CACHÉE PAR DÉFAUT (Julien 02/08). Elle permet de juger ce que chaque
   couche ajoute : on la garde, mais derrière ?debug (classe `debug3d` sur
   <html>). display:none par défaut = aucun flash au chargement. */
.scene3d-keys{ display:none; }
html.debug3d .scene3d-keys{
  position:absolute;left:50%;bottom:22px;transform:translateX(-50%);
  z-index:3;pointer-events:auto;
  display:flex;flex-wrap:wrap;gap:6px 14px;justify-content:center;
  font-family:var(--font-body);font-size:11.5px;letter-spacing:.04em;color:var(--text-faint);
  background:rgba(255,255,255,.03);border:1px solid var(--line);border-radius:999px;
  padding:7px 16px;backdrop-filter:blur(8px);
  transition:opacity .3s;
}
html.debug3d .scene3d-keys kbd{
  font-family:var(--font-body);font-size:11px;color:var(--electric-bright);
  border:1px solid rgba(77,171,255,.28);border-radius:5px;padding:1px 5px;margin-right:4px;
  background:rgba(31,143,255,.10);
}
html.debug3d .scene3d-keys.hidden{opacity:0;pointer-events:none;}

/* --- mode ISOLÉ (touche 9) : le reste de la page disparaît ---------------- */
html.scene3d-solo body > *:not(.scene3d-solo-keep){display:none !important;}
html.scene3d-solo .scene3d-section > .container{opacity:.5;}
html.scene3d-solo #cursor-glow{display:none;}

/* --- mobile / tablette : scène DÉGRADÉE, sans pin -------------------------
   ⚠️ Ce qui touche à la HAUTEUR du bloc épinglé est piloté par la classe
   `.scene3d-petit`, posée par le JS depuis le MÊME matchMedia que le profil.
   En media query pure, franchir 820 px en cours de session ferait changer la
   hauteur sans que le JS change de profil : le pin resterait actif sur un bloc
   raccourci et laisserait une bande vide de 22 % de l'écran. */
@media (max-width:820px){
  .scene3d-section{padding-top:76px;}
  .scene3d-scroll{margin-top:28px;}
  .scene3d-hud{padding:0 20px;gap:18px;}
}
.scene3d-section.scene3d-petit .scene3d-pin{min-height:78svh;}
.scene3d-section.scene3d-petit .scene3d-steps{height:136px;}
.scene3d-section.scene3d-petit .scene3d-keys{display:none;}   /* pas de clavier sur téléphone */

/* --- accessibilité : aucune scène, aucun pin, aucune boucle --------------- */
@media (prefers-reduced-motion:reduce){
  .scene3d-fallback{opacity:1;}
  .scene3d-canvas{display:none;}
  .scene3d-step{opacity:1;position:relative;transform:none;}
  .scene3d-steps{height:auto;display:flex;flex-direction:column;gap:26px;}
  .scene3d-keys{display:none;}
}
