/* SPLASH — branded cold-start animation.
 *
 * Everything is scoped under #splash so nothing leaks into the app. Pairs with
 * js/splash.js, which builds the DOM and owns the timing contract.
 *
 * ONE SPEED KNOB. Every duration and delay is calc(Xs * var(--spd)), so the
 * whole sequence retimes from a single variable. If it feels slow, change --spd
 * (or minDuration in the boot call) — do not go hunting through keyframes.
 *
 * THE MARK IS THE STORY. Three seats fill one at a time, then the fourth draws
 * itself as an empty ring and keeps breathing: "three in, one spot open", which
 * is the product in one gesture. That is also why there is no separate loader
 * bar — the pulsing empty seat IS the "still working" signal, and it means
 * something, which a sliding rectangle does not.
 *
 * FOREST ON CREAM, inverting the app icon. The app opens onto a cream UI, so a
 * forest splash would flash dark and snap light. Matches the native launch
 * screen exactly — see the handoff note in splash.js.
 *
 * WKWebView: #splash is position:fixed and therefore never carries a transform
 * (CLAUDE.md — transform on a fixed element is a paint bug). Only opacity is
 * transitioned on it; everything animated is an absolutely-positioned child. */

#splash {
  position: fixed;
  inset: 0;
  z-index: 99999;
  /* Also set inline on the element in splash.js. This rule is the one that
     survives; the inline one is the no-flash guarantee for the frame before
     this stylesheet has applied. */
  background: #F5F6F0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  opacity: 1;
  transition: opacity calc(0.5s * var(--spd)) ease;
  --spd: 1;
  font-family: var(--font, system-ui, -apple-system, sans-serif);
  -webkit-font-smoothing: antialiased;
}

/* ── Exit: contents first, then the sheet ─────────────────────────────────
 * A straight cross-dissolve of the whole overlay shows both compositions at
 * once for the full half-second — the wordmark over the app's buttons, the
 * mark over its headline — which reads as two screens fighting rather than one
 * handing over to the next.
 *
 * So the contents drop in ~0.2s, leaving a plain cream sheet, and a plain cream
 * sheet dissolving into a cream screen is invisible.
 *
 * !important is REQUIRED here, not sloppiness: these elements still carry their
 * entry animations with fill-mode both, and an animated value beats a normal
 * declaration. !important is the one thing that outranks a running animation —
 * without it these rules are silently ignored and the exit stays a dissolve. */
/* pointer-events: the overlay keeps eating taps for the length of the fade
   (plus the 900ms removal fallback) unless told not to — which on a real phone
   was "Get started does nothing, then the login screen appears under a
   still-tapping finger and errors" (2026-08-17). The moment the fade starts,
   the splash is scenery. */
#splash.is-out { opacity: 0; pointer-events: none; }

#splash.is-out .cs-bloom,
#splash.is-out .cs-mark,
#splash.is-out .cs-word,
#splash.is-out .cs-tag,
#splash.is-out .cs-copy {
  opacity: 0 !important;
  transition: opacity calc(0.2s * var(--spd)) ease;
}

/* ── Bloom ────────────────────────────────────────────────────────────────
 * A soft wash behind the mark, timed to the third seat landing. It is the only
 * "impact" beat in the sequence — Court Sort's voice is quiet, so this is a
 * warmth cue rather than the flash a game app would use. */
#splash .cs-bloom {
  position: absolute;
  left: 50%; top: 50%;
  width: min(84vw, 460px);
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0.4);
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(circle,
    rgba(92, 122, 79, 0.15) 0%,
    rgba(58, 82, 51, 0.06) 42%,
    rgba(58, 82, 51, 0) 70%);
}

@keyframes cs-bloom {
  0%   { opacity: 0;    transform: translate(-50%, -50%) scale(0.4); }
  45%  { opacity: 1; }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.45); }
}

/* ── The mark ─────────────────────────────────────────────────────────── */
#splash .cs-mark {
  position: relative;
  width: clamp(112px, 30vw, 148px);
  height: auto;
  display: block;
}

#splash .cs-seat { fill: var(--accent, #3A5233); opacity: 0; }
#splash .cs-open {
  fill: none;
  stroke: var(--accent-2, #5C7A4F);
  stroke-width: 38;
  stroke-linecap: round;
  opacity: 0;
}

/* transform-box/origin so each circle scales about its own centre rather than
   the SVG's origin. Supported from iOS 14; the deployment target is 16. */
#splash .cs-seat { transform-box: fill-box; transform-origin: 50% 50%; }

