/* ============================================================
   CELESTIAL9 — printed-sheet design system
   The page is a comic book: newsprint gutters, inked panels,
   halftone dot fields, misregistered CMYK on the loud moments.
   ============================================================ */

/* ---------- Fonts (self-hosted from the game's own asset pack) ---------- */
@font-face {
  font-family: 'Bangers';
  src: url('../assets/fonts/Bangers-Regular.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  /* Oswald Medium — tall condensed caps for labels/eyebrows/nav. Replaced Russo One,
     whose wide geometric caps read too heavy; Oswald's narrower column stays crisp at
     small tracked sizes without the bulk. Latin subset (13KB woff2). */
  font-family: 'Oswald';
  src: url('../assets/fonts/Oswald-Medium.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  /* Zilla Slab — slab-serif body face for English paragraphs. Gives the paragraphs a
     printed newsprint / comic-caption texture in place of the flat system sans. Latin
     subset (22KB woff2); Chinese body keeps PingFang. */
  font-family: 'ZillaSlab';
  src: url('../assets/fonts/ZillaSlab-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  /* 优设标题黑 — simplified Chinese display face, subset to the site's glyphs */
  font-family: 'YouSheHei';
  src: url('../assets/fonts/YouSheBiaoTiHei.subset.woff2') format('woff2');
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  /* Palette pulled directly from the game art */
  --ink:        #0B0A0F;
  --ink-soft:   #2A2733;
  --paper:      #F6ECD2;
  --paper-2:    #EADCB8;
  --paper-3:    #DCCB9F;
  --celestial:  #17A2B0;
  --gold:       #F5B32E;
  --cinnabar:   #E0332E;
  --nether:     #6B2E9E;
  --flame:      #F5721E;

  --accent: var(--cinnabar);

  /* Type */
  --f-display: 'Bangers', 'YouSheHei', system-ui, sans-serif;
  --f-cn: 'YouSheHei', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
  /* Role faces, resolved per script further down. oracle.css used to reach for
     --f-cn everywhere; for Latin that falls through YouSheHei (whose subset is
     CJK + digits only) to PingFang, which is the "why does this look like a
     default system font" problem. Ask for a role, not for a language. */
  --f-title: 'Bangers', 'YouSheHei', system-ui, sans-serif;
  --f-label: 'Oswald', 'Helvetica Neue', Arial, sans-serif;
  --f-util: 'Bangers', 'YouSheHei', 'PingFang SC', sans-serif;
  --f-body: 'ZillaSlab', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
            ui-sans-serif, -apple-system, 'Helvetica Neue', sans-serif;

  --fs-display: clamp(3.2rem, 9vw, 7rem);
  --fs-h2:      clamp(2.1rem, 5.2vw, 4.2rem);
  --fs-h3:      clamp(1.35rem, 2.4vw, 2.05rem);
  --fs-body:    clamp(1rem, 0.55vw + 0.88rem, 1.14rem);
  --fs-small:   clamp(0.82rem, 0.3vw + 0.76rem, 0.92rem);

  /* Structure */
  --bw: 4px;                    /* ink border weight */
  --shadow: 9px 9px 0 var(--ink);
  --shadow-sm: 5px 5px 0 var(--ink);
  --gutter: clamp(1.1rem, 4vw, 3.5rem);
  --maxw: 1280px;
  --radius: 3px;

  --dot: rgba(11, 10, 15, 0.15);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---------- Reset-ish ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  overflow-x: clip;
  /* Newsprint halftone across the whole sheet */
  background-image: radial-gradient(circle at center, var(--dot) 1.05px, transparent 1.15px);
  background-size: 7px 7px;
}
img, video { max-width: 100%; display: block; }
h1, h2, h3, h4, p, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; }
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
:focus-visible {
  outline: 3px solid var(--celestial);
  outline-offset: 3px;
}

/* ---------- Utilities ---------- */
.wrap { width: min(100% - var(--gutter) * 2, var(--maxw)); margin-inline: auto; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* ---------- Script-aware type ----------
   js/i18n.js stamps <html data-script="han|kana|hangul|cyrillic|latin">.
   The bundled faces do not cover everything: YouSheHei is a Chinese display
   face with no kana and no hangul, and Bangers has no Cyrillic at all. Rather
   than let the browser scatter a headline across three fallback fonts, each
   script gets its own stack, and --f-cn is redefined per script so the many
   `html[data-cjk] … { font-family: var(--f-cn) }` rules below stay correct
   for Japanese and Korean without being duplicated. */
/* CJK: both roles collapse onto the language's own face. */
html[data-cjk] {
  --f-title: var(--f-cn);
  --f-label: var(--f-cn);
}
html[data-script='kana'] {
  --f-cn: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic', 'Noto Sans JP',
          'Meiryo', sans-serif;
}
html[data-script='hangul'] {
  --f-cn: 'Apple SD Gothic Neo', 'Pretendard', 'Noto Sans KR', 'Malgun Gothic', sans-serif;
}
/* Bangers carries no Cyrillic; without this every display line in Russian
   falls back mid-word and the page loses its voice entirely. */
/* Bangers has no Cyrillic at all, so Russian headlines would shatter into
   fallback glyphs. Oswald does (the bundled woff2 is rebuilt with the Cyrillic
   block), so it takes over the display roles. ZillaSlab has no Cyrillic either
   and there is no bundled substitute — name a real serif rather than leaving
   the browser to pick one. */
html[data-script='cyrillic'] {
  --f-display: 'Oswald', 'Helvetica Neue', Arial, sans-serif;
  --f-util: 'Oswald', 'Helvetica Neue', Arial, sans-serif;
  --f-title: 'Oswald', 'Helvetica Neue', Arial, sans-serif;
  --f-body: 'PT Serif', Georgia, 'Times New Roman', serif;
}

html[data-cjk] .display { font-family: var(--f-cn); letter-spacing: 0.02em; }

/* Ink panel: the fundamental object of this page */
.panel {
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  background: var(--paper);
}

/* Halftone field, used as an overlay layer inside panels */
.halftone::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.28) 1.3px, transparent 1.4px);
  background-size: 6px 6px;
  mix-blend-mode: multiply;
  opacity: .55;
}

/* ---------- Type roles ---------- */
.display {
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: 0.012em;
  text-transform: uppercase;
}
.util {
  font-family: var(--f-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  line-height: 1.2;
}
html[data-cjk] .util { font-family: var(--f-cn); letter-spacing: 0.18em; }

/* Misregistered CMYK print — the one loud typographic move, used on section titles */
.offset-print {
  text-shadow:
     0.05em 0 0 rgba(224, 51, 46, .9),
    -0.05em 0 0 rgba(23, 162, 176, .9);
}
/* Heavy CJK strokes smear at the Latin offset — pull the registration in tighter. */
html[data-cjk] .offset-print {
  text-shadow:
     0.026em 0 0 rgba(224, 51, 46, .85),
    -0.026em 0 0 rgba(23, 162, 176, .85);
}

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--gold);
  display: inline-flex;
  align-items: center;
  gap: .6em;
  padding: .72em 1.5em .62em;
  background: var(--btn-bg);
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  font-family: var(--f-display);
  font-size: clamp(1.05rem, 1.4vw, 1.35rem);
  letter-spacing: .045em;
  text-transform: uppercase;
  text-decoration: none;
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), background-color .16s;
}
html[data-cjk] .btn { font-family: var(--f-cn); }
.btn:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--ink); }
.btn:active { transform: translate(3px, 3px); box-shadow: 1px 1px 0 var(--ink); }
.btn--ghost { --btn-bg: var(--paper); }
.btn--play { --btn-bg: var(--celestial); color: var(--paper); }
.btn[aria-disabled='true'] { opacity: .82; }

