/*
 * Spells & Sparkles website — Aurora Nocturne.
 * Token block mirrors SpellsandSparklesKit/frontend/styles/tokens.css
 * (the canonical source) — sync when the kit changes.
 */
:root {
  --ss-bg-void: #0a0d13;
  --ss-bg-canvas: #0f131b;
  --ss-surface: #161b26;
  --ss-surface-2: #1c2230;
  --ss-surface-3: #232b3b;
  --ss-border: #28303e;
  --ss-border-strong: #3a4658;
  --ss-text: #e8eaf1;
  --ss-text-dim: #aab2c4;
  --ss-muted: #7c8698;
  --ss-arcane: #7c5cff;
  --ss-arcane-hi: #9a80ff;
  --ss-sparkle: #63e6e2;
  --ss-glow-gold: #ffd27a;
  --ss-aurora: linear-gradient(120deg, #7c5cff, #c46bff, #63e6e2);
  --ss-ok: #37d39a;
  --ss-warn: #f5b34a;
  --ss-danger: #ff6b6b;
  --ss-info: #5cc7ff;
  --ss-font-ui: "Segoe UI", "Inter", system-ui, sans-serif;
  --ss-font-mono: "JetBrains Mono", Consolas, monospace;
  --ss-radius-sm: 5px;
  --ss-radius: 8px;
  --ss-radius-md: 10px;
  --ss-radius-pill: 999px;
  --ss-glow-arcane: 0 0 0 1px rgba(124, 92, 255, 0.5), 0 0 14px rgba(124, 92, 255, 0.35);
  --ss-ease-sparkle: cubic-bezier(0.2, 0.9, 0.25, 1.15);
}

/* Website-only extensions of the token set. The landing page speaks three
   voices: Cinzel for incantations, Space Grotesk for prose, DM Mono for
   machine text. The kit fonts stay untouched — /docs renders on them. */
:root {
  --font-display: "Cinzel", Georgia, serif;
  --font-body: "Space Grotesk", "Segoe UI", system-ui, sans-serif;
  --font-mono: "DM Mono", Consolas, monospace;

  /* Motion tokens. Every transition and animation on the page names one of
     these — nothing falls back to the browser's default `ease`. */
  --ease-out: cubic-bezier(0.2, 0.9, 0.25, 1);
  --ease-cast: var(--ease-out);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: var(--ss-ease-sparkle);
  --t-fast: 160ms;
  --t-base: 280ms;
  --t-open: 520ms;
  --t-close: 380ms;
  --t-reveal: 800ms;
  --t-rise: 1100ms;

  /* The dawn half of the page, where the ink turns dark. */
  --ink: #1a1436;
  --ink-dim: #4b4160;
  --ink-gold: #8a5a00;
  --edge-light: #d8caa6;
  --edge-light-soft: #e6d9b6;

  /* The seam between the halves. Static — the script only writes per-element
     properties (--hero on the hero, --seam on the seam line, …), never here. */
  --split: 50%;
  --cols: 50% 50%;
}

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

html {
  scroll-behavior: smooth; /* no-script fallback; the smoother sets it to auto */
  scrollbar-width: thin;
  scrollbar-color: #28303e #0a0d13;
}
html, body { overflow-x: clip; }

body {
  background: var(--ss-bg-void);
  color: var(--ss-text);
  font-family: var(--font-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

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

::selection { background: rgba(124, 92, 255, 0.45); }

:focus-visible {
  outline: 2px solid var(--ss-arcane-hi);
  outline-offset: 3px;
  border-radius: var(--ss-radius-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Shared container — /docs builds its layout inside this. */
.page {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ---- Fixed grounds ----
   The ground canvas draws the star lattice and the daylight (dawn sweeps it
   in, dusk sinks it behind a horizon); the grain kills gradient banding; the
   seam is a DOM line because a 1 px gradient stays crisp there at any DPR;
   the fx canvas carries cursor sparkles on top of everything.

   The lower layers sit at negative z-index rather than under a positioned
   page wrapper: difference blending only sees what is painted below it in
   the same stacking context, and the nav and the dawn headline have to blend
   against the daylight to invert over it. Page sections must never get a
   z-index for the same reason. */
.ground { position: fixed; inset: 0; pointer-events: none; }

#ground { z-index: -3; width: 100%; height: 100%; }
html.ready #ground { animation: fadein 800ms var(--ease-out) backwards; }

.grain {
  z-index: -2;
  opacity: 0.045;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

.splitline {
  position: fixed;
  top: 0;
  bottom: 0;
  left: var(--split);
  width: 1px;
  margin-left: -1px;
  z-index: -1;
  background: linear-gradient(180deg, #7c5cff, #63e6e2 50%, #ffd27a);
  box-shadow: 0 0 22px rgba(124, 92, 255, 0.6);
  opacity: var(--seam, 0);
  pointer-events: none;
}

#fx { z-index: 60; width: 100%; height: 100%; }

/* ---- Type helpers ---- */
.mono {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 18px;
}
.display {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: 0.035em;
  text-wrap: balance;
}

/* Icon frames hug the artwork: every icon PNG carries an 8 px transparent
   margin (112 px art in a 128 px file), so the frame crops it away and puts
   the ring on the painted corner — one radius, everywhere. */
.ico {
  display: block;
  flex: none;
  border-radius: 17%;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 10px 28px -6px rgba(0, 0, 0, 0.55);
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base) var(--ease-out);
}
.ico img {
  display: block;
  width: 114.3%;
  height: 114.3%;
  max-width: none;
  margin: -7.15%;
}
.ico-xs { width: 18px; height: 18px; }
.ico-tick { width: 20px; height: 20px; }
.ico-sm { width: 44px; height: 44px; }
.ico-lg { width: 84px; height: 84px; }
.ico-xl { width: 96px; height: 96px; }

/* ---- Pills: the page has exactly one button shape ---- */
.pill {
  display: inline-block;
  padding: 14px 26px;
  border: 1px solid var(--ss-border);
  border-radius: var(--ss-radius-pill);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out),
    background var(--t-fast) var(--ease-out), box-shadow var(--t-base) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}
.pill:hover { border-color: var(--ss-border-strong); color: var(--ss-glow-gold); transform: translateY(-1px); }
.pill-arcane { border-color: var(--ss-arcane); box-shadow: var(--ss-glow-arcane); }
.pill-arcane:hover {
  border-color: var(--ss-arcane-hi);
  box-shadow: 0 0 0 1px rgba(124, 92, 255, 0.7), 0 0 24px rgba(124, 92, 255, 0.5);
}
.pill-gold {
  background: var(--ss-glow-gold);
  border-color: var(--ss-glow-gold);
  color: var(--ink);
  font-weight: 500;
}
.pill-gold:hover { background: #ffdf9c; border-color: #ffdf9c; color: var(--ink); }
.pill-sm { padding: 9px 16px; letter-spacing: 0.14em; }
.pill:active, .chan:active, .punch-btn:active { transform: translateY(1px) scale(0.985); transition-duration: 80ms; }
/* Before go-live the download is present but visibly not yet a link. */
.pill[aria-disabled="true"] {
  opacity: 0.6;
  cursor: default;
  border-style: dashed;
  border-color: var(--ss-border);
  box-shadow: none;
  background: transparent;
  color: var(--ss-muted);
  transform: none;
}

/* ---- Top navigation ----
   Difference blending keeps one nav legible over both halves of the page, so
   everything in it is white: white inverts to ink over the cream half, a
   coloured hover would invert to mud. */
.topnav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
  padding: 22px 44px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #fff;
  mix-blend-mode: difference;
}
.topnav-brand { display: flex; align-items: center; gap: 12px; }
.topnav-brand i {
  display: block;
  width: 18px;
  height: 18px;
  background: currentColor;
  -webkit-mask: url("assets/wisp-white.png") center / contain no-repeat;
  mask: url("assets/wisp-white.png") center / contain no-repeat;
}
.topnav-links { display: flex; gap: 30px; }
.topnav-links a {
  opacity: 0.72;
  padding-bottom: 6px;
  background: linear-gradient(#fff, #fff) left bottom / 0 1px no-repeat;
  transition: opacity var(--t-fast) var(--ease-out), background-size 220ms var(--ease-out);
}
.topnav-links a:hover, .topnav-links a.is-active { opacity: 1; background-size: 100% 1px; }
.topnav-brand:hover { opacity: 0.8; }

/* Positioned, but z-index stays auto: a stacking context here would cut the
   blending elements inside off from the grounds they need to blend against. */
.band { position: relative; }

/* ---- Load choreography ----
   With the script running, the hero and nav stay hidden until the display
   fonts are in (or 600 ms have passed), then compose themselves in order.
   Without the script (no `js` class) everything is simply visible. */
html.js:not(.ready) :is(.hero-mark, .hero h1, .hero-sub, .hero-actions, .hero-scroll, .topnav) { opacity: 0; }
html.ready .hero-mark { animation: markin 900ms var(--ease-out) backwards; }
html.ready .hero h1 { animation: rise var(--t-rise) var(--ease-out) 250ms backwards; }
html.ready .hero-sub { animation: rise var(--t-rise) var(--ease-out) 420ms backwards; }
html.ready .hero-actions { animation: rise var(--t-rise) var(--ease-out) 600ms backwards; }
html.ready .topnav { animation: fadein 400ms var(--ease-out) 900ms backwards; }
html.ready .hero-scroll { animation: fadein 600ms var(--ease-out) 1300ms backwards; }
/* Arriving on a deep link skips the entrance — the row is the destination. */
html.arrive :is(.hero-mark, .hero h1, .hero-sub, .hero-actions, .hero-scroll, .topnav, #ground) { animation: none; opacity: 1; }

@keyframes fadein { from { opacity: 0; } }
@keyframes markin { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } }
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }

/* ---- Hero ---- */
.hero {
  min-height: 100vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 100px 24px 80px;
  --hero: 0; /* scroll progress, written by the script on this element only */
}
.hero-inner {
  display: grid;
  justify-items: center;
  gap: 28px;
  opacity: calc(1 - var(--hero) * 1.2);
}
.hero-copy {
  display: grid;
  justify-items: center;
  gap: 28px;
  transform: translateY(calc(var(--hero) * -120px));
}
.hero-mark {
  position: relative;
  width: min(38vw, 260px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  transform: translateY(calc(var(--hero) * -60px)) scale(calc(1 - var(--hero) * 0.35));
  --ring-a: 0; /* per-load random start, 0..1 of a turn, set by the script */
}
/* Two glow layers behind the mark crossfade instead of animating a blur. */
.hero-mark::before, .hero-mark::after {
  content: "";
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  pointer-events: none;
}
.hero-mark::before {
  background: radial-gradient(closest-side, rgba(124, 92, 255, 0.5), transparent 75%);
  animation: glowswap 7s ease-in-out infinite;
}
.hero-mark::after {
  background: radial-gradient(closest-side, rgba(99, 230, 226, 0.35), transparent 75%);
  animation: glowswap 7s ease-in-out -3.5s infinite;
}
@keyframes glowswap { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* Rings as comet tails: a conic tail masked to a 1 px ring, with the orbiting
   sparkle (unmasked sibling, same rotation) at its head. */
.hero-ring, .hero-orbiter {
  position: absolute;
  inset: -6%;
  border-radius: 50%;
  animation: ringspin 28s linear infinite;
  animation-delay: calc(var(--ring-a) * -28s);
}
.hero-ring {
  background: conic-gradient(from 0deg, transparent 0 72%, rgba(99, 230, 226, 0.9));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 1.5px), #000 calc(100% - 0.5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 1.5px), #000 calc(100% - 0.5px));
}
.hero-ring-b, .hero-orbiter-b {
  inset: 8%;
  animation-duration: 46s;
  animation-direction: reverse;
  animation-delay: calc(var(--ring-a) * -46s);
}
.hero-ring-b { background: conic-gradient(from 0deg, transparent 0 78%, rgba(255, 210, 122, 0.75)); }
.hero-orbiter i {
  position: absolute;
  top: -2px;
  left: 50%;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--ss-sparkle);
  box-shadow: 0 0 6px 2px rgba(99, 230, 226, 0.6);
}
.hero-orbiter-b i { background: var(--ss-glow-gold); box-shadow: 0 0 6px 2px rgba(255, 210, 122, 0.55); }

/* The wisp as a masked aurora: one alpha mask, two gradients crossfading. */
.hero-wisp {
  position: relative;
  width: 62%;
  height: 62%;
  animation: wispdrift 7s ease-in-out infinite;
}
.hero-wisp i, .hero-wisp::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask: url("assets/wisp-white.png") center / contain no-repeat;
  mask: url("assets/wisp-white.png") center / contain no-repeat;
}
.hero-wisp i {
  background: linear-gradient(135deg, #7c5cff 0%, #c46bff 45%, #63e6e2 100%);
  filter: drop-shadow(0 0 28px rgba(124, 92, 255, 0.45));
}
.hero-wisp::after {
  background: linear-gradient(135deg, #63e6e2 0%, #7c5cff 55%, #ffd27a 100%);
  animation: auroraswap 9s ease-in-out infinite;
}
@keyframes auroraswap { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }
@keyframes wispdrift {
  0%, 100% { transform: translateY(0) rotate(-1.5deg); }
  50% { transform: translateY(-8px) rotate(1.5deg); }
}
@keyframes ringspin { to { transform: rotate(360deg); } }

.hero h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(40px, 7vw, 104px);
  line-height: 1;
  letter-spacing: 0.02em;
  text-wrap: balance;
}
.hero h1 .amp {
  color: var(--ss-glow-gold);
  font-weight: 400;
  font-size: 0.82em;
  vertical-align: 0.05em;
  margin: 0 0.02em;
  letter-spacing: 0;
}
.hero-sub {
  max-width: 52ch;
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.7;
  color: var(--ss-text-dim);
  text-wrap: pretty;
}
.hero-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 10px;
}
.hero-actions .pill:not(.pill-arcane) { border-color: var(--ss-border-strong); }
.hero-scroll {
  position: absolute;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ss-muted);
  opacity: calc(1 - var(--hero) * 6); /* gone before the dawn headline arrives under it */
}
.hero-scroll i {
  display: block;
  width: 1px;
  height: 28px;
  background: linear-gradient(var(--ss-sparkle), transparent);
  transform-origin: top;
  animation: cue 1.8s var(--ease-out) infinite;
}
@keyframes cue {
  0% { transform: scaleY(0); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}
/* Nothing in the hero animates while it is off screen. */
.hero:not(.on-screen) :is(.hero-ring, .hero-orbiter, .hero-wisp, .hero-wisp::after, .hero-mark::before, .hero-mark::after, .hero-scroll i) { animation-play-state: paused; }

/* ---- Dawn: the page splits in two ----
   Both halves blend as one group against the grounds: the same white type
   inverts to ink where the daylight has already arrived. */
/* the dawn starts under the hero's last third, so its headlines arrive as
   the hero's leave; 2.1 viewports of runway (readScroll), no hold */
.dawn { height: 160vh; margin-top: -45vh; }
.dawn-stick {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  grid-template-columns: var(--cols);
  align-items: start;
  padding: 30vh 44px 0;
  mix-blend-mode: difference;
  color: #fff;
}
.dawn-dark { opacity: var(--o-dark, 0); }
.dawn-light { opacity: var(--o-light, 0); text-align: right; }
.dawn-half .display {
  font-size: clamp(44px, 6vw, 92px);
  line-height: 0.95;
  letter-spacing: 0.02em;
}
.dawn-half p {
  margin-top: 18px;
  max-width: 34ch;
  font-size: 16px;
  line-height: 1.55;
  color: #d6d9e4;
  text-wrap: pretty;
}
.dawn-light p { margin-left: auto; }
.dawn-dark .eyebrow { color: var(--ss-arcane-hi); }
.dawn-light .eyebrow { color: #fff; opacity: 0.8; }

/* ---- Shelf: spells on the night side, sparkles on the day side ---- */
.shelf {
  display: grid;
  grid-template-columns: var(--cols);
  scroll-margin-top: 0;
  margin-top: -60vh; /* under the dawn stage: the Hub arrives with the sweep, not after it */
}
.shelf-col { padding: 60px 44px 120px; min-width: 0; }
.shelf-light { color: var(--ink); position: relative; }
/* Demo stages carry wide canvases; never let their intrinsic width size a column. */
.row-body > div, .row-panel, .demo-sheet > *, .demo-tex > *, .showcase > *, .sheet-stage, .tex-stage { min-width: 0; }
.shelf-count {
  display: flex;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 30px;
}
.shelf-count .num { font-family: var(--font-display); font-size: 48px; line-height: 1; letter-spacing: 0.035em; }
.shelf-count .label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ss-muted);
}
.shelf-light .shelf-count { justify-content: flex-end; }
.shelf-light .shelf-count .label { color: var(--ink-gold); }
.shelf-end {
  margin-top: 22px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ss-muted);
  text-align: center;
}

/* The keystone: the Hub binds both halves, so it is the one object that sits
   on the seam, spans both columns and wears the page's only two-colour frame. */
.keystone-band {
  grid-column: 1 / -1;
  padding: 60px 44px 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  min-width: 0;
}
.keystone {
  position: relative;
  width: 100%;
  max-width: 880px;
  min-width: 0;
  border-radius: 18px;
  padding: 28px 32px;
  background: rgba(15, 19, 27, 0.94);
  backdrop-filter: blur(6px);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.keystone::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(90deg, #7c5cff, #63e6e2 50%, #ffd27a);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.keystone.pulse { animation: keystone-pulse 900ms var(--ease-out); }
@keyframes keystone-pulse {
  from { box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45), 0 0 0 0 rgba(124, 92, 255, 0.6); }
  to { box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45), 0 0 0 16px rgba(124, 92, 255, 0); }
}
.keystone-head {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 22px;
  align-items: center;
}
.keystone-head > div { min-width: 0; }
.keystone h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 28px;
  letter-spacing: 0.05em;
  text-wrap: balance;
}
.keystone-tagline { color: var(--ss-text-dim); font-size: 14px; margin-top: 4px; text-wrap: pretty; }
.keystone-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.keystone-line {
  margin-top: 18px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ss-text-dim);
  text-wrap: pretty;
}
.ticker {
  margin-top: 18px;
  overflow: hidden;
  min-width: 0;
  width: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.ticker-track {
  display: flex;
  gap: 28px;
  width: max-content;
  animation: ticker 48s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker-track button {
  all: unset;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ss-text-dim);
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease-out);
}
.ticker-track button:hover { color: var(--ss-text); }
.ticker-track button:focus-visible { outline: 2px solid var(--ss-arcane-hi); outline-offset: 3px; border-radius: 4px; }
@keyframes ticker { to { transform: translateX(-50%); } }

.rows { display: grid; gap: 4px; }
.shelf-light .rows { gap: 14px; }
/* Two sparkles against thirteen spells — the short column rides along. */
.shelf-light .sticky-group { position: sticky; top: 80px; display: grid; gap: 14px; }
.sparkle-note {
  padding: 24px;
  border-radius: 16px;
  border: 1px dashed #c9b98f;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-dim);
  text-wrap: pretty;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
/* The gold wisp lives in the day column's empty air. Same mask as the hero. */
.wisp-mark {
  position: absolute;
  right: -8%;
  bottom: -12%;
  width: 46%;
  aspect-ratio: 684 / 709;
  background: var(--ss-glow-gold);
  -webkit-mask: url("assets/wisp-white.png") center / contain no-repeat;
  mask: url("assets/wisp-white.png") center / contain no-repeat;
  opacity: 0.18;
  pointer-events: none;
  animation: wispfloat 7s ease-in-out infinite paused;
}
.shelf-light.in .wisp-mark { animation-play-state: running; }
@keyframes wispfloat {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-12px) rotate(2deg); }
}
.twinkle {
  position: absolute;
  width: 8px;
  height: 8px;
  background: var(--ss-glow-gold);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  opacity: 0;
  pointer-events: none;
  animation: twinkle 4.5s ease-in-out infinite paused;
}
.shelf-light.in .twinkle { animation-play-state: running; }
@keyframes twinkle {
  0%, 100% { opacity: 0; transform: scale(0.6); }
  50% { opacity: 0.9; transform: scale(1); }
}

