/* ==========================================================================
   Inner — Homepage Opening Sequence (Phase 1, revised)

   THRESHOLD (hero) -> APPROACH (scroll transition) -> SANCTUM (product reveal)

   Scoped to index.html only. Built on Phase 0 tokens (css/tokens.css) —
   no second/competing token system.

   Structural note: .threshold-wrap and .sanctum are DIRECT children of
   <body> (siblings of .page-shell, not inside it) specifically so they
   own the full viewport with no padding/margin to fight — see the
   .page-shell comment in index.html's own <style> block. No negative
   margins, no 100vw breakout hack.

   The Approach's scroll-driven motion is untouched from the first
   pass: entirely CSS (calc()/clamp() reading a --progress custom
   property set by js/home.js); reduced-motion and no-JS both fall back
   to a plain static 100vh Threshold with no scroll-jacking, because the
   tall sticky-scroll CSS only activates once JS adds `.is-enhanced`.
   ========================================================================== */

/* ---------- THRESHOLD (resting state, always present) ---------- */

.threshold-wrap {
  position: relative;
  height: 100vh;
  min-height: 560px;
}

.threshold-stage {
  position: relative;
  height: 100vh;
  min-height: 560px;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: #000;
  --progress: 0;
}

.threshold-env {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.threshold-parallax {
  position: absolute;
  inset: -6%;
  will-change: transform;
}

/* Orb layering — DISTANT / AWARE / APPROACH compose as independent
   nested layers rather than fighting over one `transform`:
     .threshold-parallax--orb   (pointer PARALLAX translate, JS, existing)
       .threshold-orb-breathe   (idle breathing scale, CSS keyframe, always on)
         .threshold-orb-proximity (pointer PROXIMITY scale, CSS var, JS-fed)
           .threshold-orb-aura  (proximity-driven glow, additive, not filter)
           .threshold-orb-img   (env-zoom + scroll-Approach scale, unchanged)
   Each nests inside the last, so their transforms compose naturally —
   no single element ever declares more than one transform effect. */
.threshold-parallax--orb {
  --orb-focal-x: 50%;
  --orb-focal-y: 43%;
}

.threshold-orb-breathe {
  width: 100%;
  height: 100%;
  transform-origin: var(--orb-focal-x) var(--orb-focal-y);
  animation: threshold-orb-breathe 9s ease-in-out infinite;
}

@keyframes threshold-orb-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.012); }
}

.threshold-orb-proximity {
  position: relative;
  width: 100%;
  height: 100%;
  transform-origin: var(--orb-focal-x) var(--orb-focal-y);
  transform: scale(calc(1 + var(--orb-proximity, 0) * 0.06));
  transition: transform 0.4s ease-out;
}

/* Additive glow layer, not a filter/brightness tweak on the artwork
   itself — screens a soft light over the Orb rather than degrading it. */
.threshold-orb-aura {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at var(--orb-focal-x) var(--orb-focal-y), rgba(150, 165, 255, 0.9) 0%, rgba(130, 145, 255, 0.45) 14%, transparent 34%);
  opacity: calc(0.12 + var(--orb-proximity, 0) * 0.7);
  transition: opacity 0.4s ease-out;
  pointer-events: none;
  mix-blend-mode: screen;
}

.threshold-bg,
.threshold-orb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 43%;
  display: block;
  will-change: transform;
  /* home_background.png/home_orb.png both carry a baked-in black
     letterbox border (the source frame is ~1920x1080 but the actual
     corridor art only occupies the middle ~86% x ~98%). At most
     desktop aspect ratios object-fit:cover doesn't crop enough on its
     own to remove that border — this zoom does, so the corridor reads
     as the environment itself rather than "an image on the page".
     Both layers read the SAME variable so their registration stays
     identical; only this scale (their shared base) needs to match —
     the Approach's extra scroll-driven scale on top of it is allowed
     to diverge (the Orb is meant to grow faster than the environment). */
  --env-zoom: 1.2;
}

