/* ============================================================
   ORIGIN — a nine-tailed portfolio
   deep obsidian · moonlight lilac · ember amber
   ============================================================ */

/* ------------------------------------------------------------
   self-hosted variable fonts — subset, same-origin, display:block.
   Head preloads land them inside the first-paint window, so the veil
   never shows fallback type. noto-sc.woff2 is subset to exactly the
   hanzi this site uses (extracted from index.html) — any NEW hanzi
   added later must be re-subset or it falls back to Songti SC/SimSun.
   ------------------------------------------------------------ */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 500;
  font-display: block;
  src: url('../assets/fonts/cormorant.woff2') format('woff2');
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300 400;
  font-display: block;
  src: url('../assets/fonts/cormorant-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 500;
  font-display: block;
  src: url('../assets/fonts/inter.woff2') format('woff2');
}
/* → and ↗ live outside Google's latin slice — tiny dedicated subset so
   "Keep scrolling →" and the ↗ visit links render in Inter, not fallback */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 500;
  font-display: block;
  src: url('../assets/fonts/inter-arrows.woff2') format('woff2');
  unicode-range: U+2192, U+2197;
}
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 200 400;
  font-display: block;
  src: url('../assets/fonts/noto-sc.woff2') format('woff2');
}

:root {
  --ink: #08070d;
  --ink-2: #0d0b16;
  --moon: #ece7f4;
  /* channel tokens — retune a hue once, every alpha follows */
  --spirit-rgb: 168, 180, 236;
  --moon-rgb: 236, 231, 244;
  --ember-rgb: 224, 160, 92;
  --moon-dim: rgba(var(--moon-rgb), 0.55);
  --moon-faint: rgba(var(--moon-rgb), 0.3);
  --hairline: rgba(var(--moon-rgb), 0.1);
  --spirit: #a8b4ec;
  --spirit-soft: rgba(var(--spirit-rgb), 0.28);
  --ember: #e0a05c;
  /* THE MOTION VOICE — two curves, three durations, site-wide.
     Anything that MOVES takes --out-expo (fast attack, long settle);
     pure opacity/color crossfades take --out-soft. The same two-curve
     restraint that carries the reference sites. */
  --out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --out-soft: cubic-bezier(0.25, 1, 0.5, 1);
  --dur-1: 0.35s; /* micro: fills, arrow nudges */
  --dur-2: 0.6s;  /* hover / state changes */
  --dur-3: 1.1s;  /* scene: veil, big reveals */
  --serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', system-ui, -apple-system, sans-serif;
  --zh: 'Noto Serif SC', 'Songti SC', 'SimSun', serif;
  --pad-x: 8vw;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: auto; }
html.no-js { scroll-behavior: smooth; }

body {
  background: var(--ink);
  color: var(--moon);
  font-family: var(--sans);
  font-weight: 300;
  font-size: 15px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

::selection { background: rgba(var(--spirit-rgb), 0.25); color: var(--moon); }

/* keyboard focus speaks the same language as hover — never the UA's
   stock blue ring on this obsidian */
:focus-visible {
  outline: 1px solid var(--spirit);
  outline-offset: 4px;
}
.veil-enter:focus-visible {
  outline-offset: 8px;
  color: var(--spirit);
}
.veil-enter:focus-visible .veil-enter-rule { width: 76px; background: var(--spirit); }

/* the scrollbar is part of the world, not the OS chrome.
   Standard properties only: modern engines (Chromium 121+, Safari
   18.2+, Firefox) all honor them, and a ::-webkit-scrollbar block
   would force macOS Safari's overlay scrollbars into a permanent
   classic gutter — a regression, not a polish. */
html {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--spirit-rgb), 0.3) var(--ink);
}

a { color: inherit; text-decoration: none; }

/* ------------------------------------------------------------
   ambient layers
   ------------------------------------------------------------ */

/* ART DIRECTION LAW — the world is ink.
   The video is a whisper beneath it: graded dark at the file level,
   held at ≤0.5 opacity, and melted into #08070d at every edge.
   Content always sits on near-black. Never let the footage read
   as a bright backdrop poster. */
.journey {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  /* the grade (brightness 0.9, contrast 1.03) is baked into the encode
     itself — no per-frame CSS filter pass (PSNR-verified identical).
     2.4s is deliberately off the --dur scale: a cinematic backdrop
     fade, like the hunt fox's sine idle, sits outside the voice. */
  transition: opacity 2.4s var(--out-soft);
  pointer-events: none;
  transform: translateZ(0); /* own compositor layer: scrub repaints stay off the page */
}
.journey.ready { opacity: 0.55; }

.journey-veil {
  position: fixed; inset: 0; z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(115% 95% at 50% 42%, transparent 24%, rgba(8, 7, 13, 0.92) 100%),
    linear-gradient(to bottom, rgba(8, 7, 13, 0.65), transparent 26%, transparent 68%, rgba(8, 7, 13, 0.82));
}

#foxfire {
  position: fixed; inset: 0; z-index: 2;
  width: 100%; height: 100%;
  pointer-events: none;
}

.tint {
  position: fixed; inset: 0; z-index: 3;
  pointer-events: none; opacity: 0;
  background: radial-gradient(60% 55% at 68% 62%, rgba(var(--ember-rgb), 0.22), transparent 70%);
}

