/* Generated by build_site.py — edit the builder, not this file. */
*, *::before, *::after { box-sizing: border-box; }

:root {
  color-scheme: light;
  /* The chart tokens are tuned for large recessive areas. A 1px line needs its
     own value or it vanishes on the dark ground: --grid there measures 1.24:1. */
  --track-grid:#e1e0d9; --track-base:#c3c2b7; --track-span:#a3a299;
  --plane:#f4f4f1; --surface:#fcfcfb; --sunk:#efefeb;
  /* --muted measured 3.59:1 on the card and 3.32:1 on the page ground, against the
     4.5:1 that text under 18.66px needs. It carries the sample sizes, the sub-lines, the
     tallies and the platform and genre columns — most of the small text on the site — so
     it is darkened while keeping its hue — far enough that it also clears 4.5:1 on the
     page ground, which is a shade darker than the cards and where the tallies and footer
     sit. The dark theme already passed at 4.85:1 and is left alone. */
  --ink:#0b0b0b; --ink-2:#52514e; --muted:#6e6c67;
  --grid:#e1e0d9; --axis:#c3c2b7; --rule:rgba(11,11,11,0.10);
  /* 4.30:1 on a card and 4.01:1 on the page ground, against the 4.5:1 that text this
     size needs — and it colours every game title and every article link. One step
     darker reaches 5.05 and 4.71 with the hue unchanged. */
  --oc:#266dc2; --mc:#eb6834;
  --good:#0ca30c; --good-text:#006300; --warning:#fab219; --critical:#d03b3b;
  /* --critical is tuned for the table's marks. In 13.5px prose it measures
     4.36:1 on the page ground against the 4.5:1 that size needs, so body copy
     referring to that red uses this darker twin — the same split as
     --good / --good-text. 5.09:1 page, 5.47:1 card, 4.87:1 sunk. */
  --critical-text:#c22f2f;
  --shadow:0 1px 2px rgba(11,11,11,.05), 0 8px 24px -16px rgba(11,11,11,.18);
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --track-grid:#3a3a37; --track-base:#4d4d48; --track-span:#6b6b64;
    --plane:#0d0d0d; --surface:#1a1a19; --sunk:#131312;
    --ink:#fff; --ink-2:#c3c2b7; --muted:#898781;
    --grid:#2c2c2a; --axis:#383835; --rule:rgba(255,255,255,0.10);
    --oc:#3987e5; --mc:#d95926; --good-text:#0ca30c;
    /* Inherited from :root, where it is tuned for a white ground: #d03b3b measures
       3.62:1 on the dark card, and it is what marks a thin sample. */
    --critical:#e65a5a; --critical-text:#e65a5a;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px -16px rgba(0,0,0,.7);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --track-grid:#3a3a37; --track-base:#4d4d48; --track-span:#6b6b64;
  --plane:#0d0d0d; --surface:#1a1a19; --sunk:#131312;
  --ink:#fff; --ink-2:#c3c2b7; --muted:#898781;
  --grid:#2c2c2a; --axis:#383835; --rule:rgba(255,255,255,0.10);
  --oc:#3987e5; --mc:#d95926; --good-text:#0ca30c;
  /* Inherited from :root, where it is tuned for a white ground: #d03b3b measures
     3.62:1 on the dark card, and it is what marks a thin sample. */
  --critical:#e65a5a; --critical-text:#e65a5a;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px -16px rgba(0,0,0,.7);
}
:root[data-theme="light"] {
  color-scheme: light;
  --track-grid:#e1e0d9; --track-base:#c3c2b7; --track-span:#a3a299;
  --plane:#f4f4f1; --surface:#fcfcfb; --sunk:#efefeb;
  --ink:#0b0b0b; --ink-2:#52514e; --grid:#e1e0d9; --axis:#c3c2b7;
  /* Keep in step with :root — this block is what an explicitly pinned light theme
     uses, and it held the old 4.30:1 blue after :root had been corrected. */
  --rule:rgba(11,11,11,0.10); --oc:#266dc2; --mc:#eb6834; --good-text:#006300;
  --critical-text:#c22f2f;
}

body { margin:0; background:var(--plane); color:var(--ink); font-size:15px;
  line-height:1.55; font-family:system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased; }
a { color:var(--oc); }

/* ---- site chrome ---- */
.topbar { position:sticky; top:0; z-index:30; background:var(--surface);
  border-bottom:1px solid var(--rule); }
.topbar-in { max-width:1180px; margin:0 auto; padding:0 20px; display:flex;
  align-items:center; gap:26px; min-height:52px; flex-wrap:wrap; }
.brand { font-size:13.5px; font-weight:660; letter-spacing:-.01em; margin-right:2px; }
/* Search sits in the site bar because it is the one control both pages want. Giving it
   a permanent home is what lets each page header collapse to a single line. */
.topfind { position:relative; display:flex; flex:1 1 200px; max-width:340px;
  min-width:150px; margin-left:auto; }
.topfind input[type="search"] { flex:1; min-width:0; padding-right:28px; font-size:13px; }
/* On a desktop the search field's own auto margin does the pushing, so the toggle sits
   directly after it. */
.topbar .theme-btn { margin-left:0; }

nav { display:flex; gap:2px; }
/* At 390px the six section links measured 520px, and a single element wider than the
   viewport makes the browser scale the entire page down — every other size on the
   page was being decided by this one row. It scrolls under a thumb instead. */
