/*
 * HeyHi design system: motion. How things arrive, drawn here once and switched on from the markup (motion_controller.js
 * explains the attributes). One curve does nearly everything: a long, soft exponential settle (--ease-out). Things rise a
 * short way as they come; hairlines draw from the left; headlines rise line by line out of their own masks.
 * Nothing loops, nothing bounces, and nothing waits on a scroll that might never come: what's in view on arrival plays
 * as the page's opening.
 *
 * Arrivals are animations that fill backwards only: they hold the waiting state through their delay, then leave nothing
 * behind, so an element's own hover transitions work exactly as written once it's in.
 *
 * While scripts run and motion is welcome, waiting elements are hidden until the controller reveals them. Should the
 * controller never start, they appear after a moment anyway (html:not(.motion-ready)). With reduced motion, none of
 * this applies and everything is simply there.
 */

/* ---------- Waiting ---------- */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  [data-reveal]:not(.is-in),
  [data-reveal-group]:not(.is-in) > * {
    opacity: 0; transform: translate3d(0, var(--reveal-rise), 0);
  }
  [data-reveal="fade"]:not(.is-in) { transform: none; }
  [data-reveal="scale"]:not(.is-in) { transform: scale(1.035); }
  [data-reveal="clip"]:not(.is-in) { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); }
  [data-reveal="line"]:not(.is-in) { opacity: 1; transform: scaleX(0); transform-origin: left center; }

  /* A headline waits whole until it's split, then each word waits inside its mask */
  [data-split]:not([data-split="done"]) { opacity: 0; }
  [data-split="done"]:not(.is-in) .split-word { transform: translate3d(0, 108%, 0); }

  /* Should the controller never start, everything shows after a moment */
  html:not(.motion-ready) :is([data-reveal], [data-split]):not(.is-in),
  html:not(.motion-ready) [data-reveal-group]:not(.is-in) > * {
    animation: motion-fallback 0.01s 2.2s forwards;
  }

  /* ---------- In ---------- */
  [data-reveal].is-in,
  [data-reveal-group].is-in > * {
    animation: arrive-rise var(--dur-reveal) var(--ease-out) backwards;
    animation-delay: calc(var(--delay, 0ms) + var(--i, 0) * var(--stagger));
  }
  [data-reveal="fade"].is-in { animation-name: arrive-fade; }
  [data-reveal="scale"].is-in { animation-name: arrive-scale; animation-duration: var(--dur-media); }
  [data-reveal="clip"].is-in { animation-name: arrive-clip; animation-duration: var(--dur-media); }
  [data-reveal="line"].is-in { animation-name: arrive-line; animation-duration: var(--dur-line); transform-origin: left center; }
  [data-split="done"].is-in .split-word {
    animation: arrive-word var(--dur-split) var(--ease-out) backwards;
    animation-delay: calc(var(--delay, 0ms) + var(--line, 0) * var(--stagger-line));
  }
}
@keyframes motion-fallback { to { opacity: 1; transform: none; clip-path: none; } }
@keyframes arrive-rise { from { opacity: 0; transform: translate3d(0, var(--reveal-rise), 0); } }
@keyframes arrive-fade { from { opacity: 0; } }
@keyframes arrive-scale { from { opacity: 0; transform: scale(1.035); } }
@keyframes arrive-clip { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes arrive-line { from { transform: scaleX(0); } }
@keyframes arrive-word { from { transform: translate3d(0, 108%, 0); } }

/* Delays, for an element that follows its neighbours in (our security policy allows no inline styles) */
[data-delay="1"] { --delay: 80ms; }
[data-delay="2"] { --delay: 160ms; }
[data-delay="3"] { --delay: 240ms; }
[data-delay="4"] { --delay: 320ms; }
[data-delay="5"] { --delay: 420ms; }
[data-delay="6"] { --delay: 560ms; }
[data-delay="8"] { --delay: 800ms; }

/* ---------- Headlines, line by line ---------- */
/* The mask is a little taller than the line, so ascenders and descenders are never clipped, and gives the room back
   with a negative margin, so the headline sits exactly as set */
.split-mask {
  display: inline-block; vertical-align: top; overflow: clip;
  padding-block: 0.06em 0.16em; margin-block: -0.06em -0.16em;
}
.split-word { display: inline-block; }

/* ---------- Scroll-linked, where the browser can (Chrome, Edge, Safari 26) ---------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* A photograph eases from slightly larger to its frame as it scrolls in */
    .scroll-settle { animation: scroll-settle linear both; animation-timeline: view(); animation-range: entry 0% cover 50%; }
    /* A photograph drifts a little slower than the page, inside its frame */
    .scroll-drift { animation: scroll-drift linear both; animation-timeline: view(); animation-range: cover 0% cover 100%; }
  }
}
/* A page's opening scene (.scene--open, 10_base.css) opens from the content's edges to the window's full width over
   the first half-screen or so of scrolling, done before the next section comes up beneath it */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 48em) {
    .scene--open { animation: scene-open linear both; animation-timeline: scroll(root); animation-range: 0 55vh; }
  }
}
@keyframes scene-open { to { clip-path: inset(0 0 round 0 0 0 0); } }
@keyframes scroll-settle { from { transform: scale(1.1); } to { transform: scale(1); } }
@keyframes scroll-drift { from { transform: translate3d(0, -5%, 0) scale(1.1); } to { transform: translate3d(0, 5%, 0) scale(1.1); } }

/* ---------- Between pages ---------- */
/* Where the browser can, one page gives way to the next with a short crossfade and a small rise, instead of a blink */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root) { animation: page-out 0.16s var(--ease-in) both; }
  ::view-transition-new(root) { animation: page-in 0.44s var(--ease-out) both; }
}
@keyframes page-out { to { opacity: 0; } }
@keyframes page-in { from { opacity: 0; transform: translate3d(0, 8px, 0); } }
