/* pub.css — rocade.cab, dressed as the cabinet's own game menu.
 *
 * The menu (frontend/menu.py) has a specific, recognisable layout, and
 * this page borrows its parts rather than its mood:
 *
 *   double cyan box around the whole screen      .frame
 *   sprite rails down both sides                 .rail
 *   blue-boxed list panel                        .panel
 *   YELLOW box marking the current row           .panel.sel
 *   cyan captioned score boxes                   .score
 *   magenta marquee pill, Pac-Man + logo + ghost footer .marquee
 *   spectrum equaliser bars                      .eq
 *
 * Colours are frontend/pixel.py's, value for value. The wordmark and the
 * sprites are GENERATED from that same source by dev_tools/gen-pub-art.py,
 * so they are the cabinet's actual art, not a redraw.
 *
 * WHERE IT DELIBERATELY STOPS: body text is not set in the 5x7 font. The
 * menu can use it everywhere because it draws at integer scale on a CRT
 * and never shows a paragraph; this page is prose someone reads once, and
 * a bitmap font at 16px is a wall. The font carries the wordmark and the
 * headings' tracking; the reading is left to a normal monospace.
 */

:root {
  /* frontend/pixel.py */
  --bg: #05050e;
  --ink: #e8e8f0;
  --dim: #2a2a38;
  --cyan: #00e5ff;
  --blue: #2b6bff;
  --green: #5dff3a;
  --yellow: #ffd400;
  --orange: #ff7a18;
  --red: #ff3355;
  --magenta: #ff2bd1;

  --muted: #8a8a9c;      /* readable dim: --dim is a BORDER, not text */
  --panel: #0b0b16;

  --s-1: .25rem; --s-2: .5rem; --s-3: 1rem;
  --s-4: 1.25rem; --s-5: 1.5rem; --s-7: 2rem;
  --fs-0: .75rem; --fs-1: .875rem; --fs-2: 1rem; --fs-3: 1.25rem;
  --touch: 44px;

  color-scheme: dark;
}

* { box-sizing: border-box; }

html { background: var(--bg); }

body {
  margin: 0;
  padding: var(--s-5) var(--s-3) 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--fs-2)/1.65 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  min-height: 100vh;
}

/* Pixel art must never be smoothed. Browsers upscale an SVG cleanly, but
 * these also get scaled to non-integer sizes by clamp(), and without this
 * the 1px sprite rows go soft. */
img { image-rendering: pixelated; }

.wordmark { height: auto; display: block; }

/* ── the double frame the menu draws round the screen ─────────────── */
.frame {
  position: fixed; inset: 10px;
  border: 2px solid var(--cyan);
  border-radius: 6px;
  pointer-events: none;
  z-index: 50;
}
.frame::before {
  content: "";
  position: absolute; inset: 4px;
  border: 1px solid rgba(0, 229, 255, .35);
  border-radius: 4px;
}
/* The scanlines the cabinet has for real. */
.frame::after {
  content: "";
  position: fixed; inset: -10px;
  background: repeating-linear-gradient(
      180deg, rgba(0,0,0,.24) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}

/* ── sprite rails ─────────────────────────────────────────────────── */
/* The menu drops these the moment the glass is too narrow for them, and
 * so does this: below 1100px they would be pressed against the text. */
.rail {
  position: fixed; top: 50%; transform: translateY(-50%);
  display: none; flex-direction: column; gap: var(--s-7);
  z-index: 40;
}
.rail img { width: 26px; height: 26px; opacity: .85; }
.rail-l { left: 26px; }
.rail-r { right: 26px; }
@media (min-width: 1100px) { .rail { display: flex; } }

/* ── hero ─────────────────────────────────────────────────────────── */
.hero, main, footer { max-width: 46rem; margin: 0 auto; }

.hero { padding: var(--s-7) 0 var(--s-5); text-align: center; }
.hero h1 { margin: 0; display: flex; justify-content: center; }
/* Width, not font-size: it is an image of a word. The 41:7 aspect comes
 * from the glyph metrics, so height follows on its own. */
.hero .wordmark { width: clamp(240px, 52vw, 420px); }
.tag {
  margin: var(--s-4) 0 0; color: var(--muted);
  font-size: var(--fs-1); letter-spacing: .18em; text-transform: uppercase;
}

/* ── panels: the menu's blue list box ─────────────────────────────── */
.panel {
  background: var(--panel);
  border: 1px solid var(--blue);
  border-radius: 5px;
  padding: var(--s-4);
  margin-bottom: var(--s-3);
}
.panel h2 {
  margin: 0 0 var(--s-3);
  font-size: var(--fs-0); font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--cyan);
}
.panel p { margin: 0 0 var(--s-3); }
.panel p:last-child { margin-bottom: 0; }