nav a { display:block; padding:7px 12px; font-size:13.5px; text-decoration:none;
  color:var(--ink-2); border-radius:3px; }
nav a:hover { background:var(--sunk); color:var(--ink); }
nav a[aria-current="page"] { color:var(--ink); font-weight:620; background:var(--sunk); }
nav a:focus-visible, .theme-btn:focus-visible { outline:2px solid var(--oc);
  outline-offset:1px; }
/* A 30px circle with a 15px icon. Small enough to sit beside the brand without
   competing with the navigation, and the same width in both themes — the old text button
   changed width between "Dark theme" and "Light theme", nudging the whole bar. */
.theme-btn { flex:none; margin-left:auto; width:30px; height:30px; padding:0;
  display:inline-flex; align-items:center; justify-content:center; appearance:none;
  background:transparent; border:1px solid var(--rule); border-radius:50%;
  color:var(--muted); cursor:pointer;
  transition:color .15s ease, border-color .15s ease, transform .1s ease; }
.theme-btn svg { width:15px; height:15px; display:block; }
.theme-btn:hover { color:var(--ink); border-color:var(--ink-2); }
.theme-btn:active { transform:scale(.94); }
@media (prefers-reduced-motion: reduce) { .theme-btn { transition:none; } }

.wrap { max-width:1180px; margin:0 auto; padding:30px 20px 72px; }
/* The reference pages lay out in two columns and their sections held 378px of content
   inside 366px on a phone, clipping the right edge with no way to scroll to it. One
   column below the breakpoint, and any table inside may scroll on its own. */
/* ---- headings ---- */
.page-head { margin-bottom:24px; padding-bottom:20px; border-bottom:1px solid var(--rule); }
.eyebrow { font-size:11px; font-weight:620; letter-spacing:.11em; text-transform:uppercase;
  color:var(--muted); }
h1 { margin:10px 0 8px; font-size:clamp(24px,3.6vw,34px); line-height:1.12;
  letter-spacing:-.022em; font-weight:660; text-wrap:balance; }
.standfirst { margin:0; max-width:64ch; color:var(--ink-2); }
.provenance { display:flex; flex-wrap:wrap; gap:5px 18px; margin-top:14px;
  font-size:12.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.provenance b { color:var(--ink-2); font-weight:600; }
section { margin-bottom:34px; }
.sec-head h2 { margin:0 0 4px; font-size:19px; font-weight:640; letter-spacing:-.012em; }
.sec-note { margin:0 0 16px; max-width:76ch; font-size:13.5px; color:var(--ink-2); }

/* ---- notice ---- */
.notice { display:flex; gap:11px; margin-bottom:24px; padding:13px 15px;
  background:var(--surface); border:1px solid var(--rule);
  border-left:3px solid var(--warning); border-radius:3px; font-size:13.5px;
  color:var(--ink-2); }
.notice strong { color:var(--ink); font-weight:620; }
/* --warning is a fill colour, not a text colour: as text it measured 1.79:1. The mark
   takes the ink colour and keeps its meaning from the icon and the notice's own
   background. */
.notice-mark { flex:none; font-weight:700; color:var(--ink-2); }
.notice-ok { border-left-color:var(--good); }

/* ---- tiles ---- */
.tiles { display:grid; gap:12px; margin-bottom:32px;
  grid-template-columns:repeat(auto-fit,minmax(158px,1fr)); }
.tile { padding:14px 15px; background:var(--surface); border:1px solid var(--rule);
  border-radius:4px; box-shadow:var(--shadow); }
.tile .k { font-size:10.5px; font-weight:620; letter-spacing:.085em;
  text-transform:uppercase; color:var(--muted); }
.tile .v { margin-top:7px; font-size:27px; font-weight:630; line-height:1;
  letter-spacing:-.02em; }
.tile .v small { font-size:15px; font-weight:500; }
.tile .s { margin-top:5px; font-size:12px; color:var(--ink-2); }
.tile.oc { border-top:2px solid var(--oc); }
.tile.mc { border-top:2px solid var(--mc); }
.tile.warn { border-top:2px solid var(--warning); }
.tile.warn .k { color:var(--warning); }

/* ---- cards ---- */
/* One inset for everything inside the framed unit. The controls and the table are now a
   single panel, so their contents have to share a left edge: 12px each, which puts the
   first table column and the first control button on the same pixel. It was 18px on the
   card against 12px in the bar, so the header row sat 6px right of the buttons above it. */
.card { background:var(--surface); border:1px solid var(--rule); border-radius:4px;
  box-shadow:var(--shadow); padding:14px 12px 10px; }
/* Trailing cell needs no right padding; reclaiming it across nine columns is
   most of the overflow this table was fighting. */
.twrap table th:last-child, .twrap table td:last-child { padding-right:0; }
.card-head { display:flex; align-items:baseline; justify-content:space-between;
  gap:14px; flex-wrap:wrap; margin-bottom:6px; }
.card-title { font-size:14px; font-weight:620; }
.card-sub { font-size:12.5px; color:var(--muted); }
.duo { display:grid; gap:16px; grid-template-columns:1fr; }
@media (min-width:900px){ .duo { grid-template-columns:1fr 1fr; } }

/* ---- controls ---- */
.controls { display:flex; flex-wrap:wrap; align-items:flex-end; gap:18px;
  margin-bottom:22px; padding:14px 16px; background:var(--surface);
  border:1px solid var(--rule); border-radius:4px; box-shadow:var(--shadow); }
.field { display:flex; flex-direction:column; gap:5px; }
.field label { font-size:10.5px; font-weight:620; letter-spacing:.1em;
  text-transform:uppercase; color:var(--muted); }
/* width:max-content so a group is exactly as wide as its options. Inside the drawer the
   field stretches to its label and hint, and a one-button group stretched with it —
   leaving 26px of empty bordered box beside the pressed button. */
.seg { display:flex; align-self:flex-start; width:max-content; max-width:100%;
  border:1px solid var(--rule); border-radius:3px; overflow:hidden; }
.seg button { appearance:none; border:0; border-right:1px solid var(--rule);
  background:transparent; color:var(--ink-2); font:inherit; font-size:13px;
  padding:5px 11px; cursor:pointer; }
.seg button:last-child { border-right:0; }
.seg button[aria-pressed="true"] { background:var(--ink); color:var(--surface);
  font-weight:600; }
.seg button:hover:not([aria-pressed="true"]) { background:var(--sunk); }
.seg button:focus-visible { outline:2px solid var(--oc); outline-offset:-2px; }
/* The search field takes the slack in the controls row so a long title stays
   readable while typing, and its hint sits under the box rather than beside it. */

/* iOS Safari zooms the whole page in when a control with text under 16px takes focus,
   and it does not zoom back out afterwards — the layout is simply left magnified. 16px
   is the documented threshold, so every form control clears it on touch-sized screens.
   `font-size` alone is not enough: Safari also measures the placeholder. */
/* At 360px the brand, both tabs and the toggle measured 351px against 336px of content
   width, so the toggle wrapped to a line of its own. Tighter tabs keep the three
   together on the narrowest common phone. */
/* ---- toolbar + drawer ----------------------------------------------------
   The whole control set used to be seven labelled groups wrapping to three rows.
   Search, period, the Show nesting and the sort now share one bar; platform, genre,
   Hidden gems and the diagnostic sorts live behind More. Labels are dropped from the
   bar because each control's own contents name it. */
/* The controls sit directly on top of the table they drive: the bar loses its bottom
   corners and border, the card loses its top ones, and the two share an edge. They were
   previously two separate cards with a gap, which read as unrelated panels. */
.toolbar { display:flex; flex-direction:row; flex-wrap:wrap; align-items:center; gap:9px;
  padding:11px 12px; background:var(--surface); border:1px solid var(--rule);
  border-bottom:0; border-radius:4px 4px 0 0; box-shadow:var(--shadow); }

.toolbar ~ .card, .chips ~ .card { border-radius:0 0 4px 4px; }
/* display:contents so the segments are direct flex children of the bar and can be
   ordered and wrapped as one line, rather than as rows inside rows. */
.tbrow { display:contents; }
/* The native clear affordance is suppressed so there is only ever one X, and this one
   can be reached by keyboard and sized for a thumb. */
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance:none; display:none; }
.qclear { position:absolute; right:2px; top:50%; transform:translateY(-50%);
  appearance:none; background:none; border:0; padding:0 7px; line-height:1;
  font-size:19px; color:var(--muted); cursor:pointer; }