.row {
  position: relative;
  border-radius: 12px;
  background: rgba(22, 27, 38, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.035);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
  scroll-margin-top: 96px;
  transition: background var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}
.row:hover { background: rgba(28, 34, 48, 0.8); border-color: rgba(124, 92, 255, 0.35); }
.row:hover .ico { transform: scale(1.04); }
.row:hover .row-arrow { transform: translateY(2px); }
.row.is-open {
  background: rgba(28, 34, 48, 0.9);
  border-color: var(--ss-arcane);
  box-shadow: 0 0 0 1px rgba(124, 92, 255, 0.35), 0 14px 40px -14px rgba(124, 92, 255, 0.5);
}
.row.pulse { animation: rowpulse 900ms var(--ease-out); }
@keyframes rowpulse {
  from { box-shadow: 0 0 0 0 rgba(124, 92, 255, 0.55); }
  to { box-shadow: 0 0 0 14px rgba(124, 92, 255, 0); }
}
.row-head {
  all: unset;
  cursor: pointer;
  width: 100%;
  display: grid;
  grid-template-columns: 44px 1fr auto auto;
  gap: 16px;
  align-items: center;
  padding: 12px 16px;
  box-sizing: border-box;
  border-radius: inherit;
}
.row-head:focus-visible { outline: 2px solid var(--ss-arcane-hi); outline-offset: -2px; }
.row-text { min-width: 0; }
.row-name { display: block; font-weight: 500; font-size: 16px; }
.row-tagline {
  display: block;
  font-size: 13px;
  color: var(--ss-text-dim);
  line-height: 1.35;
  text-wrap: pretty;
}
.row-chips { display: flex; gap: 6px; align-items: center; }
.chip {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ss-muted);
  border: 1px solid var(--ss-border);
  padding: 3px 7px;
  border-radius: var(--ss-radius-pill);
  white-space: nowrap;
  transition: color var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out);
}
.chip[data-stage="beta"] { color: var(--ss-sparkle); border-color: rgba(99, 230, 226, 0.35); }
.chip[data-stage="rc"], .chip[data-stage="stable"] { color: var(--ss-glow-gold); border-color: rgba(255, 210, 122, 0.35); }
.chip-live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ss-sparkle);
  border-color: rgba(99, 230, 226, 0.4);
}
.chip-live::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ss-sparkle);
  box-shadow: 0 0 0 0 rgba(99, 230, 226, 0.5);
}
.row.on-screen .chip-live::before { animation: livepulse 2.2s ease-out infinite; }
@keyframes livepulse { to { box-shadow: 0 0 0 6px rgba(99, 230, 226, 0); } }
.row-arrow {
  font-family: var(--font-mono);
  color: var(--ss-sparkle);
  display: inline-block;
  transition: transform var(--t-base) var(--ease-out);
}
.row.is-open .row-arrow { transform: rotate(180deg); }
.row.is-open:hover .row-arrow { transform: rotate(180deg) translateY(-2px); }
/* Attract: a closed live row hints once that it is live. Nothing opens itself. */
.ico.attract { animation: attract 900ms var(--ease-out); }
@keyframes attract {
  from { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 0 rgba(124, 92, 255, 0.6); }
  to { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 12px rgba(124, 92, 255, 0); }
}
.row-arrow.nudge { animation: nudge 300ms var(--ease-out) 2; }
@keyframes nudge { 50% { transform: translateY(3px); } }

