/* =====================================================================
   Remotos Academy — Motion system v2 ("editorial / modernist")
   -------------------------------------------------------------------
   Idea: nothing simply "rises and fades". Content is *unmasked*:
   headlines are split into words that slide out from a clipped line,
   media is uncovered by a curtain (clip-path) while the image
   decompresses (scale 1.18 → 1), body copy focuses (blur → sharp),
   cards are wiped in from a hard edge, and long images are tied to
   the scroll position (animation-timeline: view()).
   Only transform / opacity / clip-path / filter are animated.

   Hooks (script.js): html.js, html.is-ready, .reveal.is-in,
   .site-header.is-scrolled, --hero-shift, .w/.w-in + --i (split words),
   --mx/--my (magnetic buttons), --rx/--ry (card tilt).
   Load order: styles.css → motion.css → motion-hero.css →
               motion-sections.css → motion-interactions.css
   ===================================================================== */

:root {
  /* easing */
  --ease-out:    cubic-bezier(.22, 1, .36, 1);     /* signature: fast in, long settle */
  --ease-expo:   cubic-bezier(.16, 1, .3, 1);      /* masks / curtains */
  --ease-in:     cubic-bezier(.4, 0, 1, 1);        /* exits */
  --ease-inout:  cubic-bezier(.65, 0, .35, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);  /* magnetic release */
  --ease-ambient: ease-in-out;

  /* durations */
  --dur-quick: 160ms;
  --dur-std:   360ms;
  --dur-slow:  720ms;
  --dur-mask:  900ms;     /* word masks, curtains */
  --dur-media: 1200ms;    /* image decompression */

  /* rhythm */
  --stagger: 70ms;
  --word-stagger: 38ms;
  --word-cap: 420ms;      /* longest word delay → stagger totals stay < 500ms */
  --rise: 24px;
  --blur: 12px;
  --zoom: 1.18;
}

/* ---------------------------------------------------------------------
   0. Neutralize the generic scroll-reveal from styles.css.
      The container never moves; its children are choreographed.
   --------------------------------------------------------------------- */
.reveal,
.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: none;
}

/* ---------------------------------------------------------------------
   1. Shared keyframes (end state == natural state → `backwards` fill
      releases the element to hover / scroll transforms after landing)
   --------------------------------------------------------------------- */
@keyframes mm-word {              /* split word: slides out of its line mask */
  from { transform: translateY(130%) rotate(4deg); }
  to   { transform: none; }
}
@keyframes mm-focus {             /* body copy: out-of-focus → sharp */
  from { opacity: 0; filter: blur(var(--blur)); transform: translateY(10px); }
  to   { opacity: 1; filter: blur(0);           transform: none; }
}
@keyframes mm-focus-still {       /* same, no travel (for inline / small bits) */
  from { opacity: 0; filter: blur(8px); }
  to   { opacity: 1; filter: blur(0); }
}
@keyframes mm-curtain-up {        /* media uncovered from the bottom edge */
  from { clip-path: inset(100% 0 0 0 round var(--r, 0px)); }
  to   { clip-path: inset(0 0 0 0 round var(--r, 0px)); }
}
@keyframes mm-curtain-down {      /* uncovered from the top edge */
  from { clip-path: inset(0 0 100% 0 round var(--r, 0px)); }
  to   { clip-path: inset(0 0 0 0 round var(--r, 0px)); }
}
@keyframes mm-curtain-right {     /* uncovered left → right */
  from { clip-path: inset(0 100% 0 0 round var(--r, 0px)); }
  to   { clip-path: inset(0 0 0 0 round var(--r, 0px)); }
}
@keyframes mm-zoom {              /* image decompresses inside its curtain */
  from { transform: scale(var(--zoom)); }
  to   { transform: none; }
}
@keyframes mm-card {              /* card: wiped from the top + settles from below */
  from { clip-path: inset(0 0 100% 0 round var(--r, 0px)); transform: translateY(28px); }
  to   { clip-path: inset(0 0 0 0 round var(--r, 0px));    transform: none; }
}
@keyframes mm-card-x {            /* row: wiped left → right, slight slide */
  from { clip-path: inset(0 100% 0 0 round var(--r, 0px)); transform: translateX(-12px); }
  to   { clip-path: inset(0 0 0 0 round var(--r, 0px));    transform: none; }
}
@keyframes mm-pop {               /* chips / dots / numbers */
  from { opacity: 0; transform: scale(.4); }
  to   { opacity: 1; transform: none; }
}
@keyframes mm-draw-x {            /* rules / bars drawn from the left */
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes mm-float {             /* ambient drift */
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
@keyframes mm-breathe {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%      { transform: scale(1.25); opacity: .75; }
}

/* ---------------------------------------------------------------------
   2. Split-word primitive (created by script.js)
      <h2><span class="w" style="--i:0"><span class="w-in">Word</span></span> …</h2>
      Outer = line mask (clip only, negative inset keeps descenders).
      Inner = the moving glyphs.
   --------------------------------------------------------------------- */
.w {
  display: inline-block;
  clip-path: inset(-.18em -.06em -.28em -.06em);
  vertical-align: top;
}
.w-in {
  display: inline-block;
  transform-origin: 0 100%;
  will-change: transform;
}
html.js .is-split:not(.is-shown) .w-in {
  transform: translateY(130%) rotate(4deg);
}
.is-split.is-shown .w-in {
  animation: mm-word var(--dur-mask) var(--ease-expo) both;
  animation-delay: calc(var(--d, 0ms) + min(var(--i, 0) * var(--word-stagger), var(--word-cap)));
}
.is-split.is-shown .w-in:where(:not(:first-child)) { will-change: auto; }

/* Headline that could not be split (no JS / no text nodes): focus in. */
html.js .reveal:not(.is-in) h2:not(.is-split):not(.lab-feature-card h2) { opacity: 0; }
.reveal.is-in h2:not(.is-split):not(.lab-feature-card h2) {
  animation: mm-focus var(--dur-slow) var(--ease-out) backwards;
}

/* ---------------------------------------------------------------------
   3. Reduced motion: everything lands instantly, nothing blurs or clips.
      (styles.css already forces 1ms durations; this removes the rest.)
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.js .is-split:not(.is-shown) .w-in { transform: none; }
  .w-in, .w { animation: none !important; transform: none !important; clip-path: none !important; }
  .reveal *, .hero *, .site-header * {
    animation-delay: 0ms !important;
    filter: none !important;
  }
  html.js .reveal:not(.is-in) * { opacity: 1 !important; }
}
