/* ============================================================
   ASTROLOGY HERO — Eleven Bodies, Heliocentric Cinematic Scene
   Modern-mystical / Dark Academia. Desktop-first.
   ============================================================ */

:root {
  /* Core palette — pushed darker for true deep-space baseline.
     The user wants 背景不夠深空; #0a0a0c read as "dark grey" rather than
     interstellar void. #050507 is on the edge of pure black but still
     leaves room for the radial gradients to render gas-cloud structure. */
  --charcoal:      #050507;
  --space-gray:    #0c0c10;
  --space-gray-2:  #14141a;
  --off-white:     #e8e6e0;
  --muted:         #8a8a92;
  --muted-dim:     #5f5f67;

  /* Per-body accents — restrained metallics */
  --tan:           #bfa176;
  --pale-gold:     #cabfa3;
  --ochre:         #93764a;
  --bronze:        #a98f63;

  /* Per-body title bloom — JS swaps to a warm/cool tint each transition */
  --title-bloom:   rgba(201, 168, 118, 0.1);

  /* Mobile legibility — JS sets this to a stronger dark shadow when the
     active body is BRIGHT (Sun) so text stays readable. */
  --legibility-shadow: 0 0 0 rgba(0, 0, 0, 0);
  /* Bright bodies (Sun) also shift muted text brighter for contrast */
  --muted-effective: var(--muted);
  --muted-dim-effective: var(--muted-dim);

  /* Layout */
  --gutter: 6vw;
  --content-width: 34%;
  /* Unified UI insets so all four corner elements (music ♪, body-nav,
     nav-hint, cta) sit at identical distances from their respective
     viewport corners. The scope-corner crosshairs sit further out at
     a separate inset — they're the OUTER frame, the UI lives inside. */
  --ui-inset-y: 4.4vh;
  --ui-inset-x: var(--gutter);
  --scope-inset: 2.0vh;
  --scope-inset-x: 2.0vw;
}

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

html,
body {
  height: 100%;
}

body {
  background: var(--charcoal);
  color: var(--off-white);
  font-family: "Jost", "Outfit", system-ui, -apple-system, sans-serif;
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow: hidden;
}

/* ----------------------------------------------------------------
   HERO SHELL
   ---------------------------------------------------------------- */
.hero {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  min-height: 600px;
  overflow: hidden;
  isolation: isolate;
  /* We own all gestures: horizontal swipes are intercepted for
     touch-scrub, and there's no scrollable surface beneath the hero. */
  touch-action: none;
  /* Drag-to-look would otherwise trigger native text selection
     (the blue highlight). The hero is a showcase, not a document
     surface — block selection on the whole shell. */
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ----------------------------------------------------------------
   BACKGROUND — deep atmospheric cosmic void (not flat)
   Pushed for "深空" (deep space) feel: near-black baseline, a tighter
   warmer pool around the focal area, and a much darker periphery.
   The previous gradient topped out at #1a1a20 which read as dark grey;
   we now keep the highlight pool low and dramatic so the eye is drawn
   into the void rather than skimming a flat gray plate.
   ---------------------------------------------------------------- */
.bg-void {
  position: absolute;
  inset: 0;
  z-index: -3;
  background:
    radial-gradient(
      ellipse 110% 88% at 62% 42%,
      #131318 0%,
      #0c0c11 32%,
      #08080c 58%,
      #050508 78%,
      #030305 100%
    );
}

/* Subtle misty nebula clouds — slightly cooler & more distributed.
   Adds a faint horizontal "ecliptic band" suggestion sweeping across
   the orbital plane (very low contrast — barely there) so the void
   feels like it has structure, not just darkness.
   The .bg-nebula layer itself breathes slowly via CSS animation,
   giving the void a faint living-dust quality. */
.bg-nebula {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    /* faint horizontal ecliptic dust band — IPS-cloud suggestion */
    radial-gradient(
      ellipse 95% 9% at 50% 55%,
      rgba(140, 124, 148, 0.055) 0%,
      rgba(96, 92, 120, 0.025) 38%,
      rgba(20, 20, 26, 0) 78%
    ),
    radial-gradient(
      ellipse 72% 52% at 52% 116%,
      rgba(96, 86, 108, 0.13) 0%,
      rgba(64, 60, 78, 0.06) 40%,
      rgba(20, 20, 26, 0) 72%
    ),
    radial-gradient(
      ellipse 54% 40% at 20% 110%,
      rgba(128, 106, 82, 0.08) 0%,
      rgba(40, 36, 40, 0) 64%
    ),
    radial-gradient(
      ellipse 50% 38% at 86% 114%,
      rgba(82, 94, 120, 0.09) 0%,
      rgba(30, 32, 40, 0) 66%
    ),
    radial-gradient(
      ellipse 46% 60% at 60% 40%,
      rgba(120, 112, 124, 0.05) 0%,
      rgba(20, 20, 26, 0) 70%
    ),
    /* upper-cool wash — pulls eye toward the void above the system */
    radial-gradient(
      ellipse 80% 50% at 50% -6%,
      rgba(48, 56, 78, 0.10) 0%,
      rgba(20, 22, 30, 0.04) 38%,
      rgba(10, 10, 14, 0) 72%
    );
  pointer-events: none;
  /* glacial breath — 24s cycle, barely perceptible. The void itself
     subtly "inhales" so the scene feels alive rather than static. */
  animation: void-breathe 24s ease-in-out infinite;
}

@keyframes void-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.78; }
}

/* ----------------------------------------------------------------
   3D STAGE — full-width heliocentric scene
   ---------------------------------------------------------------- */
.stage {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

#scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* ----------------------------------------------------------------
   COSMIC VIGNETTE — corner darkening that pulls the eye inward.
   A radial gradient layered above the 3D scene but below all UI.
   The center is fully transparent; corners darken to ~0.55 black.
   This is the most "cinematic" single CSS tweak — it instantly
   reads as "shot through a wide cinema lens" / "deep field photo".
   ---------------------------------------------------------------- */
.cosmic-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(
      ellipse 130% 100% at 50% 50%,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0) 42%,
      rgba(2, 2, 4, 0.18) 64%,
      rgba(2, 2, 4, 0.42) 84%,
      rgba(0, 0, 0, 0.62) 100%
    );
  /* layer over canvas content but under text + nav */
  mix-blend-mode: normal;
}

/* ----------------------------------------------------------------
   DRIFTING DUST MOTES — near-camera particles, slower than stars.
   Twelve tiny dots that drift extremely slowly. Distinct from the
   3D starfield: starfield says "we are in space", dust motes say
   "we are looking through space". Sells the volumetric depth.
   ---------------------------------------------------------------- */
.dust-motes {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}

.dust-motes span {
  position: absolute;
  display: block;
  width: 1.5px;
  height: 1.5px;
  background: rgba(232, 224, 204, 0.55);
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(232, 224, 204, 0.32);
  opacity: 0;
  animation: dust-drift 38s linear infinite;
}

/* Spread the twelve motes across the viewport with varied speeds + delays
   so they never feel like a synchronized swarm. Some go up-right, some
   down-left — gentle parallactic chaos. */
.dust-motes span:nth-child(1)  { top: 12%; left: 8%;  animation-duration: 42s; animation-delay: -3s;  }
.dust-motes span:nth-child(2)  { top: 38%; left: 24%; animation-duration: 56s; animation-delay: -22s; width: 1px; height: 1px; opacity-base: 0.4; }
.dust-motes span:nth-child(3)  { top: 70%; left: 14%; animation-duration: 48s; animation-delay: -11s; }
.dust-motes span:nth-child(4)  { top: 22%; left: 78%; animation-duration: 64s; animation-delay: -34s; }
.dust-motes span:nth-child(5)  { top: 58%; left: 86%; animation-duration: 52s; animation-delay: -8s; width: 1px; height: 1px; }
.dust-motes span:nth-child(6)  { top: 88%; left: 62%; animation-duration: 70s; animation-delay: -41s; }
.dust-motes span:nth-child(7)  { top: 6%;  left: 52%; animation-duration: 60s; animation-delay: -18s; }
.dust-motes span:nth-child(8)  { top: 44%; left: 56%; animation-duration: 46s; animation-delay: -29s; width: 1px; height: 1px; }
.dust-motes span:nth-child(9)  { top: 76%; left: 40%; animation-duration: 58s; animation-delay: -14s; }
.dust-motes span:nth-child(10) { top: 30%; left: 92%; animation-duration: 68s; animation-delay: -36s; width: 1px; height: 1px; }
.dust-motes span:nth-child(11) { top: 50%; left: 4%;  animation-duration: 54s; animation-delay: -25s; }
.dust-motes span:nth-child(12) { top: 84%; left: 28%; animation-duration: 50s; animation-delay: -7s;  width: 1px; height: 1px; }

