/* 2048 Surge — Ember, the indexable landing page at `/`.
 *
 * This is the approved landing design, ported verbatim from the lab artifact
 * `web/lab/landing-concepts/concept-a.html`. Every value below is the approved
 * one; nothing here was re-tuned on the way in. The runtime that drives the
 * three animations lives in `web/production/landing.ts`.
 *
 * Spec: /DESIGN.md. The inner pages (contact, legal) keep site.css + pages.css;
 * this page owns its own sheet so the two worlds cannot collide on the generic
 * class names both use (`.board`, `.clock`, `.live`, `.lbl`).
 *
 * The lab file pulled its three faces from a public font host. The public
 * landing is the product's only indexable page and must not depend on one —
 * that is a second origin in the critical path and one more party that sees
 * every visitor — so the same faces are self-hosted here, as the rest of the
 * production site already does. See web/production/fonts/README.md.
 */

@font-face { font-family: "Archivo"; src: url("/site/fonts/archivo-700.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Archivo"; src: url("/site/fonts/archivo-800.woff2") format("woff2"); font-weight: 800; font-style: normal; font-display: swap; }
@font-face { font-family: "Instrument Sans"; src: url("/site/fonts/instrument-sans-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Instrument Sans"; src: url("/site/fonts/instrument-sans-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Instrument Sans"; src: url("/site/fonts/instrument-sans-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/site/fonts/ibm-plex-mono-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/site/fonts/ibm-plex-mono-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }

/* ------------------------------------------------------------------ *
 * Ember tokens. Source of truth: /DESIGN.md and mobile/theme/tokens.ts
 * ------------------------------------------------------------------ */
:root {
  --ground: #fff8f2;
  --surface: #ffffff;
  --hairline: #eedfce;
  --ink: #24120a;
  --ink-dim: #7a5a46;

  --hot: #e8460c;
  --gold: #ffb300;
  --rare: #ffe9a8;

  /* Tile ramp, from DESIGN.md. Every pair clears 4.5:1; the numeral
     colour flips to cream at 64, which is why the cards above that value
     set `color: #fff`. */
  --t2: #fff0dc;
  --t4: #ffdfb5;
  --t8: #ffc24d;
  --t16: #ff9a1f;
  --t32: #f26b0a;
  --t64: #ce4000;
  --t128: #b81d00;
  --t256: #a21700;

  /* Discord blurple. The only cool hue on this page, and deliberate: it is a
     third party's trademark, not part of Ember. Their brand guidelines allow
     blurple, black or white. */
  --discord: #5865f2;
  --discord-deep: #4752c4;

  --plate: 14px;
  --identity: 24px;

  --display: 'Archivo', system-ui, sans-serif;
  --ui: 'Instrument Sans', system-ui, sans-serif;
  --data: 'IBM Plex Mono', ui-monospace, monospace;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.5;
  overflow-x: hidden;
  /* No shadows anywhere: depth is the field behind and the border in front. */
}

/* The field. One fixed canvas, edge to edge, behind everything. */
#field {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  pointer-events: none;
  opacity: 0.92;
  transition: opacity 320ms ease;
}

/* The heat runs right to the edge, but it must not fight the reading:
   a cream veil sits over the field wherever long copy lives. */
#veil {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* Kept light on purpose: cream over the deepest bands at any real
     strength turns the heat into a taupe haze — the same dead-grey
     failure DESIGN.md warns about for char. The plates do the work of
     protecting the reading; this only takes the edge off. */
  background: linear-gradient(
    180deg,
    rgba(255, 248, 242, 0) 0%,
    rgba(255, 248, 242, 0.16) 45%,
    rgba(255, 248, 242, 0.34) 100%
  );
}

.page {
  position: relative;
  z-index: 1;
}

.wrap {
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 20px;
}

/* ------------------------------------------------------------------ *
 * Plates — anything read at a glance gets its own opaque surface.
 * ------------------------------------------------------------------ */
.plate {
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: var(--plate);
  padding: 22px 24px;
}

.plate--identity {
  border-width: 3px;
  border-radius: var(--identity);
}

/* ------------------------------------------------------------------ *
 * Type
 * ------------------------------------------------------------------ */
h1,
h2,
h3 {
  margin: 0;
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 0.94;
}

h1 {
  font-size: clamp(2.9rem, 8.2vw, 5.6rem);
}

h1 span {
  display: block;
  color: var(--ink);
}

h2 {
  font-size: clamp(1.75rem, 4vw, 2.6rem);
}

h3 {
  font-weight: 700;
  font-size: 1.125rem;
  letter-spacing: -0.01em;
}

p {
  margin: 0;
}

.mono {
  font-family: var(--data);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.lede {
  font-size: clamp(1rem, 1.55vw, 1.1875rem);
  line-height: 1.55;
  color: var(--ink);
}

.lede b {
  font-weight: 700;
}

.dim {
  color: var(--ink-dim);
}

.figure {
  font-family: var(--display);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * Actions. Ink is the action; nothing clickable is orange.
 * ------------------------------------------------------------------ */
.action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 52px;
  padding: 0 30px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: var(--ground);
  font-family: var(--ui);
  font-weight: 600;
  font-size: 1.0625rem;
  text-decoration: none;
  cursor: pointer;
  transition: transform 140ms ease, background-color 140ms ease, color 140ms ease;
}

.action:hover {
  transform: translateY(-1px);
}

/* Outline buttons are FILLED, never transparent — the heat behind would
   otherwise read straight through the label. */
.action--quiet {
  background: var(--surface);
  color: var(--ink);
}

.action--quiet:hover {
  background: var(--ground);
}

.action--small {
  min-height: 42px;
  padding: 0 20px;
  font-size: 0.9375rem;
}

a:focus-visible,
button:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 10px 18px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

.skip:focus {
  left: 16px;
  top: 16px;
  z-index: 5;
}

/* ------------------------------------------------------------------ *
 * Header
 * ------------------------------------------------------------------ */
.top {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0 0;
}

.top__mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 8px 16px 8px 10px;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--display);
  font-weight: 800;
  font-size: 0.9375rem;
  letter-spacing: -0.01em;
}

.top__coal {
  width: 16px;
  height: 16px;
  display: block;
  flex: none;
  border-radius: 2px;
  image-rendering: pixelated;
}

.top nav {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 6px;
}

.top nav a {
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.9375rem;
  padding: 8px 14px;
  border-radius: 999px;
}

.top nav a:hover {
  background: var(--ground);
}

/* `.top nav a` outranks `.action`, so the pill's cream label has to be
   restated here or the button paints ink on ink and reads as empty. */
.top nav a.action {
  margin-left: 4px;
  color: var(--ground);
}

.top nav a.action:hover {
  background: var(--ink);
}

/* ------------------------------------------------------------------ *
 * Hero
 * ------------------------------------------------------------------ */
/* Two columns: the identity and the actions on the left, the live match
   on the right. Below 900px the demo drops underneath. */
/* The demo column gives ground first. "vs. Player." is one unbreakable
   run, so a fixed 520px demo squeezes the copy column below the h1's
   min-content in the 900-1050 band and the title runs into the board. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, clamp(340px, 40vw, 520px));
  align-items: center;
  gap: clamp(26px, 3.6vw, 50px);
  padding: clamp(28px, 5vw, 58px) 0 clamp(44px, 6vw, 78px);
}

.hero__copy {
  display: grid;
  gap: 24px;
  justify-items: start;
  min-width: 0;
}

.identity {
  width: 100%;
  display: grid;
  gap: 14px;
  justify-items: start;
  padding: 22px clamp(16px, 2.2vw, 26px) 20px;
}

/* The rasterised wordmark. The canvas is never CSS-scaled: its box is
   exactly cols*cell by rows*cell. */
#wordmark-host {
  width: 100%;
  display: flex;
  justify-content: flex-start;
}

#wordmark-host canvas {
  display: block;
  image-rendering: pixelated;
}