/* Status, not action — used where a button would have nowhere to go. */
.badge {
  display: inline-block;
  padding: .6em 1.3em .5em;
  border: 3px dashed var(--paper-3);
  border-radius: var(--radius);
  color: var(--paper-3);
  font-family: var(--f-util);
  font-size: clamp(.8rem, 1.1vw, .95rem);
  letter-spacing: .22em;
  text-transform: uppercase;
}
html[data-cjk] .badge { font-family: var(--f-cn); letter-spacing: .14em; }

/* ============================================================
   Nav
   ============================================================ */
.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--paper);
  border-bottom: var(--bw) solid var(--ink);
}
.nav__inner {
  display: flex;
  align-items: center;
  gap: clamp(.6rem, 2vw, 1.6rem);
  min-height: 62px;
  padding-block: .5rem;
}
.nav__mark {
  display: flex; align-items: center; gap: .55rem;
  text-decoration: none; margin-right: auto;
}
.nav__mark img { height: 30px; width: auto; }
/* The Chinese title rides alongside the wordmark in both languages. */
.nav__mark span {
  font-family: var(--f-cn);
  font-size: .95rem; letter-spacing: .12em;
  padding: .18em .48em .1em;
  border: 2px solid var(--ink); border-radius: 2px;
}
.nav__links { display: flex; gap: clamp(.7rem, 1.8vw, 1.7rem); }
.nav__links a {
  font-family: var(--f-util);
  font-size: .95rem; letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none; padding: .3em 0;
  border-bottom: 3px solid transparent;
  transition: border-color .18s, color .18s;
}
html[data-cjk] .nav__links a { font-family: var(--f-cn); font-size: .9rem; letter-spacing: .1em; }
.nav__links a:hover { border-bottom-color: var(--cinnabar); }
.nav__tools { display: flex; align-items: center; gap: .6rem; }
/* Wrapper only. This used to be a bordered two-button segmented control; with
   nine languages the control became a <details> menu that draws its own border,
   so a border here double-framed it and overflow:hidden clipped the dropdown. */
.lang { display: flex; }
.nav .btn { padding: .48em 1.05em .38em; font-size: 1rem; box-shadow: 3px 3px 0 var(--ink); }
.nav__links--compact { display: none; }

/* ============================================================
   Hero — the banyan, and the banyan after
   ============================================================ */

/* Registered so the blot size can be animated and still drive mask-position. */
@property --blot {
  syntax: '<length>';
  inherits: true;
  initial-value: 460px;
}

.hero {
  position: relative;
  min-height: min(94svh, 900px);
  display: grid;
  place-items: center;
  overflow: hidden;
  border-bottom: var(--bw) solid var(--ink);
  isolation: isolate;

  /* Cursor position, in hero-local pixels. JS eases these toward the pointer. */
  --mx: 50%;
  --my: 45%;
  --blot: clamp(187px, 21.6vw, 360px);
  --reveal: 0;              /* 0 while the pointer is away, 1 while it is here */

  /* Per-blot position and size, all written by initBlot each frame. */
  --b1x: 50%; --b1y: 45%; --b1sx: calc(var(--blot) * 1.28); --b1sy: calc(var(--blot) * 1.28);
  --b2x: 50%; --b2y: 45%; --b2sx: calc(var(--blot) * .846); --b2sy: calc(var(--blot) * .613);
  --b3x: 50%; --b3y: 45%; --b3sx: calc(var(--blot) * 1.02); --b3sy: calc(var(--blot) * 1.275);
  --b4x: 50%; --b4y: 45%; --b4sx: calc(var(--blot) * .593); --b4sy: calc(var(--blot) * .529);
  --b5x: 50%; --b5y: 45%; --b5sx: calc(var(--blot) * 1.145); --b5sy: calc(var(--blot) * .909);
  --b6x: 50%; --b6y: 45%; --b6sx: calc(var(--blot) * .558); --b6sy: calc(var(--blot) * .734);
  --b7x: 50%; --b7y: 45%; --b7sx: calc(var(--blot) * 1.168); --b7sy: calc(var(--blot) * .789);
  --b8x: 50%; --b8y: 45%; --b8sx: calc(var(--blot) * .469); --b8sy: calc(var(--blot) * .533);
  --b9x: 50%; --b9y: 45%; --b9sx: calc(var(--blot) * .897); --b9sy: calc(var(--blot) * .863);
  --b10x: 50%; --b10y: 45%; --b10sx: calc(var(--blot) * .695); --b10sy: calc(var(--blot) * .518);
  --b11x: 50%; --b11y: 45%; --b11sx: calc(var(--blot) * .733); --b11sy: calc(var(--blot) * .873);
  --b12x: 50%; --b12y: 45%; --b12sx: calc(var(--blot) * .504); --b12sy: calc(var(--blot) * .42);
}
/* No overscan and no parallax here: the mask reads --mx/--my as hero-local pixels,
   so the art box and the hero box have to be the same box. The blot is the hero's
   one move — a second one would only fight it. */
