/* =========================================================
   Site-wide scroll-reveal engine (css/scroll-reveal.css)
   Pairs with js/scroll-reveal.js. Motion reference: the
   Awaiken "Artistics" demo (home-image) — character-stagger
   heading reveals from a small x-offset, left-to-right
   clip-path image wipes inside the image's own existing
   frame, and ~100ms sequential upward fades for cards/items.

   Progressive enhancement only: every rule below is scoped
   under html.sr-js, a class js/scroll-reveal.js adds to <html>
   only once it has confirmed it can run (and the visitor has
   not asked for reduced motion). No script, or reduced motion,
   or a JS error before that line → this stylesheet never hides
   anything, so content is always available. clip-path/opacity/
   transform never change box size, so nothing reserves space
   or shifts layout either way.
   ========================================================= */

html.sr-js .sr-heading-inner { display: inline; }

html.sr-js .sr-word {
  display: inline-block;
  vertical-align: baseline;
}

html.sr-js .sr-char {
  display: inline-block;
  vertical-align: baseline;
  opacity: 0;
  transform: translateX(20px);
  transition: opacity .6s var(--ease-fluid, cubic-bezier(.22, 1, .36, 1)),
              transform .6s var(--ease-fluid, cubic-bezier(.22, 1, .36, 1));
  transition-delay: var(--sr-delay, 0s);
}
html.sr-js .sr-heading-inner.is-in .sr-char {
  opacity: 1;
  transform: translateX(0);
}

/* The wipe itself is driven by element.animate() in js/scroll-reveal.js,
   not a CSS transition (a fast/long scroll jump can bring a far-off-
   screen image into view before the browser ever painted this clipped
   "before" frame, and clip-path isn't reliably layer-promoted ahead of
   time the way opacity/transform are — with a plain CSS transition
   that sometimes means there's no earlier painted state to interpolate
   from, so it just snaps straight to fully revealed instead of wiping).
   No `transition` property here on purpose: it would otherwise fire in
   parallel with that animate() call on the very same property. .is-in
   only matters as an instant fallback if animate() is ever unsupported. */
html.sr-js img.sr-img {
  clip-path: inset(0 100% 0 0);
}
html.sr-js img.sr-img.is-in {
  clip-path: inset(0 0% 0 0);
}

html.sr-js .sr-item {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .7s var(--ease-fluid, cubic-bezier(.22, 1, .36, 1)),
              transform .7s var(--ease-fluid, cubic-bezier(.22, 1, .36, 1));
  transition-delay: var(--sr-delay, 0s);
}
html.sr-js .sr-item.is-in {
  opacity: 1;
  transform: none;
}

/* Belt-and-suspenders: if the OS-level setting changes after
   js/scroll-reveal.js already ran, or in case any future edit
   ever lets one of the rules above slip out from under the
   sr-js guard, reduced motion always wins. */
@media (prefers-reduced-motion: reduce) {
  html.sr-js .sr-char,
  html.sr-js img.sr-img,
  html.sr-js .sr-item {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
}