.qclear:hover { color:var(--ink); }
.qclear:focus-visible { outline:2px solid var(--oc); outline-offset:-2px; border-radius:3px; }
.ghost { appearance:none; background:none; border:1px solid var(--rule);
  border-radius:3px; color:var(--ink-2); font:inherit; font-size:13px;
  padding:5px 11px; cursor:pointer; white-space:nowrap; }
.ghost:hover { background:var(--sunk); }
.ghost[aria-expanded="true"] { background:var(--sunk); border-color:var(--ink-2); }
.dcount { display:inline-block; margin-left:5px; padding:0 5px; border-radius:8px;
  background:var(--oc); color:var(--surface); font-size:10.5px; font-weight:700;
  font-variant-numeric:tabular-nums; }
.drawer { display:flex; align-items:flex-end; gap:20px; flex-wrap:wrap;
  margin-top:-1px; padding:13px; background:var(--sunk);
  border:1px solid var(--rule); border-top:0; border-radius:0 0 4px 4px; }
/* The hidden attribute is only display:none in the UA sheet, which any declared
   display beats — without this the drawer would render permanently open. */
[hidden] { display:none !important; }

/* A shut drawer hides state, so every non-default choice inside it is echoed here. */
/* Part of the framed unit rather than floating between it: same surface and side
   borders as the bar above and the table below, and equal space top and bottom. It had
   10px above and 0 below, so it sat flush against the first row. */
.chips { display:flex; gap:7px; flex-wrap:wrap; margin:0; padding:9px 12px;
  font-size:12px; background:var(--surface);
  border-left:1px solid var(--rule); border-right:1px solid var(--rule); }
.chip-f { display:inline-flex; align-items:center; gap:6px; padding:2px 9px;
  border:1px solid color-mix(in oklab,var(--oc) 45%,transparent); border-radius:11px;
  color:var(--oc); background:color-mix(in oklab,var(--oc) 7%,transparent); }
.chip-f button { appearance:none; background:none; border:0; padding:0; margin:0;
  color:inherit; font:inherit; font-size:13px; line-height:1; cursor:pointer; }
.chips .clearall { border:0; background:none; padding:0; font:inherit; font-size:12px;
  color:var(--muted); cursor:pointer; text-decoration:underline; align-self:center; }

