/* ============================================================================
   anim.css: the CSS half of the animation library. Pairs with assets/js/anim.js.

   CSS owns how a thing moves. JS only adds `.anim-in` / `.split-in` / `.drawn`
   and writes --p, --shift, --mx, --my. That split is the whole design: a
   designer can retime the entire site in this one file without touching JS,
   and every effect still has a finished state if the script never loads.

   Load order: this file before the site's own CSS, so page rules win.
   ========================================================================== */

:root {
  --anim-dur:     700ms;
  --anim-delay:   0ms;
  --anim-dist:    14px;
  --anim-ease:    cubic-bezier(.16, 1, .3, 1);   /* matches --ease-out */
  --split-dur:    820ms;
  --split-step:   34ms;
}

/* ----------------------------------------------------------------- reveal
   One transition declaration, six directions. `will-change` is deliberately
   absent: these elements animate once, and a permanent compositor layer on
   forty of them costs more than the transition does. */
.anim {
  opacity: 0;
  transition:
    opacity   var(--anim-dur) var(--anim-ease) var(--anim-delay),
    transform var(--anim-dur) var(--anim-ease) var(--anim-delay),
    filter    var(--anim-dur) var(--anim-ease) var(--anim-delay);
}
/* clip-path belongs to the clip variant alone. Applied to every .anim-in it
   would leave a permanent inset(0) on the element, which silently clips any
   child that overflows the box: a shadow, a badge, a pointing callout. */
[data-anim="clip"] { transition-property: opacity, transform, filter, clip-path; }
[data-anim="up"]    { transform: translate3d(0, var(--anim-dist), 0); }
[data-anim="down"]  { transform: translate3d(0, calc(var(--anim-dist) * -1), 0); }
[data-anim="left"]  { transform: translate3d(calc(var(--anim-dist) * 1.6), 0, 0); }
[data-anim="right"] { transform: translate3d(calc(var(--anim-dist) * -1.6), 0, 0); }
[data-anim="scale"] { transform: scale(.965); }
[data-anim="blur"]  { transform: translate3d(0, 16px, 0); filter: blur(9px); }
[data-anim="clip"]  { clip-path: inset(0 0 100% 0); transform: none; }

.anim.anim-in {
  opacity: 1;
  transform: none;
  filter: none;
}
[data-anim="clip"].anim-in { clip-path: inset(0 0 0 0); }

/* ------------------------------------------------------------ split text
   The outer span clips; the inner one rises into it. Words, not characters,
   by default, a headline read one letter at a time is a logo animation, not
   typography. */
.split-word, .split-char { display: inline-block; overflow: hidden; vertical-align: top; }
.split-word > span, .split-char > span {
  display: inline-block;
  transform: translate3d(0, 105%, 0);
  opacity: 0;
  transition:
    transform var(--split-dur) var(--anim-ease) calc(var(--i) * var(--split-step)),
    opacity   calc(var(--split-dur) * .7) linear calc(var(--i) * var(--split-step));
}
.split-in .split-word > span, .split-in .split-char > span { transform: none; opacity: 1; }

/* ------------------------------------------------------------- SVG draw */
[data-draw] { stroke-dasharray: var(--len, 400); stroke-dashoffset: var(--len, 400); }
[data-draw].drawn { animation: anim-draw 1150ms var(--anim-ease) 120ms forwards; }
@keyframes anim-draw { to { stroke-dashoffset: 0; } }

/* ------------------------------------------------------------- parallax
   --shift is written by JS in px; nothing here re-reads layout. */
[data-parallax] { transform: translate3d(0, var(--shift, 0px), 0); will-change: transform; }

/* --------------------------------------------------------------- marquee
   Paused on hover so a reader can actually read a row that is moving. */
.marquee { display: flex; overflow: hidden; gap: var(--marquee-gap, 10px); mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.marquee-track {
  display: flex; gap: var(--marquee-gap, 10px); flex: none; padding-right: var(--marquee-gap, 10px);
  animation: anim-marquee var(--marquee-dur, 38s) linear infinite;
}
.marquee:hover .marquee-track, .marquee:focus-within .marquee-track { animation-play-state: paused; }
@keyframes anim-marquee { to { transform: translate3d(-100%, 0, 0); } }

/* ----------------------------------------------------------- bar / meter
   A measured bar grows on the compositor: the width is the datum, the scale
   is the animation, so the number never lies mid-flight. */
.grow-x { transform-origin: left center; animation: anim-grow 820ms var(--anim-ease) both; }
@keyframes anim-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* --------------------------------------------------- cross-page transitions
   Only on browsers that ship View Transitions; `.vt` is added by JS. */
@media (prefers-reduced-motion: no-preference) {
  html.vt { view-transition-name: none; }
}
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: anim-vt-out 180ms ease both; }
::view-transition-new(root) { animation: anim-vt-in 260ms var(--anim-ease) both; }
@keyframes anim-vt-out { to { opacity: 0; } }
@keyframes anim-vt-in  { from { opacity: 0; transform: translate3d(0, 8px, 0); } }

/* ------------------------------------------------------------ kill switches
   `?still` freezes the page for ad stills; the media query is the real rule.
   Both land on the finished state, never on a blank element. */
.still .anim, .still .split-word > span, .still .split-char > span {
  opacity: 1 !important; transform: none !important; filter: none !important;
  clip-path: none !important; transition: none !important;
}
.still [data-draw] { stroke-dashoffset: 0 !important; animation: none !important; }
.still .marquee-track, .still .grow-x { animation: none !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .anim, .split-word > span, .split-char > span {
    opacity: 1 !important; transform: none !important; filter: none !important;
    clip-path: none !important; transition: none !important;
  }
  [data-draw] { stroke-dashoffset: 0 !important; animation: none !important; }
  [data-parallax] { transform: none !important; }
  .marquee-track, .grow-x { animation: none !important; transform: none !important; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}
