/* ═══════════════════════════════════════════════════════════
   landing.css — the landing module's stylesheet (boot cinematic)
   ───────────────────────────────────────────────────────────
   Moved verbatim from index.html's <style> block (2026-06-12).
   Loaded by BOTH surfaces: index.html (app boot) and onboarding.html
   (first-run setup). The gold vars are scoped to .splash-layer so the
   module is self-sufficient on pages without the app's :root palette;
   the values mirror index.html's :root --gold / --gold-soft exactly,
   so app-mode rendering is unchanged.
═══════════════════════════════════════════════════════════ */
.splash-layer{--gold:#c9a449;--gold-soft:rgba(201,164,73,0.7);}

/* ─── Boot splash — cinematic title card shown once per launch.
   Design source: paintress/promoted/landing-welcome/v8-exit-crossfade.html
   (v6 entrance + v8 crossfade exit); orchestration + live-pen pass from
   paintress/build/splash-lab/v4-splash-lab.html (nib-over-dot framing,
   220ms MAGUS cadence, quill-tempo signature, glitter, wet-to-dry ink).
   Markup sits at the top of <body>;
   lifecycle (timers, early-exit, DOM removal) lives in landing/landing.js.
   z-index 40 — above all boot-time chrome; popovers/overlays (50+/9000)
   are never up at boot. */
.splash-layer{position:fixed;inset:0;z-index:40;overflow:hidden;
  background:radial-gradient(115% 100% at 50% 30%, #0e1a12 0%, #0a140d 50%, #060c08 100%);}
.field-vignette{position:absolute;inset:0;pointer-events:none;z-index:1;
  background:radial-gradient(72% 70% at 50% 40%, transparent 42%, rgba(2,6,3,0.6) 100%);
  animation:vignette-breath 9s ease-in-out infinite alternate;}
@keyframes vignette-breath{from{transform:scale(1);opacity:1;}to{transform:scale(1.035);opacity:0.92;}}
.field-grain{position:absolute;inset:0;pointer-events:none;z-index:1;opacity:0.45;
  background:repeating-linear-gradient(120deg, rgba(255,255,255,0.012) 0 1px, transparent 1px 10px);
  animation:grain-pan 26s linear infinite alternate;}
@keyframes grain-pan{from{background-position:0 0;}to{background-position:-160px 90px;}}

/* Ghost writing — the far plane is what the quill produces: three
   oversized, illegible pen flourishes inking themselves in slowly behind
   the scene (same stroke-draw as the quill, much slower) and drifting.
   The third is still being written when the splash exits — the scene
   stays alive without repeating the feather silhouette. */
.ghost-writing{position:absolute;inset:-6%;z-index:1;pointer-events:none;opacity:0;
  filter:blur(1.8px);
  /* The wind dims through the lockup band (55-80% height): a flourish
     crossing the empty signature slot pre-trace reads as a stray text
     outline. The mask keeps the wind alive at the edges of the band. */
  -webkit-mask-image:linear-gradient(180deg,#fff 0 50%,rgba(255,255,255,0.25) 58% 78%,#fff 86%);
  mask-image:linear-gradient(180deg,#fff 0 50%,rgba(255,255,255,0.25) 58% 78%,#fff 86%);
  animation:ghost-drift 20s ease-in-out infinite alternate,
            ghost-in 1600ms ease 250ms forwards;}
.ghost-writing svg{width:100%;height:100%;display:block;}
@keyframes ghost-in{to{opacity:0.12;}}
@keyframes ghost-drift{
  from{transform:translate(0,0) rotate(0deg);}
  to{transform:translate(2.2%,-1.6%) rotate(0.8deg);}}
/* The flourishes write WITH the feather (same start window) at a fraction
   of its speed, then loop forever: draw → linger → erase → redraw.
   The erase is a trim-path: dashoffset continues past 0 toward -1, so the
   line un-draws from its origin toward its tip — chased off by an unseen
   eraser rather than fading. Opacity dips only at the exact endpoints,
   where round linecaps would otherwise leave a dot artifact. The three
   staggered cycle lengths keep the phases from ever aligning. */
.gwrite{stroke-dasharray:1;stroke-dashoffset:1;
  animation:gwrite-loop 9.5s cubic-bezier(.4,.1,.4,1) 350ms infinite;}
.gwrite:nth-of-type(2){animation-delay:900ms;animation-duration:11.5s;}
.gwrite:nth-of-type(3){animation-delay:1500ms;animation-duration:13s;}
@keyframes gwrite-loop{
  0%{stroke-dashoffset:1;opacity:0;}
  4%{opacity:1;}
  46%{stroke-dashoffset:0;}
  62%{stroke-dashoffset:0;}
  97%{opacity:1;}
  100%{stroke-dashoffset:-1;opacity:0;}}

/* Quill hero — the gold plume draws itself in (stroke-dash ink-in) and
   stays at full gold (no dim-to-watermark). Branch-growth draw: the stem
   (the old closed spine outline, split into two .qspine border paths)
   draws BOTH borders together from the base (250→2650ms), and each barb
   ignites just after the stem's head passes its attachment point —
   lagging 150-350ms behind, growing from its stem-closest endpoint
   outward at its own speed (--qt 1400-2400ms), like a branch leafing
   out. Fold-back pen traces were divided at their fold; outer contour
   lines that never touch the stem start extra-late (8ms/unit offset).
   Inline --qd/--qt computed by paintress/promoted/landing-welcome/
   _bloom_branch.js (seeded, rerunnable). Last ink lands ~4.9s; the
   Scribe trace follows. */
/* Pixel-anchored composition — the quill and the lockup hang off ONE
   anchor (viewport center) with fixed px offsets, so the nib-to-signature
   relationship is identical at every window size. (Previously the quill
   rode top:40% + 70vh sizing while the lockup hung from the bottom at
   16% padding — two anchor systems, so taller windows (the packaged
   1280x900 app) sank the nib into the written "Scribe".) */
/* nib tip aligned over the MAGUS·Scribe dot: −133px x centers the spine
   base on the dot; the extra −10px y floats it ~30px above the dot */
.quill-hero{position:absolute;left:50%;top:50%;
  transform:translate(calc(-50% - 133px),calc(-50% - 90px));
  width:min(560px,70vh);height:min(560px,70vh);z-index:2;pointer-events:none;
  filter:drop-shadow(0 0 26px rgba(201,164,73,0.30));}
.quill-hero svg{width:100%;height:100%;display:block;overflow:visible;}
.qhero{stroke-dasharray:1;stroke-dashoffset:1;
  animation:le-ink var(--qt,2200ms) cubic-bezier(.45,.1,.35,1) var(--qd,2650ms) both;}
.qhero.qspine{animation-duration:2400ms;animation-delay:250ms;}
@keyframes le-ink{0%{stroke-dashoffset:1;opacity:0;}9%{opacity:1;}100%{stroke-dashoffset:0;opacity:1;}}
.nib-spark{position:absolute;left:50%;top:50%;z-index:2;
  width:min(560px,70vh);height:min(560px,70vh);
  transform:translate(calc(-50% - 133px),calc(-50% - 90px));pointer-events:none;
  animation:spark-fade 1700ms ease-out 250ms both;}
.nib-spark::after{content:"";position:absolute;left:34%;top:62%;width:14px;height:14px;
  border-radius:50%;transform:translate(-50%,-50%);
  background:radial-gradient(circle, rgba(255,240,200,0.9), rgba(201,164,73,0.4) 45%, transparent 72%);
  filter:blur(1px);}
@keyframes spark-fade{0%{opacity:0;}15%{opacity:1;}80%{opacity:0.7;}100%{opacity:0;}}

/* Glitter — tiny gold twinkles scattered along the plume, each on its
   own slow cycle (duration/delay/peak via inline vars); they begin once
   the stem has formed (~2.6s) and live through the hold. Sized and
   dimmed for "magical", not "fireworks": a few visible at any moment. */
.qglit{position:absolute;width:3px;height:3px;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle, #fff6dd 0%, rgba(246,227,163,0.8) 40%, transparent 75%);
  box-shadow:0 0 6px 1px rgba(246,227,163,0.55);
  opacity:0;
  animation:qglit-twinkle var(--gt,2.6s) ease-in-out var(--gd,2.6s) infinite;}
.qglit--lg{width:4px;height:4px;box-shadow:0 0 9px 2px rgba(246,227,163,0.6);}
@keyframes qglit-twinkle{
  0%,100%{opacity:0;transform:scale(0.4);}
  12%{opacity:var(--go,0.7);transform:scale(1);}
  28%{opacity:var(--go,0.7);}
  50%{opacity:0;transform:scale(0.45);}}

/* Dust motes — a few near-invisible gold particles drifting up through
   the quill's glow. Background life for the settled hold; deliberately
   fewer + dimmer than feels right in isolation (kitsch threshold). */
.mote{position:absolute;z-index:2;width:4px;height:4px;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle, rgba(255,236,180,0.9), rgba(201,164,73,0.35) 60%, transparent 75%);
  filter:blur(0.5px);opacity:0;
  animation:mote-drift var(--mote-t,9s) linear var(--mote-d,0s) infinite;}
@keyframes mote-drift{
  0%{opacity:0;transform:translate3d(0,0,0);}
  18%{opacity:var(--mote-o,0.3);}
  62%{opacity:var(--mote-o,0.3);}
  100%{opacity:0;transform:translate3d(var(--mote-x,14px),-88px,0);}}
.mote--1{left:46%;top:52%;--mote-t:8.5s;--mote-d:1.2s;--mote-x:18px;--mote-o:0.30;}
.mote--2{left:55%;top:44%;--mote-t:10s;--mote-d:2.6s;--mote-x:-14px;--mote-o:0.26;width:3px;height:3px;}
.mote--3{left:50%;top:60%;--mote-t:9s;--mote-d:0.4s;--mote-x:10px;--mote-o:0.34;}
.mote--4{left:60%;top:55%;--mote-t:11s;--mote-d:3.4s;--mote-x:-20px;--mote-o:0.22;width:3px;height:3px;}

/* Lockup — wordmark writes in below the quill, glyph by glyph. */
.splash-stage{position:absolute;left:0;right:0;top:0;bottom:0;z-index:3;display:flex;
  flex-direction:column;align-items:center;justify-content:flex-end;text-align:center;
  /* lockup bottom pinned 272px below viewport center (same anchor as the
     quill); the max() floor keeps it on-screen in very short windows */
  padding:0 32px max(96px, calc(50vh - 272px));}
.lockup{display:flex;align-items:center;gap:22px;position:relative;z-index:1;}
.lockup-words{display:flex;align-items:baseline;gap:17px;white-space:nowrap;
  transform-origin:0 50%;} /* match-cut flight scales toward the left edge */
.w-magus{font-family:'Segoe UI','Helvetica Neue',Arial,sans-serif;font-size:82px;font-weight:400;
  letter-spacing:12px;color:#eaf4ea;text-shadow:0 2px 16px rgba(0,0,0,0.5);}
.w-magus span{display:inline-block;}
.w-dot{display:inline-block;width:13px;height:13px;border-radius:50%;background:var(--gold);
  transform:translateY(-16px);box-shadow:0 0 13px 2px rgba(201,164,73,0.65);}
/* "Scribe" — span box bottom = glyph baseline (flex baselines a
   replaced/empty box at its border-box bottom); the canvas inside is
   absolutely positioned over glyph-space y25..150 at 3x resolution, so
   ascenders/descenders paint outside the box. landing.js owns all the
   drawing (ink-bloom reveal + glint band — FEATHER-SPEC head+falloff). */
.w-scribe{position:relative;display:inline-block;width:268.8px;height:132px;}
.w-scribe-canvas{position:absolute;left:0;top:30px;width:268.8px;height:150px;
  pointer-events:none;filter:drop-shadow(0 3px 20px rgba(201,164,73,0.4));}
.flourish{width:0;height:2px;margin-top:16px;border-radius:2px;position:relative;z-index:1;
  background:linear-gradient(90deg,transparent,var(--gold-soft) 28%,var(--gold) 60%,transparent);
  box-shadow:0 0 10px rgba(201,164,73,0.45);}
.field-foot{position:absolute;left:0;right:0;bottom:22px;z-index:3;text-align:center;
  font-size:12.5px;letter-spacing:0.01em;color:rgba(234,244,234,0.4);
  display:flex;flex-direction:column;align-items:center;gap:7px;}
.field-foot-line{display:flex;align-items:center;justify-content:center;gap:9px;}
.field-foot .dotmark{width:4px;height:4px;border-radius:50%;background:var(--gold-soft);flex-shrink:0;}
.field-foot .ftxt{max-width:560px;line-height:1.5;}
/* Copyright plate — now the sole footer line. Engraved small caps,
   stepped up in size and legibility so it reads as a deliberate legal
   statement, not an etched whisper. First line (the notice) sits a
   touch brighter than the prohibition clause below it. */
.field-copy{font-size:11.5px;line-height:1.65;letter-spacing:0.13em;text-transform:uppercase;
  font-weight:500;color:rgba(234,244,234,0.40);max-width:600px;}

@keyframes le-glyph{from{opacity:0;transform:translateY(15px);filter:blur(4px);}to{opacity:1;transform:translateY(0);filter:blur(0);}}
@keyframes le-dot{0%{opacity:0;transform:translateY(-36px) scale(0.3);}70%{opacity:1;transform:translateY(-11px) scale(1.2);}100%{opacity:1;transform:translateY(-16px) scale(1);}}
@keyframes le-flourish{from{width:0;opacity:0;}to{width:408px;opacity:1;}}
@keyframes le-rise{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
/* MAGUS populates while the barbs are still soaking outward and the last
   letter lands right as the bloom completes (~4.85s) — the pen then picks
   up the signature immediately, no dead air in the lockup. */
/* MAGUS slowed to a deliberate cadence: 220ms between letters, 750ms
   per glyph — populates through the feather's final fill and lands its
   last letter at ~5.6s, handing off to the signature. */
.w-magus span:nth-child(1){animation:le-glyph 750ms cubic-bezier(.2,.75,.2,1) 3960ms both;}
.w-magus span:nth-child(2){animation:le-glyph 750ms cubic-bezier(.2,.75,.2,1) 4180ms both;}
.w-magus span:nth-child(3){animation:le-glyph 750ms cubic-bezier(.2,.75,.2,1) 4400ms both;}
.w-magus span:nth-child(4){animation:le-glyph 750ms cubic-bezier(.2,.75,.2,1) 4620ms both;}
.w-magus span:nth-child(5){animation:le-glyph 750ms cubic-bezier(.2,.75,.2,1) 4840ms both;}
/* Finale: the dot drops as the divider draws (both ~10.99s, right after
   the signature finishes), then the whole lockup gets a faint power-on
   glow — bright bloom, settling to a slightly lifted steady state. The
   glow lives on .lockup (not .lockup-words) so the match-cut exit
   animation can't displace it. */
.w-dot{animation:le-dot 900ms cubic-bezier(.3,1.1,.5,1) 10986ms both;}
@keyframes lockup-glowup{
  0%{filter:brightness(1) drop-shadow(0 0 0 rgba(201,164,73,0));}
  45%{filter:brightness(1.18) drop-shadow(0 0 22px rgba(201,164,73,0.45));}
  100%{filter:brightness(1.04) drop-shadow(0 0 10px rgba(201,164,73,0.22));}}
/* After the power-on settles, the lockup breathes — a slow steady
   oscillation around the lifted state, for as long as the card holds.
   Breath starts exactly where glowup ends (same filter values), and
   being later in the animation list it takes over seamlessly. */
@keyframes lockup-breath{
  0%,100%{filter:brightness(1.04) drop-shadow(0 0 10px rgba(201,164,73,0.22));}
  50%{filter:brightness(1.09) drop-shadow(0 0 16px rgba(201,164,73,0.32));}}
.lockup{animation:lockup-glowup 2200ms ease-in-out 12386ms both,
                  lockup-breath 5200ms ease-in-out 14586ms infinite;}
/* Scribe writes in AFTER MAGUS's last letter lands (~5.65s): the full
   word-cursive workup (landing/landing-vfx.js) — 8 hand-painted segments,
   each with one hand-traced timed stroke, arranged ~4.8s of writing
   (x1.04 quill tempo in landing.js) ending ~10.66s. The flourish underline
   waits for the signature; finale runs slow (dot 900ms, divider
   1100ms at 10.99s -> pause -> glow-up 2.2s at 12.39s -> breathing). */
.flourish{animation:le-flourish 1100ms cubic-bezier(.4,0,.2,1) 10986ms both;}
.field-foot{animation:le-rise 700ms cubic-bezier(.2,.75,.2,1) 2400ms both;}

/* Settled — entrance finished (or cut short); everything pinned at its
   final state so the exit always starts from a stable card. */
.splash-layer.settled .quill-hero{animation:none;opacity:1;}
.splash-layer.settled .qhero{animation:none;stroke-dashoffset:0;opacity:1;}
.splash-layer.settled .nib-spark{animation:none;display:none;}
.splash-layer.settled .w-magus span,
.splash-layer.settled .w-dot,
.splash-layer.settled .field-foot{animation:none;opacity:1;transform:none;filter:none;
  transition:opacity 150ms ease, transform 150ms ease;}
/* No settled pin for the Scribe trace: it's SMIL inside the mask (CSS
   pins wouldn't repaint there anyway). An early exit mid-trace simply
   crossfades the half-written word — accepted, it's cut-short behavior. */
.splash-layer.settled .w-dot{transform:translateY(-16px);}
.splash-layer.settled .flourish{animation:none;width:408px;opacity:1;transition:width 150ms ease;}

/* Exit — the card crossfades while its content rises away; landing.js
   removes the node on animationend (field-fade). Layered speeds give the
   curtain depth: quill lifts fastest and furthest, the lockup mark next,
   the footer least — parallax instead of a flat fade. */
.splash-layer.exiting{pointer-events:none;
  animation:field-fade 420ms cubic-bezier(.4,0,.2,1) both;}
@keyframes field-fade{0%{opacity:1;}55%{opacity:0.5;}100%{opacity:0;}}
.splash-layer.exiting .quill-hero{animation:hero-lift 340ms cubic-bezier(.4,0,.25,1) both;}
@keyframes hero-lift{from{opacity:1;}to{opacity:0;transform:translate(-50%,-78%) scale(1.07);}}
.splash-layer.exiting .splash-stage{animation:lock-rise 380ms cubic-bezier(.4,0,.25,1) both;}
.splash-layer.exiting .field-foot{animation:foot-rise 440ms cubic-bezier(.4,0,.25,1) both;}
@keyframes lock-rise{from{opacity:1;transform:translateY(0);}to{opacity:0;transform:translateY(-40px);}}
@keyframes foot-rise{from{opacity:1;transform:translateY(0);}to{opacity:0;transform:translateY(-22px);}}

/* Match cut — when landing.js measured the header brand (.mc), the stage
   stops rising as one: the wordmark flies into the header's MAGUS·Scribe
   footprint (translate+scale from --mc-* custom props, set inline at exit
   time) and dissolves mid-flight as the layer fades, so the real header
   brand is revealed exactly where it lands. Mark and flourish exit on
   their own. Without .mc (header missing), the straight rise above runs. */
.splash-layer.exiting.mc .splash-stage{animation:none;}
.splash-layer.exiting.mc .lockup-words{animation:match-cut 420ms cubic-bezier(.3,.05,.2,1) both;}
@keyframes match-cut{to{transform:translate(var(--mc-x,0px),var(--mc-y,-40px)) scale(var(--mc-s,1));}}
.splash-layer.exiting.mc .flourish{animation:flourish-fade 360ms cubic-bezier(.4,0,.25,1) both;}
@keyframes flourish-fade{to{opacity:0;}}

/* While the splash is up, the idle field's entrance animations (greeting
   rise, band-draw, magazine card-rise) hold at frame zero — paused freezes
   the delay phase too — and play from the start when landing.js drops the
   class at exit. That's the "index builds itself as the curtain lifts". */
body.splash-up #idle-field .greet-h,
body.splash-up #idle-field .greet-sub,
body.splash-up #idle-field .greet::after,
body.splash-up #idle-field .news-card { animation-play-state: paused; }

/* Reduced motion — no splash at all. CSS hides it before first paint;
   landing.js then removes the node and the splash-up class outright. */
@media (prefers-reduced-motion: reduce) {
  .splash-layer { display: none; }
}