/* The heading sits under the wordmark inside the plate, so it is set at
   reading size in the interface face — it is subtext, not a display
   line. Bumping it to display size would compete with the wordmark
   directly above it. */
.identity__sub,
.identity__detail {
  max-width: 46ch;
  font-family: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink-dim);
  text-wrap: pretty;
}

.identity__sub {
  margin: 14px 0 0;
  font-weight: 400;
  letter-spacing: 0;
}

.identity__sub b {
  font-weight: 600;
  color: var(--ink);
}

.identity__detail {
  margin: 4px 0 0;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* ------------------------------------------------------------------ *
 * The live match. Decorative and aria-hidden: everything it shows is
 * also said in the copy. It borrows the identity plate for its surface,
 * so there is no second palette — only Ember tokens below.
 * ------------------------------------------------------------------ */
.match {
  margin: 0;
  width: 100%;
  padding: 18px;
}

.match__bar {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 2px solid var(--hairline);
}

.lbl {
  display: block;
  font-family: var(--data);
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.fig {
  display: block;
  margin-top: 4px;
  font-family: var(--display);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  font-size: 2.125rem;
  line-height: 1;
  letter-spacing: -0.02em;
}

.fig--gold {
  color: var(--gold);
}

.fig--hot {
  color: var(--hot);
}

.clockbox {
  margin-left: auto;
  text-align: right;
}

.clock {
  display: block;
  margin-top: 4px;
  font-family: var(--data);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: 1.375rem;
  line-height: 1;
  color: var(--ink);
}

.live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--data);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--hot);
  background: rgba(232, 70, 12, 0.12);
  padding: 4px 9px;
  border-radius: 999px;
}