@keyframes dust-drift {
  0%   { transform: translate3d(-12px, 6px, 0);  opacity: 0; }
  18%  { opacity: 0.45; }
  82%  { opacity: 0.45; }
  100% { transform: translate3d(28px, -22px, 0); opacity: 0; }
}

/* ----------------------------------------------------------------
   SCOPE FRAME — hairline corner crosshairs that suggest the viewer
   is looking through an astronomical instrument / chart frame.
   Restrained: only the four corners, single-pixel bronze hairlines,
   very low opacity. Adds 科技感 without going kitschy.
   ---------------------------------------------------------------- */
.scope-frame {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.scope-corner {
  position: absolute;
  width: 22px;
  height: 22px;
  opacity: 0.32;
}

.scope-corner::before,
.scope-corner::after {
  content: "";
  position: absolute;
  background: rgba(184, 153, 104, 0.85);
}

/* Each corner uses ::before for the horizontal stroke, ::after for vertical */
/* Place corners at the outer viewport edge — well outside the
   body-nav and CTA so they read as the instrument's outer frame.
   They are NOT positioned at the gutter (which is reserved for UI). */
.scope-corner--tl {
  top: var(--scope-inset); left: var(--scope-inset-x);
}
.scope-corner--tl::before { top: 0; left: 0; width: 22px; height: 1px; }
.scope-corner--tl::after  { top: 0; left: 0; width: 1px; height: 22px; }

.scope-corner--tr {
  top: var(--scope-inset); right: var(--scope-inset-x);
}
.scope-corner--tr::before { top: 0; right: 0; width: 22px; height: 1px; }
.scope-corner--tr::after  { top: 0; right: 0; width: 1px; height: 22px; }

.scope-corner--bl {
  bottom: var(--scope-inset); left: var(--scope-inset-x);
}
.scope-corner--bl::before { bottom: 0; left: 0; width: 22px; height: 1px; }
.scope-corner--bl::after  { bottom: 0; left: 0; width: 1px; height: 22px; }

.scope-corner--br {
  bottom: var(--scope-inset); right: var(--scope-inset-x);
}
.scope-corner--br::before { bottom: 0; right: 0; width: 22px; height: 1px; }
.scope-corner--br::after  { bottom: 0; right: 0; width: 1px; height: 22px; }

/* ----------------------------------------------------------------
   FILM GRAIN — SVG-noise overlay at extremely low opacity. Adds
   cinematic texture so the void doesn't read as flat digital black.
   Animated very subtly so it feels like emulsion, not a static PNG.
   ---------------------------------------------------------------- */
.film-grain {
  position: absolute;
  inset: -10%;
  z-index: 3;
  pointer-events: none;
  opacity: 0.08;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.92  0 0 0 0 0.9  0 0 0 0 0.86  0 0 0 0.95 0'/></filter><rect width='200' height='200' filter='url(%23n)'/></svg>");
  background-size: 200px 200px;
  animation: grain-shift 7s steps(7) infinite;
}

@keyframes grain-shift {
  0%   { transform: translate3d(0, 0, 0); }
  10%  { transform: translate3d(-3%, -2%, 0); }
  20%  { transform: translate3d(2%, 3%, 0); }
  30%  { transform: translate3d(-1%, 2%, 0); }
  40%  { transform: translate3d(3%, -1%, 0); }
  50%  { transform: translate3d(-2%, 3%, 0); }
  60%  { transform: translate3d(1%, -2%, 0); }
  70%  { transform: translate3d(-3%, 1%, 0); }
  80%  { transform: translate3d(2%, -3%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* Subtle bottom-edge vignette so text near the bottom of the column
   has a soft anchor against the planet behind it. Much less aggressive
   than the old "stage-mist". */
.stage-mist {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 32%;
  background: linear-gradient(
    to top,
    rgba(8, 8, 10, 0.78) 0%,
    rgba(11, 11, 14, 0.42) 32%,
    rgba(14, 14, 18, 0.18) 64%,
    rgba(20, 20, 26, 0) 100%
  );
  pointer-events: none;
}

/* ----------------------------------------------------------------
   TEXT CONTENT — left third, generous negative space
   ---------------------------------------------------------------- */
.content {
  position: absolute;
  top: 50%;
  left: var(--gutter);
  transform: translateY(-50%);
  width: var(--content-width);
  max-width: 460px;
  z-index: 2;
  /* fade-only swap — used during camera flights */
  transition: opacity 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}

.content.swapping {
  opacity: 0;
}

/* ----------------------------------------------------------------
   TYPEWRITER REVEAL — per-character decode used by JS typewriteInto()
   on body-switch text swap. Pre-filled spans reserve final layout;
   visibility toggles per character. .tw-glitch flickers briefly while
   resolving — pale bronze tint + soft halo evoke a scope/cipher
   readout without breaking the per-body bloom colour assigned to
   parent elements.
   ---------------------------------------------------------------- */
.tw-char {
  /* Inherit everything from parent — no positioning, no display
     change, no transitions. Plain inline glyphs that wrap natively. */
}
.tw-glitch {
  color: var(--bronze, #b89968);
  text-shadow:
    var(--legibility-shadow),
    0 0 8px rgba(184, 153, 104, 0.55);
  opacity: 0.85;
}

.eyebrow {
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.38em;
  text-transform: uppercase;
  color: var(--muted-effective);
  margin-bottom: 1.45rem;
  padding-left: 0.04em;
  /* Legibility shadow — JS sets --legibility-shadow to a strong black
     halo when the active body is bright (Sun). On dim bodies it's a
     no-op transparent shadow. */
  text-shadow: var(--legibility-shadow);
  /* NOTE: no transition on `color` — when --muted-effective changes via
     JS, Chrome's transition resolves the var once and never re-evaluates,
     leaving the color stuck. Content's swapping opacity covers the swap. */
}

.eyebrow-sep {
  color: var(--bronze);
  margin: 0 0.5em;
  opacity: 0.7;
}

.title {
  font-size: clamp(4.6rem, 9.6vw, 9rem);
  font-weight: 200;
  line-height: 0.94;
  letter-spacing: -0.018em;
  color: var(--off-white);
  margin-bottom: 0.7rem;
  /* per-body bloom tint — JS swaps --title-bloom per body. PLUS a
     conditional legibility halo for bright bodies (Sun). We do NOT
     transition text-shadow because Chrome resolves the var once. */
  text-shadow:
    var(--legibility-shadow),
    0 0 30px var(--title-bloom),
    0 0 70px var(--title-bloom);
}

.subtitle {
  font-size: clamp(0.9rem, 1.3vw, 1.12rem);
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #a4a199;
  margin-bottom: 3.2rem;
  text-shadow: var(--legibility-shadow);
}

/* Hairline above the specs block */
.specs {
  position: relative;
  padding-top: 1.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.95rem;
}

.specs::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 46px;
  height: 1px;
  background: linear-gradient(to right, var(--bronze), rgba(184, 153, 104, 0));
}

.spec {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  font-size: 0.78rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.spec-label {
  flex: 0 0 110px;
  color: var(--muted-dim-effective);
  font-weight: 400;
  text-shadow: var(--legibility-shadow);
  /* no transition on color — see note on .eyebrow */
}

.spec-value {
  color: var(--pale-gold);
  font-weight: 400;
  letter-spacing: 0.1em;
  flex: 1;
  min-width: 0;
  text-shadow: var(--legibility-shadow);
}

/* ----------------------------------------------------------------
   TOP-RIGHT BODY NAVIGATION INDICATOR (11 ticks + counter)
   ---------------------------------------------------------------- */
.body-nav {
  position: absolute;
  top: var(--ui-inset-y);
  right: var(--ui-inset-x);
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.55rem;
  font-family: inherit;
  /* Hairline frame around the indicator — sells "instrument readout" */
  padding: 0.55rem 0.85rem 0.5rem;
  border: 1px solid rgba(184, 153, 104, 0.16);
  border-radius: 1px;
  background:
    linear-gradient(135deg,
      rgba(8, 8, 10, 0.32) 0%,
      rgba(8, 8, 10, 0.12) 100%
    );
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  /* Corner tick marks via ::before / ::after for cosmic-chart edge */
}

.body-nav::before,
.body-nav::after {
  content: "";
  position: absolute;
  width: 7px;
  height: 7px;
  border: 1px solid rgba(184, 153, 104, 0.55);
}

.body-nav::before {
  top: -1px;
  left: -1px;
  border-right: 0;
  border-bottom: 0;
}

.body-nav::after {
  bottom: -1px;
  right: -1px;
  border-left: 0;
  border-top: 0;
}

/* Row that holds the active planetary glyph + the 11 ticks.
   Glyph sits LEFT of the ticks so its centerline aligns with the
   tick row's horizontal midline — reads as "instrument current-reading
   on the left, scale on the right". */
.nav-glyph-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

/* Active planetary glyph — large bronze symbol that replaces the
   "08 / 11"-style counter. Astrological / planetary Unicode glyph
   (☉ ☽ ☿ ♀ ♁ ♂ ♃ ♄ ♅ ♆ ♇) selected per body via JS. Reads as a
   mystical instrument readout, not a date. */
.nav-glyph {
  display: inline-block;
  font-family: "Noto Serif TC", "Apple Symbols", "Segoe UI Symbol", serif;
  font-size: 1.15rem;
  line-height: 1;
  color: var(--pale-gold);
  opacity: 0.92;
  text-shadow: 0 0 12px rgba(202, 191, 163, 0.38);
  letter-spacing: 0;
  /* Reserve width so per-body glyph swaps don't shift the tick row */
  min-width: 1.25em;
  text-align: center;
  /* Subtle fade on body change — JS toggles .is-swapping to drop
     opacity during the camera flight so the glyph feels like part
     of the readout decoding alongside the typewriter text. */
  transition: opacity 0.45s ease, color 0.45s ease, text-shadow 0.45s ease;
}

.nav-glyph.is-swapping {
  opacity: 0.42;
}

.nav-ticks {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0;
  margin: 0;
}

.nav-ticks li {
  display: block;
}

.nav-tick {
  display: block;
  appearance: none;
  background: none;
  border: 0;
  padding: 8px 0 14px;
  margin: 0;
  cursor: pointer;
  position: relative;
  color: inherit;
  pointer-events: auto;
}

.nav-tick::before {
  content: "";
  display: block;
  width: 14px;
  height: 1px;
  background: var(--off-white);
  opacity: 0.32;
  transition:
    background 0.45s ease,
    width 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.45s ease,
    box-shadow 0.45s ease,
    height 0.45s ease;
}

.nav-tick:hover::before {
  opacity: 0.78;
}

.nav-tick.active::before {
  width: 30px;
  height: 1.5px;
  background: var(--pale-gold);
  opacity: 1;
  box-shadow: 0 0 8px rgba(202, 191, 163, 0.42);
}

/* Small diamond marker sits BELOW the active tick. Replaces the
   numeric "01 / 11" counter — a hairline diamond signals "you are
   here" without naming a number. Drawn via a rotated 4px square
   so it stays crisp at any DPR. */
.nav-tick::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 2px;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  background: var(--pale-gold);
  transform: rotate(45deg) scale(0);
  transform-origin: 50% 50%;
  opacity: 0;
  box-shadow: 0 0 6px rgba(202, 191, 163, 0.55);
  transition:
    transform 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.45s ease;
  pointer-events: none;
}

.nav-tick.active::after {
  transform: rotate(45deg) scale(1);
  opacity: 1;
}

.nav-tick:focus-visible {
  outline: none;
}

.nav-tick:focus-visible::before {
  outline: 1px solid var(--bronze);
  outline-offset: 4px;
}

/* Bilingual readout — Chinese distance on top row, English
   supplement underneath. Counter removed; the planetary glyph
   (top-left of this block) is the body identifier and the active
   tick's diamond marker is the position-on-scale indicator. */
.nav-readout {
  margin-top: 0.25rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

.nav-readout p {
  white-space: nowrap;
  display: block;
}

.nav-readout-zh {
  font-family: "Noto Serif TC", serif;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.18em;
  color: var(--off-white);
}

.nav-readout-en {
  font-family: "Jost", sans-serif;
  font-size: 0.54rem;
  font-weight: 300;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted-effective);
  opacity: 0.55;
  margin-top: 0.42rem;
}

.nav-readout-divider {
  margin: 0 0.55em;
  color: var(--bronze);
  opacity: 0.5;
}

.nav-distance-label {
  color: var(--muted);
  opacity: 0.86;
}

.nav-distance-label-en {
  color: var(--muted-effective);
  opacity: 0.65;
}

.nav-distance-value,
.nav-distance-value-en {
  color: var(--pale-gold);
  font-weight: 400;
  display: inline-block;
  min-width: 3.4ch;
  text-align: right;
}

.nav-distance-value-en {
  color: var(--pale-gold);
  opacity: 0.78;
}

.nav-distance-unit {
  margin-left: 0.32em;
  font-size: 0.86em;
  letter-spacing: 0.28em;
  color: var(--muted);
  opacity: 0.7;
}

.nav-distance-unit-en {
  margin-left: 0.32em;
  font-size: 1em;
  letter-spacing: 0.32em;
}

/* When focused on the Sun, hide the AU unit (and the label dividers
   collapse) — distance becomes a dimensionless "—" at the center. */
.nav-readout.is-center .nav-distance-unit,
.nav-readout.is-center .nav-distance-unit-en {
  display: none;
}

/* Geocentric sign tag — appended after the ecliptic-longitude value.
   ZH uses the same Noto Serif weight as the rest of the readout but
   slightly lifted in colour to read as the "current sign" call-out.
   EN is the compact tracked uppercase to match the rest of the line. */
.nav-sign-zh {
  color: var(--pale-gold);
  font-weight: 400;
  opacity: 0.88;
}
.nav-sign-en {
  color: var(--pale-gold);
  opacity: 0.72;
  letter-spacing: 0.30em;
}
/* When focused on Earth ("觀者之位"), the sign tag carries the
   contemplative copy on its own — gently demote the placeholder
   numeric value so it sits as a quiet "—" rather than a dash chart. */
.nav-readout.is-center .nav-sign-zh,
.nav-readout.is-center .nav-sign-en {
  color: var(--bronze);
  opacity: 0.92;
}

/* ----------------------------------------------------------------
   NAVIGATION HINT — fades out after first interaction
   ---------------------------------------------------------------- */
.nav-hint {
  position: absolute;
  left: var(--ui-inset-x);
  /* Sits ABOVE the .time-mode buttons so both share the bottom-left
     corner without colliding. ~3.6rem above the standard UI inset. */
  bottom: calc(var(--ui-inset-y) + 3.6rem);
  z-index: 2;
  font-size: 0.62rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--muted-dim);
  opacity: 0;
  transition: opacity 1.4s ease;
  pointer-events: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  font-weight: 400;
  /* small leading bronze tick — sells "instrument label" */
  padding-left: 14px;
}

.nav-hint::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 8px;
  height: 1px;
  background: linear-gradient(to right, var(--bronze), rgba(184, 153, 104, 0.15));
  transform: translateY(-50%);
  opacity: 0.7;
}

.is-ready .nav-hint {
  opacity: 0.72;
  transition-delay: 2.2s;
  animation: hint-breathe 4.6s ease-in-out infinite;
  animation-delay: 3.4s;
}

.nav-hint.is-hidden {
  opacity: 0 !important;
  transition-delay: 0s;
  animation: none;
}

.nav-hint-sep {
  margin: 0 0.7em;
  opacity: 0.42;
}

.nav-hint-key {
  color: var(--muted);
}

.nav-hint-tail {
  margin-left: 0.65em;
  opacity: 0.78;
}

@keyframes hint-breathe {
  0%, 100% { opacity: 0.72; }
  50% { opacity: 0.5; }
}

/* ----------------------------------------------------------------
   CTA — near bottom-right
   ---------------------------------------------------------------- */
.cta {
  position: absolute;
  right: var(--ui-inset-x);
  bottom: var(--ui-inset-y);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0.6rem 0.85rem 0.6rem 0.2rem;
  transition: color 0.5s ease;
}

/* Refined CTA bracket — bronze hairline corner ticks on the right edge,
   suggesting the link extends into another interface. Adds 科技感. */
.cta::before {
  content: "";
  position: absolute;
  right: 0;
  top: 0.4rem;
  width: 8px;
  height: 1px;
  background: rgba(184, 153, 104, 0.55);
}

.cta::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0.4rem;
  width: 8px;
  height: 1px;
  background: rgba(184, 153, 104, 0.55);
}

.cta-text {
  position: relative;
  padding-bottom: 3px;
}

.cta-text::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(to right, var(--bronze), rgba(184, 153, 104, 0.15));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}

