/* ==========================================================================
   24.css — the /24/ game page, zetamac's sibling on base.css v3 ("tilde rail"):
   Source Serif 4 for prose, IBM Plex Mono for anything operational or numeric
   (tiles, the expression, timers, scores, buttons, form controls). One centered
   column right of the rail; hairlines, not cards; green the working accent,
   pink on hover. Every class here is .tf-* (twenty-four); nothing is shared
   with zetamac.css, which this page never loads.
   ========================================================================== */

/* Sizes are design pixels, calc(N * var(--s, 1px)): exactly N px up to a 1512px-wide window, growing
   with wider ones like the rest of the site (--s lives on :root in base.css). 1px hairlines and media
   queries stay in true pixels. */

/* main.mjs toggles screens and overlays with the hidden attribute; author display values
   (flex/grid) would otherwise beat the UA's [hidden]{display:none}. */
.twentyfour [hidden] { display: none !important; }

/* Numbers (tiles, timer, scores, the expression) sit in mono with fixed-width digits, so none shimmy. */
.tf-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---- Type ----------------------------------------------------------------- */
.tf-heading {
  font-size: calc(24 * var(--s, 1px));
  font-weight: 600;
  margin-bottom: calc(20 * var(--s, 1px));
}
.tf-subheading {
  font-size: calc(17 * var(--s, 1px));
  font-weight: 600;
  margin-bottom: calc(10 * var(--s, 1px));
}
.tf-intro {
  color: var(--ink-muted);
  margin-bottom: calc(36 * var(--s, 1px));
  text-wrap: pretty;
}
.tf-dim {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--ink-muted);
}
.tf-blurb {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--ink-muted);
  margin: 0.5rem 0 1rem;
  max-width: 24rem;
  text-wrap: pretty;
}
/* Create/join errors under the home form; red only, the text says what went wrong. */
.tf-error {
  margin-top: calc(24 * var(--s, 1px));
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--red);
  text-wrap: pretty;
}
.tf-error:empty { display: none; }
/* The same line, but a note rather than a complaint ("type your name to join ABCDE"). */
.tf-error--note { color: var(--ink-muted); }

/* ---- Forms / controls ----------------------------------------------------- */
.tf-btn {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  text-transform: lowercase;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  padding: calc(7 * var(--s, 1px)) calc(14 * var(--s, 1px));
  cursor: pointer;
  transition:
    color 150ms ease,
    border-color 150ms ease;
}
.tf-btn--primary {
  color: var(--green);
  border-color: var(--green);
  font-weight: 500;
}
.tf-btn:hover:not(:disabled) {
  color: var(--pink-text);
  border-color: var(--pink);
}
.tf-btn:disabled {
  color: var(--ink-muted);
  border-color: var(--hairline-soft);
  cursor: default;
}
.tf-linkbtn {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--green);
  text-align: left;
  text-decoration: none;
  transition: color 150ms ease;
}
.tf-linkbtn:hover {
  color: var(--pink-text);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: calc(3 * var(--s, 1px));
}
/* ≥16px on every control: iOS Safari zooms the page when a smaller field is focused. */
.tf-input,
.tf-select {
  font-family: var(--font-mono);
  font-size: calc(16 * var(--s, 1px));
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--hairline);
  padding: calc(4 * var(--s, 1px)) calc(8 * var(--s, 1px));
}
.tf-input--code {
  width: 9ch;
  text-transform: uppercase;
  letter-spacing: 0.2ch;
}
.tf-field { margin-bottom: 1rem; }
/* One label width lines the controls up across rows (only labels with `for`). */
.tf-field > label[for] {
  display: inline-block;
  min-width: 6rem;
}
/* One convention on every screen: the way out first, the action last. */
.tf-actions {
  display: flex;
  justify-content: flex-start;
  gap: calc(10 * var(--s, 1px));
  margin-top: calc(32 * var(--s, 1px));
  flex-wrap: wrap;
}
.tf-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2.5rem;
  margin-top: 1.5rem;
}
@media (max-width: 600px) {
  .tf-columns { grid-template-columns: 1fr; }
}