.threshold-bg {
  transform: scale(var(--env-zoom, 1));
}

.threshold-orb-img {
  transform: scale(calc(var(--env-zoom, 1) * (1 + var(--progress) * 1.05)));
  opacity: clamp(0, calc((1 - var(--progress)) / 0.2), 1);
}

/* Minimum overlay needed for text contrast + edge integration — no
   decorative gradient mesh, no glow blobs. */
.threshold-vignette {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(3, 2, 8, 0.66) 0%, rgba(3, 2, 8, 0.24) 38%, rgba(3, 2, 8, 0) 62%),
    linear-gradient(180deg, rgba(3, 2, 8, 0.3) 0%, rgba(3, 2, 8, 0) 22%, rgba(3, 2, 8, 0) 74%, rgba(3, 2, 8, 0.5) 100%);
  pointer-events: none;
}

.threshold-darken {
  position: absolute;
  inset: 0;
  background: #030308;
  opacity: var(--progress);
  pointer-events: none;
}

/* Nav floats over the environment instead of sitting on its own
   website-colored band — index.html-only override of shell.css's
   default topbar (guardians.html/auditory-ascension.html keep the
   original, more opaque treatment, which suits their plain gradient
   background). Loads after shell.css, so this wins at equal
   specificity. */
.topbar {
  background: linear-gradient(180deg, rgba(4, 3, 10, 0.4) 0%, rgba(4, 3, 10, 0.05) 100%);
  backdrop-filter: blur(5px);
  border-bottom-color: transparent;
}

.threshold-content {
  position: relative;
  z-index: 2;
  max-width: 38ch;
  padding: 0 clamp(20px, 5.5vw, 84px);
  /* Fully gone by 55% scroll progress (not just asymptotically fading),
     so the final stretch of the Approach is environment/Orb alone —
     see the Threshold -> Sanctum handoff note near .threshold-index. */
  opacity: clamp(0, calc(1 - var(--progress) / 0.55), 1);
  transform: translateY(calc(var(--progress) * -36px));
  text-align: left;
}

.threshold-kicker {
  font-family: var(--font-heading);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(200, 190, 255, 0.62);
  margin: 0 0 20px 0;
  text-align: left;
}

/* Hierarchy: environment/Orb first, then headline — so the headline is
   substantial but must not out-compete the artwork. Rendered in one
   consistent warm off-white; the Orb and candlelight provide the
   color, not the type. */
.threshold-headline {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(1.55rem, 2.8vw, 2.3rem);
  line-height: 1.22;
  letter-spacing: 0.005em;
  margin: 0 0 18px 0;
  color: rgba(246, 242, 235, 0.96);
}

.threshold-headline .accent {
  color: inherit;
}

.threshold-lead {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: clamp(0.95rem, 1.2vw, 1.05rem);
  line-height: 1.55;
  color: rgba(228, 225, 236, 0.82);
  max-width: 36ch;
  margin: 0 0 28px 0;
  text-align: left;
}

.threshold-cta-label {
  font-family: var(--font-heading);
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(200, 190, 255, 0.46);
  margin: 0 0 11px 0;
  text-align: left;
}

.threshold-cta {
  justify-content: flex-start;
  gap: 26px;
}

/* Quiet, editorial conversion links — not conventional SaaS buttons.
   Thin underline, no fill, no saturated purple. */