.hero__art {
  position: absolute; inset: 0;
  z-index: -2;
}
.hero__plate {
  position: absolute; inset: 0;
  background-position: center 42%;
  background-size: cover;
  background-repeat: no-repeat;
}
/* Both plates share identical framing — that registration is what sells the reveal. */
.hero__plate--living { background-image: url('../assets/img/bg/bg_2.webp'); }
.hero__plate--ash {
  background-image: url('../assets/img/bg/bg_1.webp');
  opacity: calc(var(--reveal) * .88);
  transition: opacity .45s var(--ease);
  filter: saturate(1.12) contrast(1.06);

  /* Twelve blots over four source shapes, each URL reused three times — same file, so
     the extra layers cost no bandwidth and no extra decode. Sizes and per-link aspects
     are hand-picked for irregularity; see BLOTS in js/main.js. This list must stay the
     same length, and in the same image order, as that table. */
  -webkit-mask-image:
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png'),
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png'),
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png');
          mask-image:
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png'),
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png'),
    url('../assets/img/fx/blot-a.png'),
    url('../assets/img/fx/blot-c.png'),
    url('../assets/img/fx/blot-b.png'),
    url('../assets/img/fx/blot-d.png');
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size:
    var(--b1sx) var(--b1sy),
    var(--b2sx) var(--b2sy),
    var(--b3sx) var(--b3sy),
    var(--b4sx) var(--b4sy),
    var(--b5sx) var(--b5sy),
    var(--b6sx) var(--b6sy),
    var(--b7sx) var(--b7sy),
    var(--b8sx) var(--b8sy),
    var(--b9sx) var(--b9sy),
    var(--b10sx) var(--b10sy),
    var(--b11sx) var(--b11sy),
    var(--b12sx) var(--b12sy);
          mask-size:
    var(--b1sx) var(--b1sy),
    var(--b2sx) var(--b2sy),
    var(--b3sx) var(--b3sy),
    var(--b4sx) var(--b4sy),
    var(--b5sx) var(--b5sy),
    var(--b6sx) var(--b6sy),
    var(--b7sx) var(--b7sy),
    var(--b8sx) var(--b8sy),
    var(--b9sx) var(--b9sy),
    var(--b10sx) var(--b10sy),
    var(--b11sx) var(--b11sy),
    var(--b12sx) var(--b12sy);
  -webkit-mask-position:
    calc(var(--b1x) - var(--b1sx) / 2) calc(var(--b1y) - var(--b1sy) / 2),
    calc(var(--b2x) - var(--b2sx) / 2) calc(var(--b2y) - var(--b2sy) / 2),
    calc(var(--b3x) - var(--b3sx) / 2) calc(var(--b3y) - var(--b3sy) / 2),
    calc(var(--b4x) - var(--b4sx) / 2) calc(var(--b4y) - var(--b4sy) / 2),
    calc(var(--b5x) - var(--b5sx) / 2) calc(var(--b5y) - var(--b5sy) / 2),
    calc(var(--b6x) - var(--b6sx) / 2) calc(var(--b6y) - var(--b6sy) / 2),
    calc(var(--b7x) - var(--b7sx) / 2) calc(var(--b7y) - var(--b7sy) / 2),
    calc(var(--b8x) - var(--b8sx) / 2) calc(var(--b8y) - var(--b8sy) / 2),
    calc(var(--b9x) - var(--b9sx) / 2) calc(var(--b9y) - var(--b9sy) / 2),
    calc(var(--b10x) - var(--b10sx) / 2) calc(var(--b10y) - var(--b10sy) / 2),
    calc(var(--b11x) - var(--b11sx) / 2) calc(var(--b11y) - var(--b11sy) / 2),
    calc(var(--b12x) - var(--b12sx) / 2) calc(var(--b12y) - var(--b12sy) / 2);
          mask-position:
    calc(var(--b1x) - var(--b1sx) / 2) calc(var(--b1y) - var(--b1sy) / 2),
    calc(var(--b2x) - var(--b2sx) / 2) calc(var(--b2y) - var(--b2sy) / 2),
    calc(var(--b3x) - var(--b3sx) / 2) calc(var(--b3y) - var(--b3sy) / 2),
    calc(var(--b4x) - var(--b4sx) / 2) calc(var(--b4y) - var(--b4sy) / 2),
    calc(var(--b5x) - var(--b5sx) / 2) calc(var(--b5y) - var(--b5sy) / 2),
    calc(var(--b6x) - var(--b6sx) / 2) calc(var(--b6y) - var(--b6sy) / 2),
    calc(var(--b7x) - var(--b7sx) / 2) calc(var(--b7y) - var(--b7sy) / 2),
    calc(var(--b8x) - var(--b8sx) / 2) calc(var(--b8y) - var(--b8sy) / 2),
    calc(var(--b9x) - var(--b9sx) / 2) calc(var(--b9y) - var(--b9sy) / 2),
    calc(var(--b10x) - var(--b10sx) / 2) calc(var(--b10y) - var(--b10sy) / 2),
    calc(var(--b11x) - var(--b11sx) / 2) calc(var(--b11y) - var(--b11sy) / 2),
    calc(var(--b12x) - var(--b12sx) / 2) calc(var(--b12y) - var(--b12sy) / 2);
}




/* The ember bloom rides above the veil — under it the glow just got washed out. */
.hero__ember {
  position: absolute; inset: 0;
  z-index: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: var(--reveal);
  transition: opacity .45s var(--ease);
  background:
    radial-gradient(ellipse calc(var(--b1sx) * .52) calc(var(--b1sy) * .52) at var(--mx) var(--my),
      rgba(245, 114, 30, .44) 0%,
      rgba(224, 51, 46, .48) 40%,
      rgba(224, 51, 46, .17) 62%,
      rgba(224, 51, 46, 0) 78%);
}
/* And a bite of shadow just inside the tear, so the edge reads as torn paper. */
.hero__ember::after {
  content: '';
  position: absolute; inset: 0;
  mix-blend-mode: multiply;
  background:
    radial-gradient(ellipse calc(var(--b1sx) * .46) calc(var(--b1sy) * .46) at var(--mx) var(--my),
      rgba(11, 10, 15, 0) 0%,
      rgba(11, 10, 15, 0) 58%,
      rgba(11, 10, 15, .58) 82%,
      rgba(11, 10, 15, 0) 100%);
}

@media (prefers-reduced-motion: reduce) {
  .hero { animation: none; }
}
.hero__veil {
  position: absolute; inset: 0; z-index: -1;
  background:
    /* Pool of shadow under the copy block so type stays legible over busy art */
    radial-gradient(ellipse 46% 34% at 50% 63%, rgba(11,10,15,.72) 0%, rgba(11,10,15,.42) 55%, rgba(11,10,15,0) 100%),
    radial-gradient(ellipse 72% 58% at 50% 38%, rgba(11,10,15,0) 0%, rgba(11,10,15,.1) 58%, rgba(11,10,15,.44) 100%),
    linear-gradient(to bottom, rgba(11,10,15,.34) 0%, rgba(11,10,15,0) 24%, rgba(11,10,15,0) 56%, rgba(11,10,15,.6) 100%);
}
.hero__veil::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.34) 1.35px, transparent 1.45px);
  background-size: 6px 6px;
  mix-blend-mode: multiply;
  opacity: .5;
}
/* Page load stamps the hero on, plate by plate, the way a press lays down a sheet.
   One short moment — nothing here keeps moving once it has landed. */
@keyframes stamp {
  from { opacity: 0; transform: translateY(14px) scale(1.055); }
  to   { opacity: 1; transform: none; }
}
.hero__inner > * {
  animation: stamp .52s cubic-bezier(.16, .84, .3, 1) backwards;
}
.hero__eyebrow { animation-delay: .10s; }
.hero__logo    { animation-delay: 0s; animation-duration: .62s; }
.hero__tagline { animation-delay: .20s; }
.hero__sub     { animation-delay: .30s; }
.hero__cta     { animation-delay: .38s; }
.hero__meta    { animation-delay: .46s; }