/* ---- Lobby ----------------------------------------------------------------- */
.tf-roomcode {
  font-size: calc(32 * var(--s, 1px));
  font-weight: 500;
  letter-spacing: 0.3ch;
  margin-bottom: 0.5rem;
}
.tf-linkrow {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}
/* The URL never pushes the column sideways on a phone: it breaks anywhere. */
.tf-lobby-link {
  min-width: 0;
  overflow-wrap: anywhere;
}
.tf-lobby-format { margin-bottom: 1.25rem; }
/* The seat list: one row per player — presence dot, name, then "host" / "you". */
.tf-seats {
  margin: 0 0 1.25rem;
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  max-width: 20rem;
  border-top: 1px solid var(--hairline);
}
.tf-seat {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--hairline-soft);
}
.tf-seat-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-seat-you { font-weight: 600; }
.tf-seat-tag {
  margin-inline-start: auto;
  color: var(--ink-muted);
  white-space: nowrap;
}
.tf-seat-host .tf-seat-tag { color: var(--green); }
/* Presence dot: green while the heartbeat is fresh; the row dims and the dot goes red once it is
   a minute old. Not colour alone: the dot carries a title and the tag gains "away". */
.tf-dot {
  display: inline-block;
  flex: none;
  inline-size: calc(7 * var(--s, 1px));
  block-size: calc(7 * var(--s, 1px));
  border-radius: 50%;
  background: var(--green);
  vertical-align: 0.05em;
}
.tf-seat-away { opacity: 0.55; }
.tf-seat-away .tf-dot { background: var(--red); }
.tf-lobby-note {
  max-width: 24rem;
  text-wrap: pretty;
}