.cta-arrow {
  color: var(--bronze);
  font-size: 0.9rem;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.cta:hover {
  color: var(--off-white);
}

.cta:hover .cta-text::after {
  transform: scaleX(1);
}

.cta:hover .cta-arrow {
  transform: translateX(5px);
}

.cta:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 6px;
}

/* ----------------------------------------------------------------
   ENTRANCE MOTION — staggered reveal (initial load only)
   ---------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(14px);
}

.is-ready .reveal {
  animation: reveal-in 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.is-ready .reveal[data-reveal="1"] { animation-delay: 0.25s; }
.is-ready .reveal[data-reveal="2"] { animation-delay: 0.34s; }
.is-ready .reveal[data-reveal="3"] { animation-delay: 0.62s; }
.is-ready .reveal[data-reveal="4"] { animation-delay: 0.86s; }
.is-ready .reveal[data-reveal="5"] { animation-delay: 1.25s; }

@keyframes reveal-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Canvas fades + scales in (driven by JS adding .stage-in). */
#scene {
  opacity: 0;
  transform: scale(1.04);
  transition:
    opacity 2.1s ease,
    transform 2.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.stage.stage-in #scene {
  opacity: 1;
  transform: scale(1);
}

/* ----------------------------------------------------------------
   RESPONSIVE — graceful degradation
   ---------------------------------------------------------------- */

