/* ============================================================
   Nabux motion system (after yusrin.app)
   ------------------------------------------------------------
   No animation library. Every element ships from the server in its
   final state; `data-state="armed"` only takes effect once
   <html class="m-js"> is set by the inline script in _document.js
   (before first paint) and the visitor allows motion. The observer
   in components/motion.js flips armed → play when the element
   scrolls into view. Elements marked data-load play on mount.

   Vocabulary
     data-m="title"  words rise out of a mask, staggered by --m-i
     data-m="fade"   one block rises and fades in
     data-m="group"  direct children rise in, staggered by --m-i
     data-m="media"  a clip-path mask opens upward; the picture settles
     data-m="roll"   a number rolls up from its placeholder
     data-m="line"   a rule draws from the reading side
     .m-marquee      an endless strip, paused on hover, mirrored in RTL
     .m-track/.m-stage  a sticky stage scrubbed by --p (0 → 1)
   Logical properties only, so fa/ar mirror without overrides.
   ============================================================ */

:root {
  --m-ease: cubic-bezier(0.2, 0, 0, 1);
  --m-ease-mask: cubic-bezier(0.77, 0, 0.175, 1);
  --m-rise: 0.7s;
  --m-stagger: 60ms;
}

/* ---------- title: word masks ---------- */
[data-m='title'] {
  text-wrap: balance;
}
[data-m='title'] .m-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* Room for Arabic and Persian ascenders/descenders inside the mask. */
  padding-block: 0.18em 0.22em;
  margin-block: -0.18em -0.22em;
  padding-inline: 0.04em;
  margin-inline: -0.04em;
}
[data-m='title'] .m-wi {
  display: inline-block;
  will-change: transform;
}