.live i {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--hot);
  animation: beat 1.6s ease-in-out infinite;
}

@keyframes beat {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.25;
  }
}

/* The rail is a fraction of your board, not a fixed width: at a fixed
   150px it creeps up to the same size as your board on a narrow desktop
   and the two stop reading as "mine" and "theirs". */
.match__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.46fr);
  gap: 16px;
  align-items: start;
}

.boardwrap {
  position: relative;
}

/* The board ground is char-dark ink, so the tile ramp reads as heat. */
.board {
  position: relative;
  width: 100%;
  background: var(--ink);
  border-radius: 4px;
}

/* `.tile` is already the mode-card chip in this page, so the match tile
   gets its own name rather than a second meaning for the same class. */
.mtile {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  font-family: var(--display);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  will-change: transform;
}

.mtile.slide {
  transition: transform 110ms cubic-bezier(0.25, 0.6, 0.3, 1);
}

.mtile.pop {
  animation: pop 170ms cubic-bezier(0.3, 1.5, 0.5, 1);
}

.mtile.born {
  animation: born 170ms ease-out;
}

@keyframes pop {
  0% {
    transform: var(--tf) scale(1);
  }
  45% {
    transform: var(--tf) scale(1.14);
  }
  100% {
    transform: var(--tf) scale(1);
  }
}

@keyframes born {
  0% {
    opacity: 0;
    transform: var(--tf) scale(0.5);
  }
  100% {
    opacity: 1;
    transform: var(--tf) scale(1);
  }
}

.gain {
  position: absolute;
  pointer-events: none;
  z-index: 3;
  font-family: var(--display);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  font-size: 0.9375rem;
  color: var(--gold);
  animation: gain 900ms ease-out forwards;
}

.gain--hot {
  color: var(--hot);
  font-size: 0.75rem;
}

/* The keyframes carry translateX(-50%) themselves. Setting it inline and then
   animating `transform` silently discarded the centring the moment the
   animation started, so every float drifted half its width to the right of the
   tile it belonged to.

   `--rise` is per-instance: the opponent rail is a fraction of the main board's
   height, and a fixed 26px lifted its floats clear out of the mini board and on
   top of the score above it. */
