@layer motion {

/* ============================================================
   TY MABEE - FIELD JOURNAL · motion
   the reveal engine, every keyframe, and the reduced-motion
   contract for all of it
   ============================================================ */

/* ============================================================
   REVEAL ENGINE
   ------------------------------------------------------------
   Uses the independent `translate` property rather than
   `transform`, so a reveal never fights an element's own
   transform (specimen tilt, hover lift, drag matrix).

   NOTE THE :where(). These rules must be gated on html.js -
   without JS the page would otherwise render as a cover and
   empty space. But a plain `html.js [data-reveal]` selector has
   specificity (0,2,1), which BEATS `[data-reveal].is-inked` at
   (0,2,0): the opacity: 0 would win and nothing would ever
   reveal, with JS enabled. :where() contributes zero
   specificity, so the gate costs nothing and .is-inked still
   wins at (0,2,0) against (0,1,0).
   ============================================================ */
:where(html.js) [data-reveal] {
  opacity: 0;
  translate: 0 26px;
  transition: opacity 0.7s var(--ease-out), translate 0.7s var(--ease-out);
  transition-delay: var(--d, 0s);
}
[data-reveal].is-inked { opacity: 1; translate: 0 0; }

:where(html.js) [data-note] {
  opacity: 0;
  transform: rotate(-3deg) translateY(8px) scale(0.94);
  transition: opacity 0.5s ease 0.4s, transform 0.5s var(--ease-spring) 0.4s;
}
[data-note].is-inked { opacity: 1; transform: rotate(-3deg) translateY(0) scale(1); }

:where(html.js) [data-stamp] { opacity: 0; }
[data-stamp].is-stamped { animation: stamp-slam 0.45s var(--ease-spring) forwards; }
.stamp-project.is-stamped { --stamp-rot: 8deg; }

/* scribbled ink paths draw themselves on reveal;
   without JS they are simply visible from the start */
.scribble-path { stroke: currentColor; stroke-width: 3.4; stroke-linecap: round; }
:where(html.js) .scribble-path { stroke-dasharray: 320; stroke-dashoffset: 320; }
.is-inked .scribble-path { animation: ink-draw 0.9s var(--ease-out) 0.25s forwards; }

/* ============================================================
   AMBIENT
   ============================================================ */
.marquee-track { animation: marquee-scroll 26s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
.scroll-cue svg { animation: cue-bob 1.8s ease-in-out infinite; }

/* ============================================================
   KEYFRAMES
   ============================================================ */
@keyframes ink-draw      { to { stroke-dashoffset: 0; } }
@keyframes marquee-scroll{ to { transform: translateX(-50%); } }
@keyframes cue-bob       { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
@keyframes arrow-nudge   { 50% { transform: translateX(5px); } }
@keyframes speck-fade    { to { opacity: 0; transform: translateY(3px) scale(0.4); } }
@keyframes stamp-slam {
  0%   { opacity: 0;    transform: scale(2.6)  rotate(-14deg); }
  60%  { opacity: 1;    transform: scale(0.94) rotate(var(--stamp-rot, -9deg)); }
  100% { opacity: 1;    transform: scale(1)    rotate(var(--stamp-rot, -9deg)); }
}

/* Once the section itself is visible, capable browsers bind the
   underline to reading progress. The time-based draw above remains the
   fallback and no-JS paths still show a complete line. */
@supports (animation-timeline: view()) {
  .is-inked .underline .scribble-path {
    animation: ink-draw linear both;
    animation-timeline: view(block);
    animation-range: entry 8% cover 42%;
  }
}

/* ============================================================
   REDUCED MOTION
   ------------------------------------------------------------
   Three things here are easy to miss and all three matter:

   1. animation-duration: 0.01ms does NOT stop a scroll-driven
      animation. Those are driven by scroll progress, not time,
      so they need animation-timeline: none.
   2. ::view-transition-* pseudos live on a separate root pseudo
      tree that the global * selector never reaches.
   3. The JS side gates too - modules/cursor.js and the inertia
      loop in modules/ephemera.js never start under reduce.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    animation-timeline: none !important;
  }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }

  [data-reveal], [data-note], [data-stamp] { opacity: 1 !important; translate: none !important; }
  [data-note] { transform: rotate(-3deg) !important; }
  .scribble-path { stroke-dashoffset: 0 !important; }
  .marquee-track { animation: none; }
  .cursor { display: none; }
}

/* no custom cursor on touch devices */
@media (pointer: coarse) {
  .cursor { display: none; }
  body.cursor-active,
  body.cursor-active a,
  body.cursor-active [data-hover],
  body.cursor-active .drag { cursor: auto; }
}

}
