/* motion.css — γ1 Shared motion alphabet (canonical)
 *
 * Exact per DESIGN_ROADMAP.md Phase γ1 (lines ~233-256) + Claude Code directive
 * batch-2 (2026-05-28). Five primitives, vanilla CSS, no framework, no library.
 *
 * Primitives (canonical names; `.reveal` etc. are kept below as backward-compat
 * aliases for the existing Grok IntersectionObserver pattern already in the 19
 * placeholder index.html files):
 *
 *   1. .motion-reveal       — opacity + 12px Y translate, 600ms ease-out (IO-driven)
 *   2. .motion-bar-fill     — horizontal bar that fills 0→target on view, 1200ms
 *   3. .motion-count-up     — number that counts 0→data-target over 1500ms (JS rAF)
 *   4. .motion-evidence-pin — gentle box-shadow pulse when a footnote enters view
 *   5. .motion-cursor-block — typewriter caret blink (CSS only, no JS needed)
 *
 * Strict rules:
 *  - prefers-reduced-motion: reduce is a FIRST-CLASS state (full guard at bottom).
 *  - Austere DNA: motion teaches the data or sets the page like print. No spectacle.
 *  - GPU-friendly: opacity + transform + width + box-shadow only. No layout thrash.
 *  - No 3rd-party. Pure vanilla. JS partner is shared/motion.js (also vanilla).
 *  - WCAG 2.3.1 (no flashes >3x/s): caret blink uses 1.2s steps(2), well under limit.
 *
 * Adoption (per-site, opt-in — DO NOT auto-inject):
 *   <link rel="stylesheet" href="/motion.css">
 *   <script src="/motion.js" defer></script>
 *
 * Backward-compat: `.reveal` (Grok's existing class) is treated as an alias of
 * `.motion-reveal`. Sites already using `.reveal` get the canonical behavior for
 * free; new code should prefer the namespaced `.motion-*` names.
 *
 * Last revised: 2026-05-28 (Claude Code batch-2 motion subagent).
 */

:root {
  /* Motion tokens — tweak only with DESIGN_ROADMAP sign-off */
  --motion-reveal-duration:    600ms;
  --motion-bar-duration:      1200ms;
  --motion-count-duration:    1500ms;
  --motion-evidence-duration:  800ms;
  --motion-caret-duration:     1.2s;
  --motion-reveal-translate:    12px;

  /* Default bar fill target (overridden per-element via --motion-bar-target) */
  --motion-bar-target: 50%;
}


/* -------------------------------------------------------------------------- *
 *  1. .motion-reveal — base reveal-on-scroll
 *
 *  Author markup:
 *    <section class="motion-reveal"> ... </section>
 *  JS adds `.in` when IntersectionObserver fires (threshold 0.15).
 * -------------------------------------------------------------------------- */
.motion-reveal {
  opacity: 0;
  transform: translateY(var(--motion-reveal-translate));
  transition: opacity var(--motion-reveal-duration) ease-out,
              transform var(--motion-reveal-duration) ease-out;
  will-change: opacity, transform;
}

.motion-reveal.in {
  opacity: 1;
  transform: translateY(0);
}


/* -------------------------------------------------------------------------- *
 *  2. .motion-bar-fill — horizontal bar that fills from 0 to its target
 *
 *  Author markup:
 *    <div class="motion-bar-fill" style="--motion-bar-target: 73%;">
 *      <div class="motion-bar-fill__fill"></div>
 *    </div>
 *  Useful for Closed Ladder Capital portfolio bars, PNN aggregate visualizations.
 * -------------------------------------------------------------------------- */
.motion-bar-fill {
  position: relative;
  display: block;
  width: 100%;
  height: 6px;
  background: var(--rule, rgba(0,0,0,0.08));
  overflow: hidden;
}

.motion-bar-fill__fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent, var(--ink, #1a1714));
  transition: width var(--motion-bar-duration) ease-out;
  will-change: width;
}

.motion-bar-fill.in .motion-bar-fill__fill {
  width: var(--motion-bar-target, 50%);
}


/* -------------------------------------------------------------------------- *
 *  3. .motion-count-up — number that counts 0 → data-target over 1500ms
 *
 *  Author markup:
 *    <span class="motion-count-up" data-target="142592" data-suffix=" people">0</span>
 *  CSS prepares layout (tabular nums, monospace fallback); JS does the count.
 * -------------------------------------------------------------------------- */
