/* ============================================================
   Blackbox Command Center, motion.css
   Companion stylesheet for js/motion.js (global BlackboxMotion). Every
   color here is derived from the tokens already defined in css/main.css
   (var(--ink), var(--card), var(--border), ...) via color-mix(), so every
   effect below reads correctly in both the light and dark palette without
   a single hardcoded hex value. Durations/eases reuse this app's own
   --dur*/--ease* tokens so the new effects read as part of the existing
   "Motion pass" rather than a bolt-on.

   Every @keyframes use below is wrapped in
   @media (prefers-reduced-motion: no-preference), matching the existing
   convention in main.css (.skel, .border-beam) — belt-and-suspenders on
   top of js/motion.js's own JS-level reduced-motion checks and this app's
   blanket `*{transition:none;animation:none}` override under `reduce`.
   ============================================================ */

/* ---------- split: per-word stagger reveal ----------
   .bb-split-ready is added once, at wire time; the words themselves carry
   their own animation-delay inline. Fill-mode 'both' means a word sits at
   its "from" state for the whole of its delay, so a five-word subtitle
   doesn't flash fully-visible before cascading in. Replays for free every
   time an ancestor .screen goes display:none -> block (browsers restart a
   running keyframe animation on that transition) — the exact same
   mechanic this app's own .page-title / .hero h1 span already rely on. */
.bb-split-word { display: inline-block; }
@media (prefers-reduced-motion: no-preference) {
  .bb-split-ready .bb-split-word {
    animation: bb-motion-split-in var(--dur-slower) var(--ease) both;
  }
}
@keyframes bb-motion-split-in {
  from { opacity: 0; transform: translateY(6px); filter: blur(2px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

/* ---------- reveal: stagger a container's direct children in, once ----------
   .bb-reveal-child is added to each existing child at the moment
   js/motion.js decides to reveal (see maybeReveal's "fires once, ever, per
   container" guard) — never re-added on a later poll refresh, so this
   never replays into a flicker. */
@media (prefers-reduced-motion: no-preference) {
  .bb-reveal-child {
    animation: bb-motion-reveal-in var(--dur-slow) var(--ease) both;
  }
}
@keyframes bb-motion-reveal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* ---------- magnetic / dock ----------
   Both are pure transform, driven inline by js/motion.js on
   mousemove/mouseleave — nothing to define here beyond a stable
   transform-origin so the scale in "dock" grows from the item's own
   center rather than its box corner. */
[data-motion~="magnetic"] { display: inline-block; }
.side-nav[data-motion~="dock"] > * { transform-origin: center; }

/* ---------- tilt: pointer-tracked 3D card tilt + light sheen ----------
   The sheen is a radial highlight that follows the pointer via
   --bb-sheen-x/-y (set on mousemove) and fades in/out via --bb-sheen-o.
   It's deliberately built from var(--ink) rather than white/black: in the
   light theme --ink is a dark navy, so the sheen reads as a soft ambient
   shadow deepening toward the cursor (a highlight would be invisible on a
   white card); in the dark theme --ink is near-white, so the exact same
   rule reads as a gentle light bloom — both are "sheen," just the theme-
   appropriate version of it, entirely for free from one token. */
[data-motion~="tilt"] {
  position: relative;
  transition: box-shadow 0.16s ease;
}
[data-motion~="tilt"]::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle 200px at var(--bb-sheen-x, 50%) var(--bb-sheen-y, 50%), color-mix(in srgb, var(--ink) 10%, transparent) 0%, transparent 70%);
  opacity: var(--bb-sheen-o, 0);
  pointer-events: none;
  z-index: 1;
}
@media (prefers-reduced-motion: no-preference) {
  [data-motion~="tilt"]::after { transition: opacity var(--dur) var(--ease); }
}

/* ---------- glass: frosted surface ----------
   Two archetypes share the same attribute: a full-bleed scrim (the mobile
   nav backdrop, task drawer / lightbox / tools-gate backdrops — already
   translucent via their own main.css background rule, this only adds the
   blur) and a floating surface (the sticky mobile topbar, which gets a
   translucent var(--card) tint derived via color-mix so content scrolling
   underneath softly shows through). */
[data-motion~="glass"] {
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}
.mobile-topbar[data-motion~="glass"] {
  background: color-mix(in srgb, var(--card) 78%, transparent);
}

/* ---------- repel: words gently pushed away from the pointer ---------- */
.bb-repel-word { display: inline-block; will-change: transform; }

/* ---------- marquee: seamless infinite scroll strip ---------- */
.bb-marquee { overflow: hidden; }
.bb-marquee-track { display: flex; width: max-content; }
.bb-marquee-group { display: flex; align-items: center; gap: 32px; flex-shrink: 0; padding-right: 32px; }
@media (prefers-reduced-motion: no-preference) {
  .bb-marquee-track {
    animation: bb-motion-marquee var(--bb-marquee-duration, 20s) linear infinite;
  }
  .bb-marquee:hover .bb-marquee-track,
  .bb-motion-paused .bb-marquee-track {
    animation-play-state: paused;
  }
}
@keyframes bb-motion-marquee {
  to { transform: translateX(-50%); }
}

/* ---------- loop: cycle phrases with a blurred vertical crossfade ---------- */
.bb-loop { display: inline-block; overflow: hidden; vertical-align: bottom; }
.bb-loop-slot { display: inline-block; opacity: 1; filter: blur(0); transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .bb-loop-slot {
    transition: opacity var(--dur-slow) var(--ease), filter var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  }
}
.bb-loop-slot.bb-loop-out { opacity: 0; filter: blur(4px); transform: translateY(-8px); }
.bb-loop-slot.bb-loop-in { opacity: 0; filter: blur(4px); transform: translateY(8px); }
