/* ═══════════════════════════════════════════════════════════════════
   Biogon — biogon.moonlitfoundry.dev

   The page is a readout. The game's screen is tracked caps labels,
   hairline rules and mono numerals against slate, and this borrows
   that grammar rather than sitting a marketing layout on top of it.

   Colour comes from src/ui/theme.ts and means the same thing here as
   it does in the app: species carry the teal→pink ramp, amber is the
   single next action and never decorative, red is broken, green is
   contamination.
   ═══════════════════════════════════════════════════════════════ */

/* Archivo and Source Serif are the studio's faces, carried over so a
   visitor arriving from moonlitfoundry.dev lands in the same voice.
   Both keep their variable axes — the wordmark is Archivo at wdth 122,
   which is what makes it read as stamped rather than styled. */
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
}
@font-face {
  font-family: "SourceSerif";
  src: url("fonts/source-serif-var.woff2") format("woff2-variations");
  font-weight: 200 900;
  font-display: swap;
}

/* ── palette ────────────────────────────────────────────────────────
   Both themes are the app's, value for value. Species are fixed hex
   rather than derived: the ramp is authored, and interpolating it
   would drift the middle tiers off the colours the screenshots show. */
:root {
  --ground: #0f1216;
  --panel: #171c22;
  --panel2: #1d242c;
  --line: #262e37;
  --text: #dfe4ea;
  --muted: #79848f;
  --dim: #4d5761;
  --accent: #f0a52e;
  --accent-ink: #1a1206;
  --alert: #e2574c;
  --foe: #93c246;
  --foe-core: #4e7018;
  --comp: #f0873f;

  --s0: #3fc9bb;
  --s1: #3f9fe0;
  --s2: #7a7cee;
  --s3: #ab63ee;
  --s4: #dd5cc2;
  --s5: #ee5c86;

  --glow: #f0a52e26;
  --shot-edge: #262e37;

  color-scheme: dark;
}

:root[data-theme="light"] {
  --ground: #e9ebee;
  --panel: #ffffff;
  --panel2: #f3f5f7;
  --line: #d2d7de;
  --text: #161b21;
  --muted: #69737e;
  --dim: #a3acb6;
  --accent: #b8720a;
  --accent-ink: #fff8ec;
  --alert: #c33c33;
  --foe: #6d9420;
  --foe-core: #37500c;
  --comp: #bf5f16;

  --s0: #159c8e;
  --s1: #1f76b8;
  --s2: #5a54d8;
  --s3: #8b3dcf;
  --s4: #b8309b;
  --s5: #c9315f;

  --glow: #b8720a24;
  /* Screenshots are dark captures. On the light page they need a border
     dark enough to be an edge rather than a halo. */
  --shot-edge: #b9c0c9;

  color-scheme: light;
}

/* No [data-theme="dark"] block — the base above is it, so returning to
   dark is removing the attribute rather than setting a second one. */

:root {
  --display: "Archivo", ui-sans-serif, system-ui, sans-serif;
  --body: "SourceSerif", Georgia, "Times New Roman", serif;
  --mono: ui-monospace, "SF Mono", "Menlo", "DejaVu Sans Mono", monospace;
  --measure: 62ch;
  --pad: clamp(20px, 5vw, 64px);
  --wide: 1180px;
}

/* ── base ───────────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  color: var(--text);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

main { flex: 1 0 auto; }

p { margin: 0; max-width: var(--measure); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--text); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

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

.skip {
  position: absolute; left: 8px; top: -60px; z-index: 100;
  background: var(--text); color: var(--ground);
  padding: 9px 14px; border-radius: 2px;
  font-family: var(--display); font-size: 12px;
  letter-spacing: .12em; text-transform: uppercase; text-decoration: none;
  transition: top .15s ease;
}
.skip:focus { top: 8px; }

.wrap { width: 100%; max-width: var(--wide); margin-inline: auto; padding-inline: var(--pad); }

/* ── typographic roles ──────────────────────────────────────────────
   Tracked uppercase labels are the app's, and they are the only case
   treatment on the page that is not sentence case. Display headings
   stay lowercase, which is the studio's. */