.grain {
  /* -64px overscan is all the shake below ever needs — the old -50%
     inset made this a 200vw x 200vh composited texture (4x viewport) */
  position: fixed; inset: -64px; z-index: 40;
  pointer-events: none; opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 1.4s steps(4) infinite;
}
/* the intro veil is opaque — don't shimmer behind it */
html.gated .grain { animation-play-state: paused; }
@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  25% { transform: translate(-48px, 24px); }
  50% { transform: translate(24px, -48px); }
  75% { transform: translate(-24px, 48px); }
  100% { transform: translate(48px, -24px); }
}

.cursor-halo {
  position: fixed; top: 0; left: 0; z-index: 60;
  width: 34px; height: 34px; border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--spirit-rgb), 0.35), transparent 70%);
  pointer-events: none; mix-blend-mode: screen;
  transform: translate(-50%, -50%);
  opacity: 0;
}
@media (pointer: coarse) { .cursor-halo { display: none; } }

/* ------------------------------------------------------------
   intro veil
   ------------------------------------------------------------ */

.veil {
  position: fixed; inset: 0; z-index: 100;
  background: var(--ink);
  display: flex; align-items: center; justify-content: center;
  transition: opacity var(--dur-3) var(--out-soft), visibility var(--dur-3);
  touch-action: none; /* no dragging the page behind the veil */
}
/* while the veil is up, the page itself cannot scroll */
html.gated, html.gated body { overflow: hidden; }
.veil.gone { opacity: 0; visibility: hidden; }
html.no-js .veil { display: none; }

.veil-inner { position: relative; text-align: center; }

.veil-hanzi {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -54%);
  font-family: var(--zh); font-weight: 200;
  font-size: clamp(160px, 34vmin, 300px);
  color: rgba(var(--spirit-rgb), 0.07);
  user-select: none;
}

.veil-spirit {
  width: clamp(110px, 16vh, 160px);
  margin: 0 auto 4vh;
  animation: veil-float 6s ease-in-out infinite;
}
@keyframes veil-float {
  0%, 100% { transform: translateY(0); opacity: 0.85; }
  50% { transform: translateY(-14px); opacity: 1; }
}

.veil-word {
  position: relative;
  font-family: var(--serif); font-weight: 400;
  font-size: 24px; letter-spacing: 0.32em; text-indent: 0.32em;
}
.veil-word span { color: var(--moon-faint); font-family: var(--zh); font-weight: 200; }

.veil-line {
  position: relative; margin-top: 18px;
  font-size: 12px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--moon-dim);
}

/* the gate: a quiet count while the forest loads, then Enter */
.veil-gate { position: relative; margin-top: 54px; }
.veil-progress {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 13px;
  transition: opacity var(--dur-2) var(--out-soft);
}
.veil-pct {
  font-family: var(--serif); font-weight: 300;
  font-size: 15px; letter-spacing: 0.3em; text-indent: 0.3em;
  color: var(--moon-dim); font-variant-numeric: tabular-nums;
}
.veil-track {
  display: block; width: 132px; height: 1px;
  background: var(--hairline); overflow: hidden;
}
.veil-fill {
  display: block; height: 100%; background: var(--spirit-soft);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur-1) var(--out-expo);
}
.veil.ready .veil-progress { opacity: 0; pointer-events: none; }
.veil.ready .veil-enter { opacity: 1; pointer-events: auto; }

.veil-enter {
  position: relative; margin-top: 0;
  background: none; border: none; cursor: pointer;
  color: var(--moon);
  font-family: var(--sans); font-weight: 400;
  font-size: 11px; letter-spacing: 0.45em; text-indent: 0.45em; text-transform: uppercase;
  padding: 10px 6px;
  opacity: 0; pointer-events: none;
  transition: letter-spacing var(--dur-2) var(--out-expo), color var(--dur-2) var(--out-soft), opacity var(--dur-3) var(--out-soft) 0.2s;
}
.veil-enter:hover { letter-spacing: 0.6em; color: var(--spirit); }
.veil-enter-rule {
  display: block; margin: 12px auto 0;
  width: 46px; height: 1px; background: var(--moon-faint);
  transition: width var(--dur-2) var(--out-expo);
}
.veil-enter:hover .veil-enter-rule { width: 76px; background: var(--spirit); }

/* ------------------------------------------------------------
   fixed chrome
   ------------------------------------------------------------ */

.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  padding: 26px 4vw;
  background: linear-gradient(to bottom, rgba(8, 7, 13, 0.7), transparent);
}

.nav-mark { display: flex; align-items: baseline; gap: 12px; }
.nav-hanzi { font-family: var(--zh); font-weight: 300; font-size: 17px; color: var(--spirit); }
.nav-word { font-family: var(--serif); font-size: 15px; letter-spacing: 0.3em; }

.nav-links { display: flex; gap: clamp(16px, 2.6vw, 40px); }
.nav-links a {
  font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--moon-dim);
  transition: color var(--dur-2) var(--out-soft);
  position: relative; padding-bottom: 6px;
}
.nav-links a::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: var(--spirit);
  /* resting origin is RIGHT so the line exits the way it was going;
     the hover/active rule swaps to LEFT (untransitioned = instant), so
     the line enters left and leaves right — it passes through the word */
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur-2) var(--out-expo);
}
.nav-links a:hover, .nav-links a.active { color: var(--moon); }
.nav-links a.active::after { transform: scaleX(1); transform-origin: left; }
/* hover-only choreography stays behind a capability guard — on touch,
   :hover latches after a tap and the state would stick */
@media (hover: hover) {
  .nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }
}

