/* ==========================================================================
   zetamac.css — the /zetamac/ game page, native to base.css v3 ("tilde rail").
   Same split as the rail itself: Source Serif 4 for prose, IBM Plex Mono for
   anything operational or numeric — problems, timers, scores, buttons, form
   controls. One centered column in the space right of the rail; hairlines
   instead of cards; green as the working accent, pink strictly on hover.
   ========================================================================== */

/* No column geometry of its own, deliberately. This page used to override
   --wrap-max to `44rem + 2 * pad` = 784px against the site's 800px, and because
   .wrap's margin is (100% - wrap-max) / 3, that put the whole page 5.33px right
   of every other one at every width above the cap — so /works/ -> /zetamac/
   nudged sideways. A `.zetamac` rule then re-centred a 44rem column inside it
   and added 12px of padding-top, starting this page's content at y=52 while
   every other page starts at y=40.
   All of it is gone: the game runs in the site's own column, on the site's own
   spine. Every .z-screen still shares that one box, so switching screens still
   never moves or resizes the column.
   (The `scrollbar-gutter: stable` this sheet used to set on `html` now lives in
   base.css, so every page reserves the gutter and none of them shift relative to
   a page short enough not to scroll.) */

/* The JS toggles visibility via the hidden attribute. Author display values
   (flex/grid) would otherwise beat the UA's [hidden]{display:none}. */
[hidden] {
  display: none !important;
}

/* Numbers everywhere (timers, scores, problems) sit in mono with fixed-width
   digits so the display doesn't shimmy as they tick over. */
.z-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---- Screens ------------------------------------------------------------- */
.z-screen--game {
  position: relative;
}

/* Matches .page-title's 30px/24px, like every other page title on the site. At
   18px this was the tightest step under the largest heading. */
.z-title {
  font-size: 30px;
  font-weight: 600;
  line-height: 1.16;
  margin-bottom: 24px;
}
.z-heading {
  font-size: 24px;
  font-weight: 600;
  margin-bottom: 20px;
}
.z-subheading {
  font-size: 17px;
  font-weight: 600;
  margin-bottom: 10px;
}
.z-intro {
  color: var(--ink-muted);
  margin-bottom: 36px;
  text-wrap: pretty;
}
.z-block {
  margin-bottom: 20px;
  text-wrap: pretty;
}
.z-dim {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
}

/* ---- Notice strip -------------------------------------------------------- */
.z-notice {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 28px;
  padding: 6px 0 6px 12px;
  border-left: 2px solid var(--green);
  font-family: var(--font-mono);
  font-size: 13px;
}
.z-notice-close {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--ink-muted);
  transition: color 150ms ease;
}
.z-notice-close:hover {
  color: var(--pink-text);
}

/* ---- Landing modes: hairline rows, archive-style -------------------------
   The measure lives on the block that draws the rules, so each row's hairline
   ends where its own text does. The cap used to sit on .z-mode-desc instead
   (54ch = 424.56px) while the rules spanned the full column, leaving 279px of
   rule past the end of the text on four consecutive rows.
   In rem, not ch: `ch` resolves against the element's own font-size, so the same
   `54ch` means a different width on the 15px description than on this 17px
   block. Widening the text to the rules instead was tried and rejected — it puts
   the descriptions on a ~103-character measure. */
.z-modes {
  max-width: 26.5rem;
  border-top: 1px solid var(--hairline);
}
.z-mode {
  font: inherit;
  color: var(--ink);
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hairline-soft);
  width: 100%;
  padding: 16px 0 18px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.z-mode-name {
  font-family: var(--font-mono);
  font-size: 14px;
  text-transform: lowercase;
  color: var(--green);
  transition: color 150ms ease;
}
.z-mode-name::before {
  content: "~ " / "";
}
.z-mode:hover .z-mode-name {
  color: var(--pink-text);
}
.z-mode-desc {
  color: var(--ink-muted);
  font-size: 15px;
  line-height: 1.55;
  text-wrap: pretty;
}