/* Large tablet / small laptop */
@media (max-width: 1100px) {
  :root {
    --content-width: 42%;
    --gutter: 5vw;
  }
}

/* Tablet portrait & mobile — text overlaid w/ scrim. */
@media (max-width: 900px) {
  :root {
    --gutter: 7vw;
    /* Mobile: tighten vertical inset so the indicator + music ♪
       sit closer to the top edge without crowding the rounded
       safe-area. Bottom row keeps generous breathing room. */
    --ui-inset-y: 3.0vh;
  }

  /* Legibility scrim behind the text column.
     Constrain BOTH top and bottom so the .content column can never
     drift up into the top-right body-nav frame. The body-nav is
     ~90–110 px tall at this breakpoint; 8.2 rem (≈131 px) gives a
     consistent ~1 rem clearance below it on every phone size. */
  .content {
    top: calc(var(--ui-inset-y) + 8.2rem);
    bottom: 22vh;
    transform: none;
    width: 86%;
    max-width: none;
  }

  .content::before {
    content: "";
    position: absolute;
    inset: -160% -50% -25% -50%;
    z-index: -1;
    background: linear-gradient(
      to bottom,
      rgba(8, 8, 10, 0) 0%,
      rgba(8, 8, 10, 0.02) 30%,
      rgba(8, 8, 10, 0.08) 48%,
      rgba(8, 8, 10, 0.18) 58%,
      rgba(8, 8, 10, 0.32) 66%,
      rgba(8, 8, 10, 0.5) 73%,
      rgba(8, 8, 10, 0.68) 80%,
      rgba(8, 8, 10, 0.82) 86%,
      rgba(8, 8, 10, 0.9) 92%,
      rgba(8, 8, 10, 0.84) 96%,
      rgba(8, 8, 10, 0.36) 99%,
      rgba(8, 8, 10, 0) 100%
    );
    pointer-events: none;
  }

  /* Text shadow boost for bright bodies (Sun) — JS toggles this on. */
  .eyebrow,
  .title,
  .subtitle,
  .spec-label,
  .spec-value {
    text-shadow: var(--legibility-shadow);
  }
  .title {
    text-shadow:
      var(--legibility-shadow),
      0 0 30px var(--title-bloom),
      0 0 70px var(--title-bloom);
  }

  .title {
    font-size: clamp(3.6rem, 17vw, 6rem);
  }

  .eyebrow {
    margin-bottom: 1.4rem;
    letter-spacing: 0.3em;
  }

  .subtitle {
    margin-bottom: 2.1rem;
  }

  .cta {
    right: auto;
    left: var(--ui-inset-x);
    bottom: 7vh;
  }

  /* hint moves above CTA on mobile so they don't overlap */
  .nav-hint {
    left: var(--ui-inset-x);
    bottom: calc(7vh + 2.6rem);
  }

  .body-nav {
    top: var(--ui-inset-y);
    right: var(--ui-inset-x);
  }

  .stage-mist {
    height: 48%;
    background: linear-gradient(
      to top,
      rgba(8, 8, 10, 0.92) 0%,
      rgba(11, 11, 14, 0.68) 26%,
      rgba(14, 14, 18, 0.36) 56%,
      rgba(18, 18, 24, 0.14) 80%,
      rgba(20, 20, 26, 0) 100%
    );
  }

  .spec-label {
    flex: 0 0 96px;
  }
}

@media (max-width: 400px) {
  .spec-label {
    flex-basis: 84px;
  }
  .spec {
    font-size: 0.72rem;
  }
  /* 11 ticks at narrow widths: tighter gap + slimmer ticks. */
  .nav-ticks {
    gap: 5px;
  }
  .nav-tick::before {
    width: 9px;
  }
  .nav-tick.active::before {
    width: 18px;
  }
}

/* Short phones (< 720 px height) — typical iPhone SE 1st-gen,
   old Android 360×640, anything where the body-nav + full content
   + nav-hint + timeline can't all coexist without overlap. We give
   .content more vertical room and trade the nav-hint for breathing
   space; the hint is most useful on first visit and the timeline
   capsule is more discoverable visually anyway. */
@media (max-height: 720px) {
  .nav-hint { display: none; }
  .content {
    /* Was 22vh — reclaim 6vh for body text so specs don't bleed
       into the bottom UI band. */
    bottom: 16vh;
  }
  .title {
    font-size: clamp(2.9rem, 14vw, 4.6rem);
  }
  .eyebrow {
    margin-bottom: 0.9rem;
  }
  .subtitle {
    margin-bottom: 1.4rem;
  }
  .specs {
    gap: 0.6rem;
  }
  /* Body-extras pill — compress on short viewports so the moon-
     phase / retrograde badge doesn't bleed into the timeline. */
  .body-extras {
    margin-top: 0.5rem;
  }
  .extra-row {
    padding: 0.3rem 0.65rem 0.3rem 0.55rem;
    gap: 0.45rem;
  }
  .extra-glyph {
    width: 14px;
    height: 14px;
  }
  .extra-glyph svg {
    width: 12px;
    height: 12px;
  }
  .extra-label-zh { font-size: 0.6rem; }
  .extra-label-en { font-size: 0.38rem; letter-spacing: 0.24em; }
  .extra-meta { display: none; }
}

/* Extreme-short viewport — old Android 360×640 / 350×600, anything
   under 660 px tall. There's literally no room for the body-extras
   pill between the specs row and the timeline capsule, so hide it.
   The pill is a nice-to-have; the specs + timeline are essential.
   iPhone SE 2nd gen (667 tall) is just above the cutoff and keeps
   the pill. */
