/* Shared design tokens and the app shell.
 *
 * House style, matching the rest of this repo: plain CSS, no build step, no
 * framework. Light/dark comes from `prefers-color-scheme` by default and is
 * overridable by a `data-theme` attribute on <html>, persisted in
 * localStorage - the same mechanism shared/web/theme.js uses everywhere in
 * this project.
 *
 * General-purpose tokens (surface/text/border/accent/status colors) now
 * live in shared/web/styles/tokens.css - shared project-wide since the
 * single-PWA work (plans/project-wide-pwa.md, decision 4), which settled on
 * Wordfeud Butler's green/cream palette rather than this app's old
 * blue/neutral one. This file keeps this app's historical variable names
 * (--fg, --card, --good, etc. - every rule in this file and board.css/
 * quiz.css already reads them) as a thin alias layer onto the shared
 * tokens' names, rather than rewriting every rule to the shared names
 * directly - far less risky than a project-wide search-and-replace across
 * three CSS files. --sq-* (the board's bonus-square colors) and the
 * --tile-* set are NOT aliased: they are semantic game-state/rendering
 * colors, not brand tokens, and keep their own hardcoded values exactly as
 * before.
 *
 * Component CSS lives in its own file next to this one (board.css,
 * quiz.css). Everything in all three is built from the custom properties
 * below; a component file should not hardcode a colour (--sq-* and --tile-*
 * excepted, per above).
 */
@import url("/shared/styles/tokens.css");
@import url("/shared/styles/pull-to-refresh.css");

:root {
  /* --bg, --border, --accent and --warn are not listed here: this app happened to already use
     those exact names, so they inherit straight from the imported tokens.css with no alias
     needed - redeclaring --bg: var(--bg) here would be a cyclic reference (invalid CSS), not an
     alias. Only the names that actually differ need one. */
  --fg: var(--text);
  --muted: var(--text-muted);
  --accent-fg: var(--accent-contrast);
  --card: var(--surface);
  --card-raised: var(--surface-2);

  --good: var(--positive);
  --bad: var(--negative);

  /* Tiles and squares - semantic/rendering colors, not part of the shared brand palette. */
  --tile-bg: #f3e2be;
  --tile-fg: #2a1e0d;
  --tile-edge: #cdb98f;
  --tile-pending: #ffd98a;
  --tile-blank-fg: #8a6d3b;
  --sq-plain: #e9e5dc;
  --sq-dl: #a8c8f0;
  --sq-tl: #3f7fd0;
  --sq-dw: #f0b8a8;
  --sq-tw: #d05a3f;
  --sq-center: #d8cfbc;
  --sq-fg: #33302a;
  --sq-fg-strong: #ffffff;

  --radius: 8px;
  --gap: 0.75rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --tile-bg: #d9c69f;
    --tile-fg: #241a0b;
    --tile-edge: #8c7c58;
    --tile-pending: #ffcf70;
    --tile-blank-fg: #6d5426;
    --sq-plain: #3b3a36;
    --sq-dl: #2d4d78;
    --sq-tl: #1f5fa8;
    --sq-dw: #7a4032;
    --sq-tw: #a83c25;
    --sq-center: #4a463d;
    --sq-fg: #d8d4cb;
    --sq-fg-strong: #ffffff;
  }
}

/* The explicit toggle wins over the media query, in both directions. --fg/--bg/etc. need no
   [data-theme] override here at all: they are var() aliases onto the shared tokens, which already
   switch on [data-theme] themselves (shared/web/styles/tokens.css) - only --sq-* and --tile-*
   (not shared) need repeating per explicit theme. */
:root[data-theme="light"] {
  --tile-bg: #f3e2be;
  --tile-fg: #2a1e0d;
  --tile-edge: #cdb98f;
  --tile-pending: #ffd98a;
  --tile-blank-fg: #8a6d3b;
  --sq-plain: #e9e5dc;
  --sq-dl: #a8c8f0;
  --sq-tl: #3f7fd0;
  --sq-dw: #f0b8a8;
  --sq-tw: #d05a3f;
  --sq-center: #d8cfbc;
  --sq-fg: #33302a;
}

:root[data-theme="dark"] {
  --tile-bg: #d9c69f;
  --tile-fg: #241a0b;
  --tile-edge: #8c7c58;
  --tile-pending: #ffcf70;
  --tile-blank-fg: #6d5426;
  --sq-plain: #3b3a36;
  --sq-dl: #2d4d78;
  --sq-tl: #1f5fa8;
  --sq-dw: #7a4032;
  --sq-tw: #a83c25;
  --sq-center: #4a463d;
  --sq-fg: #d8d4cb;
}

* { box-sizing: border-box; }

