/* ============================================================================
   500x — scroll motion
   ----------------------------------------------------------------------------
   One vocabulary for every page except the homepage, which is a scrub film and
   owns its own timing.

   The restraint is the point. Four moves, used precisely:

     · type rises out of a mask, word by word, so a heading arrives rather than
       appears;
     · media settles — a shade over scale, easing to rest, so an image looks
       placed rather than pasted;
     · full-bleed art drifts against the scroll, which is what gives a flat
       page depth;
     · groups stagger, because six things arriving together is a flash and six
       things arriving in sequence is a rhythm.

   Everything is transform and opacity, so it stays on the compositor. Nothing
   animates width, top, or filter during scroll.
   ========================================================================== */

:root {
  --mo-ease: cubic-bezier(0.22, 0.7, 0.19, 1);
  --mo-dur: 1.05s;
  --mo-rise: 118%;
}

/* Until the script has classified the page, nothing hides. A visitor with a
   broken script gets the whole page, not a blank one. */
html:not(.mo-ready) [data-mo] { opacity: 1 !important; transform: none !important; }

/* ---------------------------------------------------------------- the mask */
/* Each word gets a clipping shell and an inner that starts below its own
   baseline, so type climbs into frame instead of fading up through it. */
.mo-line { display: block; overflow: hidden; padding-bottom: 0.08em; margin-bottom: -0.08em; }
.mo-w { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: 0.14em; margin-bottom: -0.14em; }
.mo-wi {
  display: inline-block;
  transform: translateY(var(--mo-rise));
  transition: transform var(--mo-dur) var(--mo-ease);
  transition-delay: calc(var(--mo-i, 0) * 42ms);
  will-change: transform;
}
.mo-in .mo-wi { transform: translateY(0); }

/* ------------------------------------------------------------- block moves */
[data-mo="up"]    { opacity: 0; transform: translateY(34px); }
[data-mo="left"]  { opacity: 0; transform: translateX(-34px); }
[data-mo="right"] { opacity: 0; transform: translateX(34px); }
[data-mo="fade"]  { opacity: 0; }
[data-mo="scale"] { opacity: 0; transform: scale(0.965); }

[data-mo] {
  transition: opacity 0.9s var(--mo-ease), transform var(--mo-dur) var(--mo-ease);
  transition-delay: calc(var(--mo-i, 0) * 78ms);
}
[data-mo].mo-in { opacity: 1; transform: none; }

/* A rule that draws itself rather than blinking on. */
[data-mo="rule"] {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.15s var(--mo-ease);
  transition-delay: calc(var(--mo-i, 0) * 78ms);
}
[data-mo="rule"].mo-in { transform: scaleX(1); }

/* ------------------------------------------------------------ media settle */
/* The frame clips; the picture inside eases back to rest. Slower than the
   type, so the image is still arriving when the words have landed. */
.mo-media { overflow: hidden; }
.mo-media > img,
.mo-media > video,
.mo-media > canvas {
  transform: scale(1.08);
  transition: transform 1.6s var(--mo-ease), opacity 1s var(--mo-ease);
  opacity: 0;
  will-change: transform;
}
.mo-media.mo-in > img,
.mo-media.mo-in > video,
.mo-media.mo-in > canvas { transform: scale(1); opacity: 1; }

/* Parallax is driven from JS on a single rAF; this only declares intent. */
[data-parallax] { will-change: transform; }

/* --------------------------------------------------------- scroll progress */
.mo-progress {
  position: fixed;
  left: 0; top: 0;
  height: 2px;
  width: 0;
  z-index: 90;
  background: linear-gradient(90deg, #7a3fa8, #2a5cdb 55%, #d94a5e);
  pointer-events: none;
  transition: opacity 0.3s ease;
}

/* -------------------------------------------------------------- page enter */
/* One quiet lift on load so a page never simply blinks into existence. */
html.mo-ready body { animation: mo-enter 0.7s var(--mo-ease) both; }
@keyframes mo-enter {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* -------------------------------------------------------- reduced motion */
/* Not a degraded version: the same page, arriving at once. */
@media (prefers-reduced-motion: reduce) {
  html.mo-ready body { animation: none; }
  [data-mo], [data-mo="rule"] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .mo-wi { transform: none !important; transition: none !important; }
  .mo-media > img, .mo-media > video, .mo-media > canvas {
    transform: none !important; opacity: 1 !important; transition: none !important;
  }
  [data-parallax] { transform: none !important; }
  .mo-progress { display: none; }
}