.hero__inner {
  position: relative;
  z-index: 2;
  text-align: center;
  padding-block: clamp(1.2rem, 5.4svh, 5.5rem);
  display: grid;
  justify-items: center;
  gap: clamp(.55rem, 1.6svh, 1.5rem);
}
.hero__eyebrow {
  display: inline-block;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--f-util);
  font-size: clamp(.72rem, 1.5vw, .95rem);
  letter-spacing: .3em;
  text-transform: uppercase;
  padding: .5em 1.1em .38em;
  border-radius: 2px;
  transform: rotate(-1.2deg);
  --dx: 0px; --dy: 0px; --heat: 0;
  box-shadow:
    var(--dx) var(--dy) 0 rgba(224, 51, 46, calc(.8 * var(--heat))),
    calc(var(--dx) * -1) calc(var(--dy) * -1) 0 rgba(23, 162, 176, calc(.8 * var(--heat)));
}
html[data-cjk] .hero__eyebrow { font-family: var(--f-cn); letter-spacing: .28em; }
.hero__logo {
  /* logo-plate.webp carries its own paper keyline and hard ink shadow (tools/build-logo.js),
     so the wordmark reads against the canopy without a glow doing the work. The extra
     width covers the padding baked into the plate. */
  /* Capped by height as well as width. A width-only cap let the logo eat a short
     window whole and pushed the tagline and buttons below the fold. 72svh of width
     is 31svh of logo, since the plate is 2.34:1. */
  width: min(660px, 78vw, 64svh);
  height: auto;
  transform-origin: 50% 60%;
  --dx: 0px; --dy: 0px; --heat: 0;
  filter:
    drop-shadow(0 0 calc(52px * var(--heat)) rgba(245, 114, 30, calc(.8 * var(--heat))))
    drop-shadow(var(--dx) var(--dy) 0 rgba(224, 51, 46, calc(.55 * var(--heat))));
}
.hero__tagline {
  font-family: var(--f-display);
  font-size: min(clamp(1.55rem, 3.9vw, 3.05rem), 5.8svh);
  line-height: 1.02;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--paper) calc((1 - var(--heat) * .55) * 100%), #FFC27A);
  max-width: 17ch;

  /* Written by initBlot: --dx/--dy is how far the wet ink has been dragged under
     this element, --heat is how much of the tear is beneath it. Both rest at zero. */
  --dx: 0px; --dy: 0px; --heat: 0;

  text-shadow:
    /* the black key plate, always in register and always on top */
    3px 0 0 var(--ink), -3px 0 0 var(--ink), 0 3px 0 var(--ink), 0 -3px 0 var(--ink),
    2px 2px 0 var(--ink), -2px 2px 0 var(--ink), 2px -2px 0 var(--ink), -2px -2px 0 var(--ink),
    /* magenta and cyan slipping out of register along the drag */
    var(--dx) var(--dy) 0 rgba(224, 51, 46, .92),
    calc(var(--dx) * -1) calc(var(--dy) * -1) 0 rgba(23, 162, 176, .92),
    /* scorch where the tear sits under the letters: a tight rim, then a wider bloom */
    0 0 calc(9px * var(--heat))  rgba(245, 114, 30, var(--heat)),
    0 0 calc(34px * var(--heat)) rgba(224, 51, 46, calc(.85 * var(--heat))),
    0 8px 0 rgba(11,10,15,.32);
}
html[data-cjk] .hero__tagline { font-family: var(--f-cn); letter-spacing: .04em; max-width: 30ch; }
/* CJK breaks anywhere by default; keep each phrase intact and let the gaps wrap. */
.hero__tagline b { font-weight: inherit; white-space: nowrap; }
.hero__tagline b:not(:last-child)::after {
  content: '·';
  padding-inline: .4em;
  color: var(--gold);
}
.hero__sub {
  max-width: 46ch;
  color: var(--paper);
  font-size: min(clamp(.98rem, 1.4vw, 1.14rem), 2.9svh);
  --dx: 0px; --dy: 0px; --heat: 0;
  /* Body copy gets a whisper of the same slip — enough to feel it, not enough to blur it. */
  text-shadow:
    var(--dx) var(--dy) 0 rgba(224, 51, 46, calc(.7 * var(--heat))),
    calc(var(--dx) * -1) calc(var(--dy) * -1) 0 rgba(23, 162, 176, calc(.7 * var(--heat))),
    0 0 calc(16px * var(--heat)) rgba(245, 114, 30, calc(.8 * var(--heat))),
    0 2px 10px rgba(11,10,15,.95), 0 0 3px rgba(11,10,15,.9);
}
.hero__cta { display: flex; flex-wrap: wrap; gap: .85rem; justify-content: center; }
.hero__meta {
  font-family: var(--f-util);
  font-size: .84rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--paper);
  text-shadow: 0 2px 8px rgba(11,10,15,.95);
}
html[data-cjk] .hero__meta { font-family: var(--f-cn); }

/* Scroll cue — a pair of jiaobei divination blocks */
.hero__cue {
  position: absolute;
  z-index: 2;
  bottom: clamp(1rem, 3vh, 2rem);
  left: 50%;
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: .45rem;
  font-family: var(--f-util);
  font-size: .74rem; letter-spacing: .26em; text-transform: uppercase;
  color: var(--paper);
  text-shadow: 0 2px 8px rgba(11,10,15,.95);
}
.hero__cue svg { animation: cue-fall 2.4s var(--ease) infinite; }
@keyframes cue-fall {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  45%      { transform: translateY(9px) rotate(7deg); }
}

/* ============================================================
   Sections
   ============================================================ */
.section { padding-block: clamp(3rem, 6.5vw, 5rem); position: relative; }
.section--tight { padding-block: clamp(2.4rem, 5vw, 3.8rem); }

.section__head {
  display: flex;
  align-items: flex-end;
  gap: clamp(.9rem, 2.5vw, 1.8rem);
  margin-bottom: clamp(1.6rem, 4vw, 3rem);
  flex-wrap: wrap;
}
/* 印章 — the section seal. The page IS a sequence (one run), so the numeral earns its place. */
.seal {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(46px, 5vw, 62px);
  height: clamp(46px, 5vw, 62px);
  background: var(--cinnabar);
  color: var(--paper);
  border-radius: 4px;
  font-family: var(--f-cn);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1;
  padding-top: .06em;
  transform: rotate(-4deg);
  box-shadow: 3px 3px 0 var(--ink);
  border: 3px solid var(--ink);
}
.section__titles { display: grid; gap: .25rem; }
.section__kicker {
  font-family: var(--f-util);
  font-size: .82rem; letter-spacing: .28em; text-transform: uppercase;
  color: var(--ink-soft);
}
html[data-cjk] .section__kicker { font-family: var(--f-cn); }
.section__title {
  font-family: var(--f-display);
  font-size: var(--fs-h2);
  line-height: .94;
  text-transform: uppercase;
}
html[data-cjk] .section__title { font-family: var(--f-cn); letter-spacing: .03em; }
.section__lede {
  max-width: 52ch;
  margin-left: auto;
  color: var(--ink-soft);
  font-size: var(--fs-small);
}

/* Torn-ink rule between sections */
.rule {
  height: var(--bw);
  background: var(--ink);
  border-radius: 2px;
}

/* ============================================================
   Video slots
   ============================================================ */
