/* ============================================================
   Stardust Materialize — page-load reveal sequence
   ------------------------------------------------------------
   The .is-intro class is set on <html> by an inline boot script
   in index.html before first paint, then removed by scripts/intro.js
   after a brief lead time. The removal triggers a staggered cascade
   via the per-element transition-delays below.

   Two patterns are used:

   (A) IMAGE-BASED LAYERS (characters, eyes, table, globe-layer)
       use a "base hidden + reveal on :not(.is-intro)" pattern. The
       hidden opacity is set in the always-on base rule so cached
       <img> elements can never flash visible before .is-intro
       resolves. The reveal rule has higher specificity (html tag
       + :not pseudo) so it takes over when .is-intro is removed.

   (B) NON-IMAGE ELEMENTS (wordmark/SVG, tagline/text, kicker/text,
       vignette/pseudo, orbit-field/borders, globe-pulse/pseudo)
       use the simpler "hidden via .is-intro, transition out" pattern.
       These have no raster data to flash, so FOUC isn't a risk.

   Timeline (anchored to .is-intro removal at t=0):
   t=0.0s   Dust burst already streaming inward (state.intro flag)
   t=0.4s   Astronaut materializes from blur
   t=0.6s   Sage materializes (offset stagger)
   t=0.8s   Table rises from below
   t=1.1s   Globe scales in + warm pulse blooms outward
   t=1.4s   Wordmark soft-blur bloom + drop
   t=1.8s   Tagline fades up
   t=2.0s   Kicker fades in
   t=2.2s   Eyes open, vignette resolves, dust returns to ambient
   ============================================================ */

/* ============================================================
   PATTERN A — IMAGE-BASED LAYERS
   Base hidden state is always-on. Reveal via html:not(.is-intro).
   ============================================================ */

/* --- Base hidden state (always-on, prevents FOUC on cached load) --- */

.layer-astronaut,
.layer-sage,
.eye-astronaut,
.eye-sage,
.layer-table,
.globe-layer {
  opacity: 0;
}

/* Character + eye blur is layered on only while .is-intro is active.
   When removed, filter goes back to default (none) and transitions. */
.is-intro .layer-astronaut,
.is-intro .layer-sage,
.is-intro .eye-astronaut,
.is-intro .eye-sage {
  filter: blur(14px) brightness(1.18);
}

/* Slide-in offsets — astronaut enters from the left edge of the screen,
   sage from the right. Using the modern `translate` property (not the
   `transform` shorthand) keeps this offset isolated from the layer's
   parallax-aware transform on .layer, so the two compose cleanly
   instead of one having to clobber the other. */
.is-intro .layer-astronaut,
.is-intro .eye-astronaut {
  translate: -5vw 0;
}

.is-intro .layer-sage,
.is-intro .eye-sage {
  translate: 5vw 0;
}

/* Table rise — layered transform only during .is-intro. */
.is-intro .layer-table {
  transform: var(--anchor-transform) translate3d(var(--layer-x), calc(var(--layer-y) + 6%), 0);
}

/* Globe scale-in — same pattern. */
.is-intro .globe-layer {
  transform: scale(0.78);
  transform-origin: 50% 50%;
}

/* --- Transitions (always-on; fire when reveal rule swaps in) --- */

.layer-astronaut,
.eye-astronaut {
  transition:
    opacity 900ms cubic-bezier(0.22, 1, 0.36, 1) 400ms,
    filter 900ms cubic-bezier(0.22, 1, 0.36, 1) 400ms,
    translate 1100ms cubic-bezier(0.22, 1, 0.36, 1) 400ms;
}

.layer-sage,
.eye-sage {
  transition:
    opacity 900ms cubic-bezier(0.22, 1, 0.36, 1) 600ms,
    filter 900ms cubic-bezier(0.22, 1, 0.36, 1) 600ms,
    translate 1100ms cubic-bezier(0.22, 1, 0.36, 1) 600ms;
}

.layer-table {
  transition:
    opacity 950ms cubic-bezier(0.22, 1, 0.36, 1) 800ms,
    transform 1100ms cubic-bezier(0.22, 1, 0.36, 1) 800ms;
}

.globe-layer {
  transition:
    opacity 900ms cubic-bezier(0.22, 1, 0.36, 1) 1100ms,
    transform 1100ms cubic-bezier(0.22, 1, 0.36, 1) 1100ms;
}

/* --- Reveal state (higher specificity beats the base opacity:0) --- */

html:not(.is-intro) .layer-astronaut,
html:not(.is-intro) .layer-sage,
html:not(.is-intro) .eye-astronaut,
html:not(.is-intro) .eye-sage,
html:not(.is-intro) .layer-table,
html:not(.is-intro) .globe-layer {
  opacity: 1;
}

/* ============================================================
   PATTERN B — NON-IMAGE ELEMENTS
   Hidden via .is-intro, transitions on base styles.
   ============================================================ */

