/* ==========================================================================
   KRESTA TEXTILE — Motion
   --------------------------------------------------------------------------
   Brand personality is "controlled (not flashy)" (brand doc §7). Long calm
   easing, no bounce, one orchestrated load sequence rather than scattered
   effects. Everything here is switched off under prefers-reduced-motion.
   ========================================================================== */

/* ------------------------------------------------- marquee glow border --
   A single light chases the button's outline: starts at the right edge,
   travels the full perimeter, then reverses and travels back — a
   there-and-back marquee, not a perpetual one-way spin.

   Needs a real child element (.btn__glow, added in markup next to
   .btn__arrow/.btn__count), not a ::before/::after on the button itself —
   .btn--glass already owns ::before for its specular sheen, and a button
   only has two pseudo-element slots. .btn__glow is the crisp light;
   .btn__glow::after is the same light, blurred, for the bloom trailing it.

   Both layers use the standard "gradient border" mask trick (two
   background layers XORed via mask-composite) so the light reads as a
   true outline rather than a filled block — that matters because
   .btn--glass is translucent, and a filled block would wash colour across
   the whole button instead of tracing its edge.

   @property registers --glow-angle as an animatable <angle> so a
   conic-gradient's rotation can be keyframed directly, then
   `animation-direction: alternate` bounces it 0deg -> 360deg -> 0deg
   instead of spinning endlessly forward. `from calc(90deg - ...)` starts
   the light at the right (conic-gradient's own 0deg is the top, angle
   increases clockwise, so +90deg lands on the right edge) and SUBTRACTS
   the animated angle so it sweeps counter-clockwise — right, up over the
   top edge moving right-to-left, left, bottom, back to right — per
   direction feedback (a plain `+` reads as left-to-right along the top
   edge, which is the opposite of what was asked for). The gradient itself
   is mostly transparent with one bright arc, not colour all the way
   round — that's what makes it read as a single travelling light rather
   than a rotating rainbow ring. inherits:true lets one animation on the
   button drive both the light and its ::after bloom in sync.

   .btn--glass sets overflow:hidden to clip its own specular sheen, which
   is harmless there (that sheen's box already matches the button exactly)
   but would also clip this light/bloom, which need to extend past the
   button's edges — hence the overflow:visible override below.

   Colours live in --glow-c1..4 for a one-place swap later; current values
   are the placeholder gold already used by the thread-reel cursor.

   Runs continuously — the animation was never actually tied to hover (it
   was always running on .btn--glow-rotate), only the opacity was gated to
   hover/focus. That gate is removed below on request: the light is always
   visible, mouse on the button or not. */

@property --glow-angle {
  syntax: '<angle>';
  inherits: true;
  initial-value: 0deg;
}

@keyframes marqueeGlowAngle {
  to {
    --glow-angle: 360deg;
  }
}

.btn--glow-rotate {
  --glow-c1: #ffd54a;
  --glow-c2: #f0c230;
  --glow-c3: #ffde6b;
  --glow-c4: #d4a317;
  position: relative;
  overflow: visible;
  animation: marqueeGlowAngle 3s linear infinite alternate;
}

.btn__glow {
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: inherit;
  padding: 2px;
  pointer-events: none;
  opacity: 1;
  background: conic-gradient(
    from calc(90deg - var(--glow-angle)),
    var(--glow-c3) 0deg,
    var(--glow-c1) 8deg,
    var(--glow-c2) 26deg,
    transparent 62deg,
    transparent 360deg
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}

.btn__glow::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  padding: 3px;
  background: conic-gradient(
    from calc(90deg - var(--glow-angle)),
    var(--glow-c3) 0deg,
    var(--glow-c1) 8deg,
    var(--glow-c2) 26deg,
    transparent 62deg,
    transparent 360deg
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  filter: blur(14px);
  opacity: 0.7;
}

@media (prefers-reduced-motion: reduce) {
  .btn--glow-rotate {
    animation: none;
  }

  .btn__glow {
    opacity: 0.5;
  }

  .btn__glow::after {
    opacity: 0.35;
  }
}

/* ------------------------------------------------------- scroll reveals -- */

.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}

