/**
 * Juye: motion.
 *
 * Scroll reveals, hero choreography and the depth that makes flat panels read
 * as objects. Everything is opt-in through .juye-motion-ready, which motion.js
 * sets on <html> once it runs, so a blocked or failed script leaves the page
 * fully visible and static rather than blank.
 *
 * Only opacity and transform are animated, so nothing here triggers layout.
 */

/* Motion tokens (--ease-out, --reveal-t, --stagger, --reveal-y) now live in
   assets/tokens.css alongside every other token. */

/* ==========================================================================
   Scroll reveal
   ========================================================================== */
.juye-motion-ready [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--juye-reveal-y), 0);
  transition:
    opacity   var(--juye-reveal-t) var(--juye-ease-out),
    transform var(--juye-reveal-t) var(--juye-ease-out);
  transition-delay: calc(var(--juye-i, 0) * var(--juye-stagger));
}

.juye-motion-ready [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* Media panels travel further and scale a hair: they read as weight. */
.juye-motion-ready [data-reveal="media"] {
  --juye-reveal-y: 26px;
  transform: translate3d(0, var(--juye-reveal-y), 0) scale(.985);
}

.juye-motion-ready [data-reveal="media"].is-in { transform: none; }

/* ==========================================================================
   Hero: plays on load, never gated on scroll
   ========================================================================== */
@keyframes juye-rise {
  from { opacity: 0; transform: translate3d(0, 20px, 0); }
  to   { opacity: 1; transform: none; }
}

/* The settle is a scale as well as a lift: a pack that only slides up reads
   as a card moving, one that also grows a hair reads as an object coming to
   rest. The scale is deliberately small -- 5% is the most you can do before
   it stops being weight and starts being a zoom. */
@keyframes juye-pack-in {
  from { opacity: 0; transform: translate3d(0, 34px, 0) scale(.95); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

/* A slow drift, barely above the threshold of notice. */
@keyframes juye-float {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -10px, 0); }
}

/* --------------------------------------------------------------------------
   Hero slides

   Everything below is scoped to `.is-active, .is-leaving` rather than to
   `.is-active` alone, and that pairing is load-bearing. When a slide changes,
   JS swaps is-active for is-leaving on the outgoing slide in the same frame.
   Because both class names sit in the same selector, the computed animation
   list does not change and the packs keep floating through the fade. Scope it
   to .is-active only and the animations are torn off the instant the class
   goes, which snaps every pack back to its resting offset while the slide is
   still almost fully opaque -- the one thing that makes a crossfade look
   broken.

   The slower timings here are the point of the exercise: nothing lands in
   under a second, and each piece overlaps the one before it.
   -------------------------------------------------------------------------- */
.juye-hero__slide {
  transition: opacity var(--juye-hero-fade) var(--juye-ease-out);
}

.juye-hero {
  /* One dial for the whole choreography. */
  --juye-hero-fade: 900ms;
  --juye-hero-rise: 1100ms;
  --juye-hero-pack: 1150ms;
}

/* Kept visible, and kept animating, purely so it can fade rather than vanish.
   JS drops the class once the fade is over. */
.juye-hero__slide.is-leaving { opacity: 0; visibility: visible; pointer-events: none; }

.juye-motion-ready .juye-hero__slide.is-active .juye-hero__copy > *,
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__copy > * {
  animation: juye-rise var(--juye-hero-rise) var(--juye-ease-out) backwards;
}

.juye-motion-ready .juye-hero__slide.is-active .juye-hero__copy > :nth-child(1),
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__copy > :nth-child(1) { animation-delay: .08s; }
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__copy > :nth-child(2),
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__copy > :nth-child(2) { animation-delay: .20s; }
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__copy > :nth-child(3),
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__copy > :nth-child(3) { animation-delay: .32s; }
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__copy > :nth-child(4),
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__copy > :nth-child(4) { animation-delay: .44s; }

/* The pack keeps its own placement transform, so the motion goes on the image
   inside it. pack-in holds its end state with `both`, then float -- declared
   last, so it wins the shared property -- takes over once the settle is done. */
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__pack img,
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__pack img {
  animation:
    juye-pack-in var(--juye-hero-pack) var(--juye-ease-out) both,
    juye-float   7s ease-in-out 1.25s infinite;
}

/* The lead pack lands first and the flankers follow, so the cluster assembles
   instead of arriving. Each float starts on its own beat; three packs rising
   and falling in step would read as one object, not three. */
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__pack--2 img,
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__pack--2 img { animation-delay: .14s, 1.45s; }
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__pack--1 img,
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__pack--1 img { animation-delay: .30s, 2.0s;  }
.juye-motion-ready .juye-hero__slide.is-active .juye-hero__pack--3 img,
.juye-motion-ready .juye-hero__slide.is-leaving .juye-hero__pack--3 img { animation-delay: .44s, 2.5s;  }

/* In a pair the first pack is the lead, so it is the one that lands first. */
.juye-motion-ready .juye-hero__stack--2 .juye-hero__pack--1 img { animation-delay: .14s, 1.45s; }
.juye-motion-ready .juye-hero__stack--2 .juye-hero__pack--2 img { animation-delay: .30s, 2.1s;  }

/* The countdown in the dot. Width rather than transform because the bar is
   only 4px tall and a scaled gradient edge shimmers at that size. JS sets the
   duration from the real interval and restarts it on every change. */
.juye-hero__dot-fill {
  transition: width var(--juye-dot-t, 0ms) linear;
}

/* ==========================================================================
   Reduced motion: keep the design, drop the movement
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .juye-motion-ready [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .juye-motion-ready .juye-hero__copy > *,
  .juye-motion-ready .juye-hero__pack img {
    animation: none;
  }

  /* The carousel still works -- the dots still change the slide -- it just
     cuts instead of fading, and JS does not advance it on its own. The dot
     stays filled rather than running a countdown to something that will not
     happen. */
  .juye-hero__slide { transition: none; }
  .juye-hero__dot-fill { transition: none; }
}
