/* ==========================================================================
   Louis Prima Jr. promo animation (adapted from Seth's Opus "hero" drop)
   Every rule is scoped under #promo-anim so nothing leaks into the page.
   The stage fills the 4:5 #promo-anim slot; #promo-anim itself is not restyled.
   Asset URLs are relative to this file (/promo-anim/assets/).
   ========================================================================== */


#promo-anim .lpj-hero {
  /* ---- Tunables (override on .lpj-hero or #your-id) ---- */
  --lpj-bg-top: #140b07;
  --lpj-bg-mid: #1f120a;
  --lpj-bg-bottom: #0a0806;
  --lpj-light: 255, 198, 128;          /* warm stage light (r, g, b) */
  --lpj-brass: 214, 160, 84;           /* brass accent (r, g, b) */
  --lpj-subject-x: 50%;                /* centred in the portrait slot */
  --lpj-subject-h: 84;                 /* performer height in % of slot height */

  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* No box: the stage dissolves into the page on all four sides */
  /* No box. The WebGL stage is transparent (only Louis, his floor pool and the light
     render), and this guard mask just softens anything that reaches the slot edge. */
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0%, #000 14%, #000 86%, transparent 100%),
    linear-gradient(180deg, transparent 0%, #000 12%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(90deg, transparent 0%, #000 14%, #000 86%, transparent 100%),
    linear-gradient(180deg, transparent 0%, #000 12%, #000 93%, transparent 100%);
          mask-composite: intersect;
  overflow: visible;
  overflow: hidden;
  isolation: isolate;
  background: transparent;
  color: #fff;
  contain: layout paint;
}

/* --------------------------------------------------------------------------
   Scene + layers
   -------------------------------------------------------------------------- */
#promo-anim .lpj-hero__scene {
  position: absolute;
  inset: 0;
  perspective: 1100px;
  perspective-origin: var(--lpj-subject-x) 40%;
  pointer-events: none;
}

#promo-anim .lpj-layer {
  position: absolute;
  inset: 0;
  will-change: transform;
  backface-visibility: hidden;
  transform: translate3d(0, 0, 0);
}

/* Layers that translate get bleed so edges never show */
#promo-anim .lpj-layer--bg,
#promo-anim .lpj-layer--beams,
#promo-anim .lpj-layer--smoke,
#promo-anim .lpj-layer--dust { inset: -8% -8%; }

/* 1. Base stage: gradients only (no image request) */
#promo-anim .lpj-bg {
  position: absolute;
  inset: 0;
  background:
    /* back-light halo behind the performer: matches the rim light in the photo */
    radial-gradient(ellipse 24% 34% at var(--lpj-subject-x) 36%,
      rgba(var(--lpj-light), .50) 0%,
      rgba(var(--lpj-light), .20) 38%,
      rgba(var(--lpj-light), 0) 72%),
    /* wide warm wash */
    radial-gradient(ellipse 60% 70% at var(--lpj-subject-x) 40%,
      rgba(var(--lpj-brass), .22) 0%,
      rgba(var(--lpj-brass), .06) 45%,
      rgba(var(--lpj-brass), 0) 75%),
    /* pool of light on the stage floor */
    radial-gradient(ellipse 36% 12% at var(--lpj-subject-x) 93%,
      rgba(var(--lpj-light), .30) 0%,
      rgba(var(--lpj-light), 0) 70%),
    linear-gradient(180deg, var(--lpj-bg-top) 0%, var(--lpj-bg-mid) 62%, var(--lpj-bg-bottom) 100%);
}

/* Film grain to dither the gradients (prevents banding). Deferred. */
#promo-anim .lpj-grain {
  position: absolute;
  inset: 0;
  opacity: 0;
  mix-blend-mode: overlay;
  background-size: 256px 256px;
  transition: opacity 1.2s ease;
}
#promo-anim .lpj-hero.is-loaded .lpj-grain {
  background-image: url("assets/grain.webp");
  opacity: .07;
}

