:root {
  /* Neutral page, violet-tinted surfaces, deep purple accent. The three steps are
     deliberate: the background stays out of the way, raised surfaces carry a hint
     of the accent's hue, and the accent itself is the only saturated thing. */
  --bg: #0e0e11;
  --panel: #1a1726;
  --line: #2b2740;
  --ink: #eeeef2;
  --muted: #8e8e99;
  --accent: #7c5cff;
  --accent-ink: #ffffff;
  --good: #4ade80;
  --warn: #e0b341;
  --dead: #5b5b66;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* Overwritten from visualViewport in app.js — window.innerHeight lies when the
     software keyboard is up, which for this game is nearly all the time.
     The fallback is svh, not dvh: dvh resolves to the tallest the viewport ever
     gets, so with `overflow: hidden` the footer ends up clipped off the bottom
     before the script runs. svh is the shortest, so the layout always fits. */
  --app-height: 100svh;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

body {
  /* Stops a frantic swipe from triggering pull-to-refresh mid-round. */
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  overflow: hidden;
}

#app {
  height: var(--app-height);
  display: flex;
  flex-direction: column;
  padding: var(--safe-top) max(14px, env(safe-area-inset-left)) var(--safe-bottom)
           max(14px, env(safe-area-inset-right));
}

section { display: none; flex: 1; min-height: 0; flex-direction: column; }
body[data-screen="loading"] section[data-screen="loading"],
body[data-screen="token"]   section[data-screen="token"],
body[data-screen="home"]    section[data-screen="home"],
body[data-screen="intro"]   section[data-screen="intro"],
body[data-screen="play"]    section[data-screen="play"],
body[data-screen="paused"]  section[data-screen="paused"],
body[data-screen="reveal"]  section[data-screen="reveal"],
body[data-screen="done"]    section[data-screen="done"],
body[data-screen="error"]   section[data-screen="error"] { display: flex; }

.centred { align-items: center; justify-content: center; text-align: center; gap: 14px; }

h1 { font-size: 1.15rem; margin: 0; letter-spacing: 0.01em; }
h2 { font-size: 1.6rem; margin: 0; line-height: 1.2; max-width: 15ch; }
.muted { color: var(--muted); margin: 0; }
.small { font-size: 0.85rem; }
.label { color: var(--muted); font-size: 0.75rem; text-transform: uppercase;
         letter-spacing: 0.08em; display: block; }
.error { color: var(--accent); }

.bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 12px 2px; border-bottom: 1px solid var(--line);
}
/* Equal side columns keep the title optically centred with a button on one side. */
.titlebar { display: grid; grid-template-columns: 40px 1fr 40px; }
.titlebar .spacer { display: block; }
.brand { display: flex; align-items: center; justify-content: center; gap: 9px; }
.brandmark { width: 30px; height: 30px; object-fit: contain; flex: none; }

.hero {
  display: block; width: 58%; max-width: 210px;
  margin: 26px auto 8px; opacity: 0.96;
}

/* Centres the outcome block only — the category rows below keep their own
   left-aligned layout. */
.done-body { text-align: center; }
.done-body .categories { text-align: left; }
.outcome { display: block; width: 46%; max-width: 176px; margin: 6px auto 10px; }
.outcome-title {
  font-size: 2.3rem; line-height: 1.1; margin: 0 0 18px;
  letter-spacing: -0.02em; text-align: center;
}
.outcome-title.won { color: var(--accent); }
.category { color: var(--muted); font-size: 0.95rem; }

.scroll { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
          padding: 12px 0; }

.actions { padding: 10px 0 14px; display: grid; gap: 8px; }

