/* Varmine motion layer: reveal initial states, parallax containment, and
   reduced-motion neutralization. Loaded last in the cascade so its hooks and
   the reduced-motion overrides win. Motion tokens come from tokens.css.

   JS enhances these hooks. Elements start hidden ONLY while JS motion is armed
   (the <html> gets .motion-ready once the animation engine takes ownership).
   Absent JS or reduced-motion, content stays in its final visible state. */

/* ---------------------------------------------------------------------------
   Reveal initial states
   Applied only under .motion-ready so a no-JS document renders final state.
   JS adds .is-revealed (or clears the class) to animate to the final state.
--------------------------------------------------------------------------- */

.motion-ready .reveal,
.motion-ready .reveal-text,
.motion-ready .reveal-image {
  will-change: opacity, transform, clip-path;
}

/* Generic content reveal: fade + rise */
.motion-ready .reveal {
  opacity: 0;
  transform: translateY(2rem);
  transition:
    opacity var(--motion-entrance-max) var(--ease-reveal),
    transform var(--motion-entrance-max) var(--ease-reveal);
}

.motion-ready .reveal.is-revealed {
  opacity: 1;
  transform: translateY(0);
}

/* Text reveal: the block hides, per-word/line children rise into place.
   .reveal-line / .reveal-word are wrapper spans injected by the reveal module;
   overflow is clipped so the rising glyphs are masked before they arrive. */
.motion-ready .reveal-text {
  opacity: 1;
}

.motion-ready .reveal-text .reveal-line,
.motion-ready .reveal-text .reveal-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}

.motion-ready .reveal-text .reveal-line > *,
.motion-ready .reveal-text .reveal-word > * {
  display: inline-block;
  opacity: 0;
  transform: translateY(100%);
  transition:
    opacity var(--motion-text-reveal-max) var(--ease-reveal),
    transform var(--motion-text-reveal-max) var(--ease-reveal);
}

.motion-ready .reveal-text.is-revealed .reveal-line > *,
.motion-ready .reveal-text.is-revealed .reveal-word > * {
  opacity: 1;
  transform: translateY(0);
}

/* Image reveal: clip-path mask wipes the image into view top-to-bottom.
   The media element itself stays fully opaque so a failed reveal still shows. */
.motion-ready .reveal-image {
  position: relative;
  overflow: hidden;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--motion-image-reveal-max) var(--ease-reveal);
}

.motion-ready .reveal-image.is-revealed {
  clip-path: inset(0 0 0 0);
}

/* An image swapped to the placeholder on load failure must skip the mask. */
.reveal-image.is-fallback,
.motion-ready .reveal-image.is-fallback {
  clip-path: none;
  transition: none;
}

/* Section transition hook: adjacent sections stay interactive throughout. */
.motion-ready .section-transition {
  opacity: 0;
  transition: opacity var(--motion-section-trans-max) var(--ease-reveal);
}

.motion-ready .section-transition.is-revealed {
  opacity: 1;
}

/* ---------------------------------------------------------------------------
   Parallax containment
   Parallax translates layers vertically only. Containers clip so nothing a
   layer's movement exposes can push horizontal (or vertical) overflow, keeping
   the page free of horizontal scroll across 320..1920px (Req 14.4).
--------------------------------------------------------------------------- */

.parallax {
  position: relative;
  overflow: hidden;
}

.parallax-layer {
  will-change: transform;
  /* Neutral position; JS drives translate3d(0, y, 0) within +/- 200px. */
  transform: translate3d(0, 0, 0);
}

/* Guard against any layer wider than its container introducing side-scroll. */
.parallax > .parallax-layer {
  max-width: 100%;
}

/* ---------------------------------------------------------------------------
   Custom cursor + scroll progress
   Cursor is armed only on fine-pointer, motion-enabled devices (.cursor-active
   is set by the cursor module). Progress fill is driven by JS transform.
--------------------------------------------------------------------------- */

.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(-50%, -50%, 0);
  transition: opacity var(--motion-magnetic-out) var(--ease-magnetic);
  will-change: transform;
}

.cursor-active .cursor {
  opacity: 1;
}

.cursor.is-hover {
  /* Distinct hover state; scale is a visible attribute change (Req 13.2). */
  transform: translate3d(-50%, -50%, 0) scale(2.2);
}

.progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9998;
  width: 100%;
  transform-origin: left center;
  pointer-events: none;
}

.progress-fill {
  transform-origin: left center;
  transform: scaleX(0);
  will-change: transform;
}

/* Magnetic elements translate only; layout position is untouched (Req 18.3). */
.magnetic {
  will-change: transform;
  transform: translate3d(0, 0, 0);
}

/* ---------------------------------------------------------------------------
   Reduced motion: hard global kill switch (Req 8.2, 14.4, 15.4, 16.5, 19.5)
   Force every motion-affected element to its final visible state defensively,
   regardless of .motion-ready or .is-revealed, and hide the custom cursor.
--------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .reveal-text,
  .reveal-image,
  .reveal-text .reveal-line > *,
  .reveal-text .reveal-word > *,
  .section-transition,
  .parallax-layer,
  .magnetic,
  .progress-fill {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
    animation: none !important;
    will-change: auto !important;
  }

  .progress-fill {
    /* Static position indicator: JS may set width instead of transform. */
    transform: none !important;
  }

  .cursor {
    display: none !important;
  }
}
