/* motion.css — scroll-driven cinema (nav filament, hero grid-drift/exit), reveal system, reduced-motion collapse (last) */
    /* ---------- scroll reveal (IntersectionObserver adds .in) ---------- */
    .reveal {
      opacity: 0;
      transform: translateY(2.75rem);
      filter: blur(6px);
      transition:
        opacity 0.9s var(--ease-spring),
        transform 0.9s var(--ease-spring),
        filter 0.9s var(--ease-spring);
      transition-delay: var(--d, 0s);
    }
    .reveal.in { opacity: 1; transform: translateY(0); filter: blur(0); }

    /* ---------- live status dot (blink; killed by the reduced-motion collapse below) ---------- */
    .footer-status .dot {
      display: inline-block;
      width: 0.5rem;
      height: 0.5rem;
      margin-right: 0.4rem;
      border-radius: 50%;
      background: var(--red);
      animation: dot-blink 1.6s steps(1) infinite;
      vertical-align: 1px;
    }
    @keyframes dot-blink { 50% { opacity: 0; } }

    /* ---------- reveal variation: sections don't all enter the same way ---------- */
    .manifesto .reveal { transform: none; filter: blur(10px); }
    .manifesto .reveal.in { filter: blur(0); }

    /* ---------- scroll cinema (progressive enhancement; scrubbed by real scroll) ---------- */
    @supports (animation-timeline: scroll()) {
      /* blueprint grid drifts off with a parallax lag while the hero exits */
      .hero::after {
        animation: grid-drift linear both;
        animation-timeline: scroll(root);
        animation-range: 0 100dvh;
      }
      .hero-inner {
        animation: hero-exit linear both;
        animation-timeline: scroll(root);
        animation-range: 0 85dvh;
      }
      /* blueprint paper becomes a FIXED sheet: content scrolls at full speed
         over paper that only creeps down 12vh across the whole page.
         body's own scrolling grid is replaced by this layer; oversized
         ±12vh so the creep never exposes an edge */
      body { background-image: none; }
      body::before {
        content: "";
        position: fixed;
        inset: -12vh 0;
        z-index: -1;
        pointer-events: none;
        opacity: 0;
        background-image:
          linear-gradient(rgba(122, 162, 247, 0.07) 1px, transparent 1px),
          linear-gradient(90deg, rgba(122, 162, 247, 0.07) 1px, transparent 1px);
        background-size: 44px 44px;
        animation:
          paper-reveal linear both,
          paper-parallax linear both;
        animation-timeline: scroll(root), scroll(root);
        animation-range: 78dvh 108dvh, normal;
      }
      /* reveals scrub with each element's own entry instead of firing once */
      .reveal {
        transition: none;
        transition-delay: 0s;
        animation: reveal-scrub linear both;
        animation-timeline: view();
        animation-range: entry 5% entry 45%;
      }
      .manifesto .reveal { animation-name: reveal-blur-scrub; }
      /* filter-swap entrances override the scrub: they're a timed spring pop, not scroll-driven */
      .filter-in {
        animation: filter-in 0.5s var(--ease-spring) both !important;
        animation-timeline: auto !important;
      }
    }
    @keyframes grid-drift {
      from { transform: translateY(0); opacity: 1; }
      to { transform: translateY(16vh); opacity: 0; }
    }
    @keyframes hero-exit {
      to { transform: translateY(-7vh); opacity: 0; }
    }
    @keyframes paper-reveal {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    @keyframes paper-parallax {
      to { transform: translateY(6vh); } /* paper creep magnitude */
    }
    @keyframes reveal-scrub {
      from { opacity: 0; transform: translateY(2.75rem); filter: blur(6px); }
      to { opacity: 1; transform: translateY(0); filter: blur(0); }
    }
    @keyframes reveal-blur-scrub {
      from { opacity: 0; filter: blur(10px); }
      to { opacity: 1; filter: blur(0); }
    }

    /* ---------- reduced motion: collapse everything ---------- */
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
      }
      .reveal { opacity: 1; transform: none; filter: none; }
      /* scroll-scrubbed cinema follows user input, but keep it off anyway */
      .hero::after, .hero-inner, .reveal, body::before {
        animation: none !important;
      }
      .type-cursor.done { animation: none; }
      .project::after { display: none; }
      .manifesto-command i,
      .runtime-trace li { animation: none; }
      .runtime-trace li { opacity: 1; transform: none; }
    }