@keyframes gain {
  0% {
    opacity: 0;
    transform: translate(-50%, 4px);
  }
  25% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, calc(-1 * var(--rise, 26px)));
  }
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rail__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
}

.rail .fig {
  font-size: 1.375rem;
}

.rail__note {
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--ink-dim);
}

.match__foot {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 2px solid var(--hairline);
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  font-family: var(--data);
  font-size: 0.625rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.match__foot b {
  color: var(--ink);
  font-weight: 600;
}

/* ------------------------------------------------------------------ *
 * Sections
 * ------------------------------------------------------------------ */
section {
  padding: clamp(30px, 5vw, 52px) 0;
}

.head {
  display: grid;
  gap: 10px;
  max-width: 640px;
  margin-bottom: 22px;
}

.head--plate {
  display: inline-grid;
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: var(--plate);
  padding: 18px 22px;
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

.grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}

.card {
  display: grid;
  gap: 10px;
  align-content: start;
}

.card p {
  color: var(--ink-dim);
  font-size: 0.9375rem;
}

.card .num {
  font-family: var(--data);
  font-weight: 600;
  font-size: 0.6875rem;
  letter-spacing: 0.11em;
  color: var(--ink-dim);
}

.tile {
  width: 46px;
  height: 46px;
  border-radius: 2px;
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  font-size: 0.9375rem;
  color: var(--ink);
}

.rule {
  height: 2px;
  background: var(--hairline);
  border: 0;
  margin: 4px 0;
}

/* The rating ladder. The chips run up the tile ramp, so the tiers read
   as heat; Diamond takes the `rare` cream and an ink border, the same
   treatment the 2048 tile gets on the board. Nothing here is a control. */
.tiers {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 24px;
  margin-top: 14px;
  padding: 18px 22px;
}

.tiers__start {
  display: grid;
  gap: 2px;
}

.tiers__figure {
  font-size: clamp(1.625rem, 3vw, 2.125rem);
  line-height: 1;
  color: var(--gold);
}

.tiers__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(94px, 1fr));
  gap: 10px;
}

.tiers__list li {
  display: grid;
  gap: 6px;
  justify-items: center;
}

.tier {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 34px;
  border: 2px solid transparent;
  border-radius: 2px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.875rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.tier--top {
  border-color: var(--ink);
}

.split {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 14px;
  align-items: stretch;
}

.kbds {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
}

.kbds li {
  font-family: var(--data);
  font-size: 0.8125rem;
  font-weight: 500;
  border: 2px solid var(--ink);
  border-radius: 8px;
  padding: 7px 12px;
  background: var(--ground);
}

/* The guide cluster. One indexed reading trail rather than a feed of generic
 * posts: the title is the crawlable anchor and the description separates each
 * search intent. */
.guide-list { margin: 0; padding: 0; list-style: none; }
.guide-list li {
  display: grid;
  grid-template-columns: minmax(14rem, .75fr) minmax(0, 1.25fr);
  gap: .75rem clamp(1.5rem, 4vw, 4rem);
  align-items: baseline;
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
  border-top: 2px solid var(--hairline);
}
.guide-list li:first-child { border-top: 0; }
.guide-list a { font-family: var(--disp); font-size: 1.18rem; font-weight: 700; }
.guide-list p { margin: 0; color: var(--ink-dim); }

/* The FAQ. One plate rather than seven cards: these are seven answers to the
   same question ("is this the thing I wanted?"), and a grid of cards would
   give each of them the weight the three race rules get. Rows are separated
   by the same hairline as `.rule`, so the list reads as one surface. */
.faq {
  display: grid;
  margin: 0;
  padding: 4px 24px;
}

.faq__row {
  display: grid;
  gap: 6px;
  padding: 18px 0;
  border-top: 2px solid var(--hairline);
}

.faq__row:first-child {
  border-top: 0;
}

.faq dt {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  line-height: 1.2;
}

.faq dd {
  margin: 0;
  color: var(--ink-dim);
  font-size: 0.9375rem;
  line-height: 1.55;
  max-width: 68ch;
}

.close {
  display: grid;
  gap: 16px;
  justify-items: center;
  text-align: center;
  padding: clamp(28px, 4vw, 40px) 24px;
}

footer {
  position: relative;
  z-index: 1;
  padding: 30px 0 46px;
}

.foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 22px;
}

