/* JEDIS Lab — first-visit opening animation (choreography in js/intro.js).
   The centered lockup is a live clone of the header brand, so the closing FLIP
   lands it pixel-exactly where the real brand sits; the real one is kept
   hidden until the clone arrives, then they swap on the same frame. */

/* set pre-paint by the inline gate in index.html; lifted once the overlay exists */
html.intro-veil { background: var(--band-dark); }
html.intro-veil .root { visibility: hidden; }

html.intro-running { overflow: hidden; }
html.intro-running .header-bar .brand { visibility: hidden; }

/* The intro plays over the LIVE sky: the star canvas and ambient glow keep
   running beneath a transparent stage, and only the page content (and grid
   texture) sits out — so when the intro closes nothing "appears", the sky
   was there all along. The hide is instant (no transition) so it holds from
   first paint; the reveal path fades back in as the lockup glides home. */
html.intro-running .content {
  opacity: 0;
  visibility: hidden; /* stops painting entirely — glass + aurora animations idle under the intro otherwise */
}
/* the grid fades via filter:opacity(), which MULTIPLIES with its natural
   opacity (0.24 in main.css) — a plain opacity:1 reveal overshot it to full
   vividness and then snapped down when the intro classes were removed */
html.intro-running .grid-overlay { filter: opacity(0); visibility: hidden; }
/* pre-warm: shortly before the glide the hidden page becomes paintable again
   (still fully transparent) so its first visible frame isn't one giant paint
   burst in the middle of the landing animation */
html.intro-running.intro-prewarm .content,
html.intro-running.intro-prewarm .grid-overlay { visibility: visible; }
html.intro-reveal .content {
  opacity: 1;
  visibility: visible;
  transition: opacity .8s ease;
}
html.intro-reveal .grid-overlay {
  filter: opacity(1);
  visibility: visible;
  transition: filter .8s ease;
}
/* The inverted bands' backdrop-filter goes blind while .content fades in —
   opacity on an ancestor makes .content the backdrop root, so the bands'
   ::before can't sample the sky: they rendered dark through the reveal and
   snapped light when it ended. While the intro owns the page, paint the
   ::before with a solid stand-in matching the inverted sky; when the intro
   classes drop it cross-fades back to transparent over the (by then live)
   real inversion. */
.invert-wrap::before { transition: background-color .5s ease; }
html.intro-running .invert-wrap::before,
html.intro-reveal .invert-wrap::before { background-color: rgb(226, 228, 233); }

.intro {
  position: fixed;
  inset: 0;
  z-index: 500; /* above quickdial (100), below the custom cursor (9999) */
}
.intro.intro-fade { pointer-events: none; }

/* dark stage over the live sky; the choreography fades it out mid-sequence
   ("skylight") so the sky dims in while the lockup assembles. JS sets the
   fade duration inline per pacing profile; handoff adds the class as a
   fallback for variants that never scheduled it. */
.intro-scrim {
  position: absolute;
  inset: 0;
  background: var(--band-dark);
  transition: opacity .8s ease;
}
.intro-scrim.intro-skylight { opacity: 0; }
.intro-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

.intro-brand {
  position: fixed;
  margin: 0;
  transform-origin: 0 0;
  will-change: transform;
  pointer-events: none;
}
.intro-brand.intro-land { transition: transform 1.15s cubic-bezier(.3, .7, .3, 1); }

/* Per-element hidden states — removing the class plays that element's reveal.
   Final states are the stock header styles, so nothing needs undoing. */
.intro-brand .brand-mark img {
  transition: opacity .85s ease, transform .95s cubic-bezier(.22, 1, .36, 1), filter .95s ease;
}
/* explicit shown-state inset: `none` ↔ inset() is not interpolable, so the
   wipe needs a matching zero-inset to ease toward. will-change promotes the
   wiping images to their own layers so the clip animation composites instead
   of repainting the scaled-up PNGs every frame. */
.intro-brand .brand-word {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1.2s cubic-bezier(.22, 1, .36, 1);
  will-change: clip-path;
}
.intro-brand .brand-mark img { will-change: clip-path, opacity, transform, filter; }
.intro-brand .brand-sub { transition: opacity .8s ease, transform .8s cubic-bezier(.22, 1, .36, 1); }

.i-mark-hide .brand-mark img { opacity: 0; transform: scale(.92); }
.i-word-hide .brand-word { clip-path: inset(0 100% 0 0); }
.i-sub-hide .brand-sub { opacity: 0 !important; transform: translateX(-5px) translateY(7px); }

/* b "meteor": the mark arrives along the meteor's heading and unblurs as it
   lands. The blur is appended after the stock invert() so the filter lists
   share a prefix and interpolate smoothly. */
.intro-brand--b.i-mark-hide .brand-mark img {
  transform: translate(30px, -18px) scale(.96);
  filter: invert(var(--wm-flip, 0)) blur(7px);
}

/* d "rays": the mark assembles behind a diagonal wipe riding its own ray
   angle — a 3-point clip triangle growing out of the bottom-left corner
   (matching point counts keep the polygon interpolable) */
.intro-brand--d .brand-mark img,
.intro-brand--g .brand-mark img,
.intro-brand--h .brand-mark img {
  clip-path: polygon(-20% 120%, 260% 120%, -20% -160%);
  transition: clip-path 1.35s cubic-bezier(.22, 1, .36, 1);
}
.intro-brand--d.i-mark-hide .brand-mark img,
.intro-brand--g.i-mark-hide .brand-mark img,
.intro-brand--h.i-mark-hide .brand-mark img {
  opacity: 1;
  transform: none;
  clip-path: polygon(-20% 120%, -20% 120%, -20% 120%);
}

/* e "gather" and g "epic": the name is written leftward out of the sparkle */
.intro-brand--e.i-word-hide .brand-word,
.intro-brand--g.i-word-hide .brand-word { clip-path: inset(0 0 0 100%); }

/* h: the badge is born centered and glides left into its lockup slot while
   the name writes out beside it (inline transform set/cleared by JS) */
.intro-brand--h .brand-mark {
  transition: transform 1.15s cubic-bezier(.22, 1, .36, 1);
  will-change: transform;
}

/* h pacing candidates (?intro=h&pace=1..4): wipe speeds per profile.
   Pace 2 is the baseline and uses the stock durations above. */
.intro-brand.intro-pace-1 .brand-word,
.intro-brand.intro-pace-4 .brand-word { transition-duration: .85s; }
.intro-brand.intro-pace-1 .brand-mark img,
.intro-brand.intro-pace-4 .brand-mark img { transition-duration: .95s; }
.intro-brand.intro-pace-3 .brand-word { transition-duration: 1.6s; }
.intro-brand.intro-pace-3 .brand-mark img { transition-duration: 1.7s; }

/* skip (any click / key / wheel): land quickly, no drama */
.intro-skip, .intro-skip * { transition-duration: .28s !important; }