.page-head.tight { margin-bottom:16px; }

/* One band where there were three. The title sits on the same line as the count and
   the link to the data sheet; everything the standfirst used to say is in that sheet. */
/* No rule beneath: the toolbar's own top border already separates the title from the
   content, so a second line was drawing a division that was already there. */
.titlebar { display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;
  margin-bottom:12px; }
.titlebar h1 { margin:0; font-size:clamp(19px,2.4vw,23px); line-height:1.2; }
.tallies { margin-top:11px; font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums; }
/* Both of these measured 14px and 20px tall on a phone, under the 24px minimum target
   size. Padding rather than a bigger font, so the type stays quiet while the tap area
   clears the floor. */
.tallies a { display:inline-block; padding:5px 2px; }
.page-head.tight h1 { margin-bottom:5px; }

/* ---- the data sheet, answering "what backs this page" at the foot -------- */
.sheet { margin-top:30px; padding-top:16px; border-top:1px solid var(--rule); }
.sheet h2 { margin:0 0 10px; font-size:15px; font-weight:640; }
.sheet dl { margin:0; display:grid; grid-template-columns:max-content 1fr;
  gap:6px 18px; font-size:12.5px; max-width:760px; }
.sheet dt { color:var(--muted); }
.sheet dd { margin:0; }
.sheet dd b { font-variant-numeric:tabular-nums; }

/* ---- phone layout --------------------------------------------------------
   720px is where the nine-column table stops fitting without a sideways scroll.
   Below it the table is hidden entirely and #mlist carries the same rows as
   summary lines that open in place. */
.mlist { display:none; }
.narrow-only, .narrow-inline { display:none; }
/* A summary line: date, title, the headline score. Everything else is one tap away,
   where it has the full width instead of a 40px column. */
.mrow { background:var(--surface); border-bottom:1px solid var(--grid); }
.mrow > summary { display:flex; align-items:baseline; gap:9px; padding:11px 12px;
  cursor:pointer; list-style:none; }
.mrow > summary::-webkit-details-marker { display:none; }
.mrow .mdate { flex:0 0 auto; font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums; }
.mrow .mname { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; font-weight:620; }
.mrow .mscore { flex:0 0 auto; font-size:19px; font-weight:640;
  font-variant-numeric:tabular-nums; }
.mrow[open] > summary { border-bottom:1px solid var(--grid); }
.mrow[open] .mname { white-space:normal; }
.mdl { margin:0; padding:10px 12px 13px; display:grid;
  grid-template-columns:max-content 1fr; gap:5px 14px; font-size:13px; }
.mdl dt { color:var(--muted); }
.mdl dd { margin:0; }
.mdl dd b { font-variant-numeric:tabular-nums; }
.mdl .thin { color:var(--critical); }
.mempty { padding:22px 12px; text-align:center; color:var(--muted); }
.mmonth { padding:9px 12px 6px; font-size:10.5px; font-weight:620; letter-spacing:.09em;
  text-transform:uppercase; color:var(--muted); background:var(--sunk);
  border-bottom:1px solid var(--grid); position:sticky; top:0; z-index:2; }
.mrow .mwhen { flex:0 0 auto; font-size:12px; color:var(--muted); white-space:nowrap; }

/* Weight only, no colour and no background: the point is to be findable while
   scanning, not to claim the game is good — hype says what people are waiting for,
   never how it turns out. */
tr.standout td.name, .mrow.standout .mname { font-weight:700; color:var(--ink); }
tr.standout td.name a, .mrow.standout .mname a { font-weight:700; }
/* The mobile accordion sets every title to 620, which left the 700 of a standout
   invisible — the whole list read as bold. On this page the ordinary title steps back
   so the marked one has somewhere to stand out to. Scoped to the page, because the
   review list has no standouts and its titles should keep their weight. */
.p-upcoming .mrow .mname { font-weight:500; }
.p-upcoming .mrow.standout .mname { font-weight:700; }

/* Wraps because the second button appears at 390px, where count + two buttons do not
   fit on one line. (The #sentinel that sat here went with the scroll-driven loading.) */
.morebar { display:flex; align-items:center; gap:12px; margin-top:12px;
  flex-wrap:wrap; font-size:12.5px; color:var(--muted); }
.morecount { font-variant-numeric:tabular-nums; }
/* The "why does this control behave like that" disclosures under the list. Closed by
   default, so the page costs one line each until asked. Stacked rather than in a row:
   the summaries are full questions and wrap badly side by side at 390px. */
.whys { margin-top:14px; display:flex; flex-direction:column; gap:2px; }
.why > summary { font-size:12.5px; color:var(--muted); cursor:pointer; padding:5px 0;
  list-style:none; }
/* Safari draws its own disclosure triangle through ::-webkit-details-marker, which
   list-style:none alone does not remove. */
.why > summary::-webkit-details-marker { display:none; }
/* The glyphs are literal characters, not CSS escapes: STYLE is a plain (non-raw)
   triple-quoted string, so "\2212" is read by Python as an octal escape long before
   CSS sees it, and the built stylesheet ends up with a stray control character. */
.why > summary::before { content:"+"; display:inline-block; width:1.1em;
  font-variant-numeric:tabular-nums; color:var(--muted); }
.why[open] > summary::before { content:"−"; }
.why > summary:hover { color:var(--ink); }
/* An open disclosure is body copy, so it gets the sec-note measure and colour rather
   than the quieter summary styling. */