.vslot {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ink);
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.vslot--sm { box-shadow: var(--shadow-sm); }
.vslot__media,
.vslot__poster {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.vslot__poster { filter: saturate(.85) contrast(1.05); }
iframe.vslot__media { border: 0; }   /* YouTube-backed slots */
.vslot__scrim {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(11,10,15,.1) 0%, rgba(11,10,15,.62) 100%);
}
.vslot__scrim::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.4) 1.4px, transparent 1.5px);
  background-size: 6px 6px;
  opacity: .45;
}
.vslot__button {
  position: absolute; inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: .8rem;
  color: var(--paper);
}
.vslot__disc {
  display: grid; place-items: center;
  width: clamp(58px, 7vw, 88px);
  aspect-ratio: 1;
  background: var(--gold);
  color: var(--ink);
  border: var(--bw) solid var(--ink);
  border-radius: 50%;
  box-shadow: 5px 5px 0 var(--ink);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.vslot__button:hover .vslot__disc { transform: translate(-3px, -3px) scale(1.04); box-shadow: 9px 9px 0 var(--ink); }
.vslot__label {
  font-family: var(--f-util);
  font-size: clamp(.74rem, 1.2vw, .88rem);
  letter-spacing: .24em; text-transform: uppercase;
  background: var(--ink);
  padding: .4em .9em .3em;
  border-radius: 2px;
}
html[data-cjk] .vslot__label { font-family: var(--f-cn); letter-spacing: .18em; }
/* A slot with no footage yet reads as a still with a corner tag — never a dead player. */
.vslot.is-empty .vslot__scrim {
  background: linear-gradient(to top, rgba(11,10,15,.7) 0%, rgba(11,10,15,0) 52%);
}
.vslot__soon {
  position: absolute;
  left: 0; bottom: 0;
  background: var(--ink);
  color: var(--paper-3);
  font-family: var(--f-util);
  font-size: .7rem; letter-spacing: .2em; text-transform: uppercase;
  padding: .42em .9em .32em;
  border-top-right-radius: 3px;
}
html[data-cjk] .vslot__soon { font-family: var(--f-cn); letter-spacing: .12em; }

.vslot.is-playing .vslot__scrim,
.vslot.is-playing .vslot__button,
.vslot.is-playing .vslot__poster { opacity: 0; pointer-events: none; }
.vslot.is-playing .vslot__scrim,
.vslot.is-playing .vslot__button { transition: opacity .3s; }

/* ============================================================
   〔壹〕Trailer
   ============================================================ */
.trailer { display: grid; gap: clamp(1.2rem, 3vw, 2rem); }
.trailer__strip {
  display: flex; flex-wrap: wrap; gap: clamp(.8rem, 2vw, 2rem);
  align-items: center; justify-content: space-between;
}
.stat { display: grid; gap: .1rem; }
.stat__n {
  font-family: var(--f-display);
  font-size: clamp(2.2rem, 4.4vw, 3.6rem);
  line-height: .9;
}
.stat__l {
  font-family: var(--f-util);
  font-size: .78rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-soft);
}
html[data-cjk] .stat__l { font-family: var(--f-cn); }

/* ============================================================
   〔贰〕The loop — three comic panels
   ============================================================ */
.loop {
  display: grid;
  gap: clamp(1.4rem, 3vw, 2.4rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}
.beat {
  display: grid;
  gap: 1rem;
  align-content: start;
  padding: clamp(1rem, 2vw, 1.4rem);
  background: var(--paper);
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  position: relative;
}
.beat:nth-child(2) { transform: rotate(.5deg); }
.beat:nth-child(3) { transform: rotate(-.4deg); }
.beat__kicker {
  font-family: var(--f-util);
  font-size: .78rem; letter-spacing: .26em; text-transform: uppercase;
  color: var(--cinnabar);
}
html[data-cjk] .beat__kicker { font-family: var(--f-cn); }
.beat__title {
  font-family: var(--f-display);
  font-size: clamp(1.9rem, 3.2vw, 2.7rem);
  line-height: .95;
  text-transform: uppercase;
}
html[data-cjk] .beat__title { font-family: var(--f-cn); letter-spacing: .03em; }
.beat__body { font-size: var(--fs-small); line-height: 1.7; }

/* ============================================================
   Deck band — the deck riffling past. Texture, no information.
   ============================================================ */
.deckband {
  margin-top: clamp(2.4rem, 6vw, 4rem);
  border-block: var(--bw) solid var(--ink);
  /* Lit from above, deepening toward the bottom — the strip reads as a table, not a bar. */
  background: linear-gradient(180deg, #191521 0%, var(--ink) 46%, #050408 100%);
  /* Taller than the cards, so they sit centred with clear air above and below rather
     than kissing the borders. The cards themselves are unchanged. */
  height: clamp(112px, 13.5vw, 172px);
  overflow: hidden;
  position: relative;
  isolation: isolate;
}
/* Halftone wash for grain, plus a faint top highlight line where the light lands. */
.deckband::before {
  content: '';
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(246,236,210,.08) 0%, transparent 14%),
    radial-gradient(circle at center, rgba(246,236,210,.05) 1.3px, transparent 1.4px);
  background-size: auto, 7px 7px;
}
/* Cards at the ends fade into the ink instead of hard-cutting at the border. */
.deckband::after {
  content: '';
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(90deg, var(--ink) 0%, transparent 8%, transparent 92%, var(--ink) 100%);
}
.deckband__rail {
  position: absolute;
  top: 50%;
  left: 0;
  display: flex;
  width: max-content;
  animation: riffle 46s linear infinite;
  will-change: transform;
}
.deckband:hover .deckband__rail { animation-play-state: paused; }

/* One dealt hand, repeated. Cards overlap, tilt, stagger and vary in depth like a
   deck pushed across felt. Overlap ratio (margin ÷ width) is held from the old size
   so the strip stays exactly as compact, only smaller. */
.deckband__run { display: flex; }
.deckband__card {
  --r: 0deg; --y: 0px; --s: 1;
  flex: none;
  width: clamp(46px, 5.6vw, 74px);
  aspect-ratio: 167 / 226;
  margin-left: clamp(-24px, -2.7vw, -37px);   /* overlap — ~0.5 of width, as before */
  transform: translateY(calc(-50% + var(--y))) rotate(var(--r)) scale(var(--s));
  transform-origin: 50% 62%;
  /* A tight ink line for the comic edge, then a soft cast for depth. */
  filter: drop-shadow(1px 2px 0 rgba(0,0,0,.6)) drop-shadow(2px 6px 7px rgba(0,0,0,.5));
  transition: transform .3s var(--ease);
}
.deckband__card:first-child { margin-left: 0; }
.deckband__card img { width: 100%; height: 100%; object-fit: contain; }
/* A turned-up suit gets a bloom of its own colour — the only spot of light in the deck. */
.deckband__card--suit {
  filter:
    drop-shadow(1px 2px 0 rgba(0,0,0,.6))
    drop-shadow(0 0 11px var(--glow))
    drop-shadow(2px 6px 7px rgba(0,0,0,.45));
}

@keyframes riffle { to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
  .deckband__rail { animation: none; }
  .deckband { overflow-x: auto; }
}

/* ============================================================
   〔叁〕The Nine — the card fan (signature element)
   ============================================================ */
.nine { position: relative; }
.fan {
  --step: clamp(66px, 8.2vw, 116px);
  position: relative;
  height: clamp(280px, 30vw, 372px);
  margin-bottom: clamp(1.4rem, 3vw, 2.2rem);
  display: flex;
  justify-content: center;
  align-items: flex-end;
}
.card {
  position: absolute;
  bottom: 0;
  width: clamp(112px, 12.4vw, 168px);
  aspect-ratio: 167 / 226;
  transform-origin: 50% 130%;
  transform: translateX(-50%) rotate(var(--rot)) translateY(var(--lift));
  transition: transform .34s var(--ease), filter .34s var(--ease);
  will-change: transform;
  padding: 0;
}
.card__face {
  position: absolute; inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: .3rem;
  padding: .55rem .4rem .5rem;
  background: var(--paper);
  border: 3px solid var(--ink);
  border-radius: 7px;
  box-shadow: 4px 5px 0 rgba(11,10,15,.55);
  overflow: hidden;
  text-align: center;
}
.card__face::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 34%, var(--fc) 0%, transparent 62%);
  opacity: .3;
}
.card__face::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.2) 1.1px, transparent 1.2px);
  background-size: 5px 5px;
  mix-blend-mode: multiply;
  opacity: .6;
}
.card__rank {
  position: relative;
  font-family: var(--f-util);
  font-size: .68rem;
  letter-spacing: .16em;
  color: var(--ink);
  opacity: .75;
}
.card__portrait {
  position: relative;
  width: 84%;
  margin-inline: auto;
  align-self: center;
  filter: drop-shadow(0 3px 0 rgba(11,10,15,.28));
}
.card__name {
  position: relative;
  font-family: var(--f-title);
  font-size: clamp(.86rem, 1.15vw, 1.08rem);
  line-height: 1.08;
  letter-spacing: .03em;
}
.card__name small {
  display: block;
  font-family: var(--f-util);
  font-size: .58rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  opacity: .72;
  margin-top: .2em;
}
.card:hover { transform: translateX(-50%) rotate(var(--rot)) translateY(calc(var(--lift) - 16px)); }
.card[aria-selected='true'] {
  transform: translateX(-50%) rotate(0deg) translateY(calc(var(--lift) - 30px)) scale(1.09);
  z-index: 5;
}
.card[aria-selected='true'] .card__face {
  border-color: var(--ink);
  box-shadow: 0 0 0 4px var(--fc), 7px 8px 0 rgba(11,10,15,.6);
}
.fan:has(.card[aria-selected='true']) .card:not([aria-selected='true']) { filter: saturate(.55) brightness(.94); }