/* Rules draw from the left rather than fading */
.reveal-rule {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-draw) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.reveal-rule.is-in {
  transform: scaleX(1);
}

/* Photography wipes open from the centre rather than fading — the same
   door motif as the hero's own reveal, but a plain clip-path transition
   since it only has to fire once, not track scroll position.

   The clip-path lives on the .band__parallax CHILD, not on .reveal-door
   itself: a clip-path that collapses an element to zero visible area
   also zeroes its IntersectionObserver intersection ratio in Chrome, so
   putting it on the observed target creates a deadlock — it can never
   cross the reveal threshold to get .is-in added, so it can never open.
   --reveal-delay is set on .reveal-door and inherits down to the child
   via normal custom-property inheritance. */
.reveal-door .band__parallax {
  clip-path: inset(0 50% 0 50%);
  transition: clip-path var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.reveal-door.is-in .band__parallax {
  clip-path: inset(0 0 0 0);
}

/* ---------------------------------------------------- hero load sequence --
   The wordmark arrives first; everything else settles beneath it.

   The tracking itself is the entrance: the letters open outward from a
   tighter set to their final spacing. It is the one flourish on the page,
   and it earns its place by being the thing the hero is actually made of. */

.hero__title .u-hero {
  opacity: 0;
  letter-spacing: 0.1em;
  transform: translateY(12px);
}

.hero__title .u-kicker,
.hero__cta,
.hero__note span {
  opacity: 0;
  transform: translateY(18px);
}

.is-ready .hero__title .u-hero {
  opacity: 1;
  letter-spacing: var(--track-hero);
  transform: none;
  transition: opacity 1400ms var(--ease-out),
    letter-spacing 1800ms var(--ease-out),
    transform 1400ms var(--ease-out);
  transition-delay: 140ms;
}

.is-ready .hero__title .u-kicker,
.is-ready .hero__cta,
.is-ready .hero__note span {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}

.is-ready .hero__title .u-kicker {
  transition-delay: 880ms;
}

.is-ready .hero__cta {
  transition-delay: 1040ms;
}

.is-ready .hero__note span {
  transition-delay: 1180ms;
}

/* ------------------------------------------------------- hero reveal ----
   js/hero-reveal.js sets --beat on each individual element, so these two
   rules cover any number of stages without knowing how many there are.

   Deliberately no CSS transition: the values track scroll position
   directly, and easing them would make the type lag the mask.

   Default of 1 matters — with JS unavailable, every beat stays visible
   rather than the hero rendering blank.                                  */

[data-hero-beat],
[data-hero-note] {
  opacity: var(--beat, 1);
}

/* Beats share one grid cell, so the inactive ones must not swallow clicks.
   The wordmark is beat 0 and holds no links, so this is safe across all. */
.hero__caption {
  pointer-events: none;
}

/* --------------------------------------------------------- reduced motion -
   Not a downgrade: the static composition must stand on its own.         */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

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

  .reveal,
  .hero__title .u-hero,
  .hero__title .u-kicker,
  .hero__cta,
  .hero__note span {
    opacity: 1;
    transform: none;
  }

  /* No scroll track: the stage collapses to one viewport and stops
     sticking. js/hero-reveal.js pins --hero-p to 1 so the finished garment
     is fully shown — it is content, not decoration, and must not be lost
     just because motion is off. The wordmark gives way to the caption. */
  .hero {
    height: auto;
  }

  .hero__stage {
    position: static;
    height: auto;
    min-height: calc(100svh - var(--frame) * 2);
  }

  .hero__media .hero__img,
  .hero__img--garment {
    transform: none;
  }

  /* Final tracking immediately — the letters must not animate open. */
  .hero__title .u-hero {
    letter-spacing: var(--track-hero);
  }

  .reveal-rule {
    transform: scaleX(1);
  }

  .reveal-door .band__parallax {
    clip-path: inset(0 0 0 0);
  }

  .band__parallax {
    transform: none !important;
  }

  .card__sheen {
    display: none;
  }
}