.why > p { margin:2px 0 10px 1em; max-width:74ch; font-size:13px; line-height:1.55;
  color:var(--muted); }
/* Named for what it looks like, not what it is: the sample turns red in the table, and
   the orientation line has to point at that same red to be worth reading. Uses the
   body-copy token — at this size --critical itself fails WCAG AA, which check.py caught. */
.thinlike { color:var(--critical-text); }
.legal { font-size:11px; color:var(--muted); max-width:78ch; line-height:1.5; }
.hint { font-size:11px; color:var(--muted); }
/* The count rides inside the button, quieter than the label it qualifies. */
.segn { margin-left:6px; font-size:11px; font-weight:500; color:var(--muted);
  font-variant-numeric:tabular-nums; }
.seg button[aria-pressed="true"] .segn { color:color-mix(in oklab,var(--surface) 75%,transparent); }
/* When a search is active it overrides the period and Show filters, which is worth
   saying out loud: a title found outside the current window would otherwise look
   like the filters had silently broken. */
.searchnote { margin:-10px 0 18px; padding:10px 14px; font-size:12.5px;
  border:1px solid var(--rule); border-left:3px solid var(--oc); border-radius:3px;
  background:var(--surface); color:var(--ink-2); }
.searchnote b { font-weight:620; color:var(--ink); }
.searchnote .miss { color:var(--critical); }
.searchnote ul { margin:6px 0 0; padding-left:18px; }
.searchnote li { margin:2px 0; }
input[type="search"] { font:inherit; font-size:13.5px; padding:6px 10px; min-width:230px;
  color:var(--ink); background:var(--surface); border:1px solid var(--rule);
  border-radius:3px; }
input[type="search"]:focus-visible { outline:2px solid var(--oc); outline-offset:-1px; }
.scope { margin-left:auto; font-size:12.5px; color:var(--muted);
  font-variant-numeric:tabular-nums; }
.view-toggle { appearance:none; border:1px solid var(--rule); background:transparent;
  color:var(--ink-2); font:inherit; font-size:12px; padding:3px 10px; border-radius:3px;
  cursor:pointer; }
.view-toggle:hover { background:var(--sunk); color:var(--ink); }
.view-toggle:focus-visible { outline:2px solid var(--oc); outline-offset:1px; }
[hidden] { display:none !important; }

/* ---- charts ---- */
.plot { position:relative; width:100%; }
.plot svg { display:block; width:100%; height:auto; overflow:visible; }
.gridline { stroke:var(--grid); stroke-width:1; }
.axisline { stroke:var(--axis); stroke-width:1; }
.tick { fill:var(--muted); font-size:10.5px; font-variant-numeric:tabular-nums; }
.axis-title { fill:var(--muted); font-size:10.5px; font-weight:600; letter-spacing:.07em;
  text-transform:uppercase; }
.mark-label { fill:var(--ink-2); font-size:11px; font-variant-numeric:tabular-nums; }
.refline { stroke:var(--muted); stroke-width:1; opacity:.55; }
.dot { stroke:var(--surface); stroke-width:2; }
.hit { fill:transparent; cursor:crosshair; }
.legend { display:flex; flex-wrap:wrap; gap:6px 16px; margin-bottom:10px;
  font-size:12.5px; color:var(--ink-2); }
.legend span { display:inline-flex; align-items:center; gap:6px; }
.swatch { width:10px; height:10px; border-radius:2px; flex:none; }
.tip { position:absolute; z-index:20; pointer-events:none; min-width:132px; max-width:260px;
  padding:9px 11px; background:var(--surface); border:1px solid var(--rule);
  border-radius:4px; box-shadow:var(--shadow); font-size:12.5px; color:var(--ink-2);
  opacity:0; transform:translateY(3px); }
.tip[data-show="1"] { opacity:1; transform:translateY(0); }
@media (prefers-reduced-motion: no-preference) {
  .tip { transition:opacity .12s ease, transform .12s ease; }
}
.tip-t { color:var(--ink); font-weight:620; margin-bottom:4px; line-height:1.3; }
.tip-r { display:flex; justify-content:space-between; gap:14px;
  font-variant-numeric:tabular-nums; }
.tip-r b { color:var(--ink); font-weight:600; }

/* ---- tables ---- */
.twrap { overflow-x:auto; margin-top:8px; }
table { width:100%; border-collapse:collapse; font-size:13px; }
caption { text-align:left; font-size:12px; color:var(--muted); padding-bottom:7px; }
th, td { text-align:left; padding:7px 10px 7px 0; border-bottom:1px solid var(--grid);
  white-space:nowrap; }
th { font-size:10.5px; font-weight:620; letter-spacing:.085em; text-transform:uppercase;
  color:var(--muted); }
th[data-sort] { cursor:pointer; user-select:none; }
th[data-sort]:hover { color:var(--ink); }
th[aria-sort] { color:var(--ink); }
th[aria-sort="descending"]::after { content:" ↓"; }
th[aria-sort="ascending"]::after { content:" ↑"; }
td.num, th.num { text-align:right; font-variant-numeric:tabular-nums; }
tbody tr:hover { background:var(--sunk); }
/* Also one line: a long title wrapping put the studio on a third row. Widest
   column, so most titles fit whole; the rest carry the full name on hover. */
