/* motion.css — tokens, states and page transition of the motion kit (API and rules: README.md).
   Everything here animates transform, opacity (and scale / translate) only. The page as authored is the END state:
   a class that holds something back is only ever added by a script, or by the head snippet before the first paint. */

/* ---------- tokens ---------- */
:root {
  /* carried over from tokyo-sg20 (src/lib/motion/tuning.ts → global.css), same names and values */
  --motion-ease: cubic-bezier(.22, 1, .36, 1);        /* the one UI ease */
  --motion-fast: 120ms;                               /* state change: colour, border, focus ring */
  --motion-base: 160ms;                               /* open / close, hover lift */
  --motion-slow: 400ms;                               /* reveal, cross-fade */
  --motion-page: 400ms;                               /* page transition */
  --motion-controls-hover-lift: 2px;
  --motion-press-scale: 0.96;                         /* press bounce: sink, then spring back past the size */
  --motion-press-in: 90ms;
  --motion-press-out: 440ms;
  --motion-press-out-fallback: 230ms;
  --motion-press-spring: linear(0, 0.323 2.9%, 0.593 5.6%, 0.809 8.1%, 0.989 10.6%, 1.133 13.1%, 1.24 15.6%, 1.319 18.3%, 1.362 21%, 1.374 24.4%, 1.346 28.3%, 1.049 45.8%, 0.99 50.8%, 0.953 56%, 0.94 65%, 1 88.8%, 1);
  --motion-press-fallback: cubic-bezier(0.34, 2.323, 0.64, 1);
  /* carried over as values (literals in tokyo-sg20's global.css), named here */
  --motion-ease-page: cubic-bezier(.32, .72, 0, 1);   /* the page push */
  --motion-ease-inout: cubic-bezier(.4, 0, .2, 1);    /* something leaving or turning */
  --motion-stagger: 60ms;                             /* reveal: block to block (tuning.ts reveal.staggerMs) */
  --motion-rise: 12px;                                /* reveal: starts this far under its place */
  /* new */
  --motion-tilt: 8deg;                                /* .m-tilt at the element's edge */
  --motion-perspective: 800px;
}

/* ---------- page transition (cross-document view transition; page.js adds the direction) ----------
   Both pages opt in by loading this file. No support: a normal page load. Reduced motion: no opt-in at all. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
::view-transition-group(*) { animation-duration: var(--motion-page); animation-timing-function: var(--motion-ease); }
::view-transition-old(root), ::view-transition-new(root) { animation-timing-function: var(--motion-ease-page); }

/* <html data-vt="push">: the next page pushes in from the right over the old one, which slides a little left and
   dims; back (a step back in the history, a link up) runs the other way. Ported from tokyo-sg20 "15 page
   transitions" (the client's navigation prototype). Without the attribute the browser's cross-fade runs. */
@keyframes m-push-in-right { from { transform: translateX(100%); } }
@keyframes m-push-out-left { to { transform: translateX(-28%); filter: brightness(.94); } }
@keyframes m-push-in-left { from { transform: translateX(-28%); filter: brightness(.94); } }
@keyframes m-push-out-right { to { transform: translateX(100%); } }
html[data-vt="push"]::view-transition-group(root) { animation: none; }
html[data-vt="push"]::view-transition-old(root),
html[data-vt="push"]::view-transition-new(root) { mix-blend-mode: normal; animation-fill-mode: both; }
html[data-vt="push"]::view-transition-old(root) { animation-name: m-push-out-left; }
html[data-vt="push"]::view-transition-new(root) { animation-name: m-push-in-right; box-shadow: -12px 0 32px rgb(0 0 0 / .14); }
html[data-vt="push"]:active-view-transition-type(back)::view-transition-old(root) { z-index: 1; animation-name: m-push-out-right; box-shadow: -12px 0 32px rgb(0 0 0 / .14); }
html[data-vt="push"]:active-view-transition-type(back)::view-transition-new(root) { animation-name: m-push-in-left; box-shadow: none; }

/* .m-keep: one element per page (the header) that takes no part — the old one's picture is never shown, the new one
   stands still above the pages from the first frame. A view-transition-name must be unique in its page. */
.m-keep { view-transition-name: m-keep; }
::view-transition-group(m-keep) { animation: none; z-index: 2; }
::view-transition-old(m-keep) { display: none; }
::view-transition-new(m-keep) { animation: none; }

/* ---------- first-screen entrance, once a visit (needs the head snippet of README.md) ----------
   html.m-first: this page has not played its entrance in this visit — held from the first paint.
   html.m-go:    the page is live (shown, its view transition over) — it plays. Without the snippet: no entrance. */
@keyframes m-enter { from { opacity: 0; transform: translate3d(0, var(--motion-rise), 0); } }
html.m-first .m-enter { opacity: 0; }
html.m-first.m-go .m-enter { opacity: 1; animation: m-enter var(--motion-slow) var(--motion-ease) calc(var(--m-i, 0) * var(--motion-stagger)) backwards; }