/* Faction detail panel */
.faction {
  position: relative;
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  background: var(--ink);
  color: var(--paper);
}
.faction__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: .34;
  filter: saturate(1.1);
}
.faction__grad {
  position: absolute; inset: 0;
  /* A radial wash of house colour rising behind the text on the left and settling into
     the site's ink outward. Radial, so there's no diagonal seam — the old 105deg
     gradient met near-black at an angle and read as a black trapezoid on the right.
     A gentle top-light / bottom-shadow adds depth and lets the figure's fade land. */
  background:
    radial-gradient(125% 140% at 22% 40%,
      color-mix(in srgb, var(--fc) 46%, #0B0A0F) 0%,
      color-mix(in srgb, var(--fc) 13%, #0B0A0F) 44%,
      #0B0A0F 80%),
    linear-gradient(180deg, rgba(246,236,210,.05) 0%, transparent 20%, transparent 74%, rgba(0,0,0,.34) 100%);
}
.faction__grad::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.55) 1.5px, transparent 1.6px);
  background-size: 7px 7px;
  opacity: .5;
}
.faction__inner {
  position: relative;
  display: grid;
  gap: clamp(1.1rem, 2.6vw, 2rem);
  padding: clamp(1.3rem, 3.4vw, 2.6rem);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 900px) {
  .faction__inner { grid-template-columns: minmax(240px, 27%) minmax(0, 1fr); align-items: center; }
}
.faction__id { display: grid; gap: .55rem; align-content: start; }
/* The house a randomly-drawn hero belongs to, set above the name as an eyebrow. */
.hero__house {
  font-family: var(--f-util);
  font-size: .8rem; letter-spacing: .24em; text-transform: uppercase;
  color: var(--fc);
  filter: brightness(1.4);
}
html[data-cjk] .hero__house { font-family: var(--f-cn); letter-spacing: .16em; }
/* Right column of the panel: the selected hero's full high-res art (assets/img/heroes-lg,
   the game's unframed character bust), standing large in place of the five-god grid. */
.hero-solo {
  position: relative;
  display: grid;
  place-items: end center;
  min-height: clamp(240px, 30vw, 380px);
  isolation: isolate;
}
/* A wide, faint bloom of house colour behind the figure — ambient light, not a
   spotlight disc. Large and low, with a long falloff, so it has no visible edge. */