body {
  margin: 0 auto;
  /* Bottom padding accounts for the home-indicator safe area on a notched/rounded-corner phone —
     env() falls back to 0px where it's unsupported, so this is exactly 3rem everywhere else
     (Wordfeud Butler's header already does the equivalent for the top inset;
     index.html's own viewport meta already sets viewport-fit=cover, needed for env() to report
     anything other than 0 at all). */
  padding: 0.75rem 0.75rem calc(3rem + env(safe-area-inset-bottom, 0px));
  max-width: 780px;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--fg);
  line-height: 1.4;
}

h1 { font-size: 1.15rem; margin: 0; }
h2 { font-size: 1rem; margin: 0 0 0.4rem; }

a { color: var(--accent); }

/* --- generic controls ------------------------------------------------------ */

button {
  font: inherit;
  font-size: 0.95rem;
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent);
  color: var(--accent-fg);
  cursor: pointer;
}
button.secondary {
  background: transparent;
  color: var(--accent);
}
/* The neutral outline button — Shuffle/Recall, Pass, Swap tiles, Resign,
 * "Change" on the New game page. Its text used to be --muted on a
 * near-invisible 12%-alpha border, which is precisely what this app's
 * disabled state looks like: an enabled Shuffle read as dead (reported
 * 2026-09-09). Full-contrast text and a real border make it a live control,
 * and leave `button:disabled`'s opacity below as the one thing that means
 * "you cannot press this". */
button.quiet {
  background: transparent;
  color: var(--fg);
  border-color: var(--muted);
}
button.quiet:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
/* The only "you cannot press this" signal, and it has to stay the only one:
 * every enabled variant above is full-contrast, so a faded button is
 * unambiguous. Not scoped to the action bar — rack tiles and quiz options
 * are disabled buttons too, and they want the same fade. */
button:disabled { opacity: 0.4; cursor: not-allowed; }

input[type="text"] {
  font: inherit;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  min-width: 0;
}
input[type="number"] {
  font: inherit;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  width: 5rem;
}

.card {
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: var(--radius);
  padding: 0.8rem;
  margin-bottom: 0.75rem;
}

.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.muted { color: var(--muted); }
.small { font-size: 0.85rem; }
.hidden { display: none !important; }

.notice {
  border-left: 3px solid var(--accent);
  padding: 0.4rem 0.6rem;
  background: var(--card);
  border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: 0.5rem;
  font-size: 0.9rem;
}
.notice.bad { border-left-color: var(--bad); color: var(--bad); }
.notice.good { border-left-color: var(--good); }
.notice.warn { border-left-color: var(--warn); }

/* --- credentials, shown exactly once --------------------------------------- */

.credentials {
  border: 2px dashed var(--warn);
  border-radius: var(--radius);
  padding: 0.8rem;
  margin-bottom: 0.75rem;
  background: var(--card-raised);
}
.credentials dl { margin: 0.5rem 0 0; display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.8rem; }
.credentials dt { color: var(--muted); font-size: 0.85rem; }
.credentials dd { margin: 0; font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: 1.05rem; }

/* --- scoreboard ------------------------------------------------------------ */

.scoreboard { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.scoreboard .side {
  flex: 1 1 8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem 0.6rem;
  background: var(--card);
}
.scoreboard .side.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.scoreboard .name { font-weight: 600; font-size: 0.9rem; word-break: break-word; }
.scoreboard .score { font-size: 1.5rem; line-height: 1.1; }
.scoreboard .cp { font-size: 0.8rem; color: var(--muted); }
.scoreboard .cp .out { color: var(--bad); }

/* --- game list / opponent search ------------------------------------------- */

.list { list-style: none; margin: 0; padding: 0; }
.list li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--border);
}
.list li:last-child { border-bottom: 0; }
.list .grow { flex: 1; min-width: 0; }
.list .sub { color: var(--muted); font-size: 0.8rem; }
.list .turn-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--border);
  flex: none;
}
.list .turn-dot.yours { background: var(--good); }

.card details { margin-top: 0.6rem; }
.card details summary { cursor: pointer; }
.card details .list { margin-top: 0.4rem; }

/* A whole game-list row is one link (lobby.js's `gameListRow`), so the <li>
   stops being the flex container and the <a> inside it becomes one — same
   layout as before, but the hit target is the row rather than a small "Open"
   button at its end. */
.list li:has(> .row-link) { padding: 0; }
.row-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
  min-width: 0;
  padding: 0.55rem 0.2rem;
  color: inherit;
  text-decoration: none;
  border-radius: 6px;
}
.row-link:hover { background: var(--card-raised); }
.row-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.row-link .row-title { display: block; }
.row-link .sub { display: block; }
.row-chevron { color: var(--muted); font-size: 1.2rem; flex: none; padding: 0 0.2rem; }

/* "+ New game" (lobby.js): a link, because /scrabble-game/new is a real page
   now, but the games card's one primary action — so it is painted as the
   primary button it replaced rather than as body-text-blue link. */