.motion-count-up {
  font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1, "zero" 1;
  opacity: 0;
  transition: opacity 240ms ease-out;
}

.motion-count-up.in {
  opacity: 1;
}


/* -------------------------------------------------------------------------- *
 *  4. .motion-evidence-pin — gentle pulse when a footnote scrolls into view
 *
 *  Author markup:
 *    <a data-evidence-id="src-mdoc-2024">…footnote¹…</a>
 *    <aside data-evidence-target="src-mdoc-2024">…source…</aside>
 *  When the [data-evidence-id] enters view, JS finds the matching
 *  [data-evidence-target] and adds `.motion-evidence-pin--pulse` for 800ms.
 * -------------------------------------------------------------------------- */
.motion-evidence-pin {
  /* Author-applied utility on the evidence target; declares the pulse target */
  transition: box-shadow var(--motion-evidence-duration) ease-out;
}

.motion-evidence-pin--pulse {
  animation: motion-evidence-pin var(--motion-evidence-duration) ease-out 1;
}

@keyframes motion-evidence-pin {
   0%   { box-shadow: 0 0 0 0   rgba(182, 50, 28, 0);   }
  20%   { box-shadow: 0 0 0 4px rgba(182, 50, 28, 0.28); }
  60%   { box-shadow: 0 0 0 6px rgba(182, 50, 28, 0.12); }
 100%   { box-shadow: 0 0 0 0   rgba(182, 50, 28, 0);   }
}


/* -------------------------------------------------------------------------- *
 *  5. .motion-cursor-block — typewriter caret blink (CSS-only)
 *
 *  Author markup:
 *    <p class="motion-cursor-block">…in-progress paragraph…</p>
 *  No JS needed. Use sparingly: ONE paragraph per page max (typesetter feel).
 * -------------------------------------------------------------------------- */
.motion-cursor-block::after {
  content: "▍";
  display: inline-block;
  margin-left: 0.05em;
  vertical-align: baseline;
  animation: motion-cursor-blink var(--motion-caret-duration) steps(2) infinite;
}

/* .cursor-block alias (Batch 1 / Design Depth Directive compatibility for prisonpodcast.com typewriter inputs) */
.cursor-block::after {
  content: "▍";
  display: inline-block;
  margin-left: 0.05em;
  vertical-align: baseline;
  animation: motion-cursor-blink var(--motion-caret-duration) steps(2) infinite;
}

@keyframes motion-cursor-blink {
   0%, 49%   { opacity: 1; }
  50%, 100%  { opacity: 0; }
}


/* -------------------------------------------------------------------------- *
 *  Backward-compat aliases — Grok's existing classes on the 19 placeholders.
 *
 *  Pre-existing pattern (per AGENT_LOG): `.reveal` + IntersectionObserver
 *  adding `.is-visible`. We treat `.reveal` as an alias of `.motion-reveal`
 *  and also honor `.is-visible` as a synonym for `.in`. motion.js detects
 *  both class names so a site can adopt the shared lib without rewriting
 *  its index.html.
 * -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(var(--motion-reveal-translate));
  transition: opacity var(--motion-reveal-duration) ease-out,
              transform var(--motion-reveal-duration) ease-out;
  will-change: opacity, transform;
}

.reveal.in,
.reveal.is-visible,
.motion-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}


/* -------------------------------------------------------------------------- *
 *  γ2 Reduced-motion — FIRST-CLASS state, exact per directive
 *
 *  Anyone with prefers-reduced-motion: reduce gets:
 *   - reveals already visible (no fade, no translate)
 *   - bars already at target width
 *   - count-ups already showing final value (JS handles the textContent)
 *   - evidence pulse skipped
 *   - cursor caret silent (no ::after content)
 * -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {

  .motion-reveal,
  .reveal,
  .motion-bar-fill .motion-bar-fill__fill,
  .motion-count-up,
  .motion-evidence-pin,
  .motion-evidence-pin--pulse {
    transition: none !important;
    animation: none !important;
  }

  .motion-reveal,
  .reveal {
    opacity: 1;
    transform: none;
  }

  .motion-bar-fill .motion-bar-fill__fill {
    width: var(--motion-bar-target, 50%);
  }

  .motion-count-up {
    opacity: 1;
  }

  .motion-cursor-block::after,
  .cursor-block::after {
    content: "";
    animation: none;
  }

  /* Forward guard — any future [data-motion="..."] hook falls through here */
  [data-motion] {
    transition: none !important;
    animation: none !important;
    transform: none !important;
  }
}
