/* ---------- 3D spinning helix hero (js/hero3d.js) ----------
   Only active when <html> carries .hero3d; otherwise the card-stack fallback
   runs and none of these elements show. */

.h3, .h3-tip { display: none; }

/* The statement h1 lives in the hero. Without the 3D hero it stays for screen
   readers only (the .statement section shows the copy visually). */
.h3-outro { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
html.hero3d .h3-outro {
  width: auto; height: auto; overflow: visible; clip: auto; clip-path: none; white-space: normal;
  position: absolute; left: 50%; top: 50%; z-index: 2; translate: -50% -50%;
  width: min(92vw, 44rem);
  display: flex; flex-direction: column; align-items: center; gap: clamp(14px, 1.6vw, 24px);
  text-align: center; text-wrap: balance;
  opacity: 0; transform-origin: 50% 50%;
  pointer-events: none;
}
html.hero3d .h3-outro-title {
  font-size: clamp(30px, 3.4vw, 56px); font-weight: 500; line-height: 1.04; letter-spacing: -.035em;
  color: var(--ink);
  text-shadow: 0 0 28px #fff, 0 0 12px #fff, 0 0 4px #fff;
}
html.hero3d .h3-outro-eyebrow, html.hero3d .h3-outro-cue { color: var(--muted); text-shadow: 0 0 10px #fff, 0 0 4px #fff; }
html.hero3d .h3-outro-cue { margin-top: clamp(4px, 1vw, 12px); }
html.hero3d .stage { display: none; }

/* Scroll pin: the wrapper is taller than the screen and the hero sticks inside it.
   js/hero3d.js maps that scroll distance onto the helix and sets --h3fade
   (0 → 1 over the last stretch) to clear the overlay before the pin releases. */
html.hero3d .hero-pin { position: relative; height: 420vh; height: 420svh; }
html.hero3d .hero { position: sticky; top: 0; height: 100vh; height: 100svh; }
html.hero3d .hero .hero-meta { filter: opacity(calc(1 - var(--h3fade, 0))); }

html.hero3d .h3 {
  display: block;
  position: absolute; inset: 0; z-index: 1;
  cursor: grab;
  touch-action: pan-y; /* vertical swipes still scroll the page */
}
html.hero3d .h3.is-drag { cursor: grabbing; }
html.hero3d .h3.is-over { cursor: pointer; }
html.hero3d .h3.is-duck { cursor: grab; }
html.hero3d .h3.is-duck:active { cursor: grabbing; }
.h3 canvas { display: block; width: 100%; height: 100%; }
/* The cards fade out at the hero's bottom edge, so none is cut flat against the Selected Work header. */
html.hero3d .h3 { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--h3m, 22vh)), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - var(--h3m, 22vh)), transparent); }

/* Hover pill: a frosted glass lens trailing the cursor, arrow + name + discipline.
   The work shows through it blurred, so it reads on light and dark covers alike. */
html.hero3d .h3-tip {
  display: flex; align-items: center; gap: 14px;
  position: fixed; left: 0; top: 0; z-index: 60;
  pointer-events: none;
  padding: 14px 26px 14px 20px; border-radius: 999px;
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  backdrop-filter: blur(18px) saturate(1.6);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .7), 0 0 0 1px var(--hair), 0 18px 40px -12px rgba(20, 20, 40, .28);
  color: var(--ink);
  white-space: nowrap;
  opacity: 0;
  scale: .86;
  transform-origin: 0 0;
  transition: opacity .22s var(--ease), scale .35s var(--pop);
}
html.hero3d .h3-tip.is-on { opacity: 1; scale: 1; }
html.hero3d .h3-tip-arrow { width: 22px; height: 22px; flex: none; transition: rotate .35s var(--pop); rotate: -45deg; }
html.hero3d .h3-tip.is-on .h3-tip-arrow { rotate: 0deg; }
html.hero3d .h3-tip-text { display: grid; gap: 1px; line-height: 1.15; }
html.hero3d .h3-tip-name { font: 600 20px/1.1 var(--sans); letter-spacing: -.01em; }
html.hero3d .h3-tip-cat { font: 400 14px/1.2 var(--sans); color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  html.hero3d .h3-tip, html.hero3d .h3-tip-arrow { transition: opacity .2s; scale: 1; rotate: 0deg; }
}

@media (max-width: 820px) {
  html.hero3d .hero-pin { height: 300vh; height: 300svh; }
  html.hero3d .h3-tip { display: none; }
}

/* ?intro=first: grid + statement first, then the helix unfolds out of the duck.
   Longer pin (gather + unfold + helix); the overlay meta starts hidden (js sets --h3fade). */
html.hero3d.intro-first .hero-pin { height: 380vh; height: 380svh; }
html.hero3d.intro-first .hero { --h3fade: 1; }
@media (max-width: 820px) {
  html.hero3d.intro-first .hero-pin { height: 330vh; height: 330svh; }
}

/* Intro mode: no navbar during the opening. It stays in the DOM and is revealed by
   js/hero3d.js (html.intro-nav-on) when the statement appears, on skip, or on failure.
   visibility keeps hidden links out of the tab order. */
html.intro-first.hero3d .nav {
  opacity: 0; visibility: hidden; translate: 0 -22px;
  transition: opacity .6s var(--ease), translate .8s var(--ease), visibility 0s linear .8s, color .3s, background-color .3s, box-shadow .3s;
}
html.intro-first.hero3d.intro-nav-on .nav {
  opacity: 1; visibility: visible; translate: 0 0;
  transition: opacity .6s var(--ease), translate .8s var(--ease), visibility 0s, color .3s, background-color .3s, box-shadow .3s;
}
/* A soft white veil behind the statement, so covers can sit close to the text. */
html.intro-first.hero3d .h3-outro::before {
  content: ""; position: absolute; z-index: -1; inset: -22% -14%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .92) 0%, rgba(255, 255, 255, .78) 55%, rgba(255, 255, 255, 0) 100%);
  pointer-events: none;
}
@media (max-width: 820px) {
  html.intro-first.hero3d .h3-outro::before {
    inset: -16% -12%;
    background: radial-gradient(closest-side, rgba(255, 255, 255, .96) 0%, rgba(255, 255, 255, .9) 62%, rgba(255, 255, 255, 0) 100%);
  }
}

/* Handoff: while the helix cards fly into Selected Work the canvas becomes a fixed full-screen
   layer above the page (it is exactly the viewport during the pin, so nothing jumps), the hero
   rises above the work section, and clicks pass through. Selected Work fades in via --ho. */
html.intro-first.hero3d .hero.is-fly { z-index: 45; }
html.intro-first.hero3d .hero.is-fly .h3 { position: fixed; inset: 0; height: 100vh; height: 100svh; pointer-events: none; }
html.intro-first.hero3d .work { --ho: 0; }
html.intro-first.hero3d .work .sec-head { opacity: var(--ho); translate: 0 calc((1 - var(--ho)) * 24px); }
html.intro-first.hero3d .work .ws-card,
html.intro-first.hero3d .work .ws-label,
html.intro-first.hero3d .work .ws-ruler,
html.intro-first.hero3d .work .wk { opacity: var(--ho); }
html.intro-first.hero3d .work .ws-card { scale: calc(.92 + .08 * var(--ho)); }
/* A soft white band behind the navbar while the grid is up, so covers never sit under it. */
html.intro-first.hero3d .hero::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 96px; z-index: 3; pointer-events: none;
  background: linear-gradient(to bottom, #fff 0%, rgba(255, 255, 255, .88) 45%, rgba(255, 255, 255, 0) 100%);
  opacity: var(--h3veil, 1);
}