/* The panel opens by animating its own row track — no measured heights.
   Closing is a symmetric ease so it does not start fast and stop dead. */
.row-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-close) var(--ease-inout);
}
.row.is-open .row-body {
  grid-template-rows: 1fr;
  transition-duration: var(--t-open);
  transition-timing-function: var(--ease-out);
}
.row-body > div { overflow: hidden; min-height: 0; }
.row-panel {
  padding: 6px 16px 20px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.row.is-open .row-panel {
  opacity: 1;
  transform: none;
  transition: opacity 400ms var(--ease-out) 120ms, transform 400ms var(--ease-out) 120ms;
}

/* Sparkle rows: bigger, brighter, day-side ink. */
.row-light {
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--edge-light-soft);
  box-shadow: 0 10px 40px rgba(120, 80, 0, 0.06);
}
.row-light:hover { background: rgba(255, 255, 255, 0.74); border-color: var(--edge-light); }
.row-light.is-open {
  background: rgba(255, 255, 255, 0.72);
  border-color: var(--ss-arcane);
  box-shadow: 0 0 0 1px rgba(124, 92, 255, 0.3), 0 14px 40px -14px rgba(120, 80, 0, 0.25);
}
.row-light .ico { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2), 0 8px 26px -6px rgba(120, 80, 0, 0.22); }
.row-light .row-head {
  grid-template-columns: 1fr 84px;
  gap: 20px;
  padding: 22px 24px;
}
.row-light .row-head:focus-visible { outline-color: var(--ink); }
.row-light .row-name {
  font-family: var(--font-display);
  font-size: 26px;
  letter-spacing: 0.05em;
  font-weight: 500;
}
.row-light .row-tagline { font-size: 14px; color: var(--ink-dim); margin-top: 6px; }
.row-light .row-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-gold);
}
.row-light .chip { color: var(--ink-gold); border-color: var(--edge-light); }
.row-light .row-panel { padding: 0 24px 24px; }
/* The SparkleSheet row plays its sprite in the icon while hovered. */
.row-light .ico-play { position: relative; }
.row-light .ico-play canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 200ms var(--ease-out);
}
.row-light .ico-play.playing canvas { opacity: 1; }
.shelf-light :focus-visible { outline-color: var(--ink); }

