/* ═══ hero ═════════════════════════════════════════════════════════════
   A pinned scene. The section is tall; the stage inside is sticky and
   exactly one viewport. Scroll distance through the section = the
   dispersal timeline. No JS pinning, no pin-spacers — position:sticky
   does it natively, so keyboard scroll and reduced-motion both behave.
   ══════════════════════════════════════════════════════════════════════ */

.saga {
  position: relative;
  /* Three acts on one timeline: the cloud reveal, the card run, then the
     Hasiru World tour. One section rather than three is what removes the
     seam that used to appear where one pinned scene handed over to another. */
  height: 1180vh;
}

.saga__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--paper);
  isolation: isolate;
}

/* dusk over the land, brought up as the card run begins */
.saga__veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: 0;
  background:
    radial-gradient(95% 75% at 50% 38%,
      color-mix(in srgb, var(--forest) 34%, transparent) 0%,
      color-mix(in srgb, var(--forest-deep) 90%, transparent) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--forest-deep) 52%, transparent) 0%,
      color-mix(in srgb, var(--forest-deep) 84%, transparent) 100%);
  will-change: opacity;
}

/* ── layer 1 · the land, waiting to be uncovered ───────────────────────── */

/* The plate. --clip-right is driven by the closing act, which pulls the
   land back into a column on the left so the panel can take the rest. */
.hero__reveal {
  --clip-right: 0%;
  --clip-bottom: 0%;
  position: absolute;
  inset: 0;
  z-index: var(--z-reveal);
  background: #E4EBE4;              /* the exports carry a cut-out sky */
  clip-path: inset(0 var(--clip-right) var(--clip-bottom) 0);
  will-change: clip-path;
}

.hero__reveal-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 62%;
  transform: scale(1.16);
  transform-origin: 50% 55%;
  will-change: transform;
}

/* The shader plate, drawn over the <img> once its texture is up. The swap
   is deliberately instant, not a cross-fade: both layers show the same
   skyline, and any moment where both are partly visible reads as a
   doubled, stepped ridge. It happens behind full cloud cover anyway. */
.hero__reveal-gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.hero__reveal-gl[data-live] { opacity: 1; }
.hero__reveal-gl[data-lost] { opacity: 0; }

/* once the plate is live the photograph underneath is pure cost */
.hero__reveal:has(> [data-live]) .hero__reveal-img { visibility: hidden; }

/* Atmospheric haze, not a cover — the clouds do the covering. This only
   pushes the land back in depth so it reads as distant until it resolves. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(125% 95% at 50% 45%,
      color-mix(in srgb, var(--paper) 58%, transparent) 0%,
      color-mix(in srgb, var(--paper) 40%, transparent) 48%,
      color-mix(in srgb, var(--paper) 14%, transparent) 100%);
  opacity: 1;
  will-change: opacity;
}

/* ── layer 2 · the clouds ──────────────────────────────────────────────── */

.hero__clouds {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* no WebGL / static tier — a painted stand-in in the same palette */
.saga__stage[data-fallback] .hero__clouds { display: none; }
.saga__stage[data-fallback]::after {
  content: '';
  position: absolute;
  inset: -10%;
  z-index: 3;
  pointer-events: none;
  opacity: var(--fallback-a, 1);
  background:
    radial-gradient(48% 40% at 30% 34%, var(--cloud-edge) 0%, transparent 62%),
    radial-gradient(54% 44% at 70% 42%, var(--cloud-edge) 0%, transparent 64%),
    radial-gradient(60% 46% at 50% 62%, #FFFFFF 0%, transparent 66%),
    radial-gradient(70% 60% at 50% 50%,
      color-mix(in srgb, var(--cloud-core) 55%, transparent) 0%, transparent 72%),
    radial-gradient(90% 78% at 50% 50%, var(--cloud-edge) 20%, transparent 78%);
  filter: blur(14px);
}

/* ── layer 3 · the copy ────────────────────────────────────────────────── */

.hero__content {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  /* minmax(0,1fr) is load-bearing: without it a single long unbreakable
     child sets the track width, the track overflows the viewport, and every
     centred item is dragged off-screen with it */
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: center;
  gap: clamp(1rem, 0.6rem + 1.2vw, 1.75rem);
  padding: 0 var(--gutter);
  text-align: center;
  color: var(--forest);
  will-change: transform, opacity;
}
.hero__content > * { max-width: 100%; }

.hero__eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: clamp(0.16em, 0.06em + 0.4vw, 0.32em);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--forest) 72%, transparent);
}
.hero__eyebrow span { padding-inline: 0.4em; }