.hero-solo::before {
  content: '';
  position: absolute; inset: -10% -6% 0; z-index: -1;
  background: radial-gradient(76% 82% at 50% 40%,
    color-mix(in srgb, var(--fc) 22%, transparent) 0%,
    color-mix(in srgb, var(--fc) 7%, transparent) 52%,
    transparent 84%);
}
.hero-solo img {
  width: min(96%, 420px);
  max-height: clamp(258px, 34vw, 430px);
  object-fit: contain;
  object-position: bottom;
  /* The source art is cut off at the waist, so a hard bottom edge looks sliced.
     Fade the lower body into the panel's shadow — the figure reads as rising out of
     the dark rather than being cropped. */
  -webkit-mask-image: linear-gradient(to bottom, #000 52%, rgba(0,0,0,.5) 78%, transparent 97%);
          mask-image: linear-gradient(to bottom, #000 52%, rgba(0,0,0,.5) 78%, transparent 97%);
  filter: drop-shadow(0 6px 22px rgba(11,10,15,.55));
}
/* A pool of shadow where the figure dissolves, so the fade lands on ground, not air. */
.hero-solo::after {
  content: '';
  position: absolute; z-index: -1;
  left: 50%; bottom: 4%;
  width: 66%; height: 30px;
  translate: -50% 0;
  background: radial-gradient(ellipse at center, rgba(5,4,8,.6) 0%, transparent 72%);
}
.faction__cn {
  font-family: var(--f-title);
  font-size: clamp(2.2rem, 4.4vw, 3.4rem);
  line-height: 1;
  letter-spacing: .04em;
  text-shadow: 4px 4px 0 rgba(11,10,15,.8);
}
.faction__en {
  font-family: var(--f-display);
  font-size: clamp(1.1rem, 1.8vw, 1.45rem);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--fc);
  filter: brightness(1.35);
}
.faction__blurb { font-size: var(--fs-small); line-height: 1.7; color: rgba(246,236,210,.9); max-width: 40ch; }
.gods {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 118px), 1fr));
  gap: clamp(.6rem, 1.5vw, 1.1rem);
}
@media (min-width: 700px) { .gods { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.god { display: grid; gap: .4rem; justify-items: center; text-align: center; }
.god img {
  width: 100%;
  max-width: 128px;
  filter: drop-shadow(0 4px 0 rgba(11,10,15,.5));
  transition: transform .25s var(--ease);
}
.god:hover img { transform: translateY(-5px) scale(1.05); }
.god__cn { font-family: var(--f-title); font-size: clamp(.8rem, 1.05vw, .95rem); letter-spacing: .03em; line-height: 1.25; }
.god__en {
  font-family: var(--f-util);
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(246,236,210,.66);
  line-height: 1.3;
}
html[lang='en'] .god__cn { order: 2; opacity: .68; font-size: .78rem; }
html[lang='en'] .god__en { order: 1; font-size: .72rem; color: rgba(246,236,210,.94); letter-spacing: .07em; }

/* ============================================================
   〔肆〕Three realms — full-bleed inked spreads
   ============================================================ */
.realms { display: grid; }
.realm {
  position: relative;
  min-height: clamp(440px, 62vh, 620px);
  display: grid;
  align-items: end;
  border-block: var(--bw) solid var(--ink);
  overflow: hidden;
  color: var(--paper);
}
.realm + .realm { border-top: 0; }
.realm__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: -2;
}
.realm__scrim {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to top, rgba(11,10,15,.94) 6%, rgba(11,10,15,.55) 45%, rgba(11,10,15,.2) 100%);
}
.realm__scrim::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.5) 1.5px, transparent 1.6px);
  background-size: 7px 7px;
  opacity: .5;
}
.realm__inner {
  display: grid;
  gap: clamp(1rem, 2.4vw, 1.8rem);
  padding-block: clamp(2rem, 5vw, 3.4rem);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 880px) {
  .realm__inner { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
}
.realm__text { display: grid; gap: .6rem; max-width: 46ch; }
.realm__num {
  font-family: var(--f-cn);
  font-size: clamp(2.6rem, 6vw, 4.6rem);
  line-height: .85;
  color: var(--gold);
  text-shadow: 4px 4px 0 rgba(11,10,15,.85);
}
.realm__name {
  font-family: var(--f-display);
  font-size: clamp(1.9rem, 4vw, 3.1rem);
  line-height: .96;
  text-transform: uppercase;
}
html[data-cjk] .realm__name { font-family: var(--f-cn); letter-spacing: .04em; }
.realm__body { font-size: var(--fs-small); color: rgba(246,236,210,.88); line-height: 1.7; }
.realm__mobs { display: flex; gap: .5rem; align-items: flex-end; margin-top: .3rem; }
.realm__mobs img { width: clamp(38px, 4.6vw, 58px); filter: drop-shadow(0 3px 0 rgba(11,10,15,.7)); opacity: .95; }
.realm__boss { display: grid; justify-items: center; gap: .5rem; }
.realm__boss img {
  width: clamp(150px, 20vw, 250px);
  filter: drop-shadow(0 8px 0 rgba(11,10,15,.6)) drop-shadow(0 0 30px rgba(224,51,46,.35));
}
.realm__bosstag {
  display: grid; justify-items: center; gap: .1rem;
  background: var(--cinnabar);
  border: 3px solid var(--ink);
  border-radius: 2px;
  box-shadow: 4px 4px 0 var(--ink);
  padding: .4em .9em .35em;
  transform: rotate(-1.5deg);
}
.realm__bosstag b { font-family: var(--f-label); font-size: 1rem; letter-spacing: .06em; font-weight: 400; }
.realm__bosstag span {
  font-family: var(--f-util);
  font-size: .64rem; letter-spacing: .2em; text-transform: uppercase; opacity: .88;
}

/* ============================================================
   〔伍〕Relics & tributes
   ============================================================ */
.spoils {
  display: grid;
  gap: clamp(1.4rem, 3vw, 2.4rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;   /* the four tributes must not stretch to match the nine relics */
}
@media (min-width: 960px) { .spoils { grid-template-columns: 1.5fr 1fr; } }
.trove {
  padding: clamp(1.1rem, 2.4vw, 1.8rem);
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  background: var(--paper-2);
  position: relative;
  overflow: hidden;
}
.trove--dark { background: var(--ink); color: var(--paper); }
.trove__title {
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  text-transform: uppercase;
  margin-bottom: .15rem;
}
html[data-cjk] .trove__title { font-family: var(--f-cn); letter-spacing: .04em; }
.trove__note { font-size: var(--fs-small); color: var(--ink-soft); margin-bottom: 1.1rem; max-width: 42ch; }
.trove--dark .trove__note { color: rgba(246,236,210,.72); }
.trove__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 96px), 1fr));
  gap: clamp(.5rem, 1.4vw, .9rem);
}
@media (min-width: 560px) {
  .trove--relics .trove__grid  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .trove--tributes .trove__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.trove__item {
  position: relative;
  display: grid; justify-items: center; gap: .3rem;
  padding: .5rem .35rem .55rem;
  background: var(--paper);
  border: 3px solid var(--ink);
  border-radius: 4px;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.trove--dark .trove__item { background: #17141F; border-color: #000; }
.trove__item:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); z-index: 2; }
.trove--dark .trove__item:hover { box-shadow: 4px 4px 0 var(--gold); }
.trove__item img { width: 78%; }
.trove__item b { font-family: var(--f-label); font-size: .76rem; font-weight: 400; letter-spacing: .03em; text-align: center; line-height: 1.2; }
.trove__item span {
  font-family: var(--f-util);
  font-size: .58rem; letter-spacing: .08em; text-transform: uppercase;
  opacity: .68; text-align: center; line-height: 1.25;
}
html[lang='en'] .trove__item b { order: 2; opacity: .6; font-size: .68rem; }
html[lang='en'] .trove__item span { order: 1; font-size: .68rem; opacity: .95; }

/* Sealed / unreleased treatment */
.trove__item--sealed { gap: .5rem; padding: 1rem .35rem .8rem; cursor: default; }
.trove__item--sealed::before {
  content: ""; position: absolute; inset: 0; border-radius: 4px; pointer-events: none;
  background: repeating-linear-gradient(45deg, transparent 0 7px, rgba(11,10,15,.055) 7px 8px);
}
.trove--dark .trove__item--sealed::before {
  background: repeating-linear-gradient(45deg, transparent 0 7px, rgba(246,236,210,.05) 7px 8px);
}
.trove__wax {
  width: clamp(2.3rem, 4.6vw, 2.9rem); height: clamp(2.3rem, 4.6vw, 2.9rem);
  display: grid; place-items: center;
  font-family: var(--f-cn); font-size: clamp(1.05rem, 2.2vw, 1.35rem); line-height: 1;
  color: var(--paper); background: var(--cinnabar);
  border-radius: 50%; border: 2px solid rgba(11,10,15,.4);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), inset 0 -3px 6px rgba(11,10,15,.3);
}
.trove--dark .trove__wax { color: var(--ink); background: var(--gold); border-color: rgba(0,0,0,.5); }
.trove__item--sealed b { opacity: .48; }
.trove__item--sealed span { opacity: .5; }
.trove__item--sealed:hover .trove__wax { transform: rotate(-6deg); transition: transform .2s var(--ease); }

/* "Classified" corner stamp */
.trove__stamp {
  position: absolute; top: clamp(.8rem, 2vw, 1.3rem); right: clamp(-.4rem, 0vw, 0rem);
  z-index: 3; pointer-events: none;
  font-family: var(--f-util); font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  padding: .28em .7em; color: var(--cinnabar);
  border: 2px solid var(--cinnabar); border-radius: 3px;
  transform: rotate(7deg); opacity: .78;
}
html[data-cjk] .trove__stamp { font-family: var(--f-cn); letter-spacing: .14em; }
.trove--dark .trove__stamp { color: var(--gold); border-color: var(--gold); }

/* ============================================================
   〔陆〕Gallery
   ============================================================ */
.gallery {
  display: grid;
  gap: clamp(.9rem, 2vw, 1.5rem);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* The hero tile takes a 2x2 block; the five singles fill the rest of the 3x3 exactly. */
.shot--hero { grid-column: span 2; grid-row: span 2; aspect-ratio: auto; }
@media (max-width: 860px) {
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shot--hero { grid-column: span 2; grid-row: span 1; aspect-ratio: 16 / 9; }
}
@media (max-width: 560px) {
  .gallery { grid-template-columns: minmax(0, 1fr); }
  .shot--hero { grid-column: span 1; }
}
.shot {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  background: var(--ink);
}
.shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.shot:hover img { transform: scale(1.045); }
.shot__cap {
  position: absolute;
  left: 0; bottom: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--f-util);
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  padding: .42em .9em .32em;
  border-top-right-radius: 3px;
}
html[data-cjk] .shot__cap { font-family: var(--f-cn); letter-spacing: .1em; }