.rail {
  position: fixed; right: 3vw; top: 50%; z-index: 20;
  transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 16px;
}
.rail-label {
  writing-mode: vertical-rl;
  font-family: var(--zh); font-weight: 200; font-size: 12px;
  letter-spacing: 0.5em; color: var(--moon-faint);
}
.rail-line {
  position: relative; width: 1px; height: 140px;
  background: var(--hairline); overflow: hidden;
}
.rail-fill {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  background: var(--spirit);
  transform: scaleY(0); transform-origin: top;
}
.rail-count {
  font-family: var(--serif); font-size: 13px; letter-spacing: 0.15em;
  color: var(--moon-dim);
}

/* ------------------------------------------------------------
   spirit foxes — ink drawings turned to moonlight
   Three stacked copies: wide bloom, tight bloom, sharp lines.
   ------------------------------------------------------------ */

.spirit-mount { position: relative; }
.spirit-mount img {
  display: block; width: 100%; height: auto;
  user-select: none; -webkit-user-drag: none;
}
.spirit-mount img.sharp {
  position: relative; z-index: 2;
  filter: invert(1) brightness(1.28);
  opacity: 0.95;
}
.spirit-mount img.bloom {
  position: absolute; inset: 0; z-index: 1; height: 100%;
  filter: invert(1) brightness(1.6) blur(5px);
  opacity: 0.6;
}
.spirit-mount img.bloom2 {
  position: absolute; inset: 0; z-index: 0; height: 100%;
  filter: invert(1) brightness(2) blur(16px);
  opacity: 0.38;
}
/* pre-baked glow layers (assets/kitsune/*-bloom*.webp): the blurs above
   are rendered into the files offline, so no live filter runs. The
   negative inset restores the blur spill the live filter drew outside
   the box (4%/8% padding baked into the files). The tails chapter's
   runtime-baked pieces stay on the live-filter path — their bitmaps
   are black ink (main.js strips data-baked when it swaps them in). */
.spirit-mount img.bloom[data-baked] {
  /* explicit % size, not auto — an abs-positioned IMAGE with width:auto
     takes its intrinsic file size, it does not stretch to the insets */
  top: -4%; left: -4%; width: 108%; height: 108%;
  filter: none;
}
.spirit-mount img.bloom2[data-baked] {
  top: -8%; left: -8%; width: 116%; height: 116%;
  filter: none;
}

/* ------------------------------------------------------------
   shared chapter grammar
   ------------------------------------------------------------ */

main { position: relative; z-index: 4; }

.chapter { position: relative; padding: clamp(120px, 12.5vw, 210px) var(--pad-x); }

.ch-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 6vw; align-items: center;
}

.ch-label {
  display: flex; align-items: center; gap: 16px;
  font-size: 10px; letter-spacing: 0.38em; text-transform: uppercase;
  color: var(--moon-dim);
  margin-bottom: clamp(28px, 2.8vw, 48px);
}
.ch-num { font-family: var(--serif); font-style: italic; font-size: 14px; letter-spacing: 0.1em; color: var(--spirit); }
.ch-rule { width: 52px; height: 1px; background: var(--hairline); }
.ch-name { white-space: nowrap; }

.ch-title {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(44px, 5.6vw, 92px);
  line-height: 1.04; letter-spacing: 0.01em;
}
.ch-title em { font-style: italic; color: #cdd5f8; }

/* masked line reveals — each line rises out of its own slot;
   the slot clips well below the baseline grid so serif descenders
   (g, y, j — italic tails run deep in Cormorant) keep their tails;
   the negative margin gives the room back, leaving line spacing
   untouched. Hidden lines start at yPercent 135 in main.js so they
   never peek through this extended window before their reveal. */
.ch-title .tl { display: block; overflow: hidden; padding-bottom: 0.24em; margin-bottom: -0.24em; }
.ch-title .tl-i { display: block; }

.ch-rule { transform-origin: left center; }

.hero-title .hc { display: inline-block; }

.edge-hanzi {
  position: absolute; right: 3.5vw; top: 22vh;
  writing-mode: vertical-rl;
  font-family: var(--zh); font-weight: 200;
  font-size: clamp(40px, 6vmin, 72px); letter-spacing: 0.4em;
  color: rgba(var(--moon-rgb), 0.06);
  pointer-events: none; user-select: none;
}

/* giant chapter watermarks — designed negative space */
.ch-watermark {
  position: absolute; left: 50%; top: 44%;
  transform: translate(-50%, -50%);
  font-family: var(--zh); font-weight: 200;
  font-size: clamp(220px, 36vmin, 480px); line-height: 1;
  color: rgba(var(--spirit-rgb), 0.04);
  white-space: nowrap;
  pointer-events: none; user-select: none;
  z-index: 0;
}
.ch-watermark-warm { color: rgba(var(--ember-rgb), 0.05); }
.origin .ch-watermark { left: 62%; top: 58%; }
.fire .ch-watermark { left: 40%; top: 46%; }
.tails .ch-watermark { left: 72%; top: 40%; }
.pool .ch-watermark { left: 50%; top: 30%; z-index: 0; }

.mut { color: var(--moon-faint); font-size: 0.85em; }

/* ------------------------------------------------------------
   00 · hero
   ------------------------------------------------------------ */

.hero { position: relative; }
.hero-pin { height: 100vh; height: 100svh; position: relative; overflow: hidden; }

.hero-zoom {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  will-change: transform, opacity;
}

.hero-moon {
  position: absolute; left: 50%; top: 38%;
  width: 52vmin; height: 52vmin;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--moon-rgb), 0.09) 0%, rgba(var(--spirit-rgb), 0.05) 45%, transparent 70%);
}