.hero__title {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  font-weight: 500;
  line-height: var(--lh-display);
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.hero__title em {
  font-style: italic;
  font-weight: 400;
  padding-right: 0.06em;
}

/* Each line clips its own reveal. The clip box has to be taller than the
   0.94 line box or it eats Playfair's ascenders and descenders — so pad it
   out and pull the padding back with an equal negative margin. */
.hero__title .line {
  display: block;
  overflow: hidden;
  padding-block: 0.26em;
  margin-block: -0.26em;
}
.hero__title .line > span {
  display: block;
  will-change: transform;
}

.hero__sub {
  max-width: var(--measure);
  font-size: var(--fs-sub);
  font-weight: 300;
  line-height: 1.62;
  color: color-mix(in srgb, var(--forest) 82%, transparent);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: clamp(0.6rem, 0.3rem + 1vw, 1.4rem);
  margin-top: clamp(0.4rem, 0.2rem + 0.8vw, 1rem);
}

@media (max-width: 560px) {
  .br-md { display: none; }
}

/* ── entrance ──────────────────────────────────────────────────────────── */

.hero__eyebrow,
.hero__sub,
.hero__actions { opacity: 0; }

.hero__title .line > span { transform: translate3d(0, 108%, 0); }

body.is-ready .hero__title .line > span {
  transform: translate3d(0, 0, 0);
  transition: transform 1150ms var(--ease-out);
}
body.is-ready .hero__title .line:nth-child(2) > span { transition-delay: 110ms; }

body.is-ready .hero__eyebrow,
body.is-ready .hero__sub,
body.is-ready .hero__actions {
  opacity: 1;
  transition: opacity 900ms var(--ease-soft), transform 900ms var(--ease-out);
}
body.is-ready .hero__eyebrow  { transition-delay: 60ms; }
body.is-ready .hero__sub      { transition-delay: 520ms; }
body.is-ready .hero__actions  { transition-delay: 640ms; }

/* subtle lift on the secondary copy */
.hero__sub,
.hero__actions { transform: translate3d(0, 14px, 0); }
body.is-ready .hero__sub,
body.is-ready .hero__actions { transform: translate3d(0, 0, 0); }

/* ── review stills (?p=) ───────────────────────────────────────────────────
   Land on the finished entrance immediately. Without this a capture can
   catch the stagger mid-flight and read as "the buttons are missing".
   Inline opacity from the timeline still wins where it is set.            */

body.is-still * { transition: none !important; animation: none !important; }
body.is-still .hero__title .line > span { transform: none !important; }
body.is-still .hero__eyebrow,
body.is-still .hero__sub,
body.is-still .hero__actions { opacity: 1 !important; transform: none !important; }

/* ── reduced motion ────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  /* Nothing pins. The stage keeps its absolutely-positioned hero layers in
     the top viewport and then grows to hold the cards as ordinary flow —
     the first static child is pushed past the hero to get out of its way. */
  .saga { height: auto; }
  .saga__stage {
    position: relative;
    height: auto;
    min-height: 100vh;
    overflow: visible;
  }
  .hero__reveal,
  .hero__clouds,
  .saga__veil { inset: 0 0 auto 0; height: 100vh; }
  .saga__veil { opacity: 0.55 !important; }
  .hero__content { inset: 0 0 auto 0; height: 100vh; }

  .hero__title .line > span { transform: none !important; transition: none !important; }
  .hero__eyebrow, .hero__sub, .hero__actions {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .hero__reveal-img { transform: scale(1.02); }
}