/* ---- Tool actions: version, download, docs ---- */
.tool-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--ss-border);
}
.tool-version {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--ss-muted);
  margin-right: 4px;
}
.row-light .tool-actions { border-top-color: var(--edge-light); }
.row-light .tool-version { color: var(--ink-dim); }
.row-light .pill { border-color: var(--edge-light); color: var(--ink-dim); }
.row-light .pill:hover { border-color: var(--ink-gold); color: var(--ink); }
.row-light .pill-gold { color: var(--ink); border-color: var(--ss-glow-gold); }
.row-light .pill[aria-disabled="true"] { color: var(--ink-dim); border-color: var(--edge-light); }

/* ---- Showcase: the panel for tools without a live demo ---- */
.showcase {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 20px;
  align-items: center;
}
.showcase p {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ss-text-dim);
  text-wrap: pretty;
  max-width: 56ch;
  transition: opacity 200ms var(--ease-out);
}
.showcase p.swap { opacity: 0; }
.showcase-links {
  display: flex;
  gap: 18px;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ss-sparkle);
}
.showcase-links a { transition: color var(--t-fast) var(--ease-out); }
.showcase-links a:hover { color: var(--ss-glow-gold); }
.row-light .showcase p { color: var(--ink-dim); }
.row-light .showcase-links { color: var(--ink-gold); }
.row-light .showcase-links a:hover { color: var(--ink); }