.hero-watermark {
  position: absolute; left: 50%; top: 44%;
  transform: translate(-50%, -50%);
  font-family: var(--zh); font-weight: 200;
  font-size: clamp(200px, 46vmin, 520px);
  color: rgba(var(--spirit-rgb), 0.045);
  white-space: nowrap; user-select: none;
}

.hero-eyebrow {
  position: relative;
  font-size: 14px; letter-spacing: 0.34em; text-indent: 0.34em; text-transform: uppercase;
  color: var(--moon);
  margin-bottom: 3vh;
}
.hero-eyebrow .hero-name { font-weight: 500; }

.hero-title {
  position: relative;
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(88px, 16.5vw, 290px);
  line-height: 0.9; letter-spacing: 0.05em; text-indent: 0.05em;
}

.hero-sub {
  position: relative;
  margin-top: 3.4vh;
  font-size: 13px; letter-spacing: 0.18em; line-height: 2.1;
  color: var(--moon-dim);
}

.hero-spirit {
  width: clamp(340px, 60vh, 620px);
  margin-top: 2vh;
}

.hero-cue {
  position: absolute; bottom: 4vh; left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  font-size: 9px; letter-spacing: 0.5em; text-indent: 0.5em; text-transform: uppercase;
  color: var(--moon-faint);
}
.hero-cue-line {
  width: 1px; height: 52px; overflow: hidden; position: relative;
  background: var(--hairline);
}
.hero-cue-line::after {
  content: ''; position: absolute; left: 0; top: 0;
  width: 100%; height: 100%; background: var(--spirit);
  /* drip via transform, not `top` — an infinite `top` animation runs
     layout on the main thread every frame, forever */
  transform: translateY(-100%);
  animation: cue-drip 2.2s ease-in-out infinite;
}
@keyframes cue-drip {
  0% { transform: translateY(-100%); }
  60%, 100% { transform: translateY(100%); }
}

/* ------------------------------------------------------------
   01 · origin
   ------------------------------------------------------------ */

.origin .ch-grid { grid-template-columns: minmax(0, 6.5fr) minmax(0, 4fr); align-items: start; }
.origin .ch-head { grid-column: 1 / -1; margin-bottom: clamp(46px, 5vw, 84px); }

.origin-body { max-width: 58ch; }
.origin-body .lead {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(21px, 1.9vw, 27px); line-height: 1.55;
  margin-bottom: clamp(20px, 2.1vw, 36px);
}
.origin-body p + p { color: var(--moon-dim); }

.origin-facts {
  /* auto-fit: keeps three columns where they breathe (>=150px each),
     drops to two before the values start crushing into ragged wraps */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 24px;
  margin-top: clamp(40px, 4.4vw, 74px); padding-top: clamp(20px, 2.1vw, 36px);
  border-top: 1px solid var(--hairline);
}
.origin-facts dt {
  font-size: 9px; letter-spacing: 0.34em; text-transform: uppercase;
  color: var(--moon-faint); margin-bottom: 8px;
}
.origin-facts dd {
  font-family: var(--serif); font-size: 17px; color: var(--moon);
  text-wrap: balance; /* two-line values break evenly, not ragged */
}
.nowrap { white-space: nowrap; }

.origin-fox {
  width: min(44vw, 580px);
  justify-self: end;
  margin: clamp(-63px, -3.75vw, -36px) -3vw 0 -8vw; /* interlock with the text column */
  position: relative; z-index: 1;
}

/* ------------------------------------------------------------
   02 · the hunt (horizontal)
   ------------------------------------------------------------ */

.hunt { padding: 0; }
.hunt-pin { height: 100vh; height: 100svh; position: relative; overflow: hidden; }

.hunt-track {
  display: flex; align-items: center;
  gap: clamp(48px, 7vw, 130px);
  height: 100%;
  padding: 0 var(--pad-x);
  width: max-content;
  will-change: transform;
}

.hunt-intro { min-width: min(58vw, 760px); }
.hunt-hint {
  margin-top: clamp(30px, 3.1vw, 52px);
  font-size: 10px; letter-spacing: 0.35em; text-transform: uppercase;
  color: var(--moon-faint);
}

.skill {
  min-width: clamp(280px, 24vw, 400px);
  padding-left: clamp(24px, 2.4vw, 44px);
  border-left: 1px solid var(--hairline);
}
.skill-hanzi {
  display: block;
  font-family: var(--zh); font-weight: 200;
  font-size: clamp(48px, 5.4vw, 80px);
  color: rgba(var(--spirit-rgb), 0.2);
  margin-bottom: clamp(14px, 1.5vw, 26px);
}
.skill h3 {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(30px, 3vw, 46px); line-height: 1;
  margin-bottom: clamp(12px, 1.25vw, 22px);
}
.skill p { color: var(--moon-dim); font-size: 14px; max-width: 30ch; }
.skill .skill-note {
  margin-top: clamp(8px, 0.9vw, 15px);
  font-family: var(--serif); font-style: italic;
  font-size: 15px; color: var(--moon-faint);
}

.hunt-outro {
  min-width: 46vw;
  font-family: var(--serif); font-style: italic; font-weight: 300;
  font-size: clamp(24px, 2.6vw, 40px); line-height: 1.3;
  color: var(--moon-dim);
}

/* the walking fox is cut off on its right edge by design —
   it sits flush against the viewport edge, forever leaving */
