/* ============================================================================
   THE THRESHOLD — GOLDINU cinematic entrance, v2
   Loaded only by the Home page. Three states share one overlay:
     STATE 1  #threshold.st-tap     — black void, gold dog-head mark, one invitation
     STATE 2  #threshold.st-sirens  — ~20s cinematic: video slot 1 + Bond-briefing typography
     STATE 3  #threshold.st-cross   — 8–10s transition: video slot 2, audio handoff, reveal
   Every color/ease token below is read from the site's own :root (site.css loads first),
   so the intro can never drift from the gallery's palette.
   ============================================================================ */

#threshold{
  position:fixed; inset:0; z-index:999; overflow:hidden;
  background:#000; color:#EFE7D6;
  opacity:1; transition:opacity 1s var(--ease);
}
#threshold.th-leaving{opacity:0; pointer-events:none}
#threshold[hidden]{display:none}
#threshold *{box-sizing:border-box}

/* ---------------------------------------------------------------------------
   shared layer stack, present under every state: VIDEO → ATMOSPHERE → TYPOGRAPHY.
   Each state's own video sits in the same slot; only one is shown at a time. */
.th-layer{position:absolute; inset:0}
.th-video{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  opacity:0; transition:opacity 1.6s var(--ease);
  background:#000; /* shows through cleanly while no <source> is present yet */
}
.th-video.on{opacity:1}