@media (max-height: 660px) {
  .body-extras { display: none !important; }
}

/* Landscape phones — short height (≤ 520) + wide aspect ratio.
   Treats the right half of the viewport as the scene stage and the
   left half as a compact text column anchored to the left edge.
   Body-nav stays top-right, timeline + music stay bottom-left.
   Eyebrow EN + subtitle hidden because they duplicate info and
   waste valuable vertical space. */
/* Landscape phone block was here but moved to end-of-file so it
   wins the cascade against later .title-zh / .spec-*-zh rules. */

/* ----------------------------------------------------------------
   REDUCED MOTION — static, composed
   ---------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    animation: none !important;
  }
  #scene {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .cta-text::after,
  .cta-arrow {
    transition: none;
  }
  /* instant swap on body change */
  .content {
    transition: opacity 0.12s ease;
  }
  .nav-tick::before {
    transition: none;
  }
  .nav-hint {
    transition: opacity 0.2s ease;
    animation: none !important;
  }
  .is-ready .nav-hint {
    animation: none !important;
  }
  .music-toggle {
    transition: none;
  }
  .title-zh, .title-en,
  .eyebrow-zh, .eyebrow-en,
  .subtitle-zh, .subtitle-en,
  .spec-label-zh, .spec-label-en,
  .spec-value-zh, .spec-value-en {
    transition: none;
  }
}

/* ============================================================
   BILINGUAL TYPOGRAPHY
   中文 primary (Noto Serif TC, refined editorial serif)
   English supplementary (Jost, smaller / tracked / low-opacity)
   ============================================================ */

/* Container resets — children own typography so the cascade is clean */
.eyebrow {
  /* keep margin + color + shadow from above; reset typographic props */
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: normal;
  text-transform: none;
  padding-left: 0;
}

.title {
  display: block;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: normal;
  line-height: 1.0;
  /* color + text-shadow stay from .title rule above; inherit to children */
}

.subtitle {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: normal;
  text-transform: none;
}

.spec-label,
.spec-value {
  font-size: inherit;
  letter-spacing: normal;
  text-transform: none;
}

/* —— EYEBROW —— */
.eyebrow-zh {
  display: block;
  font-family: "Noto Serif TC", "Songti TC", "PMingLiU", serif;
  font-weight: 300;
  font-size: 0.92rem;
  letter-spacing: 0.22em;
  color: var(--off-white);
  opacity: 0.86;
}

.eyebrow-en {
  display: block;
  margin-top: 0.85rem;
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: 0.62rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted-effective);
  opacity: 0.58;
}

/* —— TITLE (huge serif Chinese + small tracked English) —— */
.title-zh {
  display: block;
  font-family: "Noto Serif TC", "Songti TC", "PMingLiU", serif;
  font-weight: 300;
  font-size: clamp(4.0rem, 9.0vw, 8.4rem);
  letter-spacing: 0.06em;
  line-height: 1.0;
}

.title-en {
  display: block;
  margin-top: 1.4rem;
  font-family: "Jost", sans-serif;
  font-weight: 200;
  font-size: clamp(0.82rem, 1.05vw, 1.05rem);
  letter-spacing: 0.46em;
  text-transform: uppercase;
  opacity: 0.6;
}

/* —— SUBTITLE —— */
.subtitle-zh {
  display: block;
  font-family: "Noto Serif TC", serif;
  font-weight: 300;
  font-size: clamp(0.94rem, 1.18vw, 1.06rem);
  letter-spacing: 0.22em;
  color: #c1bdb3;
}

.subtitle-en {
  display: block;
  margin-top: 0.85rem;
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: clamp(0.66rem, 0.84vw, 0.78rem);
  letter-spacing: 0.36em;
  text-transform: uppercase;
  opacity: 0.55;
}

/* —— SPEC ROWS (label + value, both with stacked zh + en) —— */
.spec-label-zh {
  display: block;
  font-family: "Noto Serif TC", serif;
  font-weight: 400;
  font-size: 0.86rem;
  letter-spacing: 0.18em;
  line-height: 1.35;
}

.spec-label-en {
  display: block;
  margin-top: 0.55rem;
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: 0.58rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  opacity: 0.5;
  line-height: 1.4;
}

.spec-value-zh {
  display: block;
  font-family: "Noto Serif TC", serif;
  font-weight: 400;
  font-size: 0.86rem;
  letter-spacing: 0.08em;
  line-height: 1.4;
}

.spec-value-en {
  display: block;
  margin-top: 0.55rem;
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.5;
  line-height: 1.45;
}

/* Specs row should align tops (both label and value start with the
   zh line; let them stretch by content rather than baseline-snap). */
.spec {
  align-items: flex-start;
}

/* —— RESPONSIVE — tighter on mobile / portrait tablet —— */
@media (max-width: 900px) {
  .title-zh {
    font-size: clamp(3.0rem, 16vw, 5.2rem);
    letter-spacing: 0.04em;
  }
  .title-en {
    margin-top: 0.4rem;
    font-size: clamp(0.72rem, 2.4vw, 0.92rem);
    letter-spacing: 0.4em;
  }
  .eyebrow-zh { font-size: 0.86rem; letter-spacing: 0.2em; }
  .eyebrow-en { font-size: 0.56rem; letter-spacing: 0.3em; }
  .subtitle-zh { font-size: 0.96rem; }
  .subtitle-en { font-size: 0.66rem; }
  .spec-label-zh,
  .spec-value-zh { font-size: 0.78rem; }
  .spec-label-en,
  .spec-value-en { font-size: 0.54rem; }
}

/* ============================================================
   AMBIENT MUSIC TOGGLE — top-left corner, hairline-bronze
   ============================================================ */
.music-toggle {
  position: absolute;
  top: var(--ui-inset-y);
  left: var(--ui-inset-x);
  z-index: 10;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid rgba(184, 153, 104, 0.22);
  border-radius: 50%;
  background: rgba(8, 8, 10, 0.42);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  transition:
    color 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    border-color 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  font-family: inherit;
}

.music-toggle:hover,
.music-toggle:focus-visible {
  color: var(--off-white);
  border-color: rgba(184, 153, 104, 0.55);
  background: rgba(14, 14, 18, 0.62);
}

.music-toggle:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 4px;
}

.music-icon {
  display: block;
  width: 1em;
  height: 1em;
  font-size: 13px;
  line-height: 1;
  text-align: center;
  letter-spacing: 0;
}

.music-icon::before {
  content: "\266A"; /* eighth note ♪ — paused */
  display: block;
  transform: translateY(-0.5px);
}

.music-toggle[aria-pressed="true"] {
  color: var(--bronze);
  border-color: rgba(184, 153, 104, 0.65);
}

.music-toggle[aria-pressed="true"] .music-icon::before {
  content: "\266B"; /* beamed eighth notes ♫ — playing */
  animation: music-pulse 4.2s ease-in-out infinite;
}

@keyframes music-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}

/* =================================================================
   VIEW-MODE LINK — bridge to the geocentric companion site
   -----------------------------------------------------------------
   Sits directly under the ♪ music-toggle in the top-left cluster.
   Slim bronze capsule with "current side" highlighted and "other
   side" muted, divided by a small ↔ glyph. Bilingual stack inside
   each side mirrors the rest of the design system.
   ================================================================= */