/* ---- Forms / controls ----------------------------------------------------- */
.z-btn {
  font-family: var(--font-mono);
  font-size: 13px;
  text-transform: lowercase;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  padding: 7px 14px;
  cursor: pointer;
  transition:
    color 150ms ease,
    border-color 150ms ease;
}
.z-btn--primary {
  color: var(--green);
  border-color: var(--green);
  font-weight: 500;
}
.z-btn:hover:not(:disabled) {
  color: var(--pink-text);
  border-color: var(--pink);
}
.z-btn:disabled {
  color: var(--ink-muted);
  border-color: var(--hairline-soft);
  cursor: default;
}
.z-linkbtn {
  font-family: var(--font-mono);
  font-size: 13px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--green);
  text-decoration: none;
  transition: color 150ms ease;
}
.z-linkbtn:hover {
  color: var(--pink-text);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.z-input,
.z-select,
.z-range-input {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--hairline);
  padding: 4px 8px;
}
.z-range-input {
  width: 4.2rem;
  text-align: right;
  /* ≥16px: keep iOS Safari from auto-zooming the page when a range field
     is tapped (it inherits the 13px .z-op-detail size otherwise). */
  font-size: 16px;
}
.z-input--code {
  width: 8ch;
  text-transform: uppercase;
  letter-spacing: 0.2ch;
}
.z-check input[type="checkbox"] {
  accent-color: var(--green);
  width: 1rem;
  height: 1rem;
  margin-right: 0.3rem;
}
.z-op {
  margin-bottom: 1rem;
}
/* A row's control sat wherever its label happened to end, so no two rows agreed:
   "Target:" put its select 20.6px left of the one under "Duration:", and across
   the settings and multiplayer screens the same control landed at four different x's.
   One inline-block width for every label that names a control lines them all up.
   Only labels with `for` — a .z-check label wraps its own checkbox and is not
   this pattern. */
.z-op > label[for] {
  display: inline-block;
  min-width: 6rem;
}
.z-op-detail {
  margin-top: 0.35rem;
  /* Starts where the checkbox label's TEXT starts: the checkbox is 1rem wide
     with 0.3rem of margin after it. At 1.8rem the detail explaining a checkbox
     sat 8px right of the checkbox's own label — two indents for one level. */
  padding-left: 1.3rem;
  color: var(--ink-muted);
  font-family: var(--font-mono);
  font-size: 13px;
}
/* One convention on every screen: the way out first, the action last. A
   `--left` modifier used to mark the four screens that did the opposite, but it
   never had a rule of its own — .z-actions is already flex-start — so it styled
   nothing while the primary button moved between screens. */
.z-actions {
  display: flex;
  justify-content: flex-start;
  gap: 10px;
  margin-top: 32px;
  flex-wrap: wrap;
}

/* ---- Game arena ------------------------------------------------------------ */
.z-game-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
}
.z-game-label {
  color: var(--ink);
  font-weight: 500;
}
.z-game-timer {
  margin-bottom: 40px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
}
.z-game-timer .z-num {
  font-weight: 600;
}
/* Label-less modes (normal, multiplayer): main.mjs moves the timer into the top row
   (label's slot); the flex row provides the rhythm, so drop the
   standalone-line margin. */
.z-game-top .z-game-timer {
  margin-bottom: 0;
}
/* When the top row is the whole header (normal mode) it owns the gap
   before the arena; in multiplayer the duel bar below provides it. */
.z-game-top--solo {
  margin-bottom: 40px;
}
.z-duel-bar {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  /* last header line in multiplayer (the timer sits up in the top row),
     so it owns the gap before the arena */
  margin-bottom: 40px;
  font-family: var(--font-mono);
  font-size: 13px;
}
/* Two players sit at either end, like a scoreboard. Three or more read as a
   list — packed from the left and wrapping onto more lines as needed. */
.z-duel-bar--many {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
}
/* nowrap without min-width:0 means these flex items cannot shrink below their
   min-content width, so two long names push the row wider than the column and
   the overflow escapes to the document — a horizontal scrollbar on the whole
   page at 375px. Ellipsize instead, matching .z-duel-live-desc. */