.label {
  font-family: var(--display);
  font-variation-settings: "wdth" 88, "wght" 620;
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); margin: 0;
}

.h2 {
  font-family: var(--display);
  font-variation-settings: "wdth" 112, "wght" 740;
  font-size: clamp(24px, 3.6vw, 36px);
  line-height: 1.1; letter-spacing: -.014em; text-transform: lowercase;
  text-wrap: balance; margin: 0;
}

.h3 {
  font-family: var(--display);
  font-variation-settings: "wdth" 100, "wght" 700;
  font-size: 15px; letter-spacing: .04em; text-transform: uppercase;
  margin: 0;
}

.lede { font-size: clamp(17.5px, 2vw, 20px); line-height: 1.58; color: var(--muted); }
.lede strong { color: var(--text); font-weight: 600; }

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ── section header ─────────────────────────────────────────────────
   The app's `CHAMBERS ————— 3/3 slots`: a label, a rule that takes
   the slack, and an optional figure on the right. It is the page's
   only section divider, so sections need no top border of their own. */
.head {
  display: flex; align-items: center; gap: 16px;
  margin-bottom: clamp(22px, 3vw, 34px);
}
.head::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}
.head .fig {
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  color: var(--muted); text-transform: uppercase; order: 3;
}

.band { padding: clamp(48px, 7vw, 96px) 0; }
.band > .wrap > .h2 { max-width: 18ch; }
.band-intro { margin-top: 16px; }

/* ── masthead ───────────────────────────────────────────────────── */
.masthead {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.masthead > .wrap {
  display: flex; align-items: center; gap: 12px 22px;
  padding-block: 12px;
}

.lockup { display: inline-flex; align-items: center; gap: 11px; text-decoration: none; color: var(--text); }
.lockup .mk { width: 28px; height: 28px; flex: none; display: block; }
.lockup .wm {
  font-family: var(--display);
  font-variation-settings: "wdth" 122, "wght" 800;
  font-size: 14px; letter-spacing: .13em; text-transform: uppercase;
  line-height: 1; white-space: nowrap;
}
.lockup:hover { color: var(--text); }

/* The bar carries the play button and the theme control and nothing
   else. Section anchors would be a table of contents for six screens
   of scrolling, on a page whose only destination is the game.

   Below the fold the masthead is the only place the play button lives,
   so it joins the bar on the way past the hero rather than sitting
   there from the first paint and competing with the hero's own. */
.mast-cta {
  margin-left: auto;
  opacity: 0; transform: translateY(-4px); pointer-events: none;
  transition: opacity .25s ease, transform .25s ease;
}
.masthead[data-stuck="true"] .mast-cta { opacity: 1; transform: none; pointer-events: auto; }

.icon-btn {
  display: grid; place-items: center;
  width: 30px; height: 30px; padding: 0;
  color: var(--muted); background: none;
  border: 1px solid var(--line); border-radius: 2px;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease;
}
.icon-btn:hover { color: var(--text); border-color: var(--muted); }
.icon-btn svg { width: 15px; height: 15px; display: block; }
/* One button, two glyphs — whichever the click would switch to. */
.icon-btn .sun { display: none; }
:root[data-theme="light"] .icon-btn .sun { display: block; }
:root[data-theme="light"] .icon-btn .moon { display: none; }

/* ── buttons ────────────────────────────────────────────────────────
   Amber is the single next action in the app and holds that meaning
   here: exactly one filled control on the page at a time, and it is
   always Play. */
.btn {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--display);
  font-variation-settings: "wdth" 92, "wght" 700;
  font-size: 12.5px; letter-spacing: .1em; text-transform: uppercase;
  padding: 11px 18px; border-radius: 2px;
  text-decoration: none; cursor: pointer;
  border: 1px solid transparent;
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.btn-hot { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn-hot:hover { background: var(--text); border-color: var(--text); color: var(--ground); }
.btn-ghost { color: var(--muted); border-color: var(--line); }
.btn-ghost:hover { color: var(--text); border-color: var(--muted); }
.btn-sm { font-size: 11px; padding: 8px 13px; }

/* ── hero ───────────────────────────────────────────────────────────
   Two columns on a wide screen: the pitch left, the readout right.
   The readout is the hero image — a screenshot at this size would be
   a phone in a well of dead space. */
.hero { position: relative; overflow: hidden; border-bottom: 1px solid var(--line); }
.hero > .wrap {
  padding-block: clamp(48px, 8vw, 104px) clamp(44px, 6vw, 84px);
  display: grid; gap: clamp(38px, 5vw, 64px);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}
@media (min-width: 900px) {
  .hero > .wrap { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); }
}

/* A single amber wash, low and behind the wordmark. The ground is
   otherwise flat — the app has no gradients in it and a page full of
   them reads as a different product. Kept off the top edge, where it
   would show through the translucent masthead as a brown band. */
.hero::before {
  content: ""; position: absolute; z-index: 0;
  top: 4%; left: -12%; width: 48%; height: 66%;
  background: radial-gradient(closest-side, var(--glow), transparent 72%);
  pointer-events: none;
}
.hero-copy { position: relative; z-index: 1; display: grid; gap: 24px; justify-items: start; }

.hero-mark { width: clamp(58px, 8vw, 84px); height: clamp(58px, 8vw, 84px); display: block; }

.hero h1 {
  font-family: var(--display);
  font-variation-settings: "wdth" 122, "wght" 800;
  font-size: clamp(46px, 9vw, 92px);
  line-height: .94; letter-spacing: .02em; text-transform: uppercase;
  margin: 0;
}
.hero .lede { max-width: 34ch; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }

.plat {
  font-family: var(--display);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--dim); margin: 0;
}