/* ---- Live demos ---- */
.demo-sheet { display: grid; grid-template-columns: 1fr 140px; gap: 16px; align-items: start; }
.sheet-stage {
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--ss-border);
  background: var(--ss-surface);
}
.sheet-stage canvas { display: block; width: 100%; height: auto; }
.sheet-cells {
  position: absolute;
  inset: 0;
  display: grid;
  pointer-events: none;
}
.sheet-cells div { border: 1px solid rgba(99, 230, 226, 0.3); transition: background 120ms var(--ease-out); }
.sheet-cells div.on { background: rgba(255, 210, 122, 0.28); }
.sheet-side { display: grid; gap: 8px; }
.sheet-frame {
  border-radius: 8px;
  border: 1px solid var(--ss-border);
  background: var(--ss-bg-void);
  aspect-ratio: 1;
  overflow: hidden;
}
.sheet-frame canvas { display: block; width: 100%; height: 100%; }
.demo-note { font-family: var(--font-mono); font-size: 11px; color: var(--ss-text-dim); }
.demo-controls {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ss-text-dim);
}
.demo-controls label { display: grid; gap: 6px; }
.row-light .sheet-stage, .row-light .sheet-frame { border-color: var(--edge-light); border-radius: 10px; }
.row-light .sheet-cells div { border-color: rgba(255, 210, 122, 0.35); }
.row-light .demo-note, .row-light .demo-controls { color: var(--ink-dim); }

/* Range inputs, both pseudo-element families: a 2 px track that fills up to
   --p (written by the script), a round thumb ringed with the panel colour. */
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 18px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  --p: 50%;
  --thumb: var(--ss-arcane);
  --track: var(--ss-border);
  --ring: var(--ss-bg-void);
}
input[type="range"].fps { --thumb: var(--ss-sparkle); }
.row-light input[type="range"] { --track: var(--edge-light); --ring: #fff6dc; }
.row-light input[type="range"].fps { --thumb: #c98a00; }
input[type="range"]::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: 1px;
  background: linear-gradient(90deg, var(--thumb) var(--p), var(--track) 0);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--thumb);
  box-shadow: 0 0 0 3px var(--ring);
  transition: box-shadow var(--t-fast) var(--ease-out);
}
input[type="range"]:active::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--ring), var(--ss-glow-arcane); }
input[type="range"]::-moz-range-track { height: 2px; border-radius: 1px; background: var(--track); }
input[type="range"]::-moz-range-progress { height: 2px; border-radius: 1px; background: var(--thumb); }
input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border: 0;
  border-radius: 50%;
  background: var(--thumb);
  box-shadow: 0 0 0 3px var(--ring);
}
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--ring), 0 0 0 5px var(--ss-arcane-hi); }

.demo-tex { display: grid; grid-template-columns: 200px 1fr; gap: 18px; align-items: start; }
.tex-stage {
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--ss-border);
  aspect-ratio: 1;
}
.tex-stage canvas {
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
  transition: opacity 180ms var(--ease-out);
}
.tex-stage canvas.dip { opacity: 0.55; }
.tex-copy { display: grid; gap: 12px; align-content: start; }
.tex-copy p { font-size: 13px; line-height: 1.5; color: var(--ss-text-dim); text-wrap: pretty; }
.tex-chans { display: flex; gap: 8px; flex-wrap: wrap; }
.chan {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 9px 14px;
  border-radius: var(--ss-radius-pill);
  cursor: pointer;
  background: transparent;
  color: var(--ss-text-dim);
  border: 1px solid var(--ss-border);
  transition: color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.chan:hover { border-color: var(--ss-border-strong); color: var(--ss-text); }
.chan.on { background: var(--ss-arcane); border-color: var(--ss-arcane); color: #fff; }
.chan.on[data-chan="r"] { background: var(--ss-danger); border-color: var(--ss-danger); color: var(--ss-bg-void); }
.chan.on[data-chan="g"] { background: var(--ss-ok); border-color: var(--ss-ok); color: var(--ss-bg-void); }
.chan.on[data-chan="b"] { background: var(--ss-info); border-color: var(--ss-info); color: var(--ss-bg-void); }

.demo-punch { display: grid; grid-template-columns: auto 1fr; gap: 22px; align-items: center; }
.punch-btn {
  font: inherit;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.06em;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid var(--ss-glow-gold);
  background: transparent;
  color: var(--ss-glow-gold);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}
.punch-btn.running {
  background: var(--ss-glow-gold);
  color: var(--ink);
  animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 210, 122, 0.5); }
  70% { box-shadow: 0 0 0 14px rgba(255, 210, 122, 0); }
}
.punch-side { display: grid; gap: 8px; }
.punch-elapsed {
  font-family: var(--font-mono);
  font-size: 30px;
  font-variant-numeric: tabular-nums;
}
.punch-session {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ss-text-dim);
}
.punch-session .bar {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, #7c5cff, #63e6e2);
  min-width: 6px;
  transform-origin: left;
  animation: grow 480ms var(--ease-out) both;
}
@keyframes grow { from { transform: scaleX(0); } }
.punch-note { font-family: var(--font-mono); font-size: 11px; color: var(--ss-muted); transition: opacity 120ms var(--ease-out); }

/* ---- Dusk: one sticky stage for the night falling, the chest of the Hub
   and the lattice it empties into. It starts under the last half viewport
   of the shelf, so the sky warms behind the last rows; 1.4 viewports of
   runway belong to the sky. The chest arrives together with the headline
   (headline in the upper third, chest beneath) and gets 1.1 viewports from
   0.87 (readScroll in home.js); the rest is the hold in which it opens on
   its own clock. ---- */
.dusk { height: 230vh; margin-top: -50vh; }
.dusk-stick {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  text-align: center;
  padding: 0 24px;
  overflow: hidden;
  pointer-events: none; /* it overlaps the shelf's last rows while still invisible */
}
.dusk-inner {
  grid-area: 1 / 1;
  opacity: calc(var(--dusk-o, 0) * (1 - var(--hd, 0)));
  transform: translateY(calc(-22vh + (1 - var(--dusk-o, 0)) * 40px - var(--hd, 0) * 14vh)); /* upper third, the chest sits beneath */
}
.dusk-inner .eyebrow { color: var(--ss-sparkle); }
.dusk-inner .display {
  font-size: clamp(36px, 5vw, 72px);
  line-height: 1;
  letter-spacing: calc(0.04em + (1 - var(--dusk-o, 0)) * 0.12em);
}
.lattice-anchor {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% - 100vh);
  height: 100vh;
  pointer-events: none;
  scroll-margin-top: 0;
}

/* ---- The Hub's chest (chest in home.js drives the custom properties) ----
   An obsidian box built from CSS planes: a body of four walls and a floor;
   a lid of top, underside, four short walls, four bevel strips and four
   corner caps, hinged at its back edge. Footprint 240, body 110 tall, lid
   34 with a 10 px chamfer — every transform below is that geometry in px.
   Seen from straight above it is just the Hub's mark on a dark tile; the
   scroll tilts it to isometric, opens it, and sinks it under the tools. ---- */