/* ---------- reveal (reveal.js): .m-wait holds what is still below the screen, .m-in lets it arrive ---------- */
.m-wait { opacity: 0; transform: translate3d(0, var(--motion-rise), 0); }
[data-reveal~="fade"].m-wait, [data-reveal~="fade"] > .m-wait { transform: none; }
[data-reveal~="scale"].m-wait, [data-reveal~="scale"] > .m-wait { transform: scale(.96); }
.m-in {
  transition:
    opacity var(--motion-slow) var(--motion-ease) calc(var(--m-i, 0) * var(--motion-stagger)),
    transform var(--motion-slow) var(--motion-ease) calc(var(--m-i, 0) * var(--motion-stagger));
}

/* ---------- scroll-scrubbed keyframes (scroll.js) ----------
   .m-scrub  a tall track (--m-len); its .m-pin child stays on the screen while the track passes.
   .m-view   any element; progress = its way through the screen.
   .m-key    inside either (or on a .m-view itself): --m-kf names its @keyframes, scrubbed by the nearest track.
   With scroll-driven animations the browser runs them off the main thread; elsewhere scroll.js writes --m-p (0…1)
   on the track and the same keyframes follow it, paused, through a negative delay. One set of keyframes, two drivers.
   The kit owns the `animation` shorthand of .m-key (a shorthand written after it would reset animation-timeline). */
.m-scrub { position: relative; height: var(--m-len, 300vh); view-timeline: --m-tl block; --m-range: contain; }
.m-pin { position: sticky; top: 0; height: 100vh; height: 100svh; overflow: clip; }
.m-view { view-timeline: --m-tl block; --m-range: cover; }
.m-key { animation: var(--m-kf, none) 1s linear both; }
@supports (animation-timeline: view()) {
  .m-key { animation-timeline: --m-tl; animation-range: var(--m-range, cover); }
}
@supports not (animation-timeline: view()) {
  .m-key { animation-play-state: paused; animation-delay: calc(var(--m-p, 0) * -1s); }
}
html[data-scroll="js"] .m-key { animation-timeline: auto; animation-play-state: paused; animation-delay: calc(var(--m-p, 0) * -1s); } /* testing: the script path in any browser */

/* ---------- pointer (pointer.js) ---------- */
/* tilt(): --m-x / --m-y run −1…1. .m-layer inside a tilted element shifts by its own --m-depth (parallax). */
.m-tilt { transform: perspective(var(--motion-perspective)) rotateX(calc(var(--m-y, 0) * var(--motion-tilt) * -1)) rotateY(calc(var(--m-x, 0) * var(--motion-tilt))); }
.m-layer { translate: calc(var(--m-x, 0) * var(--m-depth, 12px)) calc(var(--m-y, 0) * var(--m-depth, 12px)); }
/* hold(): --m-hold runs 0…1. .m-fill inside is a ready-made bar. A long press must not select or call a menu. */
.m-hold { touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.m-fill { transform: scaleX(var(--m-hold, 0)); transform-origin: 0 50%; }
/* drag(): listeners are passive, so CSS says what the gesture may scroll. .m-drag-x leaves vertical scrolling. */
.m-drag { touch-action: none; user-select: none; -webkit-user-select: none; -webkit-user-drag: none; cursor: grab; }
.m-drag-x { touch-action: pan-y; }
.m-drag.m-active { cursor: grabbing; }
/* will-change only while something moves: pointer.js puts m-active on and takes it off. */
.m-active { will-change: transform; }

/* ---------- press bounce + hover lift (CSS only; ported from tokyo-sg20 "Press bounce") ----------
   `scale` and `translate` are their own properties, so the element's `transform` stays free. A `transition`
   shorthand replaces the whole list: an element that also eases its colours lists them again in its own rule.
   --press-ease switches to the linear() spring only where linear() exists (an unknown function would void the list). */
@media (prefers-reduced-motion: no-preference) {
  .m-press {
    --press-time: var(--motion-press-out-fallback);
    --press-ease: var(--motion-press-fallback);
    transition: scale var(--press-time) var(--press-ease), translate var(--motion-base) var(--motion-ease);
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  @supports (transition-timing-function: linear(0, 1)) {
    .m-press { --press-time: var(--motion-press-out); --press-ease: var(--motion-press-spring); }
  }
  .m-press:active { scale: var(--motion-press-scale); --press-time: var(--motion-press-in); --press-ease: var(--motion-ease); }
}
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .m-press:hover { translate: 0 calc(-1 * var(--motion-controls-hover-lift)); }
}

/* ---------- rendering hygiene ---------- */
/* A long section far below the fold is not laid out or painted until it comes near. Give an honest size estimate
   (--m-cv) so the scrollbar and scroll restoration stay right. Not on a .m-scrub, nor around [data-reveal] children. */
.m-cv { content-visibility: auto; contain-intrinsic-size: auto var(--m-cv, 100vh); }

/* ---------- tier a, reduced motion, print: nothing is held, nothing is pinned ---------- */
html[data-motion-tier="a"] .m-wait { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  .m-key { animation: none !important; }
  .m-scrub { height: auto !important; }
  .m-pin { position: static; height: auto; }
  .m-tilt { transform: none; }
  .m-layer { translate: none; }
}
@media print {
  .m-wait, html.m-first .m-enter { opacity: 1 !important; transform: none !important; }
  .m-key { animation: none !important; }
  .m-scrub { height: auto !important; }
  .m-pin { position: static; height: auto; }
}