.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.foot a {
  color: var(--ink);
  font-size: 0.9375rem;
}

/* ------------------------------------------------------------------ *
 * Responsive — down to 390px, no horizontal scroll.
 * ------------------------------------------------------------------ */
/* One column: the demo goes under the identity and the actions. */
@media (max-width: 1000px) {
  .hero {
    grid-template-columns: minmax(0, 1fr);
    gap: 30px;
  }
  .match {
    max-width: 560px;
  }
}

@media (max-width: 900px) {
  .grid-4 {
    grid-template-columns: repeat(2, 1fr);
  }
  .split {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 720px) {
  .grid-3 {
    grid-template-columns: 1fr;
  }
  .top nav a:not(.action) {
    display: none;
  }
  .tiers {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
  .guide-list li { grid-template-columns: minmax(0, 1fr); }
}

/* On phone the rail becomes a header strip: never two full-size boards. */
@media (max-width: 660px) {
  .match.plate {
    padding: 14px;
    border-width: 2px;
  }
  .match__body {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }
  .rail {
    order: -1;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    background: var(--ground);
    border: 2px solid var(--hairline);
    border-radius: var(--plate);
    padding: 10px;
  }
  .rail__head {
    order: 2;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
  .rail .boardwrap {
    order: 1;
    flex: 0 0 92px;
    width: 92px;
  }
  .rail .fig {
    font-size: 1.25rem;
  }
  .rail__note {
    display: none;
  }
  .fig {
    font-size: 1.75rem;
  }
  .clock {
    font-size: 1.25rem;
  }
}

@media (max-width: 430px) {
  .wrap {
    padding: 0 14px;
  }
  .plate {
    padding: 18px 16px;
  }
  .hero__actions .action {
    flex: 1 1 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
  .action:hover {
    transform: none;
  }
}

/* ------------------------------------------------------------------ *
 * The two switches, bottom-left — the same rail the app carries on
 * every screen (`mobile/components/ui/ScreenSwitches.tsx`). Motion and
 * sound are the two things a visitor may need to stop immediately, and
 * this page starts both without being asked, so both must be one press
 * away from anywhere on it.
 *
 * The corner is deliberate and is the app's: the header and its actions
 * are top, the closing action is bottom-centre, and nothing else claims
 * bottom-left. Mounted by `switches.ts`, so a page without JavaScript
 * shows no switch — it has no field and no music to switch.
 * ------------------------------------------------------------------ */
.switches {
  position: fixed;
  left: 16px;
  bottom: 16px;
  /* Above the page (1–3) and the header (2), below the skip link (5). */
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The app's SwitchPill, in CSS: opaque like everything else read at a
   glance over the field, 32px so it is a real target without being a
   button, and ink-bordered because ink is the affordance here as
   everywhere. */
.switch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: background-color 140ms ease;
}

.switch:hover,
.switch:active {
  background: var(--ground);
}

/* Both glyphs live in the same 12×12 box, so the two switches are twins. */
.switch__glyph {
  position: relative;
  display: block;
  width: 12px;
  height: 12px;
}

/* A lit ember when the field is running, a hollow outline when it is
   not. Orange is heat, never affordance: the state, not the control. */
.switch__ember {
  position: absolute;
  inset: 0;
  border: 2px solid var(--hot);
  border-radius: 999px;
  background: var(--hot);
}

/* A quaver: filled head and stem when the music is on, hollow and
   struck through when it is off. The state is legible without a word,
   and the accessible name carries the meaning. */
.switch__note {
  position: absolute;
  inset: 0;
}

.switch__head {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 7px;
  height: 7px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
}

/* Overlapping the head rather than sitting beside it: at 12px a detached
   stem reads as "a dot and a line", not as a note. */
.switch__stem {
  position: absolute;
  left: 5px;
  top: 0;
  width: 2px;
  height: 9px;
  background: var(--ink);
}

.switch__slash {
  position: absolute;
  left: -2px;
  top: 5px;
  width: 16px;
  height: 2px;
  background: var(--ink-dim);
  transform: rotate(-45deg);
  display: none;
}

/* Every off state hangs off the checked state, so a switch cannot look
   like one thing and announce another. */
.switch[aria-checked="false"] .switch__ember,
.switch[aria-checked="false"] .switch__head {
  background: transparent;
  border-color: var(--ink-dim);
}

.switch[aria-checked="false"] .switch__stem {
  background: var(--ink-dim);
}

.switch[aria-checked="false"] .switch__slash {
  display: block;
}

/* ------------------------------------------------------------------ *
 * Discord. The only thing on this page the approved design did not
 * carry: docs/apps/2048surge/design/CORE_PRD.md requires a header placement and a repeated
 * closing placement, so both are added at the quietest weight the page
 * has room for rather than as a band of their own.
 *
 * Discord's monochrome mark is one of the few colours Ember does not
 * get to choose; it ships black, exactly as the inner pages render it
 * (web/production/pages.css). Everything around it is Ember's.
 * ------------------------------------------------------------------ */

/* Header: a filled pill on the same metrics as `.action--small`, so it reads
   as a button beside "Play now" rather than a bare glyph. Blurple is the one
   cool hue on this page and it is deliberate — it is Discord's trademark, not
   part of Ember. Still icon-only: the label is there for assistive tech, and
   the nav has no room for a second word beside the primary action. */
.top nav a.discord-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  padding: 0 16px;
  border: 2px solid var(--discord);
  border-radius: 999px;
  background: var(--discord);
  color: #fff;
  text-decoration: none;
  transition: transform 140ms ease, background-color 140ms ease;
}

.top nav a.discord-action:hover {
  transform: translateY(-1px);
  background: var(--discord-deep);
  border-color: var(--discord-deep);
}

/* CSS `fill` beats the SVG presentation attribute, so the mark turns cream on
   the filled pill without touching the shared renderer in build.ts — the inner
   pages keep the mark they already had. */
.discord-action svg path {
  fill: currentColor;
}

/* The approved header drops its text links below 720px and keeps only the
   primary action. Discord survives that cut, as it does on the inner pages:
   the PRD asks for a header placement, not a desktop-only one. */
@media (max-width: 720px) {
  .top nav a.discord-action {
    display: inline-flex;
  }
}

.discord-action svg {
  display: block;
  flex: none;
  width: 18px;
  height: auto;
}

/* Visually hidden, still announced. */
.discord-action--header span {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Closing: the same filled pill as the header, carrying its label. It sits
   under the primary action and matches its shape exactly — one button shape on
   this page, only the fill tells you whose action it is. */
.discord-action--closing {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 42px;
  padding: 0 20px;
  border: 2px solid var(--discord);
  border-radius: 999px;
  background: var(--discord);
  color: #fff;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1;
  text-decoration: none;
  transition: transform 140ms ease, background-color 140ms ease;
}

.discord-action--closing:hover {
  transform: translateY(-1px);
  background: var(--discord-deep);
  border-color: var(--discord-deep);
}

/* The mark keeps its own size in the closing pill so it optically matches
   the label beside it rather than the header's bare glyph. */
.discord-action--closing svg {
  width: 17px;
}

@media (forced-colors: active) {
  .discord-action {
    forced-color-adjust: auto;
  }
}