/* Wordmark — heavy bloom + edge glow during intro, softens to a
   subtle persistent cream halo. Filter lists in both states must have
   IDENTICAL structure (same primitives, same order) for smooth
   per-component interpolation — keep blur(0)/brightness(1) on the
   reveal side rather than dropping them. */
.is-intro .brand-mark {
  opacity: 0;
  filter:
    blur(20px)
    brightness(1.35)
    drop-shadow(0 0 24px rgba(240, 229, 193, 0.7))
    drop-shadow(0 0 48px rgba(240, 229, 193, 0.4))
    drop-shadow(0 10px 14px rgba(32, 4, 3, 0.18));
  transform: translateX(-50%) translateY(-22px);
}

.is-intro .tagline {
  opacity: 0;
  filter: blur(12px);
  transform: translateY(10px);
}

.is-intro .kicker {
  opacity: 0;
}

.is-intro .orbit-field {
  opacity: 0;
}

.is-intro .bubble,
.is-intro .bubble-curve {
  opacity: 0 !important;
  visibility: hidden;
}

.brand-mark {
  /* Cream-glow drop-shadows fade to alpha 0 during the reveal so the
     wordmark settles to only its dark ground shadow — no persistent
     halo. Filter list mirrors the intro state's primitives in the
     same order so each component interpolates cleanly. */
  filter:
    blur(0)
    brightness(1)
    drop-shadow(0 0 24px rgba(240, 229, 193, 0))
    drop-shadow(0 0 48px rgba(240, 229, 193, 0))
    drop-shadow(0 10px 14px rgba(32, 4, 3, 0.18));
  transition:
    opacity 1000ms cubic-bezier(0.22, 1, 0.36, 1) 1400ms,
    filter 1200ms cubic-bezier(0.22, 1, 0.36, 1) 1400ms,
    transform 1100ms cubic-bezier(0.22, 1, 0.36, 1) 1400ms;
}

.tagline {
  transition:
    opacity 900ms cubic-bezier(0.22, 1, 0.36, 1) 1800ms,
    filter 900ms cubic-bezier(0.22, 1, 0.36, 1) 1800ms,
    transform 900ms cubic-bezier(0.22, 1, 0.36, 1) 1800ms;
}

.kicker {
  transition: opacity 600ms cubic-bezier(0.22, 1, 0.36, 1) 2000ms;
}

.orbit-field {
  transition: opacity 1800ms cubic-bezier(0.22, 1, 0.36, 1) 1600ms;
}

/* ============================================================
   KICKER TYPEWRITER — cycling phrases (no cursor)
   ============================================================ */

.kicker-text {
  display: inline-block;
  white-space: pre;
}

/* ============================================================
   GLOBE PULSE — radial bloom that fires once on intro completion
   ============================================================ */

.globe-layer::before {
  content: "";
  position: absolute;
  inset: -28%;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 218, 162, 0.7) 0%,
    rgba(255, 168, 110, 0.32) 22%,
    rgba(255, 130, 90, 0.12) 42%,
    transparent 58%
  );
  opacity: 0;
  pointer-events: none;
  z-index: -1;
  filter: blur(8px);
}

:root:not(.is-intro) .globe-layer::before {
  animation: globe-pulse 1600ms cubic-bezier(0.22, 1, 0.36, 1) 1200ms 1 both;
}

@keyframes globe-pulse {
  0% { opacity: 0; transform: scale(0.5); }
  40% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.35); }
}

/* ============================================================
   INTRO VIGNETTE — heavy darkness that retracts on reveal
   ============================================================ */

.chamber::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 85;
  pointer-events: none;
  background: radial-gradient(
    150% 110% at 50% 58%,
    transparent 0%,
    transparent 22%,
    rgba(6, 2, 2, 0.72) 78%,
    rgba(0, 0, 0, 0.92) 100%
  );
  opacity: 0;
  transition: opacity 2400ms cubic-bezier(0.22, 1, 0.36, 1);
}

.is-intro .chamber::before {
  opacity: 1;
}

/* ============================================================
   REDUCED MOTION — collapse to quick crossfade
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .is-intro .layer-astronaut,
  .is-intro .layer-sage,
  .is-intro .eye-astronaut,
  .is-intro .eye-sage,
  .is-intro .layer-table,
  .is-intro .globe-layer,
  .is-intro .brand-mark,
  .is-intro .tagline {
    transform: none;
    translate: none;
    filter: none;
  }

  .layer-astronaut,
  .layer-sage,
  .eye-astronaut,
  .eye-sage,
  .layer-table,
  .globe-layer,
  .brand-mark,
  .tagline,
  .kicker,
  .orbit-field {
    transition: opacity 400ms ease-out;
    transition-delay: 0ms;
  }

  :root:not(.is-intro) .globe-layer::before {
    animation: none;
  }

  .chamber::before {
    display: none;
  }
}