.view-mode-link {
  position: absolute;
  top: calc(var(--ui-inset-y) + 44px);  /* under music-toggle (34px + 10px gap) */
  left: var(--ui-inset-x);
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  padding: 0.28rem 0.55rem 0.3rem;
  border: 1px solid rgba(184, 153, 104, 0.22);
  border-radius: 999px;
  background: rgba(8, 8, 10, 0.42);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  text-decoration: none;
  font-family: inherit;
  transition:
    border-color 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  /* Fade-in with the rest of the UI */
  opacity: 0;
  transform: translateY(4px);
  transition-property: opacity, transform, border-color, background;
  transition-duration: 1s, 1s, 0.45s, 0.45s;
  transition-delay: 1.8s, 1.8s, 0s, 0s;
}
.is-ready .view-mode-link {
  opacity: 1;
  transform: translateY(0);
}
.view-mode-link:hover,
.view-mode-link:focus-visible {
  border-color: rgba(184, 153, 104, 0.55);
  background: rgba(14, 14, 18, 0.62);
}
.view-mode-link:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 3px;
}
.view-mode-current,
.view-mode-other {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.06rem;
  line-height: 1;
}
.view-mode-current .view-mode-zh {
  color: var(--pale-gold);
}
.view-mode-current .view-mode-en {
  color: var(--bronze);
  opacity: 0.85;
}
.view-mode-other .view-mode-zh {
  color: var(--muted);
}
.view-mode-other .view-mode-en {
  color: var(--muted-dim);
}
.view-mode-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.58rem;
  letter-spacing: 0.16em;
}
.view-mode-en {
  font-family: "Jost", sans-serif;
  font-size: 0.38rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
}
.view-mode-divider {
  color: var(--bronze);
  font-size: 0.6rem;
  opacity: 0.65;
  line-height: 1;
}
.view-mode-link:hover .view-mode-other .view-mode-zh {
  color: var(--off-white);
}
.view-mode-link:hover .view-mode-other .view-mode-en {
  color: var(--bronze);
  opacity: 0.85;
}

/* Mobile: tighten position so it sits neatly above the planet/text.
   Vertical inset is shared with .body-nav via --ui-inset-y so
   ♪ and the body-nav read as one row at the top of the frame. */
@media (max-width: 900px) {
  .music-toggle {
    top: var(--ui-inset-y);
    left: var(--ui-inset-x);
    width: 32px;
    height: 32px;
  }
}

/* ============================================================
   TIME-MODE PICKER — bottom-left
   Three radio buttons that swap the planetary configuration:
     模擬 / Sim     — compressed dynamic simulation (default)
     此刻 / Now     — snap to current real astronomical positions
     三月後 / +3 mo — snap to positions 90 days from now
   Bilingual stacked labels (Noto Serif TC + tracked Jost EN) and
   bronze hairline underline for the active radio. Matches the
   cosmic-instrument chrome of scope corners, nav-readout, etc.
   ============================================================ */
/* =================================================================
   TIMELINE — collapsed-by-default capsule (bottom-left)
   -----------------------------------------------------------------
   Default state: a slim handle button shows current epoch + mode chip
   so the timeline never blocks the scene. Tap to expand the full
   panel (slider, presets, date input, natal row) above the handle.
   ================================================================= */
.timeline {
  position: absolute;
  left: var(--ui-inset-x);
  bottom: var(--ui-inset-y);
  z-index: 3;
  width: fit-content;
  max-width: calc(100vw - var(--ui-inset-x) * 2);
  display: block;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 1s ease 1.6s, transform 1s ease 1.6s;
}
.is-ready .timeline {
  opacity: 1;
  transform: translateY(0);
}

.timeline-handle {
  appearance: none;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 14, 0.62) 0%,
    rgba(8, 8, 12, 0.78) 100%
  );
  border: 1px solid rgba(184, 153, 104, 0.32);
  border-radius: 999px;
  padding: 0.4rem 0.5rem 0.4rem 0.85rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: auto;
  font-family: inherit;
  color: var(--off-white);
  text-align: left;
  backdrop-filter: blur(10px) saturate(112%);
  -webkit-backdrop-filter: blur(10px) saturate(112%);
  box-shadow:
    inset 0 0 0 1px rgba(184, 153, 104, 0.04),
    0 8px 24px -14px rgba(0, 0, 0, 0.7);
  transition:
    border-color 0.4s ease,
    background 0.4s ease,
    box-shadow 0.4s ease;
}
.timeline-handle:hover,
.timeline-handle:focus-visible {
  border-color: rgba(184, 153, 104, 0.42);
  box-shadow:
    inset 0 0 0 1px rgba(184, 153, 104, 0.08),
    0 8px 28px -12px rgba(184, 153, 104, 0.25);
}
.timeline-handle:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 3px;
}
.timeline[data-state="open"] .timeline-handle {
  border-color: var(--bronze);
  background: linear-gradient(
    180deg,
    rgba(28, 22, 14, 0.62) 0%,
    rgba(10, 9, 14, 0.82) 100%
  );
}
.timeline-caret {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--bronze);
  opacity: 0.7;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
}
.timeline[data-state="open"] .timeline-caret {
  transform: rotate(180deg);
  opacity: 1;
}

.timeline-date {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  flex: 1;
  min-width: 0;
}
.timeline-date-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-weight: 400;
  font-size: 0.84rem;
  letter-spacing: 0.08em;
  color: var(--pale-gold);
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.timeline-date-en {
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: 0.5rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--muted-dim);
  line-height: 1.1;
}
.timeline-mode-chip {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.08rem;
  padding: 0.18rem 0.46rem 0.2rem;
  border: 1px solid rgba(184, 153, 104, 0.28);
  border-radius: 999px;
  background: rgba(14, 14, 18, 0.4);
  flex-shrink: 0;
  transition: border-color 0.45s ease, background 0.45s ease;
}
.timeline-mode-chip[data-mode="snap"] {
  border-color: var(--bronze);
  background: rgba(28, 22, 14, 0.55);
}
.timeline-mode-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  color: var(--off-white);
  line-height: 1;
}
.timeline-mode-en {
  font-family: "Jost", sans-serif;
  font-size: 0.4rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--bronze);
  opacity: 0.85;
  line-height: 1;
}

/* =================================================================
   TIMELINE PANEL — expandable controls cluster
   -----------------------------------------------------------------
   Floats above the handle as an absolutely-positioned dropdown,
   so the collapsed timeline footprint stays at handle width and
   the panel can be wider than the handle without breaking layout.
   ================================================================= */
.timeline-panel {
  position: absolute;
  bottom: calc(100% + 0.45rem);
  left: 0;
  width: clamp(248px, 30vw, 340px);
  max-width: calc(100vw - var(--ui-inset-x) * 2);
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 0 0.85rem;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(10, 9, 14, 0.42) 0%,
    rgba(8, 8, 12, 0.62) 100%
  );
  border: 1px solid rgba(184, 153, 104, 0);
  border-radius: 4px;
  backdrop-filter: blur(8px) saturate(108%);
  -webkit-backdrop-filter: blur(8px) saturate(108%);
  box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.65);
  transition:
    max-height 0.6s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.4s ease 0.05s,
    border-color 0.4s ease,
    padding 0.4s ease;
}
.timeline[data-state="open"] .timeline-panel {
  max-height: 480px;
  opacity: 1;
  pointer-events: auto;
  border-color: rgba(184, 153, 104, 0.18);
  padding: 0.7rem 0.85rem 0.75rem;
}
.timeline[data-state="open"] .timeline-panel > *:first-child {
  margin-top: 0;
}

.timeline-slider {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 22px;
  background: transparent;
  cursor: pointer;
  margin: 0;
  padding: 0;
}
.timeline-slider::-webkit-slider-runnable-track {
  height: 2px;
  background: linear-gradient(
    to right,
    rgba(184, 153, 104, 0.05) 0%,
    rgba(184, 153, 104, 0.32) 50%,
    rgba(184, 153, 104, 0.05) 100%
  );
  border-radius: 1px;
}
.timeline-slider::-moz-range-track {
  height: 2px;
  background: linear-gradient(
    to right,
    rgba(184, 153, 104, 0.05) 0%,
    rgba(184, 153, 104, 0.32) 50%,
    rgba(184, 153, 104, 0.05) 100%
  );
  border-radius: 1px;
}
.timeline-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  background: var(--pale-gold);
  border: 1px solid var(--bronze);
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(184, 153, 104, 0.1), 0 0 12px rgba(184, 153, 104, 0.22);
  margin-top: -5px;
  transition: box-shadow 0.3s ease, transform 0.2s ease;
}
.timeline-slider::-webkit-slider-thumb:hover {
  transform: scale(1.12);
  box-shadow: 0 0 0 3px rgba(184, 153, 104, 0.16), 0 0 18px rgba(184, 153, 104, 0.38);
}
.timeline-slider::-moz-range-thumb {
  width: 12px;
  height: 12px;
  background: var(--pale-gold);
  border: 1px solid var(--bronze);
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(184, 153, 104, 0.1), 0 0 12px rgba(184, 153, 104, 0.22);
  cursor: pointer;
}
.timeline-slider.is-snap::-webkit-slider-thumb {
  background: #f4e4bf;
  box-shadow: 0 0 0 3px rgba(184, 153, 104, 0.18), 0 0 18px rgba(232, 198, 140, 0.42);
}
.timeline-slider.is-snap::-moz-range-thumb {
  background: #f4e4bf;
  box-shadow: 0 0 0 3px rgba(184, 153, 104, 0.18), 0 0 18px rgba(232, 198, 140, 0.42);
}
.timeline-slider:focus-visible {
  outline: none;
}
.timeline-slider:focus-visible::-webkit-slider-thumb {
  outline: 1px solid var(--bronze);
  outline-offset: 4px;
}