/* 2. Light beams (deferred) */
#promo-anim .lpj-beams {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: center top / cover no-repeat;
  transition: opacity 2.4s ease;
}
#promo-anim .lpj-hero.is-loaded .lpj-beams {
  background-image: url("assets/beams.webp");
  opacity: .95;
  animation: lpj-beam-breathe 11s ease-in-out infinite alternate;
}
@keyframes lpj-beam-breathe {
  from { opacity: .80; }
  to   { opacity: 1; }
}

/* 3. Smoke: a 200%-wide track drifts sideways (transform-only animation) */
#promo-anim .lpj-smoke {
  position: absolute;
  left: 0;
  width: 100%;
  overflow: hidden;
  opacity: 0;
  transition: opacity 3s ease;
}
#promo-anim .lpj-smoke--back { top: 30%; height: 62%; }
#promo-anim .lpj-smoke--front { top: 52%; height: 56%; }

#promo-anim .lpj-smoke__track {
  position: absolute;
  inset: 0 auto 0 0;
  width: 200%;
  background-repeat: repeat-x;
  background-size: 50% 100%;
  will-change: transform;
}
#promo-anim .lpj-hero.is-loaded .lpj-smoke__track { background-image: url("assets/smoke.webp"); }
#promo-anim .lpj-hero.is-loaded .lpj-smoke--back { opacity: .75; }
#promo-anim .lpj-hero.is-loaded .lpj-smoke--front { opacity: .38; }
#promo-anim .lpj-smoke--back  .lpj-smoke__track { animation: lpj-drift 140s linear infinite; }
#promo-anim .lpj-smoke--front .lpj-smoke__track { animation: lpj-drift 90s linear infinite reverse; transform-origin: 0 0; }

@keyframes lpj-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* 4. The performer */
#promo-anim .lpj-layer--subject {
  transform-style: preserve-3d;
}
#promo-anim .lpj-subject {
  position: absolute;
  bottom: 7%;
  left: var(--lpj-subject-x);
  height: calc(var(--lpj-subject-h) * 1%);
  aspect-ratio: 2286 / 4955;           /* matches the cutout */
  transform: translate3d(-50%, 0, 0);
  transform-origin: 50% 100%;          /* pivots at the feet, so he stays planted */
  transform-style: preserve-3d;
}
#promo-anim .lpj-subject__rig {
  position: absolute;
  inset: 0;
  transform-origin: 50% 100%;
  transform-style: preserve-3d;
  will-change: transform;
}
#promo-anim .lpj-subject__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* Soft contact shadow + warm bounce under the feet */
#promo-anim .lpj-subject__shadow {
  position: absolute;
  left: -32%;
  right: -32%;
  bottom: -1.6%;
  height: 4.5%;
  background: radial-gradient(closest-side, rgba(0, 0, 0, .72), rgba(0, 0, 0, .35) 55%, rgba(0, 0, 0, 0));
  transform: translateZ(-1px);
}

/* Moving key light across the performer (masked to his silhouette) */
#promo-anim .lpj-subject__sheen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  -webkit-mask: var(--lpj-mask, none) center / contain no-repeat;
          mask: var(--lpj-mask, none) center / contain no-repeat;
  transition: opacity 1.5s ease;
}
#promo-anim .lpj-hero.is-ready .lpj-subject__sheen { opacity: 1; }
#promo-anim .lpj-subject__sheen-band {
  position: absolute;
  top: -10%;
  bottom: -10%;
  left: -60%;
  width: 220%;
  background: linear-gradient(100deg,
    rgba(var(--lpj-light), 0) 38%,
    rgba(var(--lpj-light), .14) 47%,
    rgba(255, 236, 205, .20) 50%,
    rgba(var(--lpj-light), .14) 53%,
    rgba(var(--lpj-light), 0) 62%);
  will-change: transform;
}