.hunt-fox {
  position: absolute; bottom: 7vh; right: 0;
  width: clamp(300px, 34vw, 480px);
  pointer-events: none;
  z-index: 0;
}
.hunt-track { position: relative; z-index: 1; }

.hunt-ground {
  position: absolute; bottom: 11vh; left: 0;
  width: 100%; height: 2px;
}
.hunt-ground path {
  fill: none; stroke: rgba(var(--moon-rgb), 0.14);
  stroke-width: 0.4; stroke-dasharray: 0.8 1.6;
  vector-effect: non-scaling-stroke;
}

/* ------------------------------------------------------------
   03 · the leap (works corridor)
   ------------------------------------------------------------ */

.works { padding: 0; }
.works-pin { height: 100vh; height: 100svh; position: relative; overflow: hidden; }

/* will-change for the corridor is set from JS only while the works pin
   is active — held permanently it pins GPU texture memory site-wide */
.works-head {
  position: absolute; left: var(--pad-x); top: 50%;
  transform: translateY(-50%);
}

.works-fox {
  position: absolute; right: 12vw; top: 8vh;
  width: clamp(200px, 21vw, 310px);
  pointer-events: none;
}

/* three spirit gates deep in the forest — each work is a gate
   you approach, read, and pass through, carved from moonlight:
   the site's own spirit-glass hairlines. */
.works-gates { position: absolute; inset: 0; pointer-events: none; }

.wgate {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  pointer-events: none;
}
html.js:not(.reduced) .wgate { opacity: 0; }

.wgate-frame {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(64vw, 1020px); height: 74vh;
}
.wgate-frame span {
  position: absolute;
  background: linear-gradient(to bottom, rgba(220, 226, 250, 0.75), rgba(var(--spirit-rgb), 0.4));
  box-shadow: 0 0 16px rgba(var(--spirit-rgb), 0.3), 0 0 50px rgba(var(--spirit-rgb), 0.1);
}
.wg-kasagi { /* top lintel — overhangs, slightly heavier */
  top: 0; left: -5%; width: 110%; height: 4px;
  background: linear-gradient(to right, transparent, rgba(200, 208, 244, 0.65) 8%, rgba(var(--moon-rgb), 0.85) 50%, rgba(200, 208, 244, 0.65) 92%, transparent);
}
.wg-shima { /* thin secondary cap under the lintel */
  top: 13px; left: -2.5%; width: 105%; height: 1px; opacity: 0.7;
}
.wg-nuki { /* tie beam */
  top: 15%; left: 0; width: 100%; height: 2px; opacity: 0.75;
}
.wg-pillar {
  top: 4px; width: 2px; height: 100%;
  background: linear-gradient(to bottom, rgba(var(--moon-rgb), 0.8), rgba(var(--spirit-rgb), 0.5) 40%, rgba(var(--spirit-rgb), 0.2) 85%, transparent);
}
.wg-l { left: 4%; transform: rotate(1deg); transform-origin: top center; }
.wg-r { right: 4%; transform: rotate(-1deg); transform-origin: top center; }

.wscene-num {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--serif); font-style: italic; font-weight: 300;
  font-size: clamp(180px, 26vw, 460px); line-height: 1;
  color: rgba(var(--spirit-rgb), 0.05);
  user-select: none;
}

.wgate-inner { position: relative; max-width: min(52vw, 860px); padding-top: 8vh; pointer-events: auto; }

.wscene-meta {
  font-size: 10px; letter-spacing: 0.4em; text-indent: 0.4em; text-transform: uppercase;
  color: var(--moon-dim); margin-bottom: 3.4vh;
}
.wscene-title {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(64px, 11vw, 185px); line-height: 0.95;
  letter-spacing: 0.015em;
}
.wscene-line {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(18px, 1.8vw, 26px); color: var(--moon-dim);
  margin-top: 3.4vh;
}
.wscene-role {
  display: inline-flex; align-items: center; gap: 18px;
  margin-top: 4.5vh;
  font-size: 10px; letter-spacing: 0.32em; text-transform: uppercase;
  color: var(--moon-faint);
}
.wscene-role::before, .wscene-role::after {
  content: ''; width: 52px; height: 1px; background: var(--hairline);
}
.wscene-visit {
  display: inline-block; margin-top: 3vh;
  font-size: 10px; letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--moon-dim);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 3px;
  transition: color var(--dur-2) var(--out-soft), border-color var(--dur-2) var(--out-soft);
}
.wscene-visit:hover { color: var(--spirit); border-color: var(--spirit-soft); }
/* layered hover: a spirit line sweeps over the resting hairline
   (enter-left / exit-right), and the ↗ answers a beat later */
.wscene-visit, .pool-mail { position: relative; }
.pool-mail { display: inline-block; }
/* the visit link owns its own line — as an inline box it shared the
   line with the role's flanking dashes at wide viewports, and the
   trailing dash ran straight into the label */
.wscene-visit {
  display: block;
  width: fit-content;
  margin-left: auto; margin-right: auto;
}
.wscene-visit::after, .pool-mail::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--spirit);
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur-2) var(--out-expo);
}
.visit-arrow {
  display: inline-block;
  transition: transform var(--dur-1) var(--out-expo) 60ms;
}
/* hover-only: on touch, :hover latches after a tap and would stick */
@media (hover: hover) {
  .wscene-visit:hover::after, .pool-mail:hover::after {
    transform: scaleX(1); transform-origin: left;
  }
  .wscene-visit:hover .visit-arrow { transform: translate(3px, -3px); }
}