.z-duel-player {
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Best of 3: which round you're in and who's won what. When it's showing it
   becomes the last header line, so the duel bar hands it the gap. */
.z-duel-bar:has(+ .z-duel-rounds:not([hidden])) {
  margin-bottom: 6px;
}
.z-duel-rounds {
  margin-bottom: 40px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
}
/* Presence dot: green while a player's heartbeat is fresh, red once it goes
   quiet. Colour is not the only signal — the dot carries an aria-label and a
   title, and the flash line still announces each transition. */
.z-dot {
  display: inline-block;
  inline-size: 7px;
  block-size: 7px;
  margin-inline-start: 6px;
  border-radius: 50%;
  background: var(--green);
  /* nudge off the text baseline so it centres on the lowercase x-height */
  vertical-align: 0.05em;
}
.z-dot--gone {
  background: var(--red);
}
/* Your own name is bold so a glance at the bar says which side is you. */
.z-duel-name-you {
  font-weight: 600;
}
.z-game-watchers {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  white-space: nowrap;
}
.z-duel-live {
  margin-top: 2.5rem;
}
.z-duel-live-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  max-width: 34rem;
  padding: 0.35rem 0;
  font-family: var(--font-mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.z-duel-live-desc {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.z-arena {
  position: relative;
  border-top: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  /* top-heavy split: digit glyphs sit high in their line box, so true
     vertical centering reads high — bias the row down to optically center */
  padding: 64px 0 56px;
  text-align: center;
}
.z-problem-row {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 0.6ch;
  flex-wrap: wrap;
}
/* The problem and the answer field must scale as one line of type. */
.z-problem,
.z-answer {
  font-family: var(--font-mono);
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  font-weight: 500;
  line-height: 1.2;
}
.z-answer {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--ink);
  width: 5ch;
  text-align: left;
  padding: 0 0.1ch;
}
.z-answer:focus-visible {
  outline: none; /* the green caret-line below replaces the box outline */
  border-bottom-color: var(--green);
}
/* The brief moment after an opponent's point takes the problem away: the box
   keeps re-emptying while your fingers land, so the digit you were already
   committed to doesn't become the first digit of the next answer. Visible on
   purpose — a silent swallow would read as a dropped keypress. */
.z-answer--locked {
  opacity: 0.4;
  border-bottom-color: var(--hairline);
}
/* Spectating a self-paced game: there is no shared problem to show, so each
   player gets a line — name, live score, and the problem they're on now. */
.z-duel-boards {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 1.25rem 1.5rem;
  max-width: 32rem;
  margin-inline: auto;
}
.z-duel-board-name {
  text-align: right;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}
.z-duel-board-score,
.z-duel-board-problem {
  font-family: var(--font-mono);
  font-size: clamp(1.25rem, 3.5vw, 1.875rem);
  font-weight: 500;
  line-height: 1.2;
}
.z-duel-board-problem {
  text-align: left;
  color: var(--ink-muted);
  white-space: nowrap;
}
.z-flash {
  position: absolute;
  top: 10px;
  left: 0;
  right: 0;
  color: var(--green);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
}

/* Overlay for countdown / pause / quit-confirm: sits over the arena. */
.z-overlay {
  position: absolute;
  inset: 0;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 12px;
  text-align: center;
}
.z-overlay-text {
  font-family: var(--font-mono);
  font-size: 32px;
  font-weight: 500;
}
.z-overlay-sub {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  max-width: 30rem;
}

/* ---- Results / recap ------------------------------------------------------- */
.z-result-score {
  font-family: var(--font-mono);
  font-size: 28px;
  font-weight: 500;
  margin-bottom: 10px;
}
.z-result-detail {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
}
/* Daily average-score graph on the normal results screen. */
.z-daily {
  margin-top: 48px;
  padding-top: 20px;
  border-top: 1px solid var(--hairline);
}
.z-daily-graph svg {
  display: block;
  width: 100%;
  height: auto;
}
.z-daily-graph text {
  font-family: var(--font-mono);
  /* True pixels now that the viewBox tracks the rendered width — 10px was
     only ever ~12.6px because the old fixed viewBox scaled up on desktop. */
  font-size: 12px;
  fill: var(--ink-muted);
}
.z-graph-grid {
  stroke: var(--hairline-soft);
}
.z-graph-axis {
  stroke: var(--hairline);
}
.z-graph-line {
  fill: none;
  stroke: var(--green);
  stroke-width: 1.5;
}
.z-graph-dot {
  fill: var(--ink);
}
.z-daily-note {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-muted);
}
.z-recap-line {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 8px 0;
  border-bottom: 1px solid var(--hairline-soft);
  font-size: 15px;
}
.z-recap-line:first-child {
  border-top: 1px solid var(--hairline);
}
.z-recap-what {
  font-weight: 600;
}
.z-recap-delta {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.z-recap-delta .up {
  color: var(--green);
  font-weight: 500;
}
.z-recap-totals {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
}

/* ---- Practice dashboard ----------------------------------------------------- */
.z-dash-user {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  margin-bottom: 1rem;
}
.z-dash-lifetime {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 2rem;
  margin-bottom: 2rem;
  font-family: var(--font-mono);
  font-size: 13px;
}
.z-life-stat b {
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.z-stats-grid {
  display: grid;
  grid-template-columns: 6rem repeat(3, 1fr);
  gap: 1px;
  background: var(--hairline-soft);
  border: 1px solid var(--hairline);
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: 13px;
}
/* On a phone the 6rem label track is 27% of the column and carries a single
   glyph — stats.mjs renders the operation as "+ - x /" precisely because the
   words never fit — while the three columns holding every number are squeezed to
   83px and wrap to three or four lines each. Give the track the width of its
   glyph and hand the rest to the data. */
@media (max-width: 480px) {
  .z-stats-grid {
    grid-template-columns: 2.2rem repeat(3, 1fr);
  }
}
.z-stat-cell {
  background: var(--bg);
  padding: 0.55rem 0.7rem;
}
.z-stat-cell--head {
  font-weight: 500;
}
.z-stat-acc {
  font-variant-numeric: tabular-nums;
}
.z-stat-ms {
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.z-stat-focus {
  color: var(--green);
  font-weight: 500;
}
.z-stat-trend {
  color: var(--ink-muted);
}
.z-facts-row {
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: 13px;
}
.z-fact {
  display: inline-block;
  margin-right: 0.9ch;
  font-variant-numeric: tabular-nums;
}
.z-fact--solid {
  font-weight: 500;
}
.z-fact--weak {
  color: var(--green);
  font-weight: 500;
}
.z-bench-line {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  margin-bottom: 0.5rem;
  font-variant-numeric: tabular-nums;
}
/* No max-width: the 44rem it used to declare WAS the old column width, so the
   cap never did anything — and at 14px it made this the loosest measure on the
   page while looking like the most deliberate one. */
.z-bench-note {
  font-size: 14px;
  color: var(--ink-muted);
  margin-bottom: 1.25rem;
}

/* ---- Plan preview ----------------------------------------------------------- */
.z-plan-block {
  display: flex;
  gap: 1rem;
  padding: 8px 0;
  border-bottom: 1px solid var(--hairline-soft);
}
.z-plan-block:first-child {
  border-top: 1px solid var(--hairline);
}
.z-plan-kind {
  flex: 0 0 7.5rem;
  font-weight: 600;
}
.z-plan-desc {
  color: var(--ink-muted);
  font-size: 15px;
}
.z-plan-secs {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- Multiplayer ----------------------------------------------------------- */
.z-duel-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2.5rem;
  margin-top: 1.5rem;
}
@media (max-width: 600px) {
  .z-duel-columns {
    grid-template-columns: 1fr;
  }
}
.z-format-blurb {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  margin: 0.5rem 0 1rem;
}
.z-duel-custom {
  margin-bottom: 1rem;
}
.z-duel-custom summary {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--green);
  margin-bottom: 0.75rem;
  transition: color 150ms ease;
}
.z-duel-custom summary:hover {
  color: var(--pink-text);
}
.z-roomcode {
  font-size: 32px;
  font-weight: 500;
  letter-spacing: 0.3ch;
  margin-bottom: 1rem;
}
/* The lobby's seat list: one row per player, presence dot first, then the
   name, then "host" / "you". */
.z-lobby-players {
  margin: 0 0 1.25rem;
  font-family: var(--font-mono);
  font-size: 13px;
  max-width: 20rem;
}
.z-lobby-player {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--hairline-soft);
}
.z-lobby-player .z-dot {
  margin-inline-start: 0;
  flex: none;
}
.z-lobby-player-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.z-lobby-player-tag {
  margin-inline-start: auto;
  color: var(--ink-muted);
  white-space: nowrap;
}
.z-duel-final {
  margin-bottom: 1rem;
}
.z-duel-final-line {
  display: flex;
  justify-content: space-between;
  max-width: 20rem;
  gap: 2rem;
  padding: 0.35rem 0;
  font-family: var(--font-mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.z-duel-final-line .winner {
  font-weight: 600;
}
/* Best of 3: the per-round breakdown under the two rows. */
.z-duel-final-note {
  max-width: 20rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--hairline-soft);
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