/* Trumpet-bell glint (foreground flare, anchored to the bell rim) */
#promo-anim .lpj-glint {
  position: absolute;
  left: 53%;
  top: 1.1%;
  width: 46%;
  aspect-ratio: 1;
  margin: -23% 0 0 -23%;               /* centre on the anchor point */
  pointer-events: none;
  opacity: 0;
  transition: opacity 2s ease;
}
#promo-anim .lpj-hero.is-loaded .lpj-glint { opacity: 1; }
#promo-anim .lpj-glint__star,
#promo-anim .lpj-glint__bloom {
  position: absolute;
  inset: 0;
  background: center / contain no-repeat;
  will-change: transform, opacity;
}
#promo-anim .lpj-hero.is-loaded .lpj-glint__star { background-image: url("assets/glint.webp"); }
#promo-anim .lpj-glint__bloom {
  inset: 30%;
  background: radial-gradient(closest-side, rgba(255, 230, 190, .42), rgba(var(--lpj-light), .12) 45%, rgba(var(--lpj-light), 0));
}

/* 5. Foreground dust (deferred) */
#promo-anim .lpj-dust {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: center / cover no-repeat;
  transition: opacity 3s ease;
}
#promo-anim .lpj-hero.is-loaded .lpj-dust {
  background-image: url("assets/bokeh.webp");
  opacity: .9;
}

/* 6. Vignette + text scrim (static) */
#promo-anim .lpj-hero__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 120% 95% at 50% 45%, rgba(0, 0, 0, 0) 52%, rgba(0, 0, 0, .6) 100%);
}

/* --------------------------------------------------------------------------
   3D stage (WebGL). hero.js inserts <canvas class="lpj-canvas"> and adds
   .is-gl once the first frame is ready; the CSS layers then fade away.
   -------------------------------------------------------------------------- */
#promo-anim .lpj-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity .9s ease;
}
#promo-anim .lpj-hero.is-gl .lpj-canvas { opacity: 1; }
#promo-anim .lpj-hero .lpj-layer { transition: opacity .6s ease .9s; }
#promo-anim .lpj-hero.is-gl .lpj-layer { opacity: 0; visibility: hidden; transition: opacity .6s ease .9s, visibility 0s linear 1.5s; }
#promo-anim .lpj-hero.is-gl .lpj-hero__vignette {
  background:
    radial-gradient(ellipse 125% 100% at 50% 45%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, .55) 100%);
}
#promo-anim .lpj-hero.is-gl { cursor: default; }

/* --------------------------------------------------------------------------
   Intro reveal (only when JS is running; no-JS shows everything immediately)
   -------------------------------------------------------------------------- */
#promo-anim .lpj-hero.lpj-js .lpj-subject__img,
#promo-anim .lpj-hero.lpj-js .lpj-subject__shadow {
  opacity: 0;
  transition: opacity 1.4s cubic-bezier(.2, .7, .2, 1);
}
#promo-anim .lpj-hero.lpj-js.is-ready .lpj-subject__img,
#promo-anim .lpj-hero.lpj-js.is-ready .lpj-subject__shadow { opacity: 1; }

/* --------------------------------------------------------------------------
   Reduced motion: a still image. No drifting, no tilt, no transitions.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  #promo-anim .lpj-hero *,
#promo-anim .lpj-hero *::before,
#promo-anim .lpj-hero *::after {
    animation: none !important;
    transition: none !important;
  }
  #promo-anim .lpj-layer,
#promo-anim .lpj-subject__rig,
#promo-anim .lpj-subject__sheen-band { will-change: auto; }
}
#promo-anim .lpj-hero.is-static .lpj-layer,
#promo-anim .lpj-hero.is-static .lpj-subject__rig { transform: none !important; }
#promo-anim .lpj-hero.is-static .lpj-subject { transform: translate3d(-50%, 0, 0) !important; }

/* Slot extras: lighter haze, as on phones (the slot is small) */
#promo-anim .lpj-smoke--front { opacity: 0 !important; }
#promo-anim .lpj-smoke__track { width: 400%; }

/* In the slot, the room (curtain, beams, haze, dust) is drawn by the section stage below,
   so the 2.5D/still version keeps only Louis, his glint and a floor pool. */
#promo-anim .lpj-bg {
  background: radial-gradient(ellipse 42% 9% at 50% 92%, rgba(var(--lpj-light), .22) 0%, rgba(var(--lpj-light), 0) 70%);
}
#promo-anim .lpj-grain,
#promo-anim .lpj-layer--beams,
#promo-anim .lpj-layer--smoke-back,
#promo-anim .lpj-layer--smoke-front,
#promo-anim .lpj-layer--dust,
#promo-anim .lpj-hero__vignette { display: none; }