.threshold-store-link {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 0 6px 0;
  border: none;
  border-bottom: 1px solid rgba(235, 232, 255, 0.3);
  color: rgba(235, 232, 255, 0.86);
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  text-decoration: none;
  background: transparent;
  transition: border-color 0.25s ease, color 0.25s ease;
}
.threshold-store-link:hover,
.threshold-store-link:focus-visible {
  color: #fff;
  border-bottom-color: rgba(235, 232, 255, 0.75);
}
.threshold-store-link .arrow {
  display: inline-block;
  font-size: 0.82em;
  opacity: 0.6;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.threshold-store-link:hover .arrow,
.threshold-store-link:focus-visible .arrow {
  opacity: 0.95;
  transform: translate(2px, -2px);
}

/* Bottom-left index marker and bottom-right scroll invitation — a
   symmetric corner frame, editorial/technical annotation rather than
   marketing copy.

   Threshold -> Sanctum handoff: both of these, plus .threshold-content
   above, are fully gone (opacity: exactly 0, via clamp — not just
   asymptotically small) well before the Approach finishes. That
   leaves roughly the last 40-45% of the scroll range as environment
   + Orb alone, so no Threshold UI is ever visible at the same time as
   Sanctum's own content scrolling in beneath it. */
.threshold-index {
  position: absolute;
  left: clamp(20px, 6vw, 96px);
  bottom: clamp(20px, 4vh, 36px);
  z-index: 2;
  margin: 0;
  font-family: var(--font-heading);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(225, 220, 255, 0.4);
  opacity: clamp(0, calc(1 - var(--progress) / 0.45), 1);
}

.threshold-scroll-cue {
  position: absolute;
  right: clamp(20px, 6vw, 96px);
  bottom: clamp(20px, 4vh, 36px);
  z-index: 2;
  font-family: var(--font-heading);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(225, 220, 255, 0.55);
  /* The progress-driven fade lives here, on the outer element. The
     breathing pulse (below) lives on an inner span instead of on this
     element directly — an always-running CSS animation overrides
     whatever opacity/transform this rule declares while it plays, so
     if both lived on the same element the breathing animation would
     silently win and this fade-to-zero would never actually finish. */
  opacity: clamp(0, calc(1 - var(--progress) / 0.45), 1);
  pointer-events: none;
}

.threshold-scroll-cue .cue-pulse {
  display: flex;
  align-items: center;
  gap: 8px;
  animation: threshold-cue-breathe 3.4s ease-in-out infinite;
}

@keyframes threshold-cue-breathe {
  0%, 100% { opacity: 0.55; transform: translateY(0); }
  50% { opacity: 0.95; transform: translateY(4px); }
}

/* ---------- APPROACH (scroll-driven enhancement — unchanged from the
   first pass; only the shared base zoom feeding into it changed) ---------- */

@media (prefers-reduced-motion: no-preference) {
  .threshold-wrap.is-enhanced {
    height: 220vh;
  }
  .threshold-wrap.is-enhanced .threshold-stage {
    position: sticky;
    top: 0;
  }
  .threshold-wrap.is-enhanced .threshold-bg {
    transform: scale(calc(var(--env-zoom, 1) * (1 + var(--progress) * 0.32)));
  }
}

@media (prefers-reduced-motion: reduce) {
  .threshold-scroll-cue .cue-pulse { animation: none; }
}

/* ---------- Responsive recomposition (not a shrink of desktop) ---------- */

@media (max-width: 680px) {
  .threshold-stage {
    align-items: flex-start;
  }
  .threshold-bg,
  .threshold-orb-img {
    /* home_background_portrait.png/orb_portrait.png are a dedicated
       portrait pair (not a crop of the landscape asset) sharing their
       own 1080x1920 coordinate system, registered the same way as the
       landscape pair. Their own baked-in letterboxing sits mostly
       top/bottom (content spans roughly y:20%-88%) rather than the
       landscape pair's left/right margins, hence the different values. */
    object-position: 50% 54%;
    --env-zoom: 1.42;
    transform-origin: 50% 54%;
  }
  .threshold-parallax--orb {
    --orb-focal-x: 50%;
    --orb-focal-y: 54%;
  }
  .threshold-vignette {
    background:
      linear-gradient(180deg, rgba(3, 2, 8, 0.56) 0%, rgba(3, 2, 8, 0.08) 30%, rgba(3, 2, 8, 0) 58%, rgba(3, 2, 8, 0.6) 100%);
  }
  .threshold-content {
    max-width: none;
    width: 100%;
    padding: clamp(84px, 20vh, 140px) 24px 0 24px;
  }
  .threshold-kicker,
  .threshold-headline,
  .threshold-lead,
  .threshold-cta-label {
    text-align: center;
  }
  .threshold-lead {
    max-width: 34ch;
    margin-left: auto;
    margin-right: auto;
  }
  .threshold-cta {
    justify-content: center;
  }
  .threshold-index,
  .threshold-scroll-cue {
    font-size: 0.6rem;
    bottom: 16px;
  }
  .threshold-index { left: 20px; }
  .threshold-scroll-cue { right: 20px; }
}

/* ---------- SANCTUM (visual design unchanged from the first pass —
   only the structural full-bleed mechanism changed, see index.html) ---------- */

.sanctum {
  position: relative;
  /* More top clearance than before: the Threshold's Approach now ends in
     a stretch of pure darkness+Orb (see the Threshold->Sanctum handoff
     note near .threshold-index), and this extra padding lets that
     darkness carry a moment longer before the Sanctum content itself
     appears, rather than the portal arriving immediately at the seam. */
  padding: 132px 0 150px 0;
  background: linear-gradient(180deg, #030308 0%, var(--color-bg-primary) 100%);
  overflow: hidden;
}

.sanctum-inner {
  max-width: var(--content-width-wide, 1180px);
  margin: 0 auto;
  padding: 0 clamp(20px, 6vw, 64px);
  display: grid;
  grid-template-columns: minmax(260px, 0.85fr) minmax(280px, 1fr);
  gap: clamp(48px, 7vw, 88px);
  align-items: center;
}

.sanctum-kicker {
  font-family: var(--font-heading);
  font-size: 0.76rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(200, 190, 255, 0.72);
  margin: 0 0 18px 0;
  text-align: left;
}

.sanctum-headline {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(1.85rem, 3.2vw, 2.55rem);
  line-height: 1.16;
  margin: 0 0 16px 0;
  color: var(--color-text-primary);
  text-align: left;
}

.sanctum-lead {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--color-text-soft);
  max-width: 38ch;
  margin: 0;
  text-align: left;
}

.sanctum-portal {
  position: relative;
  justify-self: center;
  width: 100%;
  /* Restrained on purpose: this should feel like the Sanctum quietly
     emerging inside the darkness, not a screenshot filling the page.
     Reduced from 400px — see the Phase 1 refinement report for why. */
  max-width: 280px;
  /* A beat behind .sanctum-copy (below), so the portal reads as
     materializing just after the words settle, not simultaneously —
     composing with, not replacing, the Threshold's own darkness-first
     handoff. Both still use the shared .reveal mechanism (opacity/
     transform driven by site.js's IntersectionObserver); this only
     delays when the portal's own transition starts once triggered. */
  transition-delay: 0.15s;
}

.sanctum-copy {
  transition-delay: 0s;
}

.sanctum-ui {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1320 / 2713;
}

.sanctum-portal::before,
.sanctum-portal::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 14%;
  pointer-events: none;
}
.sanctum-portal::before {
  top: 0;
  background: linear-gradient(180deg, #030308 0%, transparent 100%);
  opacity: 0.9;
}
.sanctum-portal::after {
  bottom: 0;
  background: linear-gradient(0deg, var(--color-bg-primary) 0%, transparent 100%);
}

@media (max-width: 900px) {
  .sanctum-inner {
    grid-template-columns: 1fr;
    justify-items: center;
  }
  .sanctum-copy {
    max-width: 46ch;
  }
  .sanctum-kicker,
  .sanctum-headline,
  .sanctum-lead {
    text-align: center;
  }
  .sanctum-lead {
    margin-left: auto;
    margin-right: auto;
  }
  .sanctum-portal {
    max-width: 260px;
  }
}

@media (max-width: 680px) {
  .sanctum {
    padding: 96px 0 110px 0;
  }
  .sanctum-portal {
    max-width: none;
    width: min(58vw, 250px);
  }
}