.new-game-link {
  display: inline-block;
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent);
  color: var(--accent-fg);
  font-size: 0.95rem;
  text-decoration: none;
  white-space: nowrap;
}
.new-game-link:hover { filter: brightness(1.08); }
.new-game-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- the New game page (new-game-view.js) ----------------------------------
 *
 * Each screen below the lobby carries its own way back — the shared toolbar
 * only says which *section* this is. Quiet on purpose: it is the way out, not
 * the thing to do here.
 */

.back-link {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9rem;
}
.back-link:hover { color: var(--accent); }

#new-game h1 { margin: 0.2rem 0 0.75rem; }
#new-game #opponent-search { flex: 1 1 12rem; }

/* The opponent picker is a list of full-width choices, not a <select>: all
   ten matches visible at once, each one a target the size of the name being
   read. The selected row is the only one that shows its check mark. */
.opponent-list li { padding: 0; }
.opponent {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.6rem 0.6rem;
  text-align: left;
  font-size: 1rem;
  background: transparent;
  color: var(--fg);
  border: 1px solid transparent;
  border-radius: 6px;
}
.opponent:hover { background: var(--card-raised); }
.opponent.chosen {
  background: var(--card-raised);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.opponent-mark { color: var(--accent); visibility: hidden; flex: none; }
.opponent.chosen .opponent-mark { visibility: visible; }

/* What is left once a pick collapses the list: who was chosen, and the way
   back to the names. */
.chosen-opponent {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--card-raised);
}

/* Language: the choice that decides what the game is, so it is a row of real
   buttons rather than a <select> whose options have to be opened to be seen,
   and it sits at the top of the settings card with a label of its own above
   it rather than inline beside it like the two numbers below. */
.field { margin-bottom: 0.7rem; }
.field-label { display: block; font-size: 0.85rem; margin-bottom: 0.35rem; }

.language-choices { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.language-choice {
  flex: 1 1 auto;
  padding: 0.5rem 0.7rem;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--border);
}
.language-choice:hover { border-color: var(--accent); }
.language-choice.chosen {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
  font-weight: 600;
}

/* The one way to commit, and the last thing on the page. Full width on a
   phone so it cannot be mistaken for one more secondary control. */
.start-game-row { margin-top: 0.25rem; }
.start-game-row button { flex: 1 1 auto; padding: 0.6rem 0.8rem; font-size: 1rem; }

/* --- the two modals the shell owns ------------------------------------------
 *
 * The overlay and panel styling is quiz.css's `.quiz-overlay`/`.quiz-modal`,
 * reused rather than duplicated: the blank-tile chooser and the swap picker
 * are the same kind of object as the quiz, and a second overlay style would
 * only be a second thing to keep in step. What is added here is the letter
 * grid, which is theirs alone.
 */

.blank-letters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr));
  gap: 0.4rem;
  margin: 0.75rem 0;
}

.blank-letter {
  font-size: 1.1rem;
  font-weight: 600;
  padding: 0.55rem 0.25rem;
  background: var(--tile-bg);
  color: var(--tile-fg);
  border: 1px solid var(--tile-edge);
  border-radius: 6px;
  cursor: pointer;
}
.blank-letter:hover { border-color: var(--accent); }
.blank-letter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.blank-letter.chosen {
  background: var(--tile-pending);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* A flexible gap in a .row. The top bar has its own rule for the same class;
 * this is the general one, used under the board and in the lobby's headers. */
.spacer { flex: 1 1 auto; }

/* The action bar under the board wraps rather than scrolling: on a phone six
 * buttons do not fit on one line, and a horizontally scrolling toolbar hides
 * the ones that matter. */
#actions { gap: 0.4rem 0.5rem; margin-top: 0.6rem; }
#actions button { flex: 0 1 auto; }

#move-hint { margin: 0.4rem 0 0; min-height: 1.2em; }
#status { margin: 0.5rem 0; }

/* --- this game's own rules, collapsed by default ---------------------------- */

.game-rules { margin-top: 0.8rem; }
.game-rules summary { cursor: pointer; }
#game-rules-list {
  margin: 0.5rem 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.8rem;
}
#game-rules-list dt { color: var(--muted); font-size: 0.85rem; }
#game-rules-list dd { margin: 0; }
#notices { position: sticky; top: 0; z-index: 5; }

/* The offline indicator: deliberately small and quiet. It says the socket is
 * down and being retried (connection.js's backoff); the buttons that would
 * otherwise queue a move silently are disabled meanwhile (app.js). */
.connection-state {
  margin: 0 0 0.5rem;
  padding: 0.25rem 0.6rem;
  font-size: 0.8rem;
  color: var(--muted);
  border-left: 3px solid var(--warn);
  background: var(--card);
  border-radius: 0 var(--radius) var(--radius) 0;
}