/* Warm back-light around Louis, outside the slot so it never shows an edge */
#promo-anim .pa-halo {
  position: absolute;
  inset: -30% -55% -20%;
  pointer-events: none;
  background:
    radial-gradient(closest-side at 50% 42%, rgba(255, 198, 128, .26), rgba(214, 160, 84, .09) 55%, rgba(214, 160, 84, 0) 100%);
}

/* ==========================================================================
   Section stage: the whole #promo tile becomes the animation's room
   (warm-brown curtain, spotlight beams, drifting haze, bokeh, grain).
   Textures load only once the section nears the viewport (.is-on, set by anim.js).
   ========================================================================== */
#promo { position: relative; isolation: isolate; overflow: hidden; background: var(--ink, #0a0806); }
#promo > .wrap { position: relative; z-index: 1; }
#promo .pstage { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
#promo .pstage > div { position: absolute; inset: 0; }

/* curtain: fades in from the page colour at the top and back out at the bottom */
#promo .pstage__bg {
  background:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, .16) 3.2%, rgba(0, 0, 0, 0) 6.4%),
    linear-gradient(180deg, #0a0806 0%, #150c07 14%, #1e1109 46%, #1a0f08 72%, #0d0805 90%, #0a0806 100%);
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,.35) 0%, #000 18%, #000 82%, rgba(0,0,0,.35) 100%);
          mask-image: linear-gradient(180deg, rgba(0,0,0,.35) 0%, #000 18%, #000 82%, rgba(0,0,0,.35) 100%);
}
/* stage floor glow along the bottom */
#promo .pstage__floor {
  background: radial-gradient(ellipse 60% 18% at 62% 86%, rgba(255, 198, 128, .10), rgba(255, 198, 128, 0) 70%);
}
#promo .pstage__beams,
#promo .pstage__smoke,
#promo .pstage__dust,
#promo .pstage__grain {
  opacity: 0;
  transition: opacity 2.2s ease;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 80%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 80%, transparent 100%);
}
#promo .pstage__beams { background: center top / cover no-repeat; }
#promo .pstage__beams--b { transform: scaleX(-1); background-position: 30% top; }
#promo .pstage__smoke { top: 28%; overflow: hidden; }
#promo .pstage__smoke > div {
  position: absolute; inset: 0 auto 0 0; width: 200%;
  background-repeat: repeat-x; background-size: 50% 100%;
  animation: lpj-drift 150s linear infinite; will-change: transform;
}
#promo .pstage__dust { background: center / cover no-repeat; }
#promo .pstage__grain { background-size: 256px 256px; mix-blend-mode: overlay; }

#promo .pstage.is-on .pstage__beams { background-image: url("assets/beams.webp"); opacity: .9; animation: lpj-beam-breathe 11s ease-in-out infinite alternate; }
#promo .pstage.is-on .pstage__beams--b { opacity: .45; animation-delay: -5s; }
#promo .pstage.is-on .pstage__smoke { opacity: .55; }
#promo .pstage.is-on .pstage__smoke > div { background-image: url("assets/smoke.webp"); }
#promo .pstage.is-on .pstage__dust { background-image: url("assets/bokeh.webp"); opacity: .75; }
#promo .pstage.is-on .pstage__grain { background-image: url("assets/grain.webp"); opacity: .06; }

/* keep the teaser crisp and readable on top of the room */
#promo .promo-vid .yt { box-shadow: 0 24px 60px rgba(0, 0, 0, .55); }

@media (max-width: 820px) {
  #promo .pstage__beams { top: 30%; background-size: cover; background-position: 77% top; }
  #promo .pstage__beams--b { display: none; }
  #promo .pstage__floor { background: radial-gradient(ellipse 80% 14% at 50% 90%, rgba(255, 198, 128, .10), rgba(255, 198, 128, 0) 70%); }
  #promo .pstage__smoke { top: 45%; }
  #promo .pstage__smoke > div { width: 400%; }
}

@media (prefers-reduced-motion: reduce) {
  #promo .pstage *, #promo .pstage *::before { animation: none !important; transition: none !important; }
}