/* The current row. The menu boxes it in yellow; the only actionable
 * panel here gets the same mark, including the ▸ cursor. */
.panel.sel { border-color: var(--yellow); }
.panel.sel h2 { color: var(--yellow); }
.cur { color: var(--yellow); }

code {
  font-family: inherit;
  background: #05050e;
  border: 1px solid var(--dim);
  border-radius: 2px;
  padding: 0 .3em;
  font-size: .95em;
}
pre { margin: var(--s-2) 0; overflow-x: auto; }   /* never widen a phone */
pre code { display: block; padding: var(--s-2) var(--s-3); white-space: pre; }

/* ── the download button ──────────────────────────────────────────── */
.btn {
  display: block;
  min-height: var(--touch);
  padding: var(--s-3);
  background: var(--yellow);
  color: #201a00;                 /* yellow is far too light for white */
  border-radius: 4px;
  text-align: center; text-decoration: none;
  font-size: var(--fs-3); font-weight: 700;
  letter-spacing: .06em;
}
.btn:hover, .btn:focus-visible { filter: brightness(1.12); }
.none { color: var(--muted); margin: 0; }

/* ── score boxes: the menu's captioned side column ────────────────── */
.scores {
  display: grid; gap: var(--s-2);
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin-top: var(--s-3);
}
.score {
  border: 1px solid var(--cyan);
  border-radius: 3px;
  padding: var(--s-2) var(--s-3);
  min-width: 0;
}
.score b {
  display: block;
  font-size: var(--fs-0); font-weight: 400;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--cyan);
}
.score span { font-size: var(--fs-1); }
.score.wide { grid-column: 1 / -1; }
/* 64 characters nobody reads and everybody copies: wrapping mid-hash
 * beats a scrollbar or an ellipsis. */
.sha { word-break: break-all; }

.verify { margin: var(--s-3) 0 0; font-size: var(--fs-1); color: var(--muted); }
.verify code { color: var(--ink); }

/* ── list rows ────────────────────────────────────────────────────── */
.rows { list-style: none; margin: 0; padding: 0; }
.rows li {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s-3);
  align-items: start;
  padding: var(--s-2) 0;
  border-top: 1px solid var(--dim);
}
.rows li:first-child { border-top: 0; }
/* The menu marks a row with a small coloured block; four rows, four
 * stops off the spectrum, in its order. */
.pip { width: 8px; height: 8px; margin-top: .6em; background: var(--cyan); flex: 0 0 auto; }
.rows li > div { min-width: 0; }
.rows li:nth-child(2) .pip { background: var(--green); }
.rows li:nth-child(3) .pip { background: var(--yellow); }
.rows li:nth-child(4) .pip { background: var(--orange); }

.steps { margin: 0; padding-left: 1.4em; }
.steps li { margin-bottom: var(--s-4); }
.steps li:last-child { margin-bottom: 0; }
.steps li::marker { color: var(--magenta); font-weight: 700; }

.note {
  border-left: 3px solid var(--magenta);
  padding-left: var(--s-3);
  color: var(--muted);
}
.note b { color: var(--ink); }

.warn-note { display: block; color: var(--orange); font-size: var(--fs-1); }
.warn-note code { border-color: var(--orange); }

/* ── footer: equaliser, marquee pill, legend ──────────────────────── */
footer { padding: var(--s-5) 0 var(--s-7); text-align: center; }

/* The menu's bottom-left equaliser. Static bars, because they are a
 * texture here rather than a VU meter, and an animated one would be
 * motion nobody asked for. */
.eq {
  height: 20px;
  margin: 0 auto var(--s-4);
  display: flex; align-items: flex-end; justify-content: center;
  gap: 3px;
  opacity: .85;
}
/* Each bar takes its own height and its colour from where it sits along
 * the spectrum -- the menu samples spectrum_at(k/(n-1)) for exactly this.
 * Uneven heights are the whole character of it; a uniform mask read as a
 * barcode. */
.eq i { width: 6px; height: var(--h); background: var(--cyan); }
.eq i:nth-child(n+3)  { background: var(--green); }
.eq i:nth-child(n+6)  { background: var(--yellow); }
.eq i:nth-child(n+9)  { background: var(--orange); }
.eq i:nth-child(n+12) { background: var(--red); }
.eq i:nth-child(n+15) { background: var(--magenta); }

.marquee {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--magenta);
  border-radius: 999px;
}
.marquee img:not(.wordmark) { width: 18px; height: 18px; }
.marquee .wordmark { width: 128px; }

.legend {
  margin: var(--s-4) 0 0;
  color: var(--muted); font-size: var(--fs-1);
}

/* ── a11y ─────────────────────────────────────────────────────────── */
:is(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  /* A static texture, but exactly the high-contrast pattern this
   * preference exists to ask for. */
  .frame::after { display: none; }
}