.works-count {
  position: absolute; right: var(--pad-x); bottom: 6vh;
  font-family: var(--serif); font-size: 15px; letter-spacing: 0.2em;
  color: var(--moon-dim);
}
.works-count span { font-size: 30px; color: var(--moon); }

/* ------------------------------------------------------------
   04 · foxfire
   ------------------------------------------------------------ */

.fire .ch-num, .fire .ch-name { color: var(--ember); }
.fire .ch-grid { align-items: start; gap: clamp(18px, 1.9vw, 32px) 6vw; /* tight rows: the list rises to meet the fox */ }

.fire-note { margin-top: clamp(20px, 2.1vw, 36px); max-width: 46ch; color: var(--moon-dim); font-size: 14px; }

.fire-fox {
  width: min(40vw, 520px);
  justify-self: end;
  margin: clamp(12px, 1.25vw, 21px) -4vw 0 -6vw;
  position: relative; z-index: 1;
}
.fire-fox img.sharp { filter: invert(1) sepia(0.4) saturate(1.4) brightness(1.22); }
/* ember tint over the baked white glow — color-matrix only, no blur */
.fire-fox img.bloom[data-baked] { filter: sepia(0.55) saturate(1.8) brightness(0.94); }
.fire-fox img.bloom2[data-baked] { filter: sepia(0.6) saturate(2) brightness(0.9); }

.fire-list { list-style: none; grid-column: 1 / -1; margin-top: clamp(24px, 2.5vw, 42px); }
.fire-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 24px;
  padding: clamp(18px, 2vw, 34px) 0;
  border-top: 1px solid var(--hairline);
}
.fire-list li:last-child { border-bottom: 1px solid var(--hairline); }
.fire-name {
  position: relative;
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(24px, 2.6vw, 38px);
  transition: color var(--dur-2) var(--out-soft);
}
/* the ember glow is pre-painted into a transparent-text ::after and
   crossfaded on opacity — a transitioned text-shadow would repaint
   the glyphs with a 24px blur on every frame of the hover */
.fire-name::after {
  content: attr(data-text);
  position: absolute; inset: 0;
  color: transparent;
  text-shadow: 0 0 24px rgba(var(--ember-rgb), 0.35);
  opacity: 0;
  transition: opacity var(--dur-2) var(--out-soft);
  pointer-events: none;
}
@media (hover: hover) {
  .fire-list li:hover .fire-name { color: var(--ember); }
  .fire-list li:hover .fire-name::after { opacity: 1; }
}
.fire-desc { font-size: 13px; color: var(--moon-dim); text-align: right; }

/* ------------------------------------------------------------
   05 · nine tails
   ------------------------------------------------------------ */

.tails { padding: 0; }
.tails-pin { height: 100vh; height: 100svh; position: relative; overflow: hidden; }

.tails-head { position: absolute; left: var(--pad-x); top: 12vh; z-index: 3; }

.tails-scene {
  position: absolute; left: 3vw; top: 52%;
  transform: translateY(-48%);
  width: min(64vw, 840px);
}

.tails-spirit {
  position: relative; z-index: 1;
  --wipe: 55%;
  -webkit-mask-image: linear-gradient(100deg, #000 0%, #000 var(--wipe), transparent calc(var(--wipe) + 14%));
  mask-image: linear-gradient(100deg, #000 0%, #000 var(--wipe), transparent calc(var(--wipe) + 14%));
}

/* masked mode: five untouched copies of the drawing, each baked down
   to one tail at load — the artwork is never altered. Every piece is a
   wrapper whose children reuse the standard three-layer spirit glow. */
.tails-spirit.tails-masked {
  -webkit-mask-image: none;
  mask-image: none;
}
.tails-spirit .tail-piece {
  position: absolute; inset: 0; z-index: 3;
  will-change: transform, opacity;
}
.tails-scene::before {
  content: '';
  position: absolute; inset: -8%;
  background: radial-gradient(55% 52% at 48% 55%, rgba(var(--spirit-rgb), 0.12), transparent 72%);
  pointer-events: none;
}

.tails-list {
  position: absolute; right: var(--pad-x); top: 50%;
  transform: translateY(-50%);
  list-style: none;
  width: min(320px, 34vw);
}
.tails-list li {
  display: flex; align-items: baseline; gap: 18px;
  padding: 1.35vh 0;
  border-bottom: 1px solid var(--hairline);
  opacity: 0.22;
  transition: opacity var(--dur-2) var(--out-soft);
}
.tails-list li.lit { opacity: 1; }
.tl-year { font-size: 10px; letter-spacing: 0.2em; color: var(--spirit); min-width: 42px; }
.tl-mark { font-family: var(--serif); font-size: clamp(16px, 1.5vw, 21px); }

.tails-count {
  position: absolute; left: var(--pad-x); bottom: 7vh;
  font-size: 13px; letter-spacing: 0.25em; color: var(--moon-dim);
}
.tails-count span#tailNum {
  font-family: var(--serif); font-size: 62px; font-weight: 300;
  color: var(--moon); letter-spacing: 0.04em;
  margin-right: 10px;
}
.tails-count-hanzi { font-family: var(--zh); color: var(--moon-faint); margin-left: 8px; }

/* ------------------------------------------------------------
   06 · the moon pool
   ------------------------------------------------------------ */

.pool {
  min-height: 112vh; padding: 0;
  display: flex; flex-direction: column; justify-content: center;
  overflow: hidden;
}

#poolCanvas {
  position: absolute; left: 0; bottom: 0; z-index: 1;
  width: 100%; height: 100%;
  pointer-events: none;
}


