/* ============================================================
   SERAPHIN — hero.

   Two modes, decided by whether assets/img/explode/ holds a real
   disassembly frame sequence:

     STATIC     the whole product photograph, easing forward on scroll.
                The proven treatment. Active until real footage exists.
     SCRUBBER   scroll drives a canvas frame sequence extracted from a
                genuine image-to-video disassembly of that photograph.

   Both write --t (0..1) on .xv-stage, so the headline recede, callout
   sequence and progress rail behave identically in either mode.

   A single 2D photo must never be cut into parts: lifting a component
   leaves a hole with no pixel data behind it, and every cut edge is an
   arbitrary slice through a continuous object, so it reads as torn.
   ============================================================ */

.xv {
  position: relative;
  /* generous runway so the motion reads slow and deliberate */
  height: 320vh;
  /* Sampled from the footage's own studio floor, so the frame sequence
     meets the page with no visible edge on any viewport ratio. */
  background: var(--hero-band);
}

.xv-stage {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 600px;
  overflow: hidden;
}

/* ---------- headline: over the product, recedes as it separates ---------- */
.xv-head {
  position: absolute;
  /* Clear the sticky header rather than sliding under it. */
  top: calc(var(--top-h, 80px) + clamp(1rem, 2.5vh, 1.75rem));
  left: 0; right: 0;
  z-index: 20;
  padding-inline: var(--bleed);
  max-width: var(--wrap);
  margin-inline: auto;
  pointer-events: none;
  opacity: clamp(.12, calc(1 - var(--t, 0) * 1.35), 1);
  transition: opacity .2s linear;
}
.xv-head h2 {
  font-size: clamp(2.4rem, 6vw, 5.2rem);
  line-height: .95;
  letter-spacing: -.035em;
  /* 17ch keeps "Made to travel." on one line at desktop sizes, which buys
     back a whole line of height between the headline and the chair. */
  max-width: 17ch;
}
.xv-head .lead {
  margin-top: clamp(1rem, 2vh, 1.6rem);
  font-size: clamp(.95rem, 1.15vw, 1.15rem);
  max-width: 38ch;
}

/* ---------- stage ---------- */
/* Full-bleed: the frame sequence runs edge to edge. No max-width, no
   horizontal padding. The rig fills the stage and the canvas letterboxes
   inside it onto --hero-band, which matches the footage ground. */
.xv-canvas {
  position: absolute;
  /* Start below the sticky header. The frame sequence is letterboxed to
     CONTAIN inside this box, so centring it in the full 100svh put the top
     of the chair behind the translucent nav bar for the whole scroll. */
  inset: calc(var(--top-h, 80px) + clamp(6rem, 15vh, 10.5rem)) 0 0 0;
  display: block;
  padding: 0;
}
.xv-rig {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* STATIC-mode fallback still needs a sane cap — the single photograph is
   square and must not be stretched across a 21:9 monitor. */
.xv-rig .xv-hero-shot {
  width: auto;
  max-width: min(100%, 760px);
  max-height: 100%;
  margin-inline: auto;
}
.xv-ready .xv-rig { display: block; }

/* STATIC mode — the whole photograph, easing forward. */
.xv-hero-shot {
  width: 100%;
  height: auto;
  display: block;
  transform: scale(calc(1 + var(--t, 0) * .11));
  transition: transform .14s linear;
  will-change: transform;
}

/* SCRUBBER mode — canvas sits on top of the first-frame image and only
   takes over once it has actually painted, so there is never a blank hero. */
.xv-frames {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.xv-ready .xv-frames { opacity: 1; }
.xv-ready .xv-hero-shot { visibility: hidden; }

/* ---------- callouts ---------- */
.xv-note {
  position: absolute;
  z-index: 30;
  font-family: var(--body);
  font-size: .7rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: .6rem;
  opacity: clamp(0, calc((var(--t, 0) - var(--at, 0)) * 7), 1);
  transform: translateY(calc((1 - clamp(0, calc((var(--t, 0) - var(--at, 0)) * 7), 1)) * 8px));
  transition: opacity .2s linear, transform .2s ease-out;
  pointer-events: none;
}
.xv-note-r { flex-direction: row-reverse; }
.xv-dot {
  width: 5px; height: 5px;
  background: var(--red);
  border-radius: 50%;
  flex: none;
  box-shadow: 0 0 0 4px rgba(237, 28, 36, .14);
}

/* ---------- progress rail ---------- */
.xv-rail {
  position: absolute;
  left: 0; right: 0;
  bottom: clamp(1.25rem, 3.5vh, 2.25rem);
  z-index: 20;
  padding-inline: var(--bleed);
  max-width: var(--wrap);
  margin-inline: auto;
  pointer-events: none;
}
.xv-track { position: relative; height: 1px; background: var(--line); }
.xv-fill {
  position: absolute; inset: 0 auto 0 0;
  width: calc(var(--t, 0) * 100%);
  background: var(--red);
  transition: width .12s linear;
}
.xv-steps {
  display: flex; justify-content: space-between;
  margin-top: .8rem;
  font-family: var(--display);
  font-size: .64rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
}
.xv-steps span { transition: color .2s; }
.xv-steps span[data-on="1"] { color: var(--red); }

@media (max-height: 700px) {
  .xv-head .lead { display: none; }
  .xv-steps { font-size: .58rem; }
}
@media (max-width: 640px) {
  .xv-note { font-size: .6rem; letter-spacing: .1em; }
}

/* ---------- reduced motion / no JS: flatten to a static composition ---------- */
@media (prefers-reduced-motion: reduce) {
  .xv { height: auto; }
  .xv-stage { position: static; height: auto; min-height: 0; padding: var(--gap-xl) 0; }
  .xv-head { position: static; opacity: 1; margin-bottom: var(--gap-lg); }
  .xv-canvas { position: static; display: block; padding: 0 var(--bleed); }
  .xv-rig { width: min(100%, 760px); margin-inline: auto; }
  .xv-hero-shot { transform: none; transition: none; visibility: visible; }
  .xv-frames { display: none; }
  .xv-note { opacity: 1; transform: none; }
  .xv-rail { display: none; }
}

.no-js .xv { height: auto; }
.no-js .xv-stage { position: static; height: auto; min-height: 0; padding: var(--gap-xl) 0; }
.no-js .xv-head { position: static; opacity: 1; margin-bottom: var(--gap-lg); }
.no-js .xv-canvas { position: static; display: block; padding: 0 var(--bleed); }
.no-js .xv-rig { width: min(100%, 760px); margin-inline: auto; }
.no-js .xv-hero-shot { transform: none; visibility: visible; }
.no-js .xv-frames { display: none; }
.no-js .xv-note { opacity: 1; transform: none; }
.no-js .xv-rail { display: none; }