/* ---- Game arena -------------------------------------------------------------- */
.tf-game-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem 2rem;
  flex-wrap: wrap;
  margin-bottom: calc(24 * var(--s, 1px));
}
.tf-timer-wrap {
  display: flex;
  align-items: baseline;
  gap: 0.6ch;
}
/* The timer is the biggest number on the screen bar the tiles. */
.tf-timer {
  font-size: calc(40 * var(--s, 1px));
  font-weight: 500;
  line-height: 1;
}
.tf-timer--low { color: var(--red); }
/* Live standings: "~ name score" per player, packed to the right and wrapping. */
.tf-scores {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.35rem 1.5rem;
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  min-width: 0;
}
.tf-score {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
  min-width: 0;
}
.tf-score::before {
  content: "~";
  margin-inline-end: 0.7ch;
  flex: none;
}
.tf-score-name {
  min-width: 0;
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tf-score-name::after { content: ":"; }
.tf-score > .tf-num {
  margin-inline-start: 0.4ch;
  flex: none;
}
.tf-score-you { font-weight: 600; }

/* The arena holds the tiles, the expression and the count-in overlay (inset:0 of this box). */
.tf-arena {
  position: relative;
  border-top: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  padding: calc(40 * var(--s, 1px)) 0 calc(32 * var(--s, 1px));
  text-align: center;
}
/* Four tiles in a row, big and square; a phone narrower than 420px gets a 2×2 grid. */
.tf-nums {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, calc(96 * var(--s, 1px))));
  justify-content: center;
  gap: calc(14 * var(--s, 1px));
  margin-bottom: calc(32 * var(--s, 1px));
}
@media (max-width: 420px) {
  .tf-nums { grid-template-columns: repeat(2, minmax(0, calc(120 * var(--s, 1px)))); }
}
.tf-num-tile {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.75rem, 6vw, 2.5rem);
  font-weight: 500;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition:
    color 150ms ease,
    border-color 150ms ease;
}
.tf-num-tile:hover {
  color: var(--pink-text);
  border-color: var(--pink);
}
/* The expression: one wide line in mono, underlined like zetamac's answer box. ≥16px for iOS. */
.tf-expr {
  width: 100%;
  font-family: var(--font-mono);
  font-size: clamp(1.25rem, 4.5vw, 1.75rem);
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: calc(2 * var(--s, 1px)) solid var(--ink);
  padding: 0.15em 0.2ch;
  text-align: center;
}
.tf-expr::placeholder {
  color: var(--ink-muted);
  opacity: 0.4;
  font-weight: 400;
}
.tf-expr:focus-visible {
  outline: none; /* the green underline replaces the box outline */
  border-bottom-color: var(--green);
}
/* A wrong answer: a short sideways shake, the text kept. */
.tf-shake { animation: tf-shake 320ms ease; }
@keyframes tf-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(calc(-7 * var(--s, 1px))); }
  40% { transform: translateX(calc(6 * var(--s, 1px))); }
  60% { transform: translateX(calc(-4 * var(--s, 1px))); }
  80% { transform: translateX(calc(3 * var(--s, 1px))); }
}
/* Operators and parentheses, for phones mostly; each inserts at the caret. */
.tf-keys {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: calc(8 * var(--s, 1px));
  margin-top: calc(16 * var(--s, 1px));
}
.tf-key {
  font-family: var(--font-mono);
  font-size: calc(18 * var(--s, 1px));
  line-height: 1;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  min-width: calc(44 * var(--s, 1px));
  min-height: calc(40 * var(--s, 1px));
  padding: 0 calc(10 * var(--s, 1px));
  cursor: pointer;
  transition:
    color 150ms ease,
    border-color 150ms ease;
}
.tf-key:hover {
  color: var(--pink-text);
  border-color: var(--pink);
}
/* "24!" / "+1" in green; a reason or "too slow" in red. Reserved height so the form never jumps. */
.tf-feedback {
  min-height: calc(20 * var(--s, 1px));
  margin-top: calc(14 * var(--s, 1px));
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  font-weight: 500;
  color: var(--green);
  text-wrap: pretty;
}
.tf-feedback--bad {
  color: var(--red);
  font-weight: 400;
}
.tf-feedback--dim {
  color: var(--ink-muted);
  font-weight: 400;
}
.tf-game-actions {
  justify-content: center;
  margin-top: calc(16 * var(--s, 1px));
}
/* The 3-2-1, over the arena; the digit at the lobby code's size so the hand-off reads as one count. */
.tf-countdown {
  position: absolute;
  inset: 0;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: calc(12 * var(--s, 1px));
  text-align: center;
}
.tf-countdown-text {
  font-size: calc(48 * var(--s, 1px));
  font-weight: 500;
  line-height: 1;
}

/* ---- Results ------------------------------------------------------------------ */
/* Ranked rows: rank, name (you in weight), score; the winner's row in green and weight. */
.tf-results {
  max-width: 24rem;
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: calc(14 * var(--s, 1px));
  font-variant-numeric: tabular-nums;
  border-top: 1px solid var(--hairline);
}
.tf-result {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--hairline-soft);
  white-space: nowrap;
}
.tf-result-rank {
  flex: none;
  width: 2ch;
  color: var(--ink-muted);
}
.tf-result-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tf-result-you { font-weight: 600; }
.tf-result-score { flex: none; }
.tf-result-winner {
  color: var(--green);
  font-weight: 600;
  font-size: calc(16 * var(--s, 1px));
}
.tf-results-note {
  max-width: 24rem;
  text-wrap: pretty;
}

/* ---- Phones ---------------------------------------------------------------- */
/* The column itself keeps base.css's gutters; here only the arena tightens, and the scores drop
   under the timer as a left-packed list. */
@media (max-width: 600px) {
  .tf-arena { padding-top: calc(28 * var(--s, 1px)); }
  .tf-game-top { flex-direction: column; }
  .tf-scores { justify-content: flex-start; }
  .tf-field > label[for] { min-width: 5rem; }
  /* All seven keys on one row at 375px: 7 × 40 + 6 × 6 = 316 of the 343px column. */
  .tf-keys { gap: calc(6 * var(--s, 1px)); }
  .tf-key {
    min-width: calc(40 * var(--s, 1px));
    padding: 0 calc(6 * var(--s, 1px));
  }
}