.pool-fox-wrap {
  position: absolute; right: 18vw; bottom: 10%; z-index: 2;
  width: min(28vw, 400px);
  pointer-events: none;
}
/* the plunging fox is drawn with very fine lines — lift it
   (brightness boost over the baked glow; no live blur) */
.pool-fox-wrap img.sharp { filter: invert(1) brightness(2) contrast(1.15); }
.pool-fox-wrap img.bloom[data-baked] { filter: brightness(1.5); opacity: 0.8; }
.pool-fox-wrap img.bloom2[data-baked] { filter: brightness(1.4); opacity: 0.5; }
.pool-fox { width: 100%; }

/* the padding must not eat the content cap — at wide viewports the
   old 780px box netted barely 500px and smushed the title and card */
.pool-content {
  position: relative; z-index: 3;
  padding: 0 var(--pad-x);
  max-width: calc(780px + 2 * var(--pad-x));
}

.pool-card {
  margin-top: clamp(34px, 3.75vw, 62px);
  max-width: 500px;
  padding: clamp(26px, 3vw, 40px);
  /* frost baked into the fill — a live backdrop-filter here re-blurred
     the video + snow canvas behind the card on every animated frame */
  background: linear-gradient(160deg, rgba(30, 27, 44, 0.72), rgba(13, 11, 22, 0.58));
  border: 1px solid var(--hairline);
}
.pool-mail {
  font-family: var(--serif); font-size: clamp(20px, 2.2vw, 27px);
  border-bottom: 1px solid var(--spirit-soft);
  padding-bottom: 4px;
  transition: color var(--dur-2) var(--out-soft), border-color var(--dur-2) var(--out-soft);
}
.pool-mail:hover { color: var(--spirit); border-color: var(--spirit); }

.pool-links { display: flex; gap: 28px; margin-top: 26px; }
.pool-links a {
  font-size: 10px; letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--moon-dim); transition: color var(--dur-2) var(--out-soft);
}
.pool-links a:hover { color: var(--moon); }

.pool-whisper {
  margin-top: 30px; padding-top: 22px;
  border-top: 1px solid var(--hairline);
  font-family: var(--serif); font-style: italic;
  font-size: 14px; color: var(--moon-dim);
}
.pool-whisper span { font-family: var(--zh); font-style: normal; font-size: 12px; margin-left: 10px; }

.footer {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px 4vw;
  font-size: 9px; letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--moon-faint);
}
.footer p[aria-hidden] { font-family: var(--zh); letter-spacing: 0.4em; }

/* ------------------------------------------------------------
   responsive
   ------------------------------------------------------------ */

@media (max-width: 960px) {
  :root { --pad-x: 6vw; }

  .rail, .edge-hanzi { display: none; }
  .nav { padding: 18px 5vw; }
  .nav-word, .nav-links a[data-nav="fire"], .nav-links a[data-nav="tails"] { display: none; }
  .nav-hanzi { font-size: 15px; white-space: nowrap; }
  .nav-links { gap: 16px; }
  .nav-links a { font-size: 9px; letter-spacing: 0.18em; }

  .ch-grid, .origin .ch-grid { grid-template-columns: 1fr; gap: clamp(40px, 6.5vw, 60px); }
  .origin-fox { width: min(70vw, 380px); margin: 0; justify-self: center; }
  .fire-fox { width: min(62vw, 340px); margin: 0; justify-self: center; }
  .ch-watermark { font-size: clamp(140px, 44vw, 260px); }
  /* per-section offsets assume desktop width — recentre so two-glyph
     marks don't hang half-clipped past the viewport edge */
  .origin .ch-watermark, .fire .ch-watermark, .tails .ch-watermark { left: 50%; }

  .hunt-intro { min-width: 80vw; }
  .skill { min-width: 74vw; }
  .hunt-outro { min-width: 80vw; }
  .hunt-fox { width: 200px; left: 8vw; }

  .works-head { top: 12vh; transform: none; }
  .works-fox { right: 6vw; top: 6vh; width: 130px; }
  .wgate-frame { width: 88vw; height: 62vh; }
  .wgate-inner { max-width: 80vw; }

  .tails-head { top: 8vh; }
  .tails-scene { width: 88vw; left: 50%; top: 46%; transform: translate(-50%, -50%); opacity: 0.5; }
  .tails-list { right: 50%; transform: translate(50%, -38%); width: 84vw; }
  .tails-count { bottom: 4vh; }

  .pool-fox-wrap { width: 24vw; right: 8vw; }
  .footer { flex-direction: column; gap: 6px; padding-bottom: 16px; }
}

/* ------------------------------------------------------------
   phone tier — the same story, set for a hand instead of a desk.
   Nothing here touches viewports above 480px.
   ------------------------------------------------------------ */