@keyframes cs-seat-in {
  0%   { opacity: 0; transform: scale(0.28); }
  60%  { opacity: 1; transform: scale(1.08); }   /* the overshoot is the landing */
  100% { opacity: 1; transform: scale(1); }
}

/* THE 3% OPACITY STEP IS LOAD-BEARING. stroke-linecap: round paints a visible
   dot even at zero dash length, so the ring's start cap sits on screen as a
   sage blob for the whole 0.66s it waits out its delay (animation-fill-mode is
   both, holding the from-state). Reached ~20ms in, this kills the cap dot
   without touching the draw. Do not remove it. */
@keyframes cs-draw {
  0%   { stroke-dashoffset: var(--len); opacity: 0; }
  3%   { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}

/* The empty seat keeps waiting. This is the loader. */
@keyframes cs-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.42; }
}

/* ── Type ─────────────────────────────────────────────────────────────── */
#splash .cs-word {
  text-align: center;   /* a narrow phone wraps the line; each half stays centred */
  margin: 26px 0 0;
  font-size: clamp(34px, 9vw, 42px);
  font-weight: 800;
  letter-spacing: -0.032em;
  line-height: 1;
  color: var(--ink, #1E2A20);
  opacity: 0;
}

/* 18px is the floor for anything anyone reads (CLAUDE.md). Hierarchy below the
   wordmark comes from weight and colour, never from a smaller size. */
#splash .cs-tag {
  text-align: center;
  margin: 12px 0 0;
  font-size: 18px;
  font-weight: 500;
  color: var(--accent-2, #5C7A4F);
  opacity: 0;
}

#splash .cs-copy {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(52px + env(safe-area-inset-bottom, 0px));
  text-align: center;
  font-size: 18px;
  font-weight: 400;
  color: var(--muted-2, #AEB3A4);
  opacity: 0;
}

@keyframes cs-rise {
  0%   { opacity: 0; transform: translateY(16px); }
  100% { opacity: 1; transform: none; }
}
@keyframes cs-fade {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* ── Play ─────────────────────────────────────────────────────────────────
 * Added on the frame after mount, so the keyframes are registered before the
 * class that triggers them lands. Per-element delays for the three seats are
 * set in splash.js, which is also where the stagger lives. */
#splash.is-play .cs-bloom {
  animation: cs-bloom calc(1s * var(--spd)) ease-out calc(0.3s * var(--spd)) both;
}
#splash.is-play .cs-seat {
  animation: cs-seat-in calc(0.5s * var(--spd)) cubic-bezier(0.22, 0.9, 0.3, 1) both;
}
#splash.is-play .cs-open {
  animation:
    cs-draw calc(0.66s * var(--spd)) cubic-bezier(0.25, 0.9, 0.3, 1) calc(0.66s * var(--spd)) both,
    cs-breathe calc(1.9s * var(--spd)) ease-in-out calc(1.4s * var(--spd)) infinite;
}
#splash.is-play .cs-word {
  animation: cs-rise calc(0.6s * var(--spd)) cubic-bezier(0.2, 0.8, 0.3, 1) calc(1.02s * var(--spd)) both;
}
#splash.is-play .cs-tag {
  animation: cs-rise calc(0.6s * var(--spd)) cubic-bezier(0.2, 0.8, 0.3, 1) calc(1.24s * var(--spd)) both;
}
#splash.is-play .cs-copy {
  animation: cs-fade calc(0.6s * var(--spd)) ease calc(1.9s * var(--spd)) both;
}

/* ── Reduced motion ───────────────────────────────────────────────────────
 * Skip to the resting frame rather than playing a faster version.
 *
 * stroke-dashoffset: 0 is REQUIRED, not tidiness: with the animation removed
 * the ring keeps its from-state (fully offset) and the empty seat is simply
 * never drawn. Same for the seats' opacity — animation:none alone leaves
 * everything at opacity 0 and the splash is a blank cream rectangle. */
@media (prefers-reduced-motion: reduce) {
  #splash.is-play .cs-seat,
  #splash.is-play .cs-open,
  #splash.is-play .cs-word,
  #splash.is-play .cs-tag,
  #splash.is-play .cs-copy { animation: none; }
  #splash .cs-bloom { display: none; }
  #splash .cs-seat { opacity: 1; transform: none; }
  #splash .cs-open { opacity: 1; stroke-dashoffset: 0 !important; }
  #splash .cs-word,
  #splash .cs-tag,
  #splash .cs-copy { opacity: 1; transform: none; }
}