button {
  font: inherit; border: 0; border-radius: 12px; padding: 15px 18px;
  color: var(--ink); background: var(--panel); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
button.primary { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
button.big { font-size: 1.15rem; padding: 18px 34px; }
button.ghost { background: none; color: var(--muted); padding: 8px 10px; }
button:active { opacity: 0.75; }

input[type="text"] {
  font: inherit; width: 100%; padding: 15px 16px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink);
  -webkit-user-select: text; user-select: text;
}
input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

form[data-form="token"] { display: grid; gap: 10px; width: min(320px, 100%); }

.scoreline { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; text-align: center; }
.scoreline b { font-size: 2rem; font-variant-numeric: tabular-nums; }
.scoreline.final b { font-size: 3rem; }
.status { margin: 18px 0; text-align: center; color: var(--muted); }

.categories { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.categories li {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; background: var(--panel); border-radius: 10px;
}
.categories .n { color: var(--dead); font-variant-numeric: tabular-nums; min-width: 1.2em; }
.categories .nm { flex: 1; }
.categories .nm.hidden { color: var(--dead); font-style: italic; }
.categories .sc { color: var(--muted); font-variant-numeric: tabular-nums; }

/* The category headline. It is the thing the player has to keep in mind for a
   whole minute, so it outranks the clock — which previously took the whole screen
   and left the category as grey text in a corner. */
.playhead {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 2px 10px; border-bottom: 1px solid var(--line);
}
.category-big {
  flex: 1; margin: 0; font-size: 1.55rem; font-weight: 600;
  line-height: 1.2; letter-spacing: -0.01em;
}
.playhead .ghost { flex: none; margin-top: 2px; }

.timerow { display: flex; align-items: center; gap: 12px; padding: 12px 2px 6px; }
.timerbar {
  flex: 1; height: 6px; border-radius: 3px; background: var(--panel); overflow: hidden;
}
.timerbar span {
  display: block; height: 100%; width: 100%; background: var(--good);
  transition: width 0.2s linear, background-color 0.3s ease;
}
.timerbar.low span { background: var(--accent); }
.clock {
  font-size: 1.5rem; font-weight: 600; font-variant-numeric: tabular-nums;
  min-width: 2.4ch; text-align: right; line-height: 1;
}
.clock.low { color: var(--accent); }

/* Newest first, growing downward — older answers slide out of view below rather than
   pushing the list around under your thumb while you type. */
.words { display: flex; flex-direction: column; gap: 6px; }
.word {
  display: flex; align-items: baseline; gap: 10px;
  padding: 10px 12px; background: var(--panel); border-radius: 9px;
}
.word .w { flex: 1; }
.word .p { font-variant-numeric: tabular-nums; font-weight: 600; }
.word.hit .p { color: var(--good); }
.word.fuzzy .p { color: var(--warn); }
.word.dup, .word.miss { color: var(--dead); }
.word .note { font-size: 0.8rem; color: var(--muted); }

/* Live verdicts while typing. 'unsure' is deliberately neutral, not red: the server's
   typo net may still credit it at the reveal, and telling someone they're wrong when
   they aren't is the thing this whole feature exists to prevent. */
.word .mark { font-weight: 600; min-width: 1em; text-align: center; }
.word.pending .mark { color: var(--dead); }
.word.hit .mark { color: var(--good); }
.word.dup .mark { color: var(--dead); }
.word.unsure { color: var(--muted); }
.word.unsure .mark { color: var(--dead); }

.entry { padding: 8px 0 10px; }

/* Present for the form, absent to the eye — see the note in index.html. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.roundscore { text-align: center; padding: 20px 0 10px; }
.roundscore b { font-size: 3rem; font-variant-numeric: tabular-nums; }

/* In flow on the home screen, never floating. Two earlier attempts at a fixed banner
   covered the primary button at the bottom and then the category name at the top;
   an overlay has nowhere safe to sit on a screen this small. */
#install-hint {
  position: relative; margin-bottom: 14px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 40px 12px 14px; font-size: 0.85rem;
  color: var(--muted);
}
#install-hint button { position: absolute; top: 4px; right: 4px; background: none;
  color: var(--muted); font-size: 1.3rem; padding: 4px 10px; }

@media (prefers-reduced-motion: no-preference) {
  button, .word { transition: opacity 0.12s ease; }
}