@media (max-width: 480px) {
  .chapter { padding: clamp(90px, 28vw, 128px) var(--pad-x); }

  .nav { padding: 16px 4vw; }
  .nav-links { gap: 10px; }
  .nav-links a { font-size: 8px; letter-spacing: 0.14em; }

  .hero-eyebrow { font-size: 11px; letter-spacing: 0.2em; text-indent: 0.2em; padding: 0 6vw; }
  .hero-eyebrow .hero-disc { display: none; } /* the discipline lives on in Awakening + Works */
  .hero-spirit { width: min(88vw, 48vh); }
  .hero-watermark { font-size: clamp(120px, 46vw, 200px); }

  .ch-title { font-size: clamp(32px, 10.5vw, 44px); }

  .origin-facts { grid-template-columns: 1fr; gap: 18px; }
  .origin-facts dt { margin-bottom: 4px; }

  .hunt-fox { width: 150px; opacity: 0.45; bottom: 4vh; }

  .works-head { top: 9vh; }
  .works-head .ch-title { font-size: clamp(30px, 9.5vw, 40px); }
  .wgate-frame { top: 56%; height: 54vh; }
  .wgate-inner { max-width: 88vw; }
  .wscene-title { font-size: clamp(38px, 13.5vw, 60px); }
  .wscene-line { font-size: 16px; }
  .wscene-role::before, .wscene-role::after { width: 24px; }

  .fire .ch-grid { gap: clamp(32px, 10vw, 48px) 6vw; }
  .fire-fox { margin-top: 8px; }
  .fire-list li { flex-direction: column; align-items: flex-start; gap: 6px; padding: clamp(14px, 4.5vw, 22px) 0; }
  .fire-desc { text-align: left; }

  /* tails: the fox above, the earned marks below — no more overlay */
  .tails-scene { width: 72vw; left: 50%; top: 38%; transform: translate(-50%, -50%); opacity: 0.9; }
  .tails-list { top: auto; bottom: 13vh; right: 50%; transform: translateX(50%); width: 86vw; }
  .tails-list li { padding: 0.9vh 0; gap: 12px; }
  .tl-mark { font-size: 14px; }
  .tails-count { bottom: 2.5vh; }
  .tails-count span#tailNum { font-size: 40px; }

  .pool { padding-bottom: 42vh; }
  .pool-fox-wrap { width: 30vw; right: 6vw; bottom: 12%; }
}

/* on very wide screens the columns drift apart — hold the
   composition together and let the spirits grow into the room
   instead of letting the middle go empty */
@media (min-width: 1700px) {
  .ch-grid { max-width: 1460px; margin-inline: auto; }
  .origin-fox { width: min(44vw, 680px); }
  .fire-fox { width: min(40vw, 620px); }
}

/* ------------------------------------------------------------
   no-js / reduced-motion fallbacks — the story, without travel
   ------------------------------------------------------------ */

html.no-js .hunt-pin, html.reduced .hunt-pin,
html.no-js .works-pin, html.reduced .works-pin,
html.no-js .tails-pin, html.reduced .tails-pin {
  height: auto; overflow: visible;
}

html.no-js .hunt-track, html.reduced .hunt-track {
  flex-wrap: wrap; width: auto;
  padding: clamp(120px, 12.5vw, 210px) var(--pad-x);
  gap: clamp(48px, 5vw, 84px) 6vw;
}
html.no-js .hunt-fox, html.reduced .hunt-fox,
html.no-js .hunt-ground, html.reduced .hunt-ground { display: none; }

html.no-js .works-pin, html.reduced .works-pin { padding: clamp(120px, 12.5vw, 210px) var(--pad-x); }
html.no-js .works-head, html.reduced .works-head { position: static; transform: none; margin-bottom: clamp(48px, 5vw, 84px); }
html.no-js .works-gates, html.reduced .works-gates { position: static; }
html.no-js .wgate, html.reduced .wgate {
  position: static; opacity: 1 !important; transform: none !important;
  padding: clamp(48px, 5vw, 84px) 0;
}
html.no-js .wgate-frame, html.reduced .wgate-frame,
html.no-js .wscene-num, html.reduced .wscene-num { display: none; }
html.no-js .wgate-inner, html.reduced .wgate-inner { max-width: none; padding-top: 0; }
html.no-js .works-fox, html.reduced .works-fox,
html.no-js .works-count, html.reduced .works-count { display: none; }

html.no-js .tails-pin, html.reduced .tails-pin { padding: clamp(120px, 12.5vw, 210px) var(--pad-x); }
html.no-js .tails-head, html.reduced .tails-head { position: static; margin-bottom: clamp(36px, 3.75vw, 63px); }
html.no-js .tails-scene, html.reduced .tails-scene {
  position: static; transform: none; margin: 0 auto clamp(36px, 3.75vw, 63px); opacity: 1;
}
html.no-js .tails-list, html.reduced .tails-list {
  position: static; transform: none; width: auto; max-width: 560px; margin: 0 auto;
}
html.no-js .tails-list li, html.reduced .tails-list li { opacity: 1; }
html.no-js .tails-count, html.reduced .tails-count { display: none; }

html.reduced .rail { display: none; }
html.reduced .journey, html.no-js .journey,
html.reduced .journey-veil, html.no-js .journey-veil { display: none; }
html.reduced .tails-spirit, html.no-js .tails-spirit {
  -webkit-mask-image: none; mask-image: none;
}
html.reduced .grain { animation: none; }
html.reduced .veil-spirit { animation: none; }
html.reduced .hero-cue-line::after { animation: none; }
html.reduced .cursor-halo { display: none; }

/* native calm-mode safety net: the html.reduced class above depends on
   main.js having run — this covers the window before it does (and any
   script failure) for visitors who asked the OS for reduced motion */
@media (prefers-reduced-motion: reduce) {
  .grain { animation: none; }
  .veil-spirit { animation: none; }
  .hero-cue-line::after { animation: none; }
  .cursor-halo { display: none; }
}
