/*
 * Motion states.
 *
 * Every animated element declares its resting (hidden) state here and its revealed state
 * as a single class flip. JavaScript only ever adds or removes a class or sets a delay —
 * it never sets opacity or transform directly for entrance animation. That keeps the
 * whole entrance system on the compositor and makes every timing value greppable.
 */

/* ---------- Reveals ---------- */

[data-reveal] {
  opacity: 0;
  transform: translate3d(0, 34px, 0);
  transition:
    opacity var(--dur-base) var(--ease-enter),
    transform var(--dur-base) var(--ease-enter);
}

[data-reveal="fade"] {
  transform: none;
}

[data-reveal="scale"] {
  transform: translate3d(0, 24px, 0) scale(0.97);
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* ---------- Split headlines ---------- */

[data-split] {
  /* Words are masked by their line wrapper, so the heading itself must not clip descenders. */
  overflow: visible;
}

.split-line {
  display: block;
  overflow: hidden;
  /* Room for descenders inside the mask, pulled back out of the layout. */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.split-word {
  display: inline-block;
  transform: translate3d(0, 105%, 0);
  transition: transform var(--dur-slow) var(--ease-enter);
  will-change: transform;
}

[data-split].is-revealed .split-word,
.is-revealed [data-split] .split-word {
  transform: none;
}

/*
 * A split headline overrides the generic reveal: the words carry the motion, so the
 * block itself must not also slide.
 */
[data-split][data-reveal] {
  opacity: 1;
  transform: none;
}

/* ---------- Parallax frames ---------- */

[data-parallax] {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.parallax-frame {
  overflow: hidden;
  border-radius: var(--hana-radius-panel);
}

/* ---------- SVG draw ---------- */

.draw-path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Hero recede ---------- */

/*
 * The receding hero has to stay above the section sliding up beneath it. Pinning makes it
 * position: fixed at runtime, but a fixed element with no z-index still paints below any
 * later element that carries a transform — which every revealed card does.
 */
[data-recede] {
  position: relative;
  z-index: 2;
}

[data-recede-frame] {
  transform-origin: center center;
}

/* ---------- Page overlay ---------- */

.page-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--hana-ink);
  transform: scaleX(1);
  transform-origin: right center;
  pointer-events: none;
  will-change: transform;
}

/* ---------- Intro ---------- */

/* While the intro timeline owns these elements, a CSS transition on transform would fight
   every frame GSAP writes and can leave the element stranded mid-way. */
body:not(.is-revealed-page) [data-intro-item],
body:not(.is-revealed-page) [data-intro-mark] {
  transition: none;
}

[data-intro-mark] {
  display: inline-block;
}

.intro-mask {
  display: block;
  overflow: hidden;
}

/*
 * Reduced motion.
 *
 * This is a full opt-out, not a shortened version of the same animation. Everything
 * arrives in its resting state and nothing tracks scroll position.
 */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .split-word {
    transform: none;
    transition: none;
  }

  .page-overlay {
    display: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
