/* motion.css -- MARKED LAYER hub-2d-motion-20260913. Opt-in by data attribute; tokens in ONE :root block; contract in .lanes/requests/2d-motion.md
 * 01 SCROLL [data-parallax|scrub|pin]  02 REVEAL [data-reveal|stagger]  03 HOVER [data-hover|magnet]  04 CLICK [data-press] */
:root {
  --m-dur-fast: 120ms;
  --m-dur-base: 360ms;
  --m-dur-slow: 720ms;
  --m-ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --m-ease-out: cubic-bezier(.22, .61, .36, 1);
  --m-lift: 10px;
  --m-zoom: 1.06;
  --m-stagger: 90ms;
  --m-magnet: 8px;
  --m-par: 40px;
  --m-pin-top: 0px;
}

/* 02 REVEAL: parked at opacity 0 ONLY under html.m-ready; entrance = keyframe, backwards fill, no forwards fill ------------- */
html.m-ready [data-reveal]:not(.is-in), html.m-ready [data-stagger]:not(.is-in) > * { opacity: 0; }
[data-reveal].is-in, [data-stagger].is-in > * {
  animation: m-in var(--m-dur-base) var(--m-ease-out) backwards; animation-delay: calc(var(--i, 0) * var(--m-stagger));
}
[data-reveal="clip"].is-in { animation-name: m-clip; animation-duration: var(--m-dur-slow); }
@keyframes m-in { from { opacity: 0; transform: translateY(var(--m-lift)); } }
@keyframes m-clip { from { clip-path: inset(0 100% 0 0); } }

/* 04 CLICK press + spring; magnet shares the ONE transform translate(--mx,--my) scale(--ms) ---------- */
[data-press], [data-magnet] {
  transform: translate(var(--mx, 0px), var(--my, 0px)) scale(var(--ms, 1));
  transition: transform var(--m-dur-fast) var(--m-ease-spring);
  transform-box: fill-box; transform-origin: center;
}
:where(a[data-press], a[data-magnet]) { display: inline-block; }
[data-press]:active { --ms: .96; }
[data-press][aria-busy="true"] { cursor: progress; }
[data-magnet] { transition-duration: var(--m-dur-base); }
[data-magnet]:active { --ms: .96; transition-duration: var(--m-dur-fast); }

/* 03 HOVER: the image moves, the layout never does; arrow / underline / label react ------------- */
[data-hover="zoom"] { overflow: hidden; }
[data-hover="zoom"] > :is(img, svg), [data-hover="zoom"] .m-zoom { display: block; transform-origin: center; transition: transform var(--m-dur-slow) var(--m-ease-out); }
[data-hover="zoom"]:is(:hover, :focus-within) > :is(img, svg), [data-hover="zoom"]:is(:hover, :focus-within) .m-zoom { transform: scale(var(--m-zoom)); }
[data-hover="shift"] :is(.m-arrow, .m-label) { display: inline-block; transition: transform var(--m-dur-fast) var(--m-ease-out); }
[data-hover="shift"]:is(:hover, :focus-visible) .m-arrow { transform: translateX(4px); }
[data-hover="shift"]:is(:hover, :focus-visible) .m-label { transform: translateX(2px); }
[data-hover="shift"] .m-line { background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0 1px; transition: background-size var(--m-dur-base) var(--m-ease-out); }
[data-hover="shift"]:is(:hover, :focus-visible) .m-line { background-size: 100% 1px; }

/* 01 SCROLL: scroll-driven where supported; motion.js writes --py for parallax elsewhere ---------- */
[data-pin] { position: sticky; top: var(--m-pin-top); }
@supports (animation-timeline: view()) {
  [data-parallax] { animation: m-par linear both; animation-timeline: view(); }
  [data-scrub] { animation: m-scrub linear both; animation-timeline: view(); animation-range: entry 0% cover 45%; }
  [data-scrub="out"] { animation-name: m-scrub-out; animation-range: exit 0% exit 100%; }
}
@supports not (animation-timeline: view()) {
  [data-parallax] { transform: translateY(var(--py, 0px)); }
}
@keyframes m-par { from { transform: translateY(calc(var(--m-par) * var(--m-speed, 1))); } to { transform: translateY(calc(var(--m-par) * var(--m-speed, 1) * -1)); } }
@keyframes m-scrub { from { opacity: 0; transform: translateY(var(--m-lift)); } to { opacity: 1; transform: none; } }
@keyframes m-scrub-out { from { opacity: 1; } to { opacity: 0; } }

/* REDUCED MOTION: nothing moves, everything readable is visible, state changes still shown in words ---- */
@media (prefers-reduced-motion: reduce) {
  *, ::before, ::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; }
  [data-reveal], [data-stagger] > *, [data-press], [data-magnet], [data-parallax], [data-scrub],
  [data-hover="zoom"] > :is(img, svg), [data-hover="zoom"] .m-zoom, [data-hover="shift"] :is(.m-arrow, .m-label) {
    animation: none !important; opacity: 1 !important; transform: none !important; clip-path: none !important;
  }
  [data-pin] { position: static; }
}
