/* SOMWORLD site stylesheet — clean neutral, light/dark via design tokens.
 *
 * Theme resolution, in priority order:
 *   1. <html data-theme="light|dark">  — the ACCOUNT setting, server-rendered
 *      for a signed-in member who has chosen (follows them across devices).
 *   2. prefers-color-scheme            — the device default, for anonymous
 *      visitors and members who never touched the toggle.
 * The dark token block therefore appears twice: once under the media query
 * (guarded so an explicit light choice beats a dark device), once under the
 * explicit attribute. Same values, two doors.
 */

:root {
  --bg: #fafafa;
  --surface: #ffffff;
  --text: #1a1d21;
  --muted: #5c6570;
  --border: #d9dde2;
  --accent: #3b5b7c;          /* restrained slate-blue: links, focus, buttons */
  --accent-contrast: #ffffff;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #15181c;
    --surface: #1d2126;
    --text: #e4e7ea;
    --muted: #9aa4ae;
    --border: #343a41;
    --accent: #7da7cd;
    --accent-contrast: #12151a;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  }
}
:root[data-theme="dark"] {
  --bg: #15181c;
  --surface: #1d2126;
  --text: #e4e7ea;
  --muted: #9aa4ae;
  --border: #343a41;
  --accent: #7da7cd;
  --accent-contrast: #12151a;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* ---- base ---------------------------------------------------------------- */

* { box-sizing: border-box; }

html { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  line-height: 1.55;
}

main {
  max-width: 52rem;
  margin: 0 auto;
  padding: 1.25rem 1rem 4rem;
}
/* Article pages that carry a stat grid get the screen (beta feedback 2026-08-13:
 * "on a large screen there is a lot of wasted space"). Prose is NOT widened with
 * it — a 1150px line length is unreadable — so everything in the body except the
 * table keeps the reading measure. Raising a max-width changes nothing below an
 * 832px viewport, so every phone renders exactly as before. */
main.wide { max-width: 72rem; }
main.wide article > :not(table) { max-width: 46rem; }

a { color: var(--accent); }
a:hover { text-decoration-thickness: 2px; }

h1, h2, h3 { line-height: 1.25; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.25rem; margin-top: 1.75em; }

hr { border: 0; border-top: 1px solid var(--border); }

small, .muted { color: var(--muted); }

/* ---- nav bar ------------------------------------------------------------- */
/* Moved to static/menu.css (2026-09-26), which base.html links on every page right after
 * this file. Removed here rather than overridden, so there is one place that draws the menu. */

/* ---- buttons & forms ----------------------------------------------------- */

button, input[type="submit"] {
  font: inherit;
  background: var(--accent);
  color: var(--accent-contrast);
  border: 0;
  border-radius: 6px;
  padding: 0.45rem 0.9rem;
  cursor: pointer;
}
button:hover, input[type="submit"]:hover { filter: brightness(1.08); }

input[type="text"], input[type="email"], input[type="password"], input[type="search"],
select, textarea {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.45rem 0.6rem;
  max-width: 100%;
}
input:focus, select:focus, textarea:focus, button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

form p { margin: 0.7rem 0; }
form label { display: block; margin-bottom: 0.2rem; color: var(--muted); font-size: 0.92rem; }
.helptext { display: block; color: var(--muted); font-size: 0.85rem; }
ul.errorlist { color: #b3392f; padding-left: 1rem; margin: 0.25rem 0; }
:root[data-theme="dark"] ul.errorlist { color: #e08a82; }

/* The beta site's standing banner. Same treatment as a flash message on purpose — the
 * reader already knows that shape means "the site is telling you something" — but a
 * different element, because messages drain when iterated and must render exactly once.
 * Every value here is a theme token, so it follows dark mode with no second rule and
 * stays clear of somworld/tests.py's "this fragment appears exactly twice" assertions. */
.site-banner p {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 6px;
  padding: 0.6rem 0.9rem;
  margin: 0.5rem 0;
  color: var(--muted);
  font-size: 0.9rem;
}

ul.messages { list-style: none; padding: 0; }
ul.messages li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 6px;
  padding: 0.6rem 0.9rem;
  margin: 0.5rem 0;
}

/* ---- tables (the stat grids) --------------------------------------------- */
/* display:block + overflow-x lets a 15-column platoon grid scroll INSIDE the
 * page on a phone instead of blowing the layout out sideways. */

table {
  border-collapse: collapse;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  display: block;
  max-width: 100%;
  overflow-x: auto;
  margin: 1rem 0;
}
th, td {
  border: 1px solid var(--border);
  padding: 0.2rem 0.5rem;
  text-align: left;
  white-space: nowrap;
}
th { background: var(--surface); }

/* nowrap is right for a dense grid and wrong for a draft guide. A table with
 * FEWER THAN 8 columns is never one of the dense numeric grids (6,856 of the
 * corpus's 7,880 tables have >=8), and 79 of the 81 tables carrying a prose
 * column sit in that narrow set — so let every cell wrap there. Numbers hold no
 * spaces and cannot break; what stops breaking the layout is the comments column,
 * which today forces the whole table into a horizontal scroll and drags the
 * player name off-screen with it (beta feedback 2026-08-13). The dense grids keep
 * nowrap and keep scrolling, which is what makes THEM readable.
 * :has() is baseline-2023; where it is unsupported the rule drops whole and the
 * behaviour is exactly what it was. */
table:not(:has(tr > :nth-child(8))) :is(th, td) { white-space: normal; }

/* Legacy stat cells carry their own inline light background colors
 * (background:#CCFFCC and friends). In dark mode those cells KEEP their light
 * fill, so their text must stay dark — target exactly the cells that declare
 * a background of their own. */
:root[data-theme="dark"] td[style*="background"],
:root[data-theme="dark"] th[style*="background"] { color: #1a1d21; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) td[style*="background"],
  :root:not([data-theme="light"]) th[style*="background"] { color: #1a1d21; }
}

/* The mirror case (beta feedback 2026-08-12): cells with an inline COLOR but
 * no background — color:black / dark navies from spreadsheet exports, 888
 * articles' worth — sit on the page's dark ground and vanish. They must
 * inherit the theme text color instead. Unlike the rule above, an inline
 * color EXISTS here and has to lose, hence !important. Cells that declare
 * their own background remain self-contained worlds and are excluded.
 * Inline red is semantic (negative stats) — remap it readable, don't flatten;
 * the extra attribute selector outranks the blanket rule. */
:root[data-theme="dark"] td:not([style*="background"]),
:root[data-theme="dark"] th:not([style*="background"]) { color: inherit !important; }
:root[data-theme="dark"] td[style*="color:red"]:not([style*="background"]),
:root[data-theme="dark"] th[style*="color:red"]:not([style*="background"]) { color: #e57373 !important; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) td:not([style*="background"]),
  :root:not([data-theme="light"]) th:not([style*="background"]) { color: inherit !important; }
  :root:not([data-theme="light"]) td[style*="color:red"]:not([style*="background"]),
  :root:not([data-theme="light"]) th[style*="color:red"]:not([style*="background"]) { color: #e57373 !important; }
}

/* ---- article body -------------------------------------------------------- */

article h1 { margin-top: 0.25rem; }
article p { max-width: 46rem; }

/* Restored legacy images (2026-08-19). The source markup carries intrinsic pixel
 * sizes — 549x227 card scans, 1115x458 tool screenshots, 120x120 headshots — and
 * the width/height attributes are kept so the browser reserves the space rather
 * than reflowing the article as each one loads. These two rules make that size a
 * CEILING rather than a fixed size, which is what stops a 1115px screenshot from
 * forcing a horizontal scrollbar on a phone.
 *
 * `height: auto` is load-bearing next to `max-width`: without it a shrunken
 * image keeps its attribute height and stretches. */
article img {
  max-width: 100%;
  height: auto;
}
/* An image alone in its paragraph is a figure, not a run of text — 83% of them
 * are, and `article p` above would otherwise cap them at the 46rem prose measure
 * even on a wide table page. Images inside table cells are left alone: they are
 * sized by the grid around them. */
article p > img:only-child {
  display: block;
  margin: 0.4rem 0;
}
/* Dark mode: card scans and screenshots are photographic and mostly light-ground,
 * so they sit on a dark page as bright rectangles. A hairline border gives them
 * an edge instead of letting them bleed into the background. Declared under BOTH
 * theme doors — DarkModeCellRuleTests requires every dark rule to exist under
 * data-theme AND prefers-color-scheme, and has caught that drift before. */
:root[data-theme="dark"] article img { border: 1px solid var(--border); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) article img { border: 1px solid var(--border); }
}

/* ---- league management --------------------------------------------------- */
/* The commissioner's team list. It used to be four `display:inline` forms per
 * team, which wrapped into one ragged flow — a button landing between two
 * unrelated inputs, wrap points shifting with the length of the team name. A
 * tester's screenshot of it came with "I had a hard time trying to figure out
 * which field to set" (2026-08-13). Each action is now its own block, and
 * .team-field keeps an input welded to the button that consumes it. */

.teams { list-style: none; padding-left: 0; }
.team {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 0.9rem;
  margin: 0.8rem 0;
}
.team-name { margin: 0 0 0.4rem; }
.team-managers { list-style: none; padding-left: 0; margin: 0 0 0.6rem; }
.team-managers li {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0.25rem 0;
}
.team-managers button {
  background: transparent; color: var(--muted); border: 1px solid var(--border);
  padding: 0.15rem 0.5rem; font-size: 0.8rem;
}
.team-extra { color: var(--muted); font-size: 0.9rem; }
.team-action { margin: 0.7rem 0; }
.team-field { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.team-rename summary { color: var(--muted); cursor: pointer; font-size: 0.9rem; }
.grant-extra {
  border: 1px solid var(--accent); border-radius: 6px; padding: 0.9rem 1.1rem;
  margin: 1rem 0;
}

/* ---- copy-table ---------------------------------------------------------- */
/* The button and the note are both INJECTED by copytable.js — the site's only
 * JavaScript. With scripts off neither element exists, so there is nothing here
 * to hide and the tables render exactly as they did before. */

.copy-table {
  display: block;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  padding: 0.2rem 0.55rem;
  font-size: 0.8rem;
  margin: 1rem 0 -0.5rem;
}
.copy-table:hover { color: var(--accent); border-color: var(--accent); filter: none; }

/* The note is only TRUE in dark mode, so CSS decides it, not JS: a theme read in
 * JavaScript would have to reproduce BOTH doors (the account stamp and the device
 * preference) and would flash on load. Same two-context hand-sync as the cell
 * rules above — DarkModeCellRuleTests counts this fragment in both places. */
.copy-note { font-size: 0.85rem; color: var(--muted); max-width: 46rem; display: none; }
:root[data-theme="dark"] .copy-note { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .copy-note { display: block; }
}

/* ---- show-password eye --------------------------------------------------- */
/* The wrapper and the button are both INJECTED by showpassword.js. With scripts off
 * neither exists, so every rule here is scoped to .pw-wrap and the password boxes render
 * exactly as before, Edge's own reveal button included. With scripts on, Edge's button
 * is hidden so there is only ever one eye. The input is padded on the right so typed
 * text never runs under the button, at any width. */

.pw-wrap {
  position: relative;
  display: inline-block;
  max-width: 100%;
  vertical-align: middle;
}
.pw-wrap input[type] { padding-right: 2.6rem; }
.pw-wrap input::-ms-reveal,
.pw-wrap input::-ms-clear { display: none; }

.pw-toggle {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  width: 2.4rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--muted);
  border: 0;
  border-radius: 0 5px 5px 0;
}
.pw-toggle:hover { color: var(--accent); filter: none; }
.pw-toggle[aria-pressed="true"] { color: var(--accent); }
.pw-toggle:focus-visible { outline-offset: -3px; }
.pw-toggle svg { display: block; }
