/* ==========================================================================
   ＶＯＩＤ — boot layer
   Loading overlay + staggered hero entrance. Companion to assets/js/boot.js.

   Nothing in here is active until boot.js adds `.void-boot` to <html>, so if
   the script fails to load, 404s or throws before it mounts, this stylesheet
   changes exactly nothing and the page renders normally.

   Only transform / opacity / filter are animated — no layout properties.
   1. tokens     2. overlay     3. mark + glow     4. progress line
   5. exit       6. hero stagger                   7. reduced motion
   ========================================================================== */

/* 1 ── tokens ───────────────────────────────────────────────────────────── */
:root {
  --boot-z: 200;              /* above --z-toast (95) */
  --boot-fade: .56s;          /* overlay exit — keep in sync with FADE_MS in boot.js */
  --boot-step: 90ms;          /* gap between hero steps */
  --boot-lead: 170ms;         /* head start so the fade is underway first */
  --boot-in: .7s;             /* per-step entrance duration */
  --boot-mark: clamp(52px, 12vw, 66px);
}

/* 2 ── overlay ──────────────────────────────────────────────────────────── */
.boot {
  position: fixed;
  inset: 0;
  z-index: var(--boot-z);
  display: grid;
  place-items: center;

  /* Matches the app shell in both themes. `background-color` gets a transition
     because main.js may correct <html data-theme> from the stored preference
     a few ms after this layer paints — that reads as a crossfade, not a flip. */
  background-color: var(--bg);
  transition: background-color var(--t-slow) var(--e-out);

  /* Never a blocker: the layer cannot swallow a click, so even a wedged
     sequence leaves the page fully usable underneath. */
  pointer-events: none;
  contain: layout paint;
}

.boot__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  padding: var(--gutter);
  transform: translateZ(0);
}

/* 3 ── mark + glow ──────────────────────────────────────────────────────── */
.boot__mark {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--boot-mark);
  height: var(--boot-mark);
}

.boot__star {
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
  animation: boot-breathe 2.6s var(--e-in-out) infinite;
}

.boot__glow {
  position: absolute;
  inset: -85%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(176, 108, 240, .5), rgba(224, 71, 158, .18) 45%, transparent 70%);
  filter: blur(22px);
  opacity: .75;
  animation: boot-halo 2.6s var(--e-in-out) infinite;
  pointer-events: none;
}

[data-theme="light"] .boot__glow { opacity: .55; }

@keyframes boot-breathe {
  0%, 100% { transform: scale(1) rotate(0deg); }
  50%      { transform: scale(1.07) rotate(10deg); }
}

@keyframes boot-halo {
  0%, 100% { opacity: .48; transform: scale(.92); }
  50%      { opacity: .9;  transform: scale(1.08); }
}

/* 4 ── indeterminate progress line ──────────────────────────────────────── */
.boot__track {
  position: relative;
  width: clamp(96px, 26vw, 132px);
  height: 2px;
  border-radius: var(--r-full);
  background: var(--border-strong);
  overflow: hidden;
}

/* Sweeps with transform only — the track width never changes. */
.boot__bar {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--grad-brand);
  transform-origin: left center;
  animation: boot-sweep 1.15s var(--e-in-out) infinite;
}

@keyframes boot-sweep {
  0%   { transform: translateX(-100%) scaleX(.34); }
  50%  { transform: translateX(18%)   scaleX(.6); }
  100% { transform: translateX(100%)  scaleX(.34); }
}

/* 5 ── exit: fade + blur ────────────────────────────────────────────────── */
html.void-boot-out .boot {
  opacity: 0;
  transition:
    opacity var(--boot-fade) var(--e-out),
    background-color var(--t-slow) var(--e-out);
}

/* The blur rides on the small stage rather than the full-viewport layer —
   same look, a fraction of the compositing cost. */
html.void-boot-out .boot__stage {
  opacity: 0;
  transform: translate3d(0, -10px, 0) scale(1.14);
  filter: blur(13px);
  transition:
    opacity var(--boot-fade) var(--e-out),
    transform var(--boot-fade) var(--e-out),
    filter var(--boot-fade) var(--e-out);
}

html.void-boot-out .boot__star,
html.void-boot-out .boot__glow,
html.void-boot-out .boot__bar { animation-play-state: paused; }

/* 6 ── hero stagger ─────────────────────────────────────────────────────── */
/* Scoped under html.void-boot so it outranks `.reveal.is-in` from style.css
   (0,2,1 beats 0,2,0) while the sequence owns the hero, and releases it
   completely the moment boot.js drops the class. */
html.void-boot .boot-step {
  /* Resolved here, not on :root — `--boot-i` is set inline per element, so the
     substitution has to happen on the element that carries it. */
  --boot-delay: calc(var(--boot-i, 0) * var(--boot-step) + var(--boot-lead));
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  filter: blur(9px);
  will-change: opacity, transform, filter;
}

html.void-boot .boot-step.is-boot-in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  filter: blur(0);
  transition:
    opacity var(--boot-in) var(--e-out) var(--boot-delay),
    transform var(--boot-in) var(--e-out) var(--boot-delay),
    filter var(--boot-in) var(--e-out) var(--boot-delay);
}

/* 7 ── reduced motion ───────────────────────────────────────────────────── */
/* boot.js already bails out entirely here; this is the belt to that braces,
   covering a preference switched on mid-sequence. */
@media (prefers-reduced-motion: reduce) {
  .boot { display: none !important; }

  html.void-boot .boot-step,
  html.void-boot .boot-step.is-boot-in {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
    will-change: auto !important;
  }
}