.chest-scene {
  grid-area: 1 / 1;
  place-self: center;
  position: relative;
  width: 240px;
  height: 240px;
  perspective: 1400px;
  perspective-origin: 50% 35%;
  pointer-events: none;
  will-change: transform, opacity;
  --tilt: 0deg; --tiltn: 0; --spin: 0deg; --zoom: 0.62; --lid: 0deg; --glow: 0; --sink: 0; --in: 0;
  --fit: 1; /* the open lid stands 250 px above the box: on a short viewport the whole scene shrinks */
  opacity: calc(var(--in) * (1 - var(--sink)));
  transform: translateY(calc(6vh + var(--sink) * 56px)) scale(calc(var(--fit) * (1 - var(--sink) * 0.16)));
}
@media (max-height: 820px) { .chest-scene { --fit: 0.84; } }
.chest {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--tilt)) rotateZ(var(--spin)) scale3d(var(--zoom), var(--zoom), var(--zoom));
}
.chest-body, .chest-lid { position: absolute; inset: 0; transform-style: preserve-3d; }
.chest-lid { transform: translate3d(0, -120px, 55px) rotateX(var(--lid)) translate3d(0, 120px, -55px); }
.cf { position: absolute; left: 50%; top: 50%; display: block; }
.cf-floor {
  width: 220px; height: 220px; margin: -110px 0 0 -110px;
  transform: translateZ(-45px);
  background: radial-gradient(55% 55% at 50% 50%, rgba(255, 214, 130, calc(0.95 * var(--glow))), rgba(124, 92, 255, calc(0.55 * var(--glow))) 45%, #0a0913 78%);
}
.cf-side {
  width: 240px; height: 110px; margin: -55px 0 0 -120px;
  transform: rotateZ(calc(var(--i) * 90deg)) rotateX(-90deg) translate3d(0, 0, 120px);
  background: radial-gradient(60% 80% at 50% 100%, rgba(255, 200, 120, calc(0.22 * var(--glow))), transparent 70%), #0b0a14;
}
.cf-side.side0 { background: linear-gradient(180deg, #1a1631, #0c0a16); }
.cf-side.side3 { background: linear-gradient(180deg, #120f24, #06050b); }
.cf-lside {
  width: 240px; height: 24px; margin: -12px 0 0 -120px;
  transform: rotateZ(calc(var(--i) * 90deg)) rotateX(-90deg) translate3d(0, -67px, 120px);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent 45%), #0c0a16;
}
.cf-lside.lside0 { background: linear-gradient(180deg, rgba(255, 255, 255, 0.09), transparent 45%), #1b1733; }
.cf-lside.lside3 { background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), transparent 45%), #120f24; }
.cf-top {
  width: 220px; height: 220px; margin: -110px 0 0 -110px;
  transform: translateZ(89px);
  backface-visibility: hidden;
  background: radial-gradient(70% 70% at 30% 25%, rgba(140, 120, 220, 0.2), transparent 70%), linear-gradient(135deg, #1e1a35 0%, #0e0c19 55%, #171430 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.cf-under {
  width: 220px; height: 220px; margin: -110px 0 0 -110px;
  transform: translateZ(88px) rotateX(180deg);
  background: radial-gradient(60% 60% at 50% 50%, rgba(255, 210, 122, calc(0.45 * var(--glow))), transparent 70%), #0a0913;
}
.cf-bevel {
  width: 220px; height: 14.14px; margin: -7.07px 0 0 -110px;
  transform: rotateZ(calc(var(--i) * 90deg)) translate3d(0, 115px, 84px) rotateX(-45deg);
  background: linear-gradient(90deg, rgba(99, 230, 226, 0.18), rgba(170, 150, 255, 0.62) 50%, rgba(99, 230, 226, 0.18)), #2a2650;
}
.cf-corner {
  width: 14.14px; height: 12.25px; margin: -6.12px 0 0 -7.07px;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  transform: rotateZ(calc(var(--i) * 90deg)) translate3d(-113.3px, 113.3px, 82.3px) rotateZ(45deg) rotateX(-54.7356deg) translateY(2.04px);
  background: #4a4488;
}
/* the rim goes gold while the box is open */
.cf-bevel::after, .cf-corner::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: var(--glow);
  background: linear-gradient(90deg, rgba(255, 210, 122, 0.3), rgba(255, 224, 160, 0.95) 50%, rgba(255, 210, 122, 0.3));
}
.chest-plaque { position: absolute; left: 50%; top: 50%; width: 96px; height: 96px; margin: -48px 0 0 -48px; }
.chest-plaque .ico {
  width: 96px;
  height: 96px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 210, 122, 0.35), 0 0 26px rgba(255, 210, 122, 0.28);
}
.chest-shadow {
  position: absolute; left: 50%; top: 50%;
  width: 320px; height: 130px; margin: -65px 0 0 -160px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.3) 55%, transparent);
  opacity: calc(var(--tiltn) * 0.9);
  transform: translateY(calc(var(--tiltn) * 100px)) scaleX(calc(0.6 + 0.4 * var(--tiltn)));
}
.chest-light {
  position: absolute; left: 50%; top: 50%;
  width: 460px; height: 460px; margin: -270px 0 0 -230px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 214, 130, 0.6), rgba(255, 200, 130, 0.36) 22%, rgba(124, 92, 255, 0.28) 46%, rgba(124, 92, 255, 0.08) 66%, transparent 78%);
  opacity: var(--glow);
  transform: scale(calc(0.45 + 0.65 * var(--glow)));
}
.chest-beam {
  position: absolute; left: 50%; bottom: 52%;
  width: 200px; height: 62vh; margin-left: -100px;
  background: linear-gradient(180deg, rgba(255, 214, 130, 0), rgba(255, 214, 130, 0.14) 65%, rgba(255, 214, 130, 0.38));
  -webkit-mask: linear-gradient(90deg, transparent, #000 35%, #000 65%, transparent);
  mask: linear-gradient(90deg, transparent, #000 35%, #000 65%, transparent);
  opacity: var(--glow);
  transform-origin: 50% 100%;
  transform: scaleY(calc(0.2 + 0.8 * var(--glow))) scaleX(calc(0.5 + 0.5 * var(--glow)));
}

/* ---- Lattice (inside the dusk stage; --lat and --head come from chest) ---- */
.lattice-inner {
  grid-area: 1 / 1;
  pointer-events: auto;
  width: min(1200px, calc((100vh - 230px) * 16 / 9));
  max-width: 100%;
  text-align: left;
  --lat: 0;
  --head: 0;
}
.lattice-inner .section-head {
  margin-bottom: 22px;
  opacity: var(--head);
  transform: translateY(calc((1 - var(--head)) * 16px));
}
.section-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 34px;
}
.section-head h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(28px, 4vw, 52px);
  letter-spacing: 0.035em;
  text-wrap: balance;
}
.section-head p {
  max-width: 44ch;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ss-text-dim);
  text-wrap: pretty;
}
.section-head p .stat { display: block; margin-top: 6px; color: var(--ss-muted); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; }
.lattice-box {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
}
/* the frame is a layer of its own, so it can arrive after the nodes */
.lattice-box::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid var(--ss-border);
  background: radial-gradient(60% 60% at 50% 50%, rgba(124, 92, 255, 0.1), transparent 70%), rgba(15, 19, 27, 0.8);
  box-shadow: inset 0 0 90px rgba(5, 7, 12, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
  opacity: var(--lat, 1);
  pointer-events: none;
}
.lattice-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* Draw-in: the script sets each edge's dash to its own screen length (with
   non-scaling strokes, dash units are pixels) and clears it once settled. */