td.name { white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  min-width:180px; max-width:236px; }
td.name .sub { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rank { color:var(--muted); font-variant-numeric:tabular-nums; }

/* ---- pills, chips, deltas ---- */
.pill { display:inline-block; padding:1px 8px; border-radius:9px; font-size:11px;
  font-weight:600; border:1px solid var(--rule); color:var(--ink-2); }
.pill-mighty { border-color:color-mix(in oklab,var(--oc) 55%,transparent); color:var(--oc); }
.pill-strong { border-color:color-mix(in oklab,var(--good) 45%,transparent);
  color:var(--good-text); }
.pill-fair { border-color:color-mix(in oklab,var(--warning) 60%,transparent); }
.pill-weak { border-color:color-mix(in oklab,var(--critical) 50%,transparent);
  color:var(--critical); }
.chip { display:inline-flex; align-items:center; gap:5px; font-size:11.5px; font-weight:600; }
.chip::before { content:""; width:7px; height:7px; border-radius:50%; flex:none; }
.chip-fresh::before { background:var(--good); }
.chip-aging::before { background:var(--warning); }
.chip-stale::before { background:var(--critical); }
.chip-fresh { color:var(--good-text); }
.chip-stale { color:var(--critical); }
.delta-pos { color:var(--oc); font-weight:600; }
.delta-neg { color:var(--mc); font-weight:600; }
.empty { padding:26px 4px; color:var(--muted); font-size:13.5px; }
.dim { color:var(--muted); }
.nowrap { white-space:nowrap; }
td .sub { margin-top:2px; font-size:11px; color:var(--muted); font-weight:400; }
td.sp { width:96px; }

/* Dumbbell on a shared 40-100 scale. Hairline gridlines sit a shade off the
   surface; the span bar shows how far the two aggregators are apart. */
.track { position:relative; display:block; height:22px;
  background-image:linear-gradient(var(--track-grid),var(--track-grid)),
    linear-gradient(var(--track-grid),var(--track-grid)),
    linear-gradient(var(--track-grid),var(--track-grid));
  background-size:1px 100%,1px 100%,1px 100%;
  background-position:25% 0,50% 0,75% 0; background-repeat:no-repeat; }
.track::after { content:""; position:absolute; left:0; right:0; top:50%;
  height:1px; background:var(--track-base); }
/* The span is a data mark, not chrome: it shows how far apart the two
   aggregators landed, so it carries more contrast than the baseline. */
.track .span { position:absolute; top:50%; height:3px; margin-top:-1.5px;
  border-radius:2px; background:var(--track-span); }
.track .pt { position:absolute; top:50%; width:11px; height:11px; margin:-5.5px 0 0 -5.5px;
  border-radius:50%; box-shadow:0 0 0 2px var(--surface); }
.pt-oc { background:var(--oc); }
.pt-mc { background:var(--mc); }
/* Players are a third series, so they get a distinct shape, not a third hue. */
.pt-user { background:var(--surface); border:2px solid var(--ink-2); width:9px;
  height:9px; margin:-4.5px 0 0 -4.5px; }
.scale { position:relative; height:14px; font-size:10px; color:var(--muted);
  font-variant-numeric:tabular-nums; }
.scale span { position:absolute; transform:translateX(-50%); }

/* Hollow = the mark rests on too few reviews to trust. */
/* Three bands, ruled apart: nine columns otherwise read as one undifferentiated
   run, when the row is really three separate claims. */
th.band { text-align:center; font-size:10px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted); padding-bottom:2px; }
.bandstart { border-left:1px solid var(--rule); padding-left:10px; }
/* Every row is two lines high, so columns stay aligned down the table. */
tbody td { vertical-align:top; }
/* One line of text plus one .sub line = exactly two rows, always. Letting these
   wrap made rows 54, 68, 74 and 86px tall, which destroys the column scanning the
   two-line layout exists to provide. Full value stays available on hover. */
td.small { font-size:11.5px; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; max-width:132px; }
td.small .sub { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* The two-line row is spec'd, not emergent: two 20px line boxes over 7px+7px of
   cell padding is exactly 54px. Left to natural line-height the stack settled at
   55px, and a padded, bordered pill on either line pushed it to 58-59 — enough
   wobble to break the column scanning the two-line layout exists to give. */
#body tr { height:54px; }
#body td { line-height:20px; }
#body td .sub { margin-top:0; line-height:20px; }
#body .sig { box-sizing:border-box; height:20px; line-height:18px;
  padding-top:0; padding-bottom:0; margin-top:0; margin-bottom:0; vertical-align:top; }

.sub.thinsample { color:var(--critical); }

.pt-weak { background:var(--surface) !important; }
.pt-oc.pt-weak { border:2px solid var(--oc); }
.pt-mc.pt-weak { border:2px solid var(--mc); }
.pt-user.pt-weak { border-style:dotted; }
/* Release type is context, not a finding. A bordered uppercase badge competed
   with the scores for attention; it now reads as the quiet metadata it is, at the
   same weight as the platform text beside it. Only genuinely notable states —
   thin evidence, a player revolt — keep a badge. */
.rtype { font-size:11px; font-weight:400; color:var(--muted); }
select { font:inherit; font-size:13px; padding:5px 8px; color:var(--ink);
  background:var(--surface); border:1px solid var(--rule); border-radius:3px;
  max-width:210px; }
select:focus-visible { outline:2px solid var(--oc); outline-offset:-1px; }
/* Outweighed, not wrong: the Metacritic player average is kept legible but
   stops competing with Steam's far larger sample for the same question. */