/* Sky-view observer mood — atmospheric haze fading up from the bottom,
   plus a subtle horizon-light bloom. Reads as "standing on a planetary
   surface at dusk looking up at the sky" without painting any literal
   terrain. Sits ABOVE the 3D canvas and BELOW the UI controls. */
.ground-haze {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  background:
    linear-gradient(
      180deg,
      transparent 0%,
      transparent 52%,
      rgba(28, 22, 38, 0.18) 70%,
      rgba(36, 26, 46, 0.38) 85%,
      rgba(20, 16, 30, 0.62) 100%
    );
  mix-blend-mode: multiply;
}

.horizon-glow {
  position: absolute;
  left: 50%;
  bottom: -28%;
  transform: translateX(-50%);
  width: 180%;
  height: 60%;
  pointer-events: none;
  z-index: 3;
  background:
    radial-gradient(
      ellipse at 50% 100%,
      rgba(98, 70, 124, 0.32) 0%,
      rgba(64, 48, 88, 0.18) 18%,
      rgba(38, 28, 56, 0.08) 35%,
      rgba(20, 14, 28, 0) 60%
    );
  filter: blur(8px);
  opacity: 0.85;
}

/* Retrograde-zone band — sits between the slider and the date axis.
   Empty when the focused body has no retrograde periods (Sun, Moon,
   Earth) OR when none fall in the ±365 d window. Each child band
   is positioned absolutely with computed left/width %. Oxidised
   copper hue so it reads as a quiet "this is when this body walks
   backward" without competing with the slider thumb. */
.timeline-retro {
  position: relative;
  height: 3px;
  margin: 4px 4px 0;
  pointer-events: none;
}
.timeline-retro-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background: linear-gradient(
    180deg,
    rgba(196, 112, 64, 0.66) 0%,
    rgba(166, 88, 48, 0.78) 50%,
    rgba(196, 112, 64, 0.66) 100%
  );
  border-radius: 1px;
  box-shadow: 0 0 4px rgba(196, 112, 64, 0.32);
}

.timeline-axis {
  position: relative;
  height: 14px;
  margin-top: -4px;
}
.timeline-axis-tick {
  position: absolute;
  top: 0;
  width: 1px;
  height: 4px;
  background: rgba(184, 153, 104, 0.45);
  transform: translateX(-0.5px);
}
.timeline-axis-tick.is-major {
  height: 7px;
  background: var(--bronze);
}
.timeline-axis-label {
  position: absolute;
  top: 6px;
  font-family: "Jost", sans-serif;
  font-size: 0.54rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted-dim);
  opacity: 0.75;
  line-height: 1.2;
}
.timeline-axis-label--l { left: 0; }
.timeline-axis-label--m { left: 50%; transform: translateX(-50%); color: var(--bronze); opacity: 0.95; }
.timeline-axis-label--r { right: 0; }

.timeline-presets {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  border-top: 1px solid rgba(184, 153, 104, 0.12);
  padding-top: 0.55rem;
}
.timeline-preset {
  appearance: none;
  background: transparent;
  border: 0;
  border-right: 1px solid rgba(184, 153, 104, 0.18);
  border-bottom: 1px solid transparent;
  padding: 0.4rem 0.2rem 0.32rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.22rem;
  color: var(--muted);
  font-family: inherit;
  transition: color 0.45s ease, border-color 0.45s ease;
}
.timeline-preset:last-child {
  border-right: 0;
}
.timeline-preset:hover,
.timeline-preset:focus-visible {
  color: var(--off-white);
}
.timeline-preset:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 2px;
}
.timeline-preset.is-active {
  color: var(--pale-gold);
  border-bottom-color: var(--bronze);
  padding-bottom: calc(0.32rem - 1px);
}
.timeline-preset.is-natal {
  color: var(--bronze);
}
.timeline-preset.is-natal:hover {
  color: #f4e4bf;
}
.timeline-preset-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  line-height: 1;
}
.timeline-preset-en {
  font-family: "Jost", sans-serif;
  font-size: 0.42rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  opacity: 0.6;
  line-height: 1;
}
.timeline-preset.is-active .timeline-preset-en {
  color: var(--bronze);
  opacity: 0.9;
}

.timeline-precise {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  border-top: 1px solid rgba(184, 153, 104, 0.08);
  padding-top: 0.5rem;
}
.timeline-precise-label {
  display: flex;
  flex-direction: column;
  gap: 0.08rem;
  cursor: pointer;
}
.timeline-precise-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--muted);
}
.timeline-precise-en {
  font-family: "Jost", sans-serif;
  font-size: 0.42rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--muted-dim);
}
.timeline-precise-input {
  appearance: none;
  background: rgba(10, 9, 14, 0.55);
  border: 1px solid rgba(184, 153, 104, 0.22);
  border-radius: 2px;
  color: var(--off-white);
  font-family: "Jost", sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  padding: 0.3rem 0.5rem;
  cursor: pointer;
  color-scheme: dark;
}
.timeline-precise-input:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 2px;
  border-color: var(--bronze);
}

/* Natal row — slide-down panel when "生辰 / Natal" preset tapped */
.natal-row {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  border-top: 0 solid rgba(184, 153, 104, 0.18);
  margin-top: 0;
  transition:
    max-height 0.6s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.6s ease,
    border-top-width 0.4s ease,
    margin-top 0.4s ease;
}
.natal-row.is-open {
  max-height: 200px;
  opacity: 1;
  border-top: 1px solid rgba(184, 153, 104, 0.18);
  margin-top: 0.1rem;
}
.natal-row-inner {
  padding-top: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.natal-label {
  display: flex;
  flex-direction: column;
  gap: 0.08rem;
}
.natal-label-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  color: var(--bronze);
}
.natal-label-en {
  font-family: "Jost", sans-serif;
  font-size: 0.44rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted-dim);
}
.natal-controls {
  display: flex;
  gap: 0.4rem;
  align-items: stretch;
}
.natal-input {
  flex: 1;
  appearance: none;
  background: rgba(10, 9, 14, 0.55);
  border: 1px solid rgba(184, 153, 104, 0.22);
  border-radius: 2px;
  color: var(--off-white);
  font-family: "Jost", sans-serif;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  padding: 0.35rem 0.5rem;
  color-scheme: dark;
}
.natal-input:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 2px;
  border-color: var(--bronze);
}
.natal-btn {
  appearance: none;
  background: rgba(28, 22, 14, 0.45);
  border: 1px solid var(--bronze);
  border-radius: 2px;
  color: var(--pale-gold);
  cursor: pointer;
  padding: 0.32rem 0.6rem;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.14rem;
  transition: background 0.3s ease, color 0.3s ease;
}
.natal-btn:hover,
.natal-btn:focus-visible {
  background: rgba(48, 36, 22, 0.75);
  color: #f4e4bf;
}
.natal-btn:focus-visible {
  outline: 1px solid #f4e4bf;
  outline-offset: 2px;
}
.natal-btn-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  line-height: 1;
}
.natal-btn-en {
  font-family: "Jost", sans-serif;
  font-size: 0.42rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--bronze);
  line-height: 1;
}