/* ── readout ────────────────────────────────────────────────────────
   The app's top bar, live. Numbers move because a still frame of an
   idle game is the one thing that cannot show what it is. */
.readout {
  position: relative; z-index: 1;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: 4px;
  padding: 18px 20px 20px;
  display: grid; gap: 16px;
  box-shadow: 0 24px 60px #0006;
}
:root[data-theme="light"] .readout { box-shadow: 0 18px 44px #0f121612; }

.ro-top { display: flex; align-items: flex-start; gap: 16px; }
.ro-stat { display: grid; gap: 2px; }
.ro-stat.right { margin-left: auto; text-align: right; }
.ro-val {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: clamp(26px, 4vw, 34px); line-height: 1.05; letter-spacing: -.02em;
}
.ro-rate { font-family: var(--mono); font-size: 12px; }
.ro-rate.up { color: var(--s0); }
.ro-rate.hot { color: var(--accent); }

.ro-waste { display: flex; align-items: center; gap: 12px; }
.ro-waste .label { color: var(--dim); }
.ro-bar { flex: 1; height: 3px; background: var(--line); border-radius: 2px; overflow: hidden; }
.ro-bar i { display: block; height: 100%; background: var(--alert); width: 22%; transition: width .8s linear; }

/* Species row, as the app draws it: a tile per owned species, a bar
   under each, and the selected one ringed. */
.ro-species { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.ro-cell {
  background: var(--panel2); border: 1px solid var(--line); border-radius: 4px;
  padding: 10px 8px 8px; display: grid; gap: 7px; justify-items: center;
}
.ro-cell[data-sel="true"] { border-color: color-mix(in srgb, var(--c) 60%, transparent); background: color-mix(in srgb, var(--c) 9%, var(--panel2)); }
.ro-cell .poly { width: 15px; height: 15px; }
.ro-cell .n { font-family: var(--mono); font-size: 13px; font-variant-numeric: tabular-nums; }
.ro-cell .fill { width: 100%; height: 2px; background: var(--line); border-radius: 2px; overflow: hidden; }
.ro-cell .fill i { display: block; height: 100%; background: var(--c); transition: width .9s linear; }

.ro-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-top: 13px; border-top: 1px solid var(--line);
}
.ro-foot .label { color: var(--dim); }
.ro-foot .st { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
.ro-foot .st b { color: var(--accent); font-weight: 400; }

/* ── polygons ───────────────────────────────────────────────────────
   One class, coloured by --c wherever it is used. Filled below the
   fusion chain and stroked inside it, because the chain is about the
   step between two of them and outlines read as a diagram.

   Paint goes on the element itself, never on a descendant: the shapes
   arrive through <use>, and only inherited properties — fill, stroke,
   stroke-width, stroke-linejoin — cross into that shadow tree. */
.poly { display: block; fill: var(--c); stroke: none; }
.poly.hollow { fill: none; stroke: var(--c); stroke-width: 7; stroke-linejoin: round; }

/* ── the ladder ─────────────────────────────────────────────────────
   The game's one irreducible idea, so it gets the diagram. Six rungs
   and the five steps between them, read bottom-up. Nothing here is a
   control: a species is a rung and a colour, and there is no second
   fact about one worth a panel to hold it. */
.rungs { display: grid; gap: 8px; list-style: none; margin: 0; padding: 0; }

.rung {
  display: grid; grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: center; gap: 14px;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: 4px;
  border-left: 2px solid var(--c);
  padding: 11px 14px;
}
.rung .poly { width: 26px; height: 26px; }
.rung .nm {
  font-family: var(--display);
  font-variation-settings: "wdth" 100, "wght" 640;
  font-size: 13px; letter-spacing: .09em; text-transform: uppercase;
}
.rung .yd { font-family: var(--mono); font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* The step between two rungs, drawn in the gutter rather than as a row
   of its own: it is the edge between them, not a seventh thing. */
.step {
  display: grid; grid-template-columns: 34px minmax(0, 1fr);
  align-items: center; gap: 14px;
  padding: 0 14px; margin: -2px 0;
  font-family: var(--mono); font-size: 11.5px; color: var(--muted);
}
.step .tick { justify-self: center; width: 1px; height: 16px; background: var(--line); }
.step .cost { color: var(--alert); }

/* ── screens ────────────────────────────────────────────────────────
   Phone captures, so height is the shared dimension — equal widths
   would leave a ragged row. They scroll horizontally under the
   breakpoint rather than stacking into a column of tall images. */
.shots {
  list-style: none; margin: 0; padding: 0 var(--pad) 6px;
  display: flex; gap: 16px;
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
.shots > li { scroll-snap-align: center; flex: none; display: grid; gap: 12px; width: min(74vw, 260px); }
.shots img {
  display: block; width: 100%; height: auto;
  background: var(--ground);
  border: 1px solid var(--shot-edge); border-radius: 6px;
}
.shots figcaption, .shots .cap {
  font-family: var(--display);
  font-variation-settings: "wdth" 92, "wght" 500;
  font-size: 13px; line-height: 1.45; color: var(--muted);
}
.shots .cap b {
  display: block;
  font-variation-settings: "wdth" 88, "wght" 680;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text); margin-bottom: 4px;
}
/* The strip breaks the page's max-width on purpose: captures running
   to the edge say there are more of them than the row can hold. */
.shots-rail { margin-inline: calc(50% - 50vw); width: 100vw; }
.shots-rail .shots { max-width: calc(var(--wide) + 2 * var(--pad)); margin-inline: auto; }

/* ── field / prose split ────────────────────────────────────────────
   Used by the two sections that are an argument rather than a list. */
.split { display: grid; gap: clamp(26px, 4vw, 56px); }
@media (min-width: 880px) { .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; } }
.split-copy { display: grid; gap: 18px; align-content: start; justify-items: start; }
.split-copy p { font-size: 16.5px; }

.facts { list-style: none; margin: 0; padding: 0; display: grid; }
.facts li {
  display: grid; grid-template-columns: minmax(96px, auto) 1fr; gap: 4px 20px;
  padding: 13px 0; border-top: 1px solid var(--line); align-items: baseline;
}
.facts li:last-child { border-bottom: 1px solid var(--line); }
.facts .k {
  font-family: var(--display);
  font-variation-settings: "wdth" 88, "wght" 660;
  font-size: 10.5px; letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}
.facts .v { font-size: 16px; line-height: 1.45; }
.facts .v .num { font-size: 15px; }

/* The contamination field, as the battle screen draws it: blobs on an
   arc, a base at the centre, and the interception radius between. */
.field-art {
  position: relative; aspect-ratio: 4 / 3; width: 100%;
  background: var(--panel); border: 1px solid var(--line); border-radius: 4px;
  overflow: hidden;
}
.field-art svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.blob { fill: var(--foe); stroke: var(--foe-core); stroke-width: 1.6; }
.blob-core { fill: var(--foe-core); }
.reach { fill: none; stroke: var(--dim); stroke-width: 1; }
.shot.s0 { fill: var(--s0); }
.shot.s1 { fill: var(--s1); }
.shot.s2 { fill: var(--s2); }
.base-ring { fill: none; stroke: var(--s0); stroke-width: 1; opacity: .45; }
.base {
  fill: var(--s0); fill-opacity: .22;
  stroke: var(--s0); stroke-width: 1.8; stroke-linejoin: round;
}
.drift { animation: drift 9s cubic-bezier(.4, 0, .75, .5) infinite; }
@keyframes drift {
  0%   { transform: translate(0, 0); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)); opacity: 0; }
}
.tracer { animation: tracer 2.6s linear infinite; }
@keyframes tracer {
  0%   { transform: translate(0, 0); opacity: 0; }
  10%  { opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .drift, .tracer { animation: none; }
}

/* ── closing call ───────────────────────────────────────────────── */
.close { border-top: 1px solid var(--line); }
.close > .wrap {
  padding-block: clamp(56px, 8vw, 104px);
  display: grid; gap: 22px; justify-items: center; text-align: center;
}
.close .h2 { max-width: 20ch; }
.close p { color: var(--muted); }

/* ── footer ─────────────────────────────────────────────────────── */
.foot { border-top: 1px solid var(--line); }
.foot > .wrap {
  padding-block: clamp(22px, 3vw, 32px);
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px 28px;
}
.foot .lockup { margin-right: auto; }
.foot .lockup .mk { width: 22px; height: 22px; }
.foot .lockup .wm { font-size: 11.5px; }
.foot-nav { display: flex; flex-wrap: wrap; gap: 10px 24px; padding: 0; margin: 0; list-style: none; }
.foot-nav a, .foot-copy {
  font-family: var(--display);
  font-variation-settings: "wdth" 88, "wght" 560;
  font-size: 11px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.foot-nav a:hover { color: var(--text); }
.foot-copy { color: var(--dim); }

/* ── motion ─────────────────────────────────────────────────────── */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes wipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }

.hero-mark { animation: rise .5s cubic-bezier(.2,.7,.3,1) both; }
.hero h1 { animation: wipe .7s cubic-bezier(.4,0,.15,1) .12s both; }
.hero .lede { animation: rise .5s ease .5s both; }
.hero-actions { animation: rise .5s ease .62s both; }
.hero .plat { animation: rise .5s ease .7s both; }
.readout { animation: rise .6s cubic-bezier(.2,.7,.3,1) .3s both; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ── narrow ─────────────────────────────────────────────────────────
   The section figures are cut rather than reflowed: squeezing one
   wraps the label beside it and breaks the rule between them. */
@media (max-width: 620px) {
  .head .fig { display: none; }
}

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