.lattice-edges line {
  stroke-width: 0.8;
  vector-effect: non-scaling-stroke;
  transition: stroke var(--t-base) var(--ease-out), stroke-width var(--t-base) var(--ease-out),
    stroke-dashoffset 1.2s var(--ease-out);
}
.lattice-edges .e-hub { stroke: rgba(124, 92, 255, 0.14); }
.lattice-edges .e-bus { stroke: rgba(99, 230, 226, 0.18); }
.lattice-edges .e-hub.on { stroke: #ffd27a; stroke-width: 1.6; }
.lattice-edges .e-bus.on { stroke: #63e6e2; stroke-width: 1.6; }
.lattice-edges .pulse { pointer-events: none; }
.node {
  position: absolute;
  display: grid;
  justify-items: center;
  gap: 8px;
  text-align: center;
  --dx: 0px;
  --dy: 0px;
  /* --fx/--fy/--fs/--fr/--fo: the flight out of the chest; --lit: hover dimming */
  --fx: 0px;
  --fy: 0px;
  --fs: 1;
  --fr: 0deg;
  --fo: 1;
  --lit: 1;
  opacity: calc(var(--fo) * var(--lit));
  transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) translate(var(--fx), var(--fy)) rotate(var(--fr)) scale(var(--fs));
  transition: opacity var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-spring);
}
.lattice-box:not(.lattice-in) .node { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
.lattice-in:not(.lattice-settled) .node { transition-delay: calc(var(--k) * 45ms); }
/* while the scroll drives the flight nothing may ease on its own */
.lattice-box.unfolding .node { transition: none; }
.lattice-box.unfolding .lattice-edges line { transition: stroke var(--t-base) var(--ease-out), stroke-width var(--t-base) var(--ease-out); }
.node:hover, .node:focus-visible { transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(1.08); }
.node-ico { position: relative; }
.node .ico { transition: transform var(--t-base) var(--ease-out), filter var(--t-base) var(--ease-out); }
.node.lit .ico { filter: drop-shadow(0 0 10px rgba(99, 230, 226, 0.45)); }
.node.hub.lit .ico { filter: drop-shadow(0 0 12px rgba(255, 210, 122, 0.5)); }
.node.hub .ico { animation: hubglow 4s ease-in-out infinite; }
@keyframes hubglow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 10px 28px -6px rgba(0, 0, 0, 0.55), 0 0 0 rgba(255, 210, 122, 0); }
  50% { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 10px 28px -6px rgba(0, 0, 0, 0.55), 0 0 18px rgba(255, 210, 122, 0.35); }
}
.node .beat {
  position: absolute;
  inset: 0;
  border-radius: 24%;
  border: 1px solid var(--ss-arcane);
  opacity: 0;
  pointer-events: none;
}
.node .beat.on { animation: beat 1400ms var(--ease-out); }
@keyframes beat { from { transform: scale(1); opacity: 0.5; } to { transform: scale(2.2); opacity: 0; } }
.node span {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ss-text-dim);
  white-space: nowrap;
}
.lattice-caption {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 14px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ss-muted);
  pointer-events: none;
  opacity: var(--lat, 1);
  transition: opacity 200ms var(--ease-out);
}
.lattice-caption.swap { opacity: 0; }

/* ---- News ---- */
.news { padding: 40px 44px 100px; scroll-margin-top: 84px; }
.news-inner { max-width: 1100px; margin: 0 auto; }
.news-list { display: grid; border-top: 1px solid var(--ss-border); }
.news-item {
  display: grid;
  grid-template-columns: 132px 1fr;
  gap: 20px;
  padding: 22px 0;
  border-bottom: 1px solid var(--ss-border);
}
.news-date {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--ss-glow-gold);
  padding-top: 3px;
}
.news-title { font-size: 17px; font-weight: 500; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.news-title a { transition: color var(--t-fast) var(--ease-out); }
.news-title a:hover { color: var(--ss-glow-gold); }
.news-tools { display: inline-flex; gap: 6px; }
.news-tools button { all: unset; cursor: pointer; display: block; border-radius: 17%; }
.news-tools button:focus-visible { outline: 2px solid var(--ss-arcane-hi); outline-offset: 2px; }
.news-tools .ico:hover { transform: scale(1.08); }
.news-body { color: var(--ss-text-dim); max-width: 72ch; text-wrap: pretty; margin-top: 4px; }
.news-body a { color: var(--ss-sparkle); transition: color var(--t-fast) var(--ease-out); }
.news-body a:hover { color: var(--ss-glow-gold); }
.news-empty { padding: 22px 0; color: var(--ss-muted); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em; }

/* ---- License ---- */
.license { padding: 100px 44px 120px; border-top: 1px solid var(--ss-surface-2); scroll-margin-top: 84px; }
.license-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 60px;
  align-items: start;
}
.license .eyebrow { color: var(--ss-glow-gold); }
.license h2 {
  margin-bottom: 22px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(30px, 4vw, 56px);
  letter-spacing: 0.035em;
  line-height: 1.05;
  text-wrap: balance;
}
.license p { font-size: 16px; line-height: 1.6; color: var(--ss-text-dim); max-width: 40ch; text-wrap: pretty; }
.license-actions { display: flex; gap: 14px; margin-top: 30px; flex-wrap: wrap; }
.rules {
  display: grid;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.7;
  color: var(--ss-text-dim);
  border-top: 1px solid var(--ss-border);
}
.rule {
  display: grid;
  grid-template-columns: 36px 1fr;
  gap: 12px;
  padding: 16px 0;
  border-bottom: 1px solid var(--ss-border);
}
.rule span:first-child { color: var(--ss-glow-gold); }

/* ---- AI / docs callout ---- */
.ai-band { padding: 0 44px; }
.ai-callout {
  max-width: 1100px;
  margin: 0 auto;
  padding: 30px 32px;
  border: 1px dashed var(--ss-border-strong);
  border-radius: 16px;
  display: flex;
  gap: 28px;
  align-items: center;
  flex-wrap: wrap;
  background: rgba(15, 19, 27, 0.6);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
.ai-callout-copy { flex: 1; min-width: 280px; }
.ai-callout h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 24px;
  letter-spacing: 0.05em;
  margin-bottom: 10px;
}
.ai-callout p { color: var(--ss-text-dim); max-width: 66ch; text-wrap: pretty; }
.ai-callout code { font-family: var(--font-mono); font-size: 13px; color: var(--ss-sparkle); }
.ai-callout-actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* ---- Footer ---- */
.site-footer {
  max-width: 1100px;
  margin: 90px auto 0;
  padding: 0 44px 44px;
  display: flex;
  justify-content: space-between;
  gap: 20px 32px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ss-muted);
}
.site-footer nav { display: flex; gap: 24px; flex-wrap: wrap; }
.site-footer a { transition: color var(--t-fast) var(--ease-out); }
.site-footer a:hover { color: var(--ss-glow-gold); }