.th-atmos{
  position:absolute; inset:0;
  background:
    radial-gradient(ellipse 80% 65% at 50% 46%, rgba(0,0,0,.15), rgba(0,0,0,.82) 70%, #000 100%),
    linear-gradient(180deg, rgba(0,0,0,.5), rgba(0,0,0,.1) 32%, rgba(0,0,0,.3) 66%, rgba(0,0,0,.78));
}
/* a slow, near-imperceptible drift in the dark — the "aura" doing work in place of
   the not-yet-generated footage; harmless once real video replaces it as the eye's focus */
.th-atmos::before{
  content:""; position:absolute; inset:-10%;
  background:radial-gradient(ellipse 46% 40% at 32% 38%, rgba(140,100,32,.10), transparent 60%),
             radial-gradient(ellipse 40% 36% at 70% 64%, rgba(140,100,32,.07), transparent 58%);
  animation:th-drift 26s ease-in-out infinite alternate;
  opacity:0; transition:opacity 3s var(--ease);
}
#threshold.st-sirens .th-atmos::before, #threshold.st-cross .th-atmos::before{opacity:1}
@keyframes th-drift{
  0%{transform:translate(0,0) scale(1)}
  100%{transform:translate(-2%,2%) scale(1.06)}
}
.th-grain{
  position:absolute; inset:-2px; pointer-events:none; opacity:.045; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='90' height='90'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size:180px 180px;
}

/* Both Sequence 1 AND Sequence 2 play completely unfiltered — each finished export is graded
   exactly as intended, so nothing sits between the viewer and it: no vignette, no grain, no color
   treatment. .th-atmos/.th-grain were originally a placeholder "ambience" standing in for footage
   that didn't exist yet (see the STATE 3 comment above) — now that Sequence 2 is real, finished
   video, they're hidden for the entire .st-cross state too, exactly like Sequence 1 already was,
   rather than only for the brief pre-Sequence-2 hold. That hold (the locked ~1s silent/black delay
   in intro.js before Sequence 2 starts) reads as plain black either way, since #threshold's own
   background is already #000 underneath. */
#threshold.st-sirens .th-atmos,
#threshold.st-sirens .th-grain,
#threshold.st-cross .th-atmos,
#threshold.st-cross .th-grain{ opacity:0; }

/* ===========================================================================
   STATE 1 — TAP TO BEGIN
   Pure black. One gold mark. One line of small, tracked, understated text.
   No gallery, no marble, nothing that reads as a loading animation. */
.th-tap-scene{
  position:absolute; inset:0; z-index:2; display:grid; justify-items:center; align-content:center; gap:26px;
  background:#000; opacity:1; transition:opacity 1.1s var(--ease), visibility 1.1s;
}
#threshold:not(.st-tap) .th-tap-scene{opacity:0; visibility:hidden; pointer-events:none}
.th-tap-btn{background:none; border:0; padding:0; display:grid; justify-items:center; gap:26px; cursor:pointer; -webkit-tap-highlight-color:transparent}
/* the real GOLDINU sculpture (cut from the DexScreener PFP) — a small golden artifact resting in the
   dark, not a giant logo. Only a warm glow behind it is added; the metallic detail itself is untouched. */
.th-mark{
  position:relative;
  width:clamp(58px,9vw,86px); height:auto; aspect-ratio:480/647;
  filter:drop-shadow(0 0 0 rgba(217,180,92,0));
  opacity:.92; transition:filter 1.4s var(--ease);
}
.th-mark::before{
  content:""; position:absolute; inset:-46% -60%; z-index:-1; border-radius:50%;
  background:radial-gradient(ellipse 55% 50% at 50% 46%, rgba(191,150,70,.32), transparent 72%);
  opacity:.7;
}
.th-mark img{width:100%; height:100%; object-fit:contain; display:block}
.th-tap-btn:hover .th-mark, .th-tap-btn:focus-visible .th-mark{
  filter:drop-shadow(0 0 20px rgba(217,180,92,.45));
}
.th-mark.th-breathe{animation:th-mark-breathe 5.5s ease-in-out infinite}
@keyframes th-mark-breathe{0%,100%{opacity:.78; filter:drop-shadow(0 0 4px rgba(217,180,92,.12))}50%{opacity:1; filter:drop-shadow(0 0 15px rgba(217,180,92,.3))}}
.th-tap-label{
  font-family:var(--mono); font-weight:500; font-size:10.5px; letter-spacing:.34em; text-transform:uppercase;
  color:rgba(239,231,214,.56); transition:color .4s var(--ease);
}
.th-tap-btn:hover .th-tap-label, .th-tap-btn:focus-visible .th-tap-label{color:rgba(239,231,214,.86)}

/* ===========================================================================
   STATE 2 — THE SIRENS SEQUENCE (~20s)
   Cinematic-title typography, distinct from TAKE A CHANCE: narrow, cold,
   high-fashion/editorial — Bodoni Moda pushed slightly condensed, wide tracked,
   uppercase. A mission-briefing register, not the gallery's warm serif. */
.th-sirens-scene{position:absolute; inset:0; z-index:1; opacity:0; visibility:hidden; transition:opacity 1s var(--ease), visibility 1s}
#threshold.st-sirens .th-sirens-scene, #threshold.st-cross .th-sirens-scene{opacity:1; visibility:visible}
#threshold.st-cross .th-sirens-scene{transition-delay:0s}

/* .th-brief / .th-brief-line — the four narrative beats, restored over the finished video (the
   export doesn't carry this copy itself). Text sits cleanly on top of the unfiltered video; its own
   weight/shadow give it legibility rather than any dark layer behind it — the footage itself is never
   darkened or filtered to make room for the type. Set in Cinzel: monumental, inscription-like, meant
   to read as a decree from an old private institution rather than a caption. */
.th-brief{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(90vw,780px); text-align:center; pointer-events:none;
}
.th-brief-line{
  position:absolute; inset:0; margin:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
  font-family:"Cinzel",var(--serif); font-weight:600; font-style:normal; text-transform:uppercase;
  letter-spacing:.05em;
  font-size:clamp(21px,2.75vw,34px); line-height:1.4; color:#F5EAD2;
  text-shadow:0 1px 3px rgba(0,0,0,.85), 0 8px 34px rgba(0,0,0,.68);
  opacity:0; filter:blur(3px);
  transition:opacity 1.5s var(--ease), filter 1.7s var(--ease);
}
.th-brief-line.on{opacity:1; filter:blur(0)}
.th-brief-line .hair{display:block; font-family:var(--mono); font-weight:500; font-size:9px; letter-spacing:.4em; color:rgba(217,180,92,.6); margin-bottom:16px; transform:scaleX(1.06)}

/* ===========================================================================
   TAKE A CHANCE — typography unchanged from the first pass: thin, elongated,
   gold-cast, ancient-adjacent without copying any recognizable fantasy
   lettering, deliberately its own register, separate from the Cinzel
   narrative lines above. This pass only makes it larger and more alive:
   a materializing entrance, a slow continuous heartbeat, and a firmer hover —
   the words themselves are the only thing on screen once the vault holds. */
.th-gate{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  display:grid; justify-items:center; gap:22px; z-index:2;
  opacity:0; pointer-events:none;
}
#threshold.th-gate-in .th-gate{opacity:1; pointer-events:auto}
.th-chance{
  position:relative; background:none; border:0; padding:14px 10px; cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.th-chance-t{
  position:relative; z-index:1; display:flex; gap:.85em; white-space:nowrap;
  font-family:"Cormorant",var(--serif); font-weight:300; font-style:normal;
  font-size:clamp(37px,6.4vw,72px); letter-spacing:.34em; text-transform:uppercase;
  line-height:1; transform:scaleY(1.16) skewX(-1.5deg); transform-origin:center;
  background-image:linear-gradient(100deg,#8C6420 0%,#D9B45C 22%,#F2DFA3 42%,#D9B45C 58%,#8C6420 82%,#D9B45C 100%);
  background-size:260% 100%; background-position:38% 0;
  -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; color:#D9B45C;
  filter:drop-shadow(0 0 14px rgba(217,180,92,.22));
  opacity:1; /* hits at full strength the instant the gate is revealed — no entrance animation,
                see intro.js: this is a hard cut, not a materialize. */
  transition:background-position 1.6s var(--ease), filter 1.1s var(--ease), letter-spacing 1.1s var(--ease);
}
.th-w{display:inline-block}

/* no entrance animation — TAKE A CHANCE appears instantly at full opacity/size/gold-treatment
   (see .th-chance-t's base opacity:1 above). Only the continuous heartbeat below ever animates it,
   toggled by intro.js via .tc-pulse on the button the moment the gate is revealed. */
@keyframes th-chance-pulse-scale{
  0%,100%{ transform:scaleY(1.16) skewX(-1.5deg) scale(1); }
  50%{ transform:scaleY(1.16) skewX(-1.5deg) scale(1.01); }
}
.th-chance.tc-pulse .th-chance-t{ animation:th-chance-pulse-scale 3s ease-in-out infinite; opacity:1; }

.th-chance-glow{
  position:absolute; inset:-30% -10%; z-index:0; pointer-events:none; border-radius:50%;
  background:radial-gradient(ellipse 60% 70% at 50% 50%, rgba(217,180,92,.16), transparent 70%);
  opacity:0; transition:opacity 1.1s var(--ease);
}
/* a faint, independent ambient glow that breathes in time with the heartbeat scale above — kept on
   its own element/property so it never fights the hover glow (.th-chance-glow) for control of opacity */
.th-chance-pulse-glow{
  position:absolute; inset:-30% -10%; z-index:0; pointer-events:none; border-radius:50%;
  background:radial-gradient(ellipse 60% 70% at 50% 50%, rgba(217,180,92,.24), transparent 70%);
  opacity:0;
}
@keyframes th-chance-glow-pulse{ 0%,100%{opacity:.14} 50%{opacity:.30} }
.th-chance.tc-pulse .th-chance-pulse-glow{ animation:th-chance-glow-pulse 3s ease-in-out infinite; }

/* hover/focus: !important on transform alone is enough to win over the still-running heartbeat
   animation (author !important outranks a CSS animation in the cascade) without pausing or
   restarting it — so the pulse continues underneath, unaffected, the instant hover ends. The
   transform bump is scoped to real mouse :hover only (not :focus-visible) — revealGate() moves
   keyboard focus onto this button the instant it appears, and if :focus-visible also pinned the
   transform, that permanent (non-mouse) focus would mask the heartbeat pulse entirely, since an
   !important declaration would otherwise override the running animation indefinitely. */
.th-chance:hover .th-chance-t, .th-chance:focus-visible .th-chance-t{
  background-position:100% 0; letter-spacing:.38em;
  filter:drop-shadow(0 0 26px rgba(242,223,163,.5));
}
.th-chance:hover .th-chance-t{
  transform:scaleY(1.16) skewX(-1.5deg) scale(1.015) !important;
}
.th-chance:hover .th-chance-glow, .th-chance:focus-visible .th-chance-glow{opacity:1}
.th-chance:active .th-chance-t{filter:drop-shadow(0 0 34px rgba(242,223,163,.7))}
#threshold.th-departing .th-chance-t{background-position:100% 0; filter:drop-shadow(0 0 40px rgba(242,223,163,.85))}

/* ===========================================================================
   STATE 3 — THE THRESHOLD CROSSING (8–10s)
   Video slot 2 fills the frame; the Sirens→Cycle audio handoff happens near
   its end (driven from JS, timed to this state's own video, not a fixed CSS
   delay) while everything else recedes. */
#threshold.st-cross .th-gate{opacity:0; pointer-events:none; transition:opacity .6s var(--ease)}
#threshold.st-cross .th-brief-line.on{opacity:0; transition:opacity .8s var(--ease)}
#threshold.st-cross .th-atmos{transition:background 2.4s var(--ease)}

/* the instant TAKE A CHANCE/Skip commits (st-cross added synchronously with the click), Sequence 1
   relinquishes the screen — a quick 200ms fade rather than the video layer's normal 1.6s, so it
   never lingers as a stuck frame while Sequence 2 (or, until that footage exists, the atmosphere
   layer above) takes over. */
#threshold.st-cross #thVideoSirens{ opacity:0; transition:opacity .2s linear; }

/* end-of-Sequence-1 blackout (see intro.js's goBlack()): once the cinematic reaches its visual
   endpoint (~25.07s), this snaps in INSTANTLY (no transition) over the whole picture — video,
   atmosphere, grain, brief text — to a genuine, opaque #000, not a dimmed/overlaid video and not a
   frozen poster frame. z-index sits above everything else in this stack (video/atmos/grain/brief all
   default to the stack's base level) but still below .th-gate (TAKE A CHANCE, z-index:2), so the words
   float on pure black once revealed. This is a deliberate hard cut — the next rendered frame is pure
   black with TAKE A CHANCE already at full strength, not a fade-to-black. #thVideoSirens keeps
   playing, unseen, underneath purely to carry its audio forward — see intro.js. */
.th-blackout{
  position:absolute; inset:0; z-index:5;
  background:#000; opacity:0; pointer-events:none;
}
#threshold.th-fadeblack .th-blackout{ opacity:1; }
/* the black becomes the transition's own starting point into Sequence 2 — it fades back out the
   instant crossing begins, revealing whatever's underneath (the atmosphere placeholder today, or
   the real Sequence 2 export once it exists) rather than staying opaque through the whole crossing. */
#threshold.st-cross .th-blackout{ opacity:0; transition:opacity .3s linear; }

/* first-gesture unlock happens on the STATE 1 button itself now — no separate
   veil-tap layer is needed, but we keep a focus ring affordance for a11y */
.th-tap-btn:focus-visible{outline:1px solid rgba(217,180,92,.5); outline-offset:10px}

.th-skip{
  position:absolute; left:calc(var(--gutter) / 1.4); bottom:calc(var(--gutter) / 1.4); z-index:3;
  padding:10px 6px; background:none; border:0; cursor:pointer;
  font-family:var(--mono); font-weight:500; font-size:9.5px; letter-spacing:.26em; text-transform:uppercase;
  color:rgba(239,231,214,.32); opacity:0; transition:opacity 1.2s var(--ease), color .4s var(--ease);
}
.th-skip:hover, .th-skip:focus-visible{color:rgba(239,231,214,.72)}
#threshold.th-chrome-in .th-skip{opacity:1}
#threshold.st-cross .th-skip{opacity:0; pointer-events:none}

/* ===========================================================================
   HERO TEXT/UI — delayed reveal after the Sequence 2 → live site handoff
   Controlled entirely by intro.js (document.documentElement classes), never by site.css's own
   normal page-load fade (that stays exactly as-is for a direct/non-intro visit). While the intro
   is in control, the hero's text/UI and the nav bar are held at opacity 0 — otherwise, by the time
   the 9.15s cinematic handoff actually happens tens of seconds later, that load-time fade would
   already be finished, and the text would pop in instantly alongside the background. The hero
   BACKGROUND (#hero .scene picture/img) is never touched by any of this — it appears the instant
   #threshold is removed, exactly matching Sequence 2's final frame, per the locked hard-cut spec.
   intro.js swaps .th-hero-pending → .th-hero-in ~300–500ms after that hard cut (see revealSite()),
   letting the clean hero hold briefly before the text/nav gracefully fade up together. */
html.th-hero-pending #hero .wall > *{
  animation:none !important; opacity:0; transform:translateY(6px); filter:blur(5px);
}
html.th-hero-pending #nav{ opacity:0; }

html.th-hero-in #hero .wall > *{
  transition:opacity 900ms var(--ease), transform 900ms var(--ease), filter 900ms var(--ease);
  opacity:1; transform:translateY(0); filter:blur(0px);
}
/* restrained ~120ms stagger between the hero's own text elements, in their natural reading order */
html.th-hero-in #hero .wall .eyebrow{ transition-delay:0ms; }
html.th-hero-in #hero .wall .mark{ transition-delay:120ms; }
html.th-hero-in #hero .wall .tag{ transition-delay:240ms; }
html.th-hero-in #hero .wall .acts{ transition-delay:360ms; }
/* nav is secondary UI — it arrives alongside the later part of the text reveal, not before it */
html.th-hero-in #nav{
  transition:opacity 900ms var(--ease); transition-delay:360ms; opacity:1;
}