/* ============================================================
   Closing CTA + footer
   ============================================================ */
.close {
  position: relative;
  border-top: var(--bw) solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  overflow: hidden;
}
.close__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: .34;
}
/* Halftone over the gate, matching every other plate on the sheet. */
.close::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at center, rgba(11,10,15,.5) 1.5px, transparent 1.6px);
  background-size: 7px 7px;
  opacity: .45;
}
.close__inner {
  position: relative;
  z-index: 1;
  display: grid; justify-items: center; gap: 1.1rem;
  text-align: center;
  padding-block: clamp(3.5rem, 9vw, 6.5rem);
}
/* Pool of shadow so the copy holds up over the column of light behind it. */
.close__inner::before {
  content: '';
  position: absolute;
  inset: 6% -6%;
  z-index: -1;
  background: radial-gradient(ellipse 52% 58% at 50% 52%,
    rgba(11,10,15,.66) 0%, rgba(11,10,15,.38) 52%, rgba(11,10,15,0) 100%);
}
.close__title {
  font-family: var(--f-display);
  font-size: var(--fs-h2);
  line-height: .95;
  text-transform: uppercase;
  max-width: 18ch;
}
html[data-cjk] .close__title { font-family: var(--f-cn); letter-spacing: .04em; }
.close__sub { max-width: 44ch; color: rgba(246,236,210,.82); font-size: var(--fs-small); }
/* Channel row under the wishlist button. Typographic rather than a strip of brand
   logos: this sheet is set in tracked caps everywhere else, and six coloured chips
   would be the one place it stopped looking printed. */
.close__follow {
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: center;
  gap: .5rem 1.1rem;
  font-family: var(--f-util);
  font-size: clamp(.66rem, 1.5vw, .76rem);
  letter-spacing: .18em; text-transform: uppercase;
  color: rgba(246,236,210,.86);
}
.close__follow:empty { display: none; }
html[data-cjk] .close__follow { font-family: var(--f-cn); letter-spacing: .14em; }
.close__follow-label { color: rgba(246,236,210,.45); }
.close__follow a {
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding-bottom: 2px;
  transition: border-color .16s, color .16s;
}
.close__follow a:hover { border-bottom-color: var(--cinnabar); color: var(--paper); }
/* Channel names are brand words, not copy. In Chinese the surrounding text switches
   to --f-cn, whose subset carries no Latin, so left alone they would drop to
   PingFang beside a YouSheHei label. --f-util keeps Bangers on the Latin. */
html[data-cjk] .close__follow a,
html[data-cjk] .foot__links a,
html[data-cjk] .foot__copy { font-family: var(--f-util); letter-spacing: .14em; }

.foot {
  border-top: var(--bw) solid var(--ink);
  background: var(--paper);
  padding-block: 1.6rem;
}
.foot__inner {
  display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: center; justify-content: space-between;
  font-family: var(--f-util);
  font-size: .78rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft);
}
html[data-cjk] .foot__inner { font-family: var(--f-cn); letter-spacing: .1em; }
.foot__links { display: flex; flex-wrap: wrap; gap: .5rem 1.05rem; }
.foot__links a { text-decoration: none; border-bottom: 2px solid transparent; }
.foot__links a:hover { border-bottom-color: var(--cinnabar); }

/* ============================================================
   Scroll reveal
   ============================================================ */
.rise { opacity: 0; transform: translateY(26px); }
.rise.is-in {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 860px) {
  .nav__links { display: none; }
  .nav__links--compact { display: flex; }
  .nav__mark span { display: none; }

  /* The fan collapses into an honest scrolling rail */
  .fan {
    position: static;
    height: auto;
    display: flex;
    gap: .7rem;
    overflow-x: auto;
    padding: .5rem .2rem 1.2rem;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  .card {
    position: static;
    flex: 0 0 auto;
    width: 132px;
    transform: none !important;
    scroll-snap-align: center;
  }
  .card[aria-selected='true'] .card__face { box-shadow: 0 0 0 4px var(--fc), 4px 5px 0 rgba(11,10,15,.55); }
  .fan:has(.card[aria-selected='true']) .card:not([aria-selected='true']) { filter: saturate(.6); }
}
/* Short window: drop the two things that only exist to fill space, before they
   start colliding with the buttons. */
@media (max-height: 660px) {
  .hero__meta { display: none; }
  .hero__cue { display: none; }
}

@media (max-width: 560px) {
  /* Narrow crop sits higher on the plate so the sky — the part that turns red — stays in frame. */
  .hero__plate { background-position: center 30%; }
  .beat { transform: none !important; }
  .section__lede { margin-left: 0; }
  .hero__cue { display: none; }
}
/* Below this the wishlist button matters more than the section links. */
@media (max-width: 480px) {
  .nav__links--compact { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero__inner > * { animation: none; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .rise { opacity: 1; transform: none; }
}


/* ---------------------------------------------------------------
   Language picker
   Nine languages will not fit in a row of buttons, so the switcher is a
   <details> menu: it opens without JS wiring and closes on Escape for free.
   --------------------------------------------------------------- */
.langpick { position: relative; }
.langpick__btn {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .42em .7em .38em;
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  font-family: var(--f-util);
  font-size: .82rem;
  letter-spacing: .1em;
  line-height: 1;
  cursor: pointer;
  list-style: none;                 /* kill the default disclosure triangle */
  user-select: none;
}
.langpick__btn::-webkit-details-marker { display: none; }
.langpick__btn:hover { background: var(--gold); }
.langpick[open] .langpick__btn { background: var(--gold); }
.langpick__now { min-width: 1.4em; text-align: center; }

.langpick__menu {
  /* Explicit ink, not inherit: on the oracle the surrounding text is cream and
     the menu would render cream-on-cream — invisible. */
  color: var(--ink);
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  min-width: 12.5rem;
  padding: .35rem;
  margin: 0;
  border: var(--bw) solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-sm);
  list-style: none;
}
.langpick__menu button {
  display: block;
  color: var(--ink);
  width: 100%;
  padding: .5em .7em;
  border-radius: calc(var(--radius) - 4px);
  font-family: var(--f-body);
  font-size: .92rem;
  text-align: left;
  cursor: pointer;
}
.langpick__menu button:hover { background: var(--paper-2, rgba(11,10,15,.07)); }
.langpick__menu button[aria-pressed='true'] {
  background: var(--ink);
  color: var(--paper);
}

/* On the oracle the chrome is dark, so the picker has to invert. */
.oracle-nav .langpick__btn {
  border-color: rgba(246,236,210,.28);
  background: rgba(246,236,210,.06);
  color: var(--paper);
}
.oracle-nav .langpick__btn:hover,
.oracle-nav .langpick[open] .langpick__btn {
  background: rgba(246,236,210,.16);
}

/* The reading printed under the Chinese verse on a drawn lot. Hidden outright
   for zh-*, where the verse is already in the reader's language. */
.result-poem-read {
  margin-top: .5rem;
  font-family: var(--f-body);
  font-size: clamp(.84rem, 1.5vw, .95rem);
  font-style: italic;
  line-height: 1.6;
  opacity: .72;
}