td.superseded b { color:var(--muted); font-weight:600; }
td.superseded .sub { opacity:.7; }
.pt-super { opacity:.4; }
/* A score that links to its own reviews stays a number first; the underline on
   hover is the whole affordance. */
.srclink { text-decoration:none; color:inherit; }
.srclink:hover b { text-decoration:underline; text-decoration-color:var(--oc); }
/* A caveat is deliberately quiet: it qualifies a number, it is not a finding. */

.split { display:flex; gap:2px; height:9px; align-items:stretch; }
.split .seg { border-radius:2px; min-width:2px; }
.legend .pt { position:static; margin:0; display:inline-block; }
.legend .seg { display:inline-block; width:10px; height:10px; border-radius:2px; }
td .in { display:inline-block; min-width:34px; margin-left:8px; font-size:11px;
  color:var(--muted); }
tr.month-row th { padding:14px 0 6px; font-size:11px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink); border-bottom:1px solid var(--rule);
  background:var(--surface); }
td.sigs { white-space:nowrap; }
.sig { display:inline-block; min-width:52px; margin-right:5px; padding:1px 7px;
  border-radius:3px; border:1px solid var(--rule); font-size:11px; font-weight:600;
  text-align:center; font-variant-numeric:tabular-nums; color:var(--muted); }

.legend .sig { min-width:0; margin-right:0; }
/* The chip is the affordance; the anchor must not repaint it as body-copy link text. */
a.sig { text-decoration:none; }

a.sig:hover { border-color:var(--mc); background:color-mix(in oklab,var(--mc) 10%,transparent); }
/* The wiki state lives on the sub-line, so its link inherits that line's size and
   weight and signals clickability with the underline alone. */
#body td .sub a { color:var(--oc); text-decoration:underline;
  text-decoration-color:color-mix(in oklab,var(--oc) 45%,transparent);
  text-underline-offset:2px; }
#body td .sub a:hover { text-decoration-color:var(--oc); }

footer { margin-top:44px; padding-top:18px; border-top:1px solid var(--rule);
  font-size:12.5px; color:var(--muted); display:flex; flex-wrap:wrap; gap:6px 20px; }
code { font-size:12.5px; color:var(--ink-2); }


/* ==========================================================================
   Narrow screens.

   These live at the end of the stylesheet deliberately. A media query does not add
   specificity, so a rule inside one only beats its unqualified twin if it comes later
   in the file. While this block sat in the middle, three separate rules silently lost:
   `nav a` kept the desktop's padding, `.theme-btn` lost its auto margin to
   `.topbar .theme-btn`, and `select { font-size:16px }` — the fix for iOS zooming on
   focus — did nothing at all. Each needed a class bolted on to win an argument it
   should never have been having.

   Ordered widest query first, so a narrower one can still override it.
   ========================================================================== */