@media (prefers-reduced-motion: reduce){
  #threshold, .th-video, .th-brief-line, .th-gate, .th-chance-t, .th-mark, .th-atmos::before, .th-blackout{transition-duration:.4s !important; animation:none !important}
  html.th-hero-pending #hero .wall > *, html.th-hero-pending #nav{ opacity:1 !important; transform:none !important; filter:none !important; }
  html.th-hero-in #hero .wall > *, html.th-hero-in #nav{ transition-duration:.2s !important; transition-delay:0s !important; }
  /* TAKE A CHANCE already renders at full opacity by default (no entrance animation exists to
     suppress); this just guarantees the heartbeat pulse's animation:none above can never leave it
     hidden. */
  .th-chance-t{opacity:1 !important}
  .th-chance-pulse-glow{opacity:0 !important}
}

/* ---------------------------------------------------------------------------
   MOBILE — narrower, closer, more intimate. Same three states, same beats,
   different proportions. */
@media (max-width:720px){
  .th-mark{width:56px; height:auto}
  .th-tap-label{font-size:9.5px; letter-spacing:.28em}
  .th-brief{width:min(90vw,380px)}
  .th-brief-line{font-size:clamp(18px,5.6vw,23px); line-height:1.35; letter-spacing:.035em; padding:0 6px}
  .th-brief-line .hair{font-size:8px; letter-spacing:.32em; margin-bottom:12px}
  .th-gate{gap:16px; width:min(92vw,380px)}
  /* white-space:normal + flex-wrap is a safety net, not the intent — at these sizes it should still
     render as one line on ordinary phone widths, but must never clip off-screen if it doesn't */
  .th-chance-t{
    font-size:clamp(29px,9.2vw,40px); letter-spacing:.2em; gap:.5em;
    white-space:normal; flex-wrap:wrap; justify-content:center; row-gap:.15em; max-width:92vw;
  }
  .th-chance:hover .th-chance-t, .th-chance:focus-visible .th-chance-t{letter-spacing:.23em}
  .th-skip{left:var(--m-inline,20px); bottom:calc(var(--m-inline,20px) + env(safe-area-inset-bottom,0px))}
}
