/* =========================================================
   motion.css — scroll reveal + smooth scrolling
   Pairs with motion.js.

   Every start state below is scoped to .js-motion, which motion.js sets on
   <html> before first paint. With JS off, or if the file fails to load,
   nothing is hidden — the motion is decoration, never a gate on content.

   Theme it by setting these on :root in your own stylesheet:
     --motion-ease      easing curve
     --motion-rise      travel distance for .rise   (default 12px)
     --motion-duration  duration for .rise          (default 0.95s)
   ========================================================= */

/* ---------------------------------------------------------
   Vocabulary — put these classes in your markup:

   .reveal   a block that triggers when it scrolls into view.
             It gets .in, and everything below keys off that.
   .rise     a child of .reveal that fades up. Stagger with --d.
   .mask     a heading whose single <span> child rises out of a
             clipped line box:  <h2 class="mask"><span>About</span></h2>
   .wipe     a block whose top hairline draws left → right.
             Set --wipe-color to the rule colour.
   --------------------------------------------------------- */

/* ---- fade up ---- */
.js-motion .rise {
  opacity: 0;
  transform: translateY(var(--motion-rise, 12px));
  transition:
    opacity   var(--motion-duration, 0.95s) var(--motion-ease, cubic-bezier(0.22, 0.61, 0.36, 1)) var(--d, 0s),
    transform var(--motion-duration, 0.95s) var(--motion-ease, cubic-bezier(0.22, 0.61, 0.36, 1)) var(--d, 0s);
}
.js-motion .in .rise,
.js-motion .rise.in { opacity: 1; transform: none; }

/* ---- heading rises out of its own line box ---- */
.js-motion .mask {
  overflow: hidden;
  padding-block: 0.14em;                 /* room for ascenders/descenders … */
  margin-block: -0.14em;                 /* … taken back out of the layout   */
}
.js-motion .mask > span {
  display: block;
  transform: translateY(110%);
  transition: transform 1.15s var(--motion-ease, cubic-bezier(0.22, 0.61, 0.36, 1));
}
.js-motion .in .mask > span,
.js-motion .mask.in > span { transform: none; }

/* ---- hairline wipes in left → right ---- */
.js-motion .wipe { position: relative; border-top-color: transparent !important; }
.js-motion .wipe::before {
  content: "";
  position: absolute;
  inset: -1px 0 auto 0;
  height: 1px;
  background: var(--wipe-color, currentColor);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.2s var(--motion-ease, cubic-bezier(0.22, 0.61, 0.36, 1));
}
.js-motion .wipe.in::before,
.js-motion .in.wipe::before { transform: scaleX(1); }

/* ---- smooth scrolling ----
   motion.js adds .smooth only when it actually takes over. The wrapper is
   fixed and the content inside it is translated every frame.
   NOTE: position:fixed and position:sticky do NOT work inside a transformed
   ancestor. Mark any fixed/sticky element with data-outside so motion.js
   leaves it out of the wrapper. */
.smooth #smooth-wrap {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: 2;
}
.smooth #smooth-content { will-change: transform; }

/* ---- reduced motion: show everything, move nothing ---- */
@media (prefers-reduced-motion: reduce) {
  .js-motion .rise        { opacity: 1; transform: none; }
  .js-motion .mask > span { transform: none; }
  .js-motion .wipe::before { transform: none; }
  .js-motion * { transition-duration: 0.01ms !important; }
}