@media (max-width:720px) {
  /* Two lines: the brand and both tabs with the theme toggle hard right, then the
     search field across the full width. */
  .topbar-in { flex-wrap:wrap; padding:4px 12px 6px; gap:8px 10px; min-height:0; }
  nav { order:2; overflow-x:auto; scrollbar-width:none;
    -webkit-overflow-scrolling:touch; }
  nav::-webkit-scrollbar { display:none; }
  nav a { flex:0 0 auto; padding:6px 9px; }
  .brand { order:1; }
  /* Last on its line, so the auto margin has something to push against. Ordered before
     the tabs it sat mid-line at 390px, and only looked right at 360px because the tabs
     happened to wrap away from it. */
  /* `.topbar` here for specificity, not order: the desktop rule that zeroes this margin
     is `.topbar .theme-btn`, which a bare `.theme-btn` cannot beat from anywhere. */
  .topbar .theme-btn { order:3; margin-left:auto; }
  .topfind { order:4; flex:1 1 100%; max-width:none; margin-left:0; }
}
@media (max-width:720px) {
  .duo { grid-template-columns:1fr !important; }
  section > .twrap, .card > .twrap { overflow-x:auto; }
  .tiles { grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); }
}
@media (max-width:720px) {
  /* Attribute-qualified out of necessity, not habit: `input[type="search"]` outranks a
     bare `input` on specificity, so moving this block to the end of the file does not
     help. Anything under 16px makes iOS Safari zoom the page in on focus. */
  input, select, textarea, input[type="search"],
  .topfind input[type="search"] { font-size:16px; }
  input::placeholder { font-size:16px; }
  /* The controls were sized for 12.5px text, so they are re-padded rather than left to
     grow: same visual weight, no zoom. */
  /* Six sort options at touch-sized text needed 400px against 390px, so the padding
     is tightened. The sort group wraps as square buttons where it still overflows, so
     the "#" the desktop shows on Played and Reviews is kept rather than dropped; the
     tap target stays over the 24px minimum on height. */
  .toolbar .seg button, .drawer .seg button, #drawerbtn, .ghost { font-size:14px;
    padding:5px 5px; }
  .topbar nav a { font-size:14px; }
}
@media (max-width:720px) {
  .mlist { display:block; }
  .twrap { display:none; }
  .wide-only { display:none; }
  .narrow-only { display:flex; }
  /* The same connected, square segments the desktop uses, one group per line.
     They were rounded chips here, which made the phone read as a different site. Chips
     were adopted because a bordered group that wraps or scrolls gets its border clipped,
     leaving what looks like an empty box after the last option — so instead each group
     takes a line to itself and sizes to its contents, keeping the desktop's shape
     without ever clipping.

     Full bleed like the card below it, so the controls and the rows share a left edge. */
  .toolbar { flex-direction:row; flex-wrap:wrap; margin:0 -12px; padding:10px 12px;
    gap:8px; border-left:0; border-right:0; border-radius:0; }
  .chips { margin:0 -12px; border-left:0; border-right:0; }
  .tbrow { display:contents; }
  .narrow-inline { display:inline-flex; }
  .toolbar .seg { flex:0 0 auto; width:max-content; max-width:100%; }
  /* Sizing lives in the touch-target block below, which sets 14px for the iOS zoom
     threshold; a second padding here of equal specificity simply undid it. */
  .toolbar .seg button { flex:0 0 auto; }
  /* The 5px padding above is the sort row's concession, not these groups': the period
     group shares its line with only the More button and was leaving a gulf of dead
     space between them, and the Releases month row had the same cramped look. Roomier
     here; the narrower query below walks the month row back where it stops fitting. */
  .toolbar .seg[data-seg="period"] button,
  .toolbar .seg[data-seg="month"] button,
  .toolbar .seg[data-seg="scope"] button { padding:5px 8px; }
  /* Date group first, then the drawer button on the same line, then the rest. */
  .toolbar .seg[data-seg="period"], .toolbar .seg[data-seg="month"] { order:1; }
  .toolbar .seg[data-seg="sort"], .toolbar .seg[data-seg="scope"] { order:3; }
  /* Six options at touch-sized text come to 390px against 364px available, and a
     connected group that overflows is clipped at the edge rather than wrapped. So this
     one group wraps as square buttons — the same concession the drawer's long groups
     make, for the same reason. */
  .toolbar .seg[data-seg="sort"] { border:0; border-radius:0; overflow:visible;
    flex-wrap:wrap; gap:3px; width:auto; }
  .toolbar .seg[data-seg="sort"] button { border:1px solid var(--rule); border-radius:3px; }
  #drawerbtn { order:2; margin-left:auto; font-size:12.5px; padding:5px 10px; }
  .drawer { flex-direction:column; align-items:stretch; gap:13px; }
  /* Every drawer group wraps as square, individually bordered buttons. Two of them —
     eleven periods and five diagnostic sorts — measured 377px against 366px available,
     and a connected group that overflows is clipped at the edge rather than wrapped.
     Square buttons keep the desktop's shape; only the shared border is given up, and
     only where it could not survive. */
  .drawer .seg { border:0; border-radius:0; overflow:visible; flex-wrap:wrap;
    gap:5px; width:auto; }
  .drawer .seg button { flex:0 0 auto; padding:5px 9px; border:1px solid var(--rule);
    border-radius:3px; }
  .page-head.tight h1 { font-size:clamp(21px,6vw,27px); }
  .sheet dl { grid-template-columns:1fr; gap:1px 0; }
  .sheet dt { margin-top:8px; font-weight:620; }

  /* Nested padding was eating 20% of a 390px screen: 20px from .wrap, 18px from the
     card and 12px from the row, so text began 63px in from each edge. The page keeps
     12px, and the card gives its share back by reaching past it to the edges — a
     panel drawn around a list that already has its own row separators is decoration
     the width cannot afford. */
  .wrap { padding:14px 12px 44px; }
  /* Only the two list pages bleed their card to the screen edge. Unscoped, this reached
     the reference pages too, where a card sits inside a section that is not full bleed —
     390px of card inside 366px, clipping the right edge with no way to scroll to it. */
  .p-released .card, .p-upcoming .card { margin:0 -12px; padding:0;
    background:transparent; border-width:1px 0; border-radius:0; box-shadow:none; }
  .mrow > summary { padding:9px 12px; gap:8px; }
  .mdl { padding:8px 12px 12px; gap:4px 12px; }
  .card-head { display:none; }
  /* Pages other than Reviewed still carry a full standfirst. Three lines is enough to
     say what the page is; the rest is reference reading that costs two releases on
     screen. Nothing is removed from the document, so it is still selectable and
     searchable — only the visible height is capped. */
  .standfirst { display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical;
    overflow:hidden; }
  .provenance { font-size:11px; gap:10px; }
  .page-head { margin-bottom:10px; padding-bottom:8px; }
  .page-head.tight h1 { margin-bottom:3px; }
  .page-head.tight .standfirst { display:none; }
  #scope { display:block; margin:8px 0 10px; }
  .searchnote { margin:0 0 10px; padding:8px 10px; }
  .chips { margin-top:8px; }
  .sheet { margin-top:22px; padding-top:12px; }
  .sec-note { margin:12px 0; }
  .page-head.tight { margin-bottom:12px; }
  .page-head.tight .standfirst { font-size:13.5px; }
  #scope { font-size:11.5px; }
}
@media (max-width:400px) {
  nav a { padding:6px 5px; font-size:12.5px; }
  .brand { font-size:13px; }
}
@media (max-width:389px) {
  /* The month row's eight labels measure 354px at the 8px padding set above, which
     fits a 390px screen exactly and clips Jan off anything narrower. 6px brings it
     to 322px, one uncut line down to 360px. The period row is fine at 8px throughout
     — six labels plus More still clear 360px — so only the month group steps down. */
  .toolbar .seg[data-seg="month"] button { padding:5px 6px; }
}