@keyframes m-rise {
  from { transform: translateY(110%); }
  to { transform: none; }
}
@keyframes m-fade {
  from { opacity: 0; transform: translateY(22px); }
  to { opacity: 1; transform: none; }
}
@keyframes m-mask-open {
  from { clip-path: inset(100% 0 0 0 round var(--m-radius, 0)); }
  to { clip-path: inset(0 0 0 0 round var(--m-radius, 0)); }
}
@keyframes m-settle {
  from { transform: translateY(8%) scale(1.18); }
  to { transform: none; }
}
@keyframes m-roll-up {
  from { transform: none; }
  to { transform: translateY(-50%); }
}
@keyframes m-draw {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* The same keyframes under a second name: changing animation-name is what
   restarts the motion when the state flips from armed to play. */
@keyframes m-rise-late { from { transform: translateY(110%); } to { transform: none; } }
@keyframes m-fade-late { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes m-mask-open-late { from { clip-path: inset(100% 0 0 0 round var(--m-radius, 0)); } to { clip-path: inset(0 0 0 0 round var(--m-radius, 0)); } }
@keyframes m-settle-late { from { transform: translateY(8%) scale(1.18); } to { transform: none; } }
@keyframes m-roll-up-late { from { transform: none; } to { transform: translateY(-50%); } }
@keyframes m-draw-late { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (prefers-reduced-motion: no-preference) {
  /* Armed: hidden until the observer plays it. The animation under the
     armed state is a safety net: if the script never arrives (slow
     hydration, a blocked bundle), everything still reveals itself after
     --m-safe, so no reader is left with an empty page. */
  .m-js [data-m='title'][data-state='armed'] .m-wi {
    animation: m-rise-late var(--m-rise) var(--m-ease) both;
    animation-delay: calc(var(--m-safe, 2200ms) + var(--m-i, 0) * var(--m-stagger));
  }
  .m-js [data-m='fade'][data-state='armed'] {
    animation: m-fade-late 0.8s var(--m-ease) var(--m-safe, 2200ms) both;
  }
  .m-js [data-m='group'][data-state='armed'] > :not([data-m]) {
    animation: m-fade-late 0.8s var(--m-ease) both;
    animation-delay: calc(var(--m-safe, 2200ms) + var(--m-i, 0) * var(--m-group-stagger, 90ms));
  }
  .m-js [data-m='media'][data-state='armed'] {
    animation: m-mask-open-late 1.1s var(--m-ease-mask) var(--m-safe, 2200ms) both;
  }
  .m-js [data-m='media'][data-state='armed'] > * {
    animation: m-settle-late 1.3s var(--m-ease-mask) var(--m-safe, 2200ms) both;
  }
  .m-js [data-m='roll'][data-state='armed'] .m-roll-stack {
    animation: m-roll-up-late 0.5s var(--m-ease) var(--m-safe, 2200ms) both;
  }
  .m-js [data-m='line'][data-state='armed'] {
    animation: m-draw-late 1.3s cubic-bezier(0.65, 0, 0.35, 1) var(--m-safe, 2200ms) both;
  }

  /* Play. */
  [data-m='title'][data-state='play'] .m-wi {
    animation: m-rise var(--m-rise) var(--m-ease) both;
    animation-delay: calc(var(--m-delay, 0ms) + var(--m-i, 0) * var(--m-stagger));
  }
  [data-m='fade'][data-state='play'] {
    animation: m-fade 0.8s var(--m-ease) both;
    animation-delay: var(--m-delay, 0ms);
  }
  [data-m='group'][data-state='play'] > :not([data-m]) {
    animation: m-fade 0.8s var(--m-ease) both;
    animation-delay: calc(var(--m-delay, 0ms) + var(--m-i, 0) * var(--m-group-stagger, 90ms));
  }
  [data-m='media'][data-state='play'] {
    animation: m-mask-open 1.1s var(--m-ease-mask) both;
    animation-delay: var(--m-delay, 0ms);
  }
  [data-m='media'][data-state='play'] > * {
    animation: m-settle 1.3s var(--m-ease-mask) both;
    animation-delay: var(--m-delay, 0ms);
  }
  [data-m='roll'][data-state='play'] .m-roll-stack {
    animation: m-roll-up 0.5s var(--m-ease) both;
    animation-delay: calc(var(--m-delay, 0ms) + var(--m-i, 0) * 120ms);
  }
  [data-m='line'][data-state='play'] {
    animation: m-draw 1.3s cubic-bezier(0.65, 0, 0.35, 1) both;
    animation-delay: var(--m-delay, 0ms);
  }
}

[data-m='media'] {
  position: relative;
  overflow: hidden;
  border-radius: var(--m-radius, 0);
}
[data-m='media'] > * {
  display: block;
  transform-origin: 50% 100%;
}
[data-m='line'] {
  transform-origin: left;
}
[dir='rtl'] [data-m='line'] {
  transform-origin: right;
}

/* ---------- roll ---------- */
[data-m='roll'] {
  display: inline-block;
  height: 1.2em;
  overflow: hidden;
  vertical-align: bottom;
  line-height: 1.2em;
}
.m-roll-stack {
  display: flex;
  flex-direction: column;
  transform: translateY(-50%);
}
.m-roll-stack > span {
  display: block;
  height: 1.2em;
  line-height: 1.2em;
  white-space: nowrap;
}

/* ---------- marquee ---------- */
.m-marquee {
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.m-marquee-track {
  display: flex;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  animation: m-marquee var(--m-marquee-dur, 38s) linear infinite;
}
[dir='rtl'] .m-marquee-track {
  animation-name: m-marquee-rtl;
}
.m-marquee:hover .m-marquee-track,
.m-marquee:focus-within .m-marquee-track {
  animation-play-state: paused;
}
.m-marquee-track li {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding-inline-end: 2.6rem;
  white-space: nowrap;
}
.m-marquee-track li::before {
  content: '';
  width: 0.38rem;
  height: 0.38rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
}
@keyframes m-marquee {
  to { transform: translateX(-50%); }
}
@keyframes m-marquee-rtl {
  to { transform: translateX(50%); }
}
@media (prefers-reduced-motion: reduce) {
  .m-marquee { mask-image: none; -webkit-mask-image: none; }
  .m-marquee-track { width: auto; flex-wrap: wrap; justify-content: center; row-gap: 0.4rem; animation: none; }
  .m-marquee-track[aria-hidden='true'] { display: none; }
}

/* ---------- scrubbed stage ---------- */
.m-track {
  position: relative;
}
.m-stage {
  position: sticky;
  top: var(--m-pin-top, calc(var(--nav-h, 70px) + 16px));
}

/* ---------- page transition ---------- */
@media (prefers-reduced-motion: no-preference) {
  main {
    transition: opacity 0.22s ease, transform 0.22s ease;
  }
  .m-leaving main {
    opacity: 0;
    transform: translateY(8px);
  }
}