/* =================================================================
   BODY-EXTRAS — moon-phase + retrograde inline pill
   -----------------------------------------------------------------
   Slim horizontal capsule so it doesn't compete with .specs for
   vertical room. Sits below specs, reads as a single status line.
   ================================================================= */
.body-extras {
  margin-top: 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.body-extras[hidden] { display: none; }
.extra-row {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.42rem 0.85rem 0.42rem 0.7rem;
  background: linear-gradient(
    90deg,
    rgba(28, 22, 14, 0.28) 0%,
    rgba(8, 8, 12, 0.4) 100%
  );
  border: 1px solid rgba(184, 153, 104, 0.28);
  border-radius: 999px;
  width: fit-content;
  max-width: 100%;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.extra-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  line-height: 1;
  color: var(--pale-gold);
  filter: drop-shadow(0 0 6px rgba(232, 198, 140, 0.28));
  flex-shrink: 0;
}
.extra-glyph svg {
  width: 14px;
  height: 14px;
  display: block;
}
.extra-glyph.glyph-text {
  font-size: 0.96rem;
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-weight: 400;
  width: auto;
  height: auto;
}
/* Bilingual stack — matches the rest of the design system
   (zh on top, en smaller below, micrometa optional third line). */
.extra-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  min-width: 0;
}
.extra-label-zh {
  font-family: "Noto Serif TC", "Songti TC", serif;
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  color: var(--off-white);
  line-height: 1.05;
  white-space: nowrap;
}
.extra-label-en {
  font-family: "Jost", sans-serif;
  font-weight: 300;
  font-size: 0.44rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--bronze);
  opacity: 0.85;
  line-height: 1.1;
  white-space: nowrap;
}
.extra-meta {
  font-family: "Jost", sans-serif;
  font-size: 0.42rem;
  letter-spacing: 0.22em;
  color: var(--muted-dim);
  line-height: 1.15;
  white-space: nowrap;
  text-transform: uppercase;
}
.extra-row.is-retro {
  border-color: rgba(216, 102, 84, 0.4);
  background: linear-gradient(
    90deg,
    rgba(56, 18, 16, 0.28) 0%,
    rgba(12, 8, 10, 0.42) 100%
  );
}
.extra-row.is-retro .extra-glyph {
  color: #e0a89a;
  filter: drop-shadow(0 0 6px rgba(216, 102, 84, 0.32));
}
.extra-row.is-retro .extra-label-en {
  color: #e0a89a;
}
/* Direct motion uses the base bronze palette — no green override,
   keeps the readout in the dark-academia palette. */

/* Mobile tightening */
@media (max-width: 900px) {
  /* On phones the right-side CTA collides with .nav-hint and the
     collapsed timeline capsule. The natal action it points to is
     fully reachable via the timeline's 「生辰 / Natal」preset, so
     hide the CTA at this breakpoint to free the bottom strip. */
  .cta {
    display: none;
  }
  .nav-hint {
    /* Sit just above the collapsed timeline handle (~46px tall) */
    bottom: calc(var(--ui-inset-y) + 3.4rem);
    font-size: 0.5rem;
    letter-spacing: 0.24em;
    /* Subtle drop-shadow so the hint is readable over the moon/planet */
    text-shadow: 0 0 6px rgba(0, 0, 0, 0.85);
  }
  /* Keep timeline collapsed to fit-content on mobile too — the
     panel is absolute-positioned and uses its own clamp(). */
  .timeline {
    max-width: calc(100vw - var(--ui-inset-x) * 2);
  }
  .timeline-panel {
    width: min(320px, calc(100vw - var(--ui-inset-x) * 2));
  }
  .timeline-handle {
    padding: 0.36rem 0.5rem 0.36rem 0.8rem;
  }
  .timeline-date-zh { font-size: 0.78rem; }
  .timeline-date-en { font-size: 0.46rem; }
  .timeline-mode-zh { font-size: 0.54rem; letter-spacing: 0.14em; }
  .timeline-mode-en { font-size: 0.36rem; letter-spacing: 0.24em; }
  .timeline-panel {
    padding: 0 0.7rem;
    gap: 0.5rem;
  }
  .timeline[data-state="open"] .timeline-panel {
    padding: 0.6rem 0.7rem 0.65rem;
  }
  .timeline-preset { padding: 0.32rem 0.16rem 0.28rem; }
  .timeline-preset-zh { font-size: 0.56rem; letter-spacing: 0.1em; }
  .timeline-preset-en { font-size: 0.36rem; letter-spacing: 0.2em; }
  .natal-input { font-size: 0.72rem; }
  .extra-label-zh { font-size: 0.62rem; }
  .extra-label-en { font-size: 0.38rem; letter-spacing: 0.26em; }
}

/* Reduced-motion: disable the timeline animations */
@media (prefers-reduced-motion: reduce) {
  .timeline,
  .timeline-handle,
  .timeline-panel,
  .timeline-mode-chip,
  .timeline-preset,
  .timeline-caret,
  .natal-row,
  .natal-btn {
    transition: none;
  }
}

/* =================================================================
   LANDSCAPE PHONE — orientation landscape + max-height 520
   -----------------------------------------------------------------
   Placed at end of file so cascade wins against later .title-zh /
   .spec-*-zh base rules (which had higher document order than the
   earlier short-viewport block).
   ================================================================= */
@media (orientation: landscape) and (max-height: 520px) {
  :root {
    --ui-inset-x: 3.5vw;
    --ui-inset-y: 3.2vh;
  }
  /* CRITICAL: the base .hero has min-height: 600px, which on a 390-tall
     landscape phone forces .hero to be 600px while the viewport is only
     390. Anything positioned via "bottom" inside .hero then anchors to
     the 600px hero — meaning content + timeline land below the visible
     viewport. Reset min-height + height to the actual viewport on
     landscape phones so absolute positioning resolves correctly. */
  .hero {
    min-height: 100vh;
    min-height: 100dvh;
    height: 100vh;
    height: 100dvh;
  }
  .content {
    top: calc(var(--ui-inset-y) + 5.4rem);
    bottom: calc(var(--ui-inset-y) + 3.4rem);
    left: var(--ui-inset-x);
    right: auto;
    width: 46%;
    max-width: 420px;
    transform: none;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.18rem;
  }
  .content::before { display: none; }
  .eyebrow-en { display: none; }
  .subtitle { display: none; }
  .eyebrow { margin-bottom: 0.25rem; }
  .eyebrow-zh { font-size: 0.62rem; letter-spacing: 0.18em; }
  .title { margin: 0; }
  .title-zh { font-size: clamp(2.0rem, 6.5vh, 2.9rem); line-height: 0.95; }
  .title-en { font-size: 0.58rem; margin-top: 0.3rem; letter-spacing: 0.34em; }
  .specs { margin-top: 0.4rem; gap: 0.22rem; }
  .spec { font-size: 0.6rem; }
  .spec-label { flex-basis: 62px; }
  .spec-label-zh { font-size: 0.64rem; letter-spacing: 0.14em; line-height: 1.15; }
  .spec-label-en { font-size: 0.4rem; margin-top: 0.14rem; letter-spacing: 0.22em; line-height: 1.2; }
  .spec-value-zh { font-size: 0.64rem; line-height: 1.2; letter-spacing: 0.06em; }
  .spec-value-en { font-size: 0.42rem; margin-top: 0.14rem; letter-spacing: 0.16em; line-height: 1.2; }

  .body-nav {
    padding: 0.4rem 0.6rem 0.36rem;
    gap: 0.32rem;
  }
  .nav-glyph { font-size: 1rem; }
  .nav-readout p { line-height: 1.05; }
  .nav-readout-zh, .nav-readout-en { font-size: 0.5rem; letter-spacing: 0.2em; }
  .timeline { bottom: var(--ui-inset-y); }
  .timeline-handle { padding: 0.32rem 0.5rem 0.32rem 0.75rem; }
  .timeline-date-zh { font-size: 0.7rem; }
  .timeline-date-en { font-size: 0.42rem; }
  .nav-hint { display: none; }
  .cta { display: none; }
  .body-extras { display: none !important; }
}