/* ---- Toast (unknown deep link) ---- */
.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%) translateY(8px);
  z-index: 70;
  padding: 10px 18px;
  border: 1px solid var(--ss-border);
  border-radius: var(--ss-radius-pill);
  background: rgba(15, 19, 27, 0.92);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ss-text);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.toast.on { opacity: 1; transform: translateX(-50%); }

/* ---- 404 ---- */
.lost {
  min-height: 100vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 100px 24px;
}
.lost-inner { display: grid; justify-items: center; gap: 22px; }
.lost-wisp {
  width: 120px;
  height: 124px;
  background: var(--ss-muted);
  -webkit-mask: url("assets/wisp-white.png") center / contain no-repeat;
  mask: url("assets/wisp-white.png") center / contain no-repeat;
}
.lost h1 { font-family: var(--font-display); font-weight: 500; font-size: clamp(32px, 5vw, 64px); letter-spacing: 0.035em; text-wrap: balance; }
.lost .mono { color: var(--ss-muted); }
.lost-actions { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-top: 10px; }

/* ---- Scroll reveal (motion-gated; content is visible by default) ---- */
@media (prefers-reduced-motion: no-preference) {
  .reveal { transition: opacity var(--t-reveal) var(--ease-out), transform var(--t-reveal) var(--ease-out); }
  .reveal:not(.in) { opacity: 0; transform: translateY(16px); }
  .reveal-lg:not(.in) { transform: translateY(28px); }
}

/* ---- Reduced motion: the ground still follows the scroll (the day half has
   to stay under the day column) but nothing moves on its own. ---- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.ready :is(#ground, .hero-mark, .hero h1, .hero-sub, .hero-actions, .topnav, .hero-scroll) { animation: none; }
  .hero-ring, .hero-orbiter, .hero-wisp, .hero-wisp::after, .hero-mark::before, .hero-mark::after, .hero-scroll i,
  .ticker-track, .wisp-mark, .twinkle, .chip-live::before, .node.hub .ico, .punch-btn.running { animation: none; }
  .hero-mark::after { opacity: 0.5; }
  .row-body, .row-panel, .row-arrow, .node, .pill, .ico, .lattice-edges line, .tex-stage canvas, .showcase p { transition: none; }
  .lattice-box:not(.lattice-in) .node { opacity: 1; transform: translate(-50%, -50%); }
  .lattice-edges line { stroke-dashoffset: 0; }
  .ticker-track { flex-wrap: wrap; width: auto; }
  .ticker { -webkit-mask-image: none; mask-image: none; }
  .punch-session .bar { animation: none; }
}

/* ---- Responsive ----
   Below the breakpoint the two halves stack, so the day column can no longer
   sit on the fixed daylight — it brings its own light instead, and the ground
   stays at night (see NARROW in home.js). ---- */
@media (max-width: 980px) {
  .shelf { grid-template-columns: minmax(0, 1fr); }
  .shelf-col { padding: 48px 20px 60px; }
  .keystone-band { padding: 48px 20px 0; }
  .keystone { padding: 22px 20px; }
  .keystone-head { grid-template-columns: 64px 1fr; gap: 16px; }
  .keystone h2 { font-size: 22px; }
  .shelf-light {
    background: linear-gradient(180deg, #fff6dc 0%, #f4ead3 70%);
    border-radius: 24px;
    margin: 0 12px 60px;
    padding: 40px 20px 48px;
  }
  .shelf-light .sticky-group { position: static; }
  .wisp-mark, .twinkle { display: none; }
  .dawn { height: auto; margin-top: 0; }
  .shelf { margin-top: 0; }
  .dawn-stick {
    position: static;
    height: auto;
    grid-template-columns: 1fr;
    gap: 40px;
    padding: 80px 20px;
    mix-blend-mode: normal;
  }
  .dawn-dark, .dawn-light { opacity: 1; text-align: left; }
  .dawn-light p { margin-left: 0; }
  .dawn-light .eyebrow { color: var(--ss-glow-gold); opacity: 1; }
  .news, .license, .ai-band { padding-left: 20px; padding-right: 20px; }
  .site-footer { padding-left: 20px; padding-right: 20px; }
  .license-inner { grid-template-columns: 1fr; gap: 40px; }
  .lattice-box { min-height: 0; aspect-ratio: 4 / 3; }
}

@media (max-width: 680px) {
  .topnav { padding: 16px 20px; gap: 12px; }
  .topnav-links { gap: 16px; }
  .topnav-links .nav-more { display: none; }
  .topnav-brand span { display: none; }
  .hero { padding: 84px 20px 64px; }
  .row-head { grid-template-columns: 40px 1fr auto; gap: 12px; padding: 12px; }
  .ico-sm { width: 40px; height: 40px; }
  .row-arrow { display: none; }
  .row-light .row-head { grid-template-columns: 1fr 56px; padding: 18px; }
  .row-light .ico-lg { width: 56px; height: 56px; }
  .demo-sheet, .demo-tex, .demo-punch, .showcase { grid-template-columns: 1fr; }
  .demo-punch { justify-items: center; }
  .ico-xl { width: 72px; height: 72px; }
  .news-item { grid-template-columns: 1fr; gap: 4px; }
  .site-footer { margin-top: 60px; letter-spacing: 0.2em; }
  .node:not(.hub) > span:last-child { display: none; } /* 16 labels do not fit a phone; the icons carry it */
  input[type="range"]::-webkit-slider-thumb { width: 18px; height: 18px; margin-top: -8px; }
  input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; }
}

/* ---- No chest: narrow viewports and reduced motion get the headline and
   the lattice as plain flow, the lattice entering as it always did. ---- */
@media (max-width: 980px), (prefers-reduced-motion: reduce) {
  .dusk { height: auto; margin-top: 0; }
  .dusk-stick { position: static; display: block; height: auto; padding: 80px 20px 40px; overflow: visible; pointer-events: auto; }
  .dusk-inner { opacity: var(--dusk-o, 0); transform: translateY(calc((1 - var(--dusk-o, 0)) * 40px)); }
  .chest-scene { display: none; }
  .lattice-inner { width: auto; max-width: 1200px; margin: 72px auto 0; --lat: 1; --head: 1; }
  .lattice-inner .section-head { margin-bottom: 34px; opacity: 1; transform: none; }
  .lattice-anchor { top: auto; bottom: 0; height: min(100vh, 100%); }
}

/* a phone gets a taller lattice box, so sixteen icons and the caption fit */
@media (max-width: 680px) {
  .lattice-box { aspect-ratio: 4 / 5; }
  .lattice-caption { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
