/* gamewatch app styles. The design tokens (palette, type, radius, spacing) live in tokens.css,
   which base.html loads before this file. Fonts are self-hosted (WhiteNoise): CSP font-src 'self'. */

/* Archivo (variable, wght 400-700) and Archivo Black, latin subset; OFL texts in static/fonts/. */
@font-face {
  font-family: "Archivo";
  src: url("../fonts/archivo-latin-wght-normal.84e52810340f.woff2") format("woff2");
  font-weight: 400 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Archivo Black";
  src: url("../fonts/archivo-black-latin-400-normal.4b6914a69e6a.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* App-owned, not tokens: the motion curve, the shell's measurements and the breakpoints. */
:root {
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  /* Two breakpoints (#353): mobile < 768px, tablet 768-1179px, desktop >= 1180px. Author
     mobile-first with min-width; a max-width query uses 767px or 1179px. @media can't read custom
     properties, so these document the values and every @media block repeats the literal px. */
  --bp-md: 768px;
  --bp-lg: 1180px;
  /* The sticky header's height. Browse's sticky sidebar and month headings stack below it through
     this one token, so the header can change height without re-tuning them. */
  --header-h: 56px;
  --tabbar-h: 56px;  /* the mobile tab bar, before the safe-area inset */
}
@media (min-width: 768px) { :root { --header-h: var(--nav-h); } }

html { color-scheme: dark; font-synthesis: none; } /* never faux-bold Archivo Black */
* { box-sizing: border-box; }

/* Accessible focus ring everywhere (a11y) */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
summary:focus-visible, textarea:focus-visible, .cover-link:focus-visible {
  outline: 2px solid var(--accent-strong); outline-offset: 2px; border-radius: 4px;
}
:focus:not(:focus-visible) { outline: none; }

body {
  margin: 0;
  font-family: var(--font-ui);
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
h1 { margin: 0 0 0.5rem; font-size: 1.7rem; }
h2 { margin: 1.75rem 0 0.75rem; font-size: 1.25rem; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }

/* Header / nav */
/* Visually hidden but available to screen readers (live-region announcements, etc.). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Toast for failed HTMX requests (paired with a polite live-region announcement). */
.htmx-toast {
  position: fixed; left: 50%; bottom: 1.25rem; transform: translate(-50%, 1rem);
  background: var(--surface-2); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: var(--r-panel); padding: 0.7rem 1.1rem; font-size: 0.88rem; max-width: 90vw;
  opacity: 0; pointer-events: none; z-index: 200;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.htmx-toast.show { opacity: 1; transform: translate(-50%, 0); }
/* Loading state while a month-nav request is in flight (HTMX toggles .htmx-request). */
.rm-arrow.htmx-request { opacity: 0.5; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .htmx-toast { transition: opacity 0.2s ease; } }
@media (max-width: 767px) {
  .htmx-toast { bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-5)); }
}

/* Keyboard skip link: off-screen until focused, then slides in as the first tab stop. */
.skip-link {
  position: absolute; left: 0.75rem; top: -3.5rem; z-index: 100;
  background: var(--accent); color: var(--bg); font-weight: 600;
  padding: 0.5rem 0.9rem; border-radius: var(--r-ctl);
  text-decoration: none; transition: top 0.15s ease;
}
.skip-link:focus { top: 0.75rem; outline: 2px solid var(--text); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .skip-link { transition: none; } }

/* ==== App shell (#353, #373) ============================================================
   One panel holds the header, the page and the footer. Below 768px it fills the viewport; from
   768px it gets its outline and radius, and from 1180px it caps at --content-max, centred on
   --bg. The header is sticky at every width. The primary nav is ONE <nav>: a row in the header
   from 768px, and a fixed bottom tab bar below that (hidden on auth pages, body.is-auth). */
body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
body.is-auth { padding-bottom: 0; }
.app {
  display: flex; flex-direction: column;
  min-height: 100vh; min-height: 100dvh;
  background: var(--surface-1);
}

.site-header {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: var(--s-5);
  height: var(--header-h); padding: 0 var(--page-pad-x-m);
  background: var(--surface-0); border-bottom: 1px solid var(--border);
}
.brand {
  margin-right: auto;
  font-family: var(--font-display); font-weight: 400; font-size: 17px; letter-spacing: -0.01em;
  color: var(--text); text-decoration: none; white-space: nowrap;
}
.brand:hover { color: var(--text); text-decoration: none; }
.brand-accent { color: var(--accent); }

/* Primary nav: the mobile tab bar. Text only, 13px/600; the current tab gets a 2px accent bar. */
.site-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  display: grid; grid-template-columns: repeat(4, 1fr);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--surface-0); border-top: 1px solid var(--border);
}
.site-nav a {
  position: relative; display: flex; align-items: center; justify-content: center;
  min-height: var(--tabbar-h);
  font-size: 13px; font-weight: 600; color: var(--text-3); text-decoration: none;
  transition: color 120ms ease-out;
}
.site-nav a:hover { color: var(--text); text-decoration: none; }
.site-nav a[aria-current="page"] { color: var(--text); }
.site-nav a[aria-current="page"]::before {
  content: ""; position: absolute; top: -1px; left: 25%; right: 25%; height: 2px;
  background: var(--accent);
}
body.is-auth .site-nav { display: none; }

/* Header search. Below 1180px it is the magnifier, which expands the field in place
   (nav-search.js); from 1180px the 260px field sits in the header row. */
.nav-search { position: relative; display: none; align-items: center; margin: 0; }
.nav-search .ns-ic {
  position: absolute; left: var(--s-4); display: inline-flex; color: var(--text-3); pointer-events: none;
}
.nav-search input {
  width: 100%; height: 44px; padding: 0 var(--s-5) 0 34px;
  font: inherit; font-size: 16px; /* under 16px, iOS zooms the page on focus */
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
}
.nav-search input::placeholder { color: var(--text-3); }
.nav-search input:focus { border-color: var(--accent); }
.nav-search-open, .nav-search-close {
  display: none; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; padding: 0;
  border: 0; border-radius: var(--r-ctl); background: none; color: var(--text-2); cursor: pointer;
}
.nav-search-open { display: inline-flex; }
.nav-search-open:hover, .nav-search-close:hover { color: var(--text); background: none; }

.hdr-actions { display: flex; align-items: center; gap: var(--s-1); }

/* Search takeover below 1180px: the field fills the header row, and the chrome steps aside. */
.site-header.searching .brand,
.site-header.searching .hdr-actions { display: none; }
.site-header.searching .nav-search { display: flex; flex: 1; }
.site-header.searching .nav-search-close { display: inline-flex; }

/* Signed out: a --text-filled button, not accent (accent means "you watch this"). */
.btn-login {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--s-6);
  border-radius: var(--r-ctl); background: var(--text); color: var(--bg);
  font-size: 13px; font-weight: 700; text-decoration: none; white-space: nowrap;
}
.btn-login:hover { color: var(--bg); text-decoration: none; }

/* Account menu: a <details> disclosure. A bottom sheet on phones, a popover from 768px. */
.acct { position: relative; }
.acct > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; list-style: none; cursor: pointer; border-radius: 50%;
}
.acct > summary::-webkit-details-marker { display: none; }
.acct > summary:focus-visible { outline: none; }
.acct > summary:focus-visible .avatar { outline: 2px solid var(--accent); outline-offset: 2px; }
.avatar {
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  background: var(--surface-5); color: var(--text); font-size: 12px; font-weight: 700;
}
.acct-scrim {
  position: fixed; inset: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
}
.acct-menu {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; flex-direction: column; gap: var(--s-1);
  padding: var(--s-3) var(--s-3) calc(var(--s-3) + env(safe-area-inset-bottom));
  background: var(--surface-2); border-top: 1px solid var(--border);
  border-radius: var(--r-app) var(--r-app) 0 0;
}
.acct-name {
  margin: 0; padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--border);
  font-size: 13px; font-weight: 600; color: var(--text); overflow-wrap: anywhere;
}
.acct-menu form { margin: 0; }
.acct-menu a, .acct-menu button {
  display: flex; align-items: center; width: 100%; min-height: 44px; padding: 0 var(--s-5);
  border: 0; border-radius: var(--r-ctl); background: none; color: var(--text-2);
  font: inherit; font-size: 15px; font-weight: 500; text-align: left; text-decoration: none; cursor: pointer;
}
.acct-menu a:hover, .acct-menu button:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }

/* Bell: a link to the watchlist's alert bar (#355), badged with the unseen date-change count (#354).
   The badge is text in a filled pill; the link's aria-label carries the number for screen readers. */
.nav-bell {
  position: relative; display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; border-radius: var(--r-ctl); color: var(--text-2); text-decoration: none;
}
.nav-bell:hover { color: var(--text); text-decoration: none; }
.nav-bell-count {
  position: absolute; top: 6px; right: 4px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 8px; background: var(--accent); color: var(--accent-fg);
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}

.container { flex: 1; width: 100%; margin: 0; padding: var(--s-9) var(--page-pad-x-m); }

@media (max-width: 767px) {
  body:has(.acct[open]) { overflow: hidden; }
}

/* Tablet and up: the panel gets its outline, the nav moves into the header row, and the search
   field and account popover take their desktop shape.
   Search stays the magnifier until 1180px (#402). With the 260px field the row needs about 754px
   signed in and 771px signed out (the bell adds 44px signed in), but a 768px viewport leaves 678px
   inside the panel. No field width fits there without cutting off "Search N games", so tablets
   keep the phone's magnifier and takeover (about 514px / 531px). Here the takeover also steps the
   nav aside. */
@media (min-width: 768px) {
  body { padding-bottom: 0; }
  .app {
    width: min(100% - 2 * var(--s-7), var(--content-max));
    min-height: calc(100dvh - 2 * var(--s-7));
    margin: var(--s-7) auto;
    border: 1px solid var(--border); border-radius: var(--r-app);
  }
  .site-header {
    gap: var(--s-12); padding: 0 var(--page-pad-x);
    border-radius: var(--r-app) var(--r-app) 0 0;
  }
  .brand { margin-right: 0; font-size: 19px; }
  .site-nav, body.is-auth .site-nav {
    position: static; display: flex; align-self: stretch; gap: var(--s-11); margin-right: auto;
    padding: 0; background: none; border: 0;
  }
  .site-nav a { min-height: 0; font-size: 15px; font-weight: 500; }
  .site-nav a[aria-current="page"]::before { top: auto; bottom: -1px; left: 0; right: 0; }
  .nav-search input { height: 36px; font-size: 13px; }
  .site-header.searching { gap: var(--s-5); }
  .site-header.searching .site-nav { display: none; }
  .btn-login { min-height: 36px; }
  .avatar { width: 34px; height: 34px; }
  .acct-scrim { display: none; }
  .acct-menu {
    position: absolute; top: calc(100% + var(--s-3)); right: 0; left: auto; bottom: auto;
    width: 220px; padding: var(--s-2);
    border: 1px solid var(--border); border-radius: var(--r-panel);
  }
  .acct-menu a, .acct-menu button { min-height: 36px; font-size: 14px; }
  .container { padding: var(--s-12) var(--page-pad-x); }
}
@media (min-width: 1180px) {
  .app {
    width: min(100% - 2 * var(--s-12), var(--content-max));
    min-height: calc(100dvh - 2 * var(--s-11));
    margin-block: var(--s-11);
  }
  .nav-search { display: flex; flex: none; width: 260px; }
  .nav-search-open, .site-header.searching .nav-search-close { display: none; }
}
@media (prefers-reduced-motion: reduce) { .site-nav a { transition: none; } }

.tagline { color: var(--text-3); margin-top: 0; }
.muted { color: var(--text-3); }
.faint { color: var(--text-3); }

/* Badges — generic plus status modifiers */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--text-3);
  padding: 0.2rem 0.62rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.badge--released { background: var(--ok-bg); border-color: transparent; color: var(--ok); }
.badge--upcoming { background: var(--surface-4); border-color: transparent; color: var(--text); }
.badge--tba { background: var(--surface-3); border-color: transparent; color: var(--unknown); }
/* Game.year_variant's this-year/unknown pill — not TBA, but the same quiet palette. */
.badge--neutral { background: var(--surface-3); border-color: transparent; color: var(--unknown); }
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* Standalone status dot (in front of titles / dates) — status is text + dot, never color alone */
.sdot { width: 8px; height: 8px; border-radius: 50%; flex: none; display: inline-block; }
.sdot--released { background: var(--ok); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 14%, transparent); }
.sdot--upcoming { background: var(--text-2); box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-2) 14%, transparent); }
.sdot--tba { background: var(--text-3); box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-3) 14%, transparent); }

code {
  background: var(--surface-3); border: 1px solid var(--border); border-radius: 5px;
  padding: 0.05rem 0.35rem; font-size: 0.9em;
}
ul { padding-left: 1.25rem; }

/* ==== Shared partials (redesign, #393) ====================================================
   What the screen builds share: the status and type badges (templates/games/_status_badge.html,
   _type_badge.html), the platform chips (_platform_chips.html), the countdown chip
   (_countdown.html), the cover slot (_cover.html), the release date and the release row
   (_release_date.html, _release_row.html), and the watch button (subscriptions/_watch_button.html).
   Status colours never take the accent (README §1.4); here the accent only means "you watch this".
   Glyphs are inline SVG from {% glyph %} (catalog/templatetags/ui.py), because the self-hosted
   Archivo subset has no arrows or check mark: each is 1em square, in currentColor. */
.glyph { display: inline-block; flex: none; width: 1em; height: 1em; vertical-align: -0.125em; }

/* Badges: README §1.3's single shape, for status and type alike. */
.status-badge, .type-badge {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--border-input); border-radius: var(--r-badge);
  background: none; color: var(--text-2);
  font-family: var(--font-ui); font-size: 11px; font-weight: 600; line-height: 1.2;
  letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap;
}
.status-badge--confirmed {
  color: var(--badge-confirmed); background: var(--badge-confirmed-bg); border-color: var(--badge-confirmed-border);
}
.status-badge--delayed, .status-badge--fuzzy, .type-badge--warn {
  color: var(--warn); background: var(--warn-bg); border-color: var(--warn-border);
}
.status-badge--early-access, .status-badge--out {
  color: var(--ok); background: var(--ok-bg); border-color: var(--ok-border);
}
.status-badge--cancelled { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-border); }
.status-badge--tba { color: var(--unknown); border-style: dashed; }
/* Foreign pill (#142, #356 §12): "JP only" / "Not on PC". Neutral, sentence case: it is not a status. */
.foreign-pill {
  display: inline-flex; align-items: center; padding: var(--s-1) var(--s-3);
  border: 1px solid var(--border-input); border-radius: var(--r-badge); color: var(--text-2);
  font-size: 11px; font-weight: 600; line-height: 1.2; letter-spacing: 0.02em; white-space: nowrap;
}

/* Platform chips: neutral. A platform that ships later is dashed --warn with its window inline. */
.plat-chips { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-width: 0; }
.plat-chip {
  display: inline-flex; align-items: center;
  padding: var(--s-1) var(--s-2);
  border: 1px solid transparent; border-radius: var(--r-chip);
  background: var(--surface-4); color: var(--text-2);
  font-size: 11px; font-weight: 600; line-height: 1.2; letter-spacing: 0.06em; white-space: nowrap;
}
.plat-chip--more { color: var(--text-3); cursor: default; }
.plat-chip--later { background: none; border-style: dashed; border-color: var(--warn-border); color: var(--warn); }

/* Countdown chip: the server renders the days; countdown.js reveals and ticks hours and minutes. */
.cd-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-6); }
.cd-chip {
  display: inline-flex; align-items: center;
  padding: var(--s-4) var(--s-6);
  border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  background: var(--scrim-chip); font-variant-numeric: tabular-nums;
}
.cd-chip [hidden] { display: none; }
.cd-units { display: inline-flex; align-items: baseline; gap: var(--s-5); }
.cd-unit { display: inline-flex; align-items: baseline; gap: var(--s-2); }
.cd-n { font-family: var(--font-display); font-weight: 400; font-size: 18px; line-height: 1; color: var(--text); }
.cd-u { font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-3); }
.cd-chip--compact .cd-u { font-size: 11px; letter-spacing: 0.14em; }
.cd-when { font-size: 15px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }

/* Cover slot: the art over its fallback gradient, sized by the layout and the shape. */
.cover-slot { position: relative; display: block; width: 100%; overflow: hidden; border-radius: var(--r-badge); }
.cover-slot--portrait { aspect-ratio: 3 / 4; }
.cover-slot--landscape { aspect-ratio: 16 / 9; }
.cover-slot--card { aspect-ratio: 400 / 126; }
.cover-slot--fill { height: 100%; }
.cover-slot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover-slot.is-tba img, .release-row.is-tba .cover-slot img { opacity: 0.75; }
.cover-slot--from-cover img { filter: blur(24px); transform: scale(1.2); }

/* Watch button: "+ Watch" / "✓ Watching", swapped in place by htmx. Watching is the accent fill;
   a large "+ Watch" is --text-filled and the rest are outlined. 44px hit areas below 768px. */
.watch-form { display: inline-flex; margin: 0; }
.watch-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-strong); border-radius: var(--r-input);
  background: none; color: var(--text);
  font: inherit; font-size: 13px; font-weight: 700; line-height: 1.2;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}
.watch-btn:hover { background: var(--wash-hover); border-color: var(--text-3); color: var(--text); text-decoration: none; }
.watch-btn.watch-btn--lg { padding: 0 var(--s-8); border-radius: var(--r-ctl); font-size: 15px; font-weight: 700; }
.watch-btn--lg:not(.is-watching), .watch-btn--signin { background: var(--text); border-color: var(--text); color: var(--bg); }
.watch-btn--lg:not(.is-watching):hover, .watch-btn--signin:hover {
  background: var(--text-fill-hover); border-color: var(--text-fill-hover); color: var(--bg);
}
.watch-btn.is-watching { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.watch-btn.is-watching:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--accent-fg); }
.watch-btn--row { width: 44px; padding: 0; font-size: 15px; font-weight: 600; color: var(--text-2); }

/* Track pill: the release table's per-platform narrowing control (#372). Outlined next to the
   watch button's fill, because it never unwatches -- it only narrows what a watch alerts on. */
.track-form { display: inline-flex; margin: 0; }
.track-pill {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 32px; padding: 0 var(--s-4);
  border: 1px solid var(--border-strong); border-radius: var(--r-input);
  background: none; color: var(--text-2);
  font: inherit; font-size: 12px; font-weight: 700; line-height: 1.2;
  white-space: nowrap; cursor: pointer;
  transition: background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}
.track-pill:hover { background: var(--wash-hover); border-color: var(--text-3); color: var(--text); }
.track-pill.is-tracked { border-color: var(--accent); color: var(--accent); }
.track-pill.is-tracked:hover { background: var(--accent-bg); border-color: var(--accent-strong); color: var(--accent-strong); }

/* Release date: README §1.6's treatment of messy release data. */
.rdate {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3);
  font-variant-numeric: tabular-nums;
}
.rdate-value { font-size: 15px; font-weight: 700; color: var(--text); }
.rdate--fuzzy .rdate-value { color: var(--warn); }
.rdate--tba .rdate-value { color: var(--text-3); }
.rdate-change { display: inline-flex; align-items: center; gap: var(--s-1); font-size: 13px; font-weight: 600; }
.rdate-change--delayed { color: var(--warn); }
.rdate-change--earlier { color: var(--ok); }
.rdate-change--confirmed { color: var(--badge-confirmed); }
.rdate-change--tba { color: var(--unknown); }
.rdate-change--cancelled { color: var(--text-3); font-weight: 400; }

/* Release row: a two-line card below 768px, and a grid row in each screen's columns above it. */
.release-rows {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-panel); overflow: hidden;
}
.release-row {
  display: grid; align-items: center; gap: var(--s-2) var(--s-5);
  grid-template-columns: 48px minmax(0, 1fr) auto;
  grid-template-areas: "cover main main" "cover chips chips" "cover when action";
  padding: var(--s-5) var(--s-7); border-bottom: 1px solid var(--border);
  transition: background-color 120ms ease-out;
}
.release-row:last-child { border-bottom: 0; }
.release-row:hover { background: var(--surface-2); }
.release-row.is-delayed { background: var(--warn-row-bg); }
.rr-day { grid-area: day; display: none; }
.rr-cover { grid-area: cover; align-self: start; display: block; }
.rr-main { grid-area: main; display: flex; flex-direction: column; min-width: 0; }
.rr-title { font-size: 16px; font-weight: 600; line-height: 1.3; color: var(--text); overflow-wrap: anywhere; }
.rr-title:hover { color: var(--text); text-decoration: none; }
.rr-subtitle { font-weight: 400; color: var(--text-3); }
.rr-meta { font-size: 13px; color: var(--text-3); }
.rr-chips { grid-area: chips; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-width: 0; }
.rr-when { grid-area: when; display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-1); min-width: 0; }
.rr-proximity { font-size: 13px; color: var(--text-3); }
.rr-action { grid-area: action; justify-self: end; }
.rr-plats { display: none; font-size: 13px; color: var(--text-3); }
.rr-later { color: var(--warn); }

/* Browse's phone card (README §2.2 "Mobile"): "art + title + platforms on line one, date + track
   button on line two". The platforms ride the meta as a text line — three chips do not fit beside
   a title at 320px — which frees line two for the date, the badges and the watch button. The Grid
   card makes the same swap (`.rcard-plats`); the radar and the watchlist keep their own stack. */
@media (max-width: 767px) {
  .release-row--browse {
    grid-template-columns: 48px auto minmax(0, 1fr) auto;
    grid-template-areas: "cover main main main" "cover when chips action";
  }
  .release-row--browse.release-row--ranked {
    grid-template-columns: 24px 48px auto minmax(0, 1fr) auto;
    grid-template-areas: "rank cover main main main" "rank cover when chips action";
  }
  .release-row--browse .rr-plats { display: block; }
  .release-row--browse .plat-chips { display: none; }
  .release-row--browse .rr-when { flex-direction: row; align-items: baseline; gap: var(--s-3); }
  .release-row--browse .rr-chips { flex-wrap: nowrap; overflow: hidden; }
}
/* Browse's Hype sort: a rank column ahead of the row, "01" in the accent (README §1.4 "Ranking"). */
.release-row--ranked {
  grid-template-columns: 24px 48px minmax(0, 1fr) auto;
  grid-template-areas: "rank cover main main" "rank cover chips chips" "rank cover when action";
}
.rr-rank {
  grid-area: rank; align-self: center;
  font-family: var(--font-display); font-size: 16px; line-height: 1.25;
  font-variant-numeric: tabular-nums; color: var(--text-4);
}
.rr-rank.is-first { color: var(--accent); }
.rr-hype { font-size: 13px; font-weight: 700; color: var(--text-2); white-space: nowrap; }

@media (min-width: 768px) {
  .cd-n { font-size: 24px; }
  .cd-chip--compact .cd-n { font-size: 22px; }
  .watch-btn { min-height: 32px; }
  .watch-btn.watch-btn--lg { min-height: 42px; }
  .watch-btn--row { width: 28px; min-height: 28px; }
  .track-pill { min-height: 28px; }
  .release-row {
    grid-template-columns: 66px 40px minmax(0, 1fr) 200px 120px 46px;
    grid-template-areas: "day cover main chips when action";
    gap: var(--s-6);
  }
  .release-row--ranked {
    grid-template-columns: 24px 66px 40px minmax(0, 1fr) 200px 120px 46px;
    grid-template-areas: "rank day cover main chips when action";
  }
  .release-row--radar { grid-template-columns: 52px 44px minmax(0, 1fr) 190px 104px 28px; }
  .release-row--watchlist {
    grid-template-columns: 52px minmax(0, 1fr) auto;
    grid-template-areas: "cover main action" "cover chips when";
  }
  .rr-day { display: flex; flex-direction: column; }
  .release-row--watchlist .rr-day { display: none; }
  .rr-day b { font-family: var(--font-display); font-weight: 400; font-size: 15px; line-height: 1.2; color: var(--text-2); }
  .rr-day span { font-size: 12px; color: var(--text-3); }
  .rr-cover { align-self: center; }
  .rr-when { align-items: flex-end; text-align: right; }
}
@media (min-width: 1180px) {
  .release-row--watchlist {
    grid-template-columns: 52px minmax(0, 1fr) 230px 190px 150px;
    grid-template-areas: "cover main chips when action";
  }
}
@media (prefers-reduced-motion: no-preference) {
  .cover-slot img, .gd-frame-img { transition: transform 120ms ease-out; }
  a:hover .cover-slot:not(.cover-slot--from-cover) img,
  .release-row:hover .cover-slot:not(.cover-slot--from-cover) img,
  .gd-frame-link:hover .gd-frame-img { transform: scale(1.02); }
}
@media (prefers-reduced-motion: reduce) { .watch-btn, .track-pill, .release-row { transition: none; } }

/* Buttons */
button {
  font: inherit; font-weight: 500; cursor: pointer;
  color: var(--accent-fg); background: var(--accent); border: 0;
  border-radius: var(--r-ctl); padding: 0.5rem 0.95rem;
  transition: background 0.12s ease;
}
button:hover { background: var(--accent-strong); }
.linkish {
  background: transparent; color: var(--text-3); padding: 0; font-weight: 400;
  text-decoration: underline;
}
.linkish:hover { background: transparent; color: var(--text); }

/* Footer: the one-line IGDB attribution (ADR-0010), at the end of the panel on every page. */
.site-footer {
  padding: var(--s-7) var(--page-pad-x-m); border-top: 1px solid var(--border);
  color: var(--text-3); font-size: 13px;
}
.site-footer p { margin: 0; }
.site-footer a { color: var(--text-2); }
@media (min-width: 768px) { .site-footer { padding-inline: var(--page-pad-x); } }

/* Auth / preference forms */
.auth-form { max-width: 22rem; }
.auth-form input[type="text"],
.auth-form input[type="password"],
.auth-form input[type="email"],
.auth-form input[type="number"] {
  width: 100%; padding: 0.5rem 0.65rem; margin: 0.2rem 0 0.7rem;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-ctl); color: var(--text);
}
.auth-form input:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-bg);
}
.error { color: var(--danger); }
.ok { color: var(--ok); }

/* Flash messages (the single messages loop in base.html). A flash is an *event*, so it gets a
   panel and a left rule — that is what separates it from the .muted help paragraphs it lands
   beside; the level modifier only tints it (#304). Colour alone was the old design and it
   failed for info, which shared .muted with static body copy. */
.flash {
  margin: 0 0 var(--s-7);
  padding: var(--s-4) var(--s-6);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--r-ctl);
  color: var(--text);
}
.flash-ok { border-left-color: var(--ok); }
.flash-error { border-left-color: var(--danger); color: var(--danger); }
.flash-info { border-left-color: var(--text-3); }
.pref-section { margin-bottom: 2rem; }
.pref-section h2 { font-size: 1.1rem; margin-bottom: 0.75rem; }
.check { display: flex; align-items: center; gap: 0.45rem; color: var(--text-3); }
input[type="checkbox"] { accent-color: var(--accent); cursor: pointer; }

/* ==== Preferences page (redesign, #383) ==================================================
   /accounts/preferences/ (#360): a settings nav of plain anchors (a sticky column from 768px, a
   sideways-scrolling chip row below that), the Appearance picker, then four cards. Every selector
   starts with .prefs, or .prefs-page (the body class the page sets), so no shared rule changes on
   any other page; tests/test_accounts_preferences.py pins that. Colours are tokens only: the
   swatches and the live preview paint through data-hue and the accent tokens. Comments inside
   this region start with "prefs:" so that guard can find where the region ends. */
.prefs { display: flex; flex-direction: column; gap: var(--s-9); }
.prefs :where(p) { margin: 0; }
.prefs-main { display: flex; flex-direction: column; gap: var(--s-11); min-width: 0; }
.prefs-section { scroll-margin-top: calc(var(--header-h) + var(--s-7)); }

/* prefs: the settings nav. Below 768px it is a row of chips under the page gutter. */
.prefs-nav {
  --prefs-nav-edge: var(--border-input);
  --prefs-nav-fill: var(--surface-2);
  --prefs-nav-fill-current: var(--surface-3);
  margin-inline: calc(-1 * var(--page-pad-x-m));
  overflow-x: auto; scrollbar-width: none;
}
.prefs-nav::-webkit-scrollbar { display: none; }
.prefs-nav-list { display: flex; gap: var(--s-3); width: max-content; padding-inline: var(--page-pad-x-m); }
.prefs-nav-title { display: none; }
.prefs-nav a {
  display: flex; align-items: center; min-height: 44px; padding: 0 var(--s-6);
  border: 1px solid var(--prefs-nav-edge); border-radius: var(--r-ctl); background: var(--prefs-nav-fill);
  color: var(--text-2); font-size: 14px; font-weight: 500; white-space: nowrap; text-decoration: none;
  transition: color 120ms ease-out;
}
.prefs-nav a:hover { color: var(--text); text-decoration: none; }
/* prefs: there is no scroll-spy (#360), so the first item is current unless the URL's :target
   names another section (the account menu's Security link, or a click here). These are separate
   rules so a browser without :has() still marks the first item instead of dropping the list. */
.prefs-nav a[href="#appearance"] {
  border-color: var(--accent); background: var(--prefs-nav-fill-current); color: var(--text); font-weight: 600;
}
.prefs:has(.prefs-section:target:not(#appearance)) .prefs-nav a[href="#appearance"] {
  border-color: var(--prefs-nav-edge); background: var(--prefs-nav-fill); color: var(--text-2); font-weight: 500;
}
.prefs:has(#platforms:target) .prefs-nav a[href="#platforms"],
.prefs:has(#notifications:target) .prefs-nav a[href="#notifications"],
.prefs:has(#content:target) .prefs-nav a[href="#content"],
.prefs:has(#security:target) .prefs-nav a[href="#security"] {
  border-color: var(--accent); background: var(--prefs-nav-fill-current); color: var(--text); font-weight: 600;
}

/* prefs: type. Archivo Black only for the page title, never below 14px. */
.prefs-title {
  margin: 0 0 var(--s-2); font-family: var(--font-display); font-size: 30px; font-weight: 400;
  line-height: 1.1; letter-spacing: -0.03em; color: var(--text);
}
.prefs-lede { font-size: 15px; color: var(--text-3); }
.prefs-eyebrow {
  margin: 0; padding: 0; font-family: var(--font-ui); font-size: 12px; font-weight: 500;
  line-height: 1.4; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.prefs-help { font-size: 13px; line-height: 1.5; color: var(--text-3); }
.prefs-quiet { color: var(--text-3); }
.prefs-ok { color: var(--ok); font-weight: 600; }
.prefs-warn { color: var(--warn); font-weight: 600; }
.prefs-accent { color: var(--accent); font-weight: 600; }
.prefs-error { font-size: 13px; color: var(--danger); }

/* prefs: cards, rows, switches and controls. */
.prefs-card {
  min-width: 0; padding: var(--s-9);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-app);
}
.prefs-grid { display: grid; gap: var(--s-8); grid-template-columns: minmax(0, 1fr); }
.prefs-card-head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--s-2) var(--s-5); margin-bottom: var(--s-6);
}
.prefs-status { font-size: 13px; font-weight: 600; color: var(--ok); }
.prefs-status-failed { color: var(--danger); }
.prefs-card-foot { margin-top: var(--s-2); }
.prefs-rows { display: flex; flex-direction: column; gap: var(--s-6); margin: 0; }
.prefs-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s-7); }
.prefs-row-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.prefs-row-label { font-size: 15px; color: var(--text); cursor: pointer; }
.prefs-row-label strong { font-weight: 600; }

.prefs-switch { position: relative; flex: none; width: 44px; height: 26px; }
.prefs-switch input {
  position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer;
}
.prefs-switch-track {
  position: absolute; inset: 0; border: 1px solid var(--border-input); border-radius: 13px;
  background: var(--surface-4); transition: background 120ms ease-out, border-color 120ms ease-out;
}
.prefs-switch-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--text-4); transition: transform 120ms ease-out;
}
.prefs-switch input:checked + .prefs-switch-track { background: var(--accent); border-color: var(--accent); }
.prefs-switch input:checked + .prefs-switch-track::after { transform: translateX(18px); background: var(--accent-fg); }
.prefs-switch input:focus-visible + .prefs-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }

.prefs-select select,
.prefs-field input,
.prefs-custom-hue input {
  min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl); background: var(--surface-3);
  color: var(--text); font: inherit; font-size: 16px; /* under 16px, iOS zooms the page on focus */
}
.prefs-select select { cursor: pointer; }
.prefs-select select:focus, .prefs-field input:focus, .prefs-custom-hue input:focus { border-color: var(--accent); }

.prefs-btn {
  display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 var(--s-8);
  border: 1px solid var(--accent); border-radius: var(--r-ctl); background: var(--accent);
  color: var(--accent-fg); font-size: 14px; font-weight: 700; transition: background 120ms ease-out;
}
.prefs-btn:hover { border-color: var(--accent-strong); background: var(--accent-strong); }
.prefs-btn-quiet { border-color: var(--border-strong); background: transparent; color: var(--text); font-weight: 600; }
.prefs-btn-quiet:hover { border-color: var(--text-3); background: var(--wash-hover); }
.prefs-link {
  padding: 0; border: 0; border-radius: 0; background: none; color: var(--accent);
  font: inherit; font-size: 14px; font-weight: 600; text-decoration: none; cursor: pointer;
}
.prefs-link:hover { background: none; color: var(--accent-strong); text-decoration: none; }
.prefs summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* prefs: the Appearance picker. The ring places hue h at h degrees clockwise from the top, and the
   handle reads --accent-h, so data-hue on <html> moves it with no script (accent-picker.js, #384,
   inverts the same mapping for a pointer). */
.prefs-appearance { display: flex; flex-direction: column; gap: var(--s-9); }
.prefs-picker { display: grid; gap: var(--s-12); }
.prefs-picker-controls { display: flex; flex-direction: column; gap: var(--s-9); min-width: 0; }
.prefs-picker-intro { display: flex; flex-direction: column; gap: var(--s-1); }
.prefs-picker-intro .prefs-help { font-size: 14px; }
.prefs-wheel-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-11); }
.prefs-wheel {
  position: relative; flex: none; width: 168px; height: 168px; border-radius: 50%;
  background: conic-gradient(
    oklch(var(--accent-l) var(--accent-c) 0), oklch(var(--accent-l) var(--accent-c) 30),
    oklch(var(--accent-l) var(--accent-c) 60), oklch(var(--accent-l) var(--accent-c) 90),
    oklch(var(--accent-l) var(--accent-c) 120), oklch(var(--accent-l) var(--accent-c) 150),
    oklch(var(--accent-l) var(--accent-c) 180), oklch(var(--accent-l) var(--accent-c) 210),
    oklch(var(--accent-l) var(--accent-c) 240), oklch(var(--accent-l) var(--accent-c) 270),
    oklch(var(--accent-l) var(--accent-c) 300), oklch(var(--accent-l) var(--accent-c) 330),
    oklch(var(--accent-l) var(--accent-c) 360));
}
.prefs-wheel-hub {
  position: absolute; inset: 34px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; border: 1px solid var(--border); border-radius: 50%; background: var(--surface-2);
}
.prefs-wheel-value { font-family: var(--font-display); font-size: 19px; line-height: 1; color: var(--accent); }
.prefs-wheel-unit { font-size: 11px; line-height: 1.2; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-3); }
.prefs-wheel-handle {
  position: absolute; top: 50%; left: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border: 3px solid var(--text); border-radius: 50%; background: var(--accent); box-shadow: var(--shadow-handle);
  transform: rotate(calc(var(--accent-h) * 1deg)) translateY(-67px); /* 67px: the ring's centre line */
}
/* prefs: once accent-picker.js has taken the wheel over (.is-live) the ring is dragged, so a touch
   on it must not scroll the page or select text; the hub ignores presses. Nothing animates, so the
   handle tracks the pointer and reduced motion needs no override. The visually hidden range's
   focus ring goes round the whole wheel: on the handle it would sit on its own colour. */
.prefs-wheel.is-live { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; }
.prefs-wheel.is-dragging { cursor: grabbing; }
.prefs-wheel.is-live .prefs-wheel-hub { cursor: default; }
.prefs-wheel:has(> input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 4px; }
.prefs-readout { display: flex; flex: 1 1 170px; flex-direction: column; gap: var(--s-5); min-width: 0; }
.prefs-readout-label { margin-bottom: var(--s-2); font-size: 12px; color: var(--text-3); }
.prefs-readout-field {
  display: flex; align-items: center; gap: var(--s-4); padding: var(--s-3) var(--s-5);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl); background: var(--surface-3);
  font-size: 14px; font-weight: 600; letter-spacing: 0.02em; color: var(--text);
}
.prefs-readout-swatch { flex: none; width: 20px; height: 20px; border-radius: var(--r-badge); background: var(--accent); }
.prefs-readout-meta { font-size: 12px; line-height: 1.5; color: var(--text-4); }
.prefs-readout-contrast { font-size: 13px; font-weight: 600; color: var(--ok); }
.prefs-presets { min-width: 0; margin: 0; padding: 0; border: 0; }
.prefs-presets legend { margin-bottom: var(--s-4); }
.prefs-swatches { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.prefs-swatch { position: relative; display: block; cursor: pointer; }
.prefs-swatch-chip {
  display: block; width: 44px; height: 44px; border: 2px solid transparent; border-radius: var(--r-panel);
  background: var(--accent);
}
.prefs-swatch input:checked + .prefs-swatch-chip { border-color: var(--text); }
.prefs-swatch input:focus-visible + .prefs-swatch-chip { outline: 2px solid var(--accent); outline-offset: 2px; }
.prefs-custom-hue {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); margin-top: var(--s-6);
  font-size: 13px; color: var(--text-3);
}
.prefs-custom-hue input { width: 6rem; }
.prefs-toggles { padding-top: var(--s-8); border-top: 1px solid var(--border); }
.prefs-preview { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; }
.prefs-rule { display: flex; align-items: center; gap: var(--s-5); }
.prefs-rule::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.prefs-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); }
.prefs-note { font-size: 13px; color: var(--text-3); }

/* prefs: the live preview, a scaled-down radar drawn from the page's accent tokens. */
.prefs-preview-frame {
  container-type: inline-size;
  overflow: hidden; border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-1);
}
/* prefs: on the narrowest phones the mini nav's third tab would be cut off at the frame's edge. */
@container (max-width: 299px) { .prefs .pv-tab:last-child { display: none; } }
.prefs .pv-nav {
  display: flex; align-items: center; gap: var(--s-6); height: 44px; padding: 0 var(--s-6);
  border-bottom: 1px solid var(--border); background: var(--surface-0); font-size: 12px; font-weight: 500;
}
.prefs .pv-brand { margin-right: var(--s-1); font-family: var(--font-display); font-size: 14px; color: var(--text); }
.prefs .pv-brand-accent { color: var(--accent); }
.prefs .pv-tab { color: var(--text-3); }
.prefs .pv-tab.is-current { padding-bottom: 2px; border-bottom: 2px solid var(--accent); color: var(--text); }
.prefs .pv-hero {
  display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end;
  height: 168px; padding: var(--s-7);
}
.prefs .pv-tag {
  margin-bottom: var(--s-3); padding: 3px var(--s-3); border-radius: var(--r-chip); background: var(--accent);
  color: var(--accent-fg); font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}
.prefs .pv-title {
  margin-bottom: var(--s-4); font-family: var(--font-display); font-size: 24px; line-height: 1;
  letter-spacing: -0.03em; color: var(--text);
}
.prefs .pv-actions { display: flex; gap: var(--s-3); }
.prefs .pv-btn {
  padding: 7px var(--s-5); border: 1px solid var(--border-strong); border-radius: var(--r-input);
  font-size: 12px; font-weight: 600; line-height: 1.3; color: var(--text);
}
.prefs .pv-btn-accent { border-color: var(--accent); background: var(--accent); color: var(--accent-fg); font-weight: 700; }
.prefs .pv-body { display: flex; flex-direction: column; gap: var(--s-5); padding: var(--s-6); }
.prefs .pv-eyebrow {
  display: flex; align-items: center; gap: var(--s-4);
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-3);
}
.prefs .pv-eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.prefs .pv-rank { display: grid; grid-template-columns: 22px 30px minmax(0, 1fr) auto; align-items: center; gap: var(--s-4); }
.prefs .pv-rank-no { font-family: var(--font-display); font-size: 14px; color: var(--accent); }
.prefs .pv-thumb { display: block; height: 40px; border-radius: var(--r-chip); }
.prefs .pv-rank-title { display: block; font-size: 13px; font-weight: 600; color: var(--text); }
.prefs .pv-hype { display: block; height: 3px; margin-top: 5px; border-radius: 2px; background: var(--border-input); }
.prefs .pv-hype-fill { display: block; width: 92%; height: 100%; border-radius: inherit; background: var(--accent); }
.prefs .pv-rank-count { font-size: 11px; font-weight: 700; color: var(--text-2); }
.prefs .pv-badges { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.prefs .pv-badge {
  padding: var(--s-1) var(--s-3); border: 1px solid var(--border-input); border-radius: var(--r-badge);
  font-size: 11px; font-weight: 600; line-height: 1.3; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-3);
}
.prefs .pv-badge-confirmed { border-color: var(--badge-confirmed-border); background: var(--badge-confirmed-bg); color: var(--badge-confirmed); }
.prefs .pv-badge-delayed { border-color: var(--warn-border); background: var(--warn-bg); color: var(--warn); }
.prefs .pv-badge-watching { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); }

/* prefs: Platforms & regions. Neutral chips only (#360 §3), never the accent. */
.prefs-sentence { font-size: 15px; line-height: 1.6; color: var(--text-body); }
.prefs-sentence strong { font-weight: 700; color: var(--text); }
.prefs-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-5); }
.prefs-chip {
  display: inline-flex; align-items: center; min-height: 26px; padding: 0 var(--s-3);
  border: 1px solid var(--border-input); border-radius: var(--r-badge); background: var(--surface-4);
  color: var(--text); font-size: 12px; font-weight: 600; line-height: 1; white-space: nowrap;
}
.prefs-chip-outline { background: transparent; color: var(--text-3); }
.prefs-more > summary { display: flex; align-items: center; min-height: 44px; list-style: none; cursor: pointer; }
.prefs-more > summary::-webkit-details-marker { display: none; }
.prefs-more > summary:hover .prefs-chip { border-color: var(--border-strong); color: var(--text-2); }
.prefs-more[open] { flex-basis: 100%; }
.prefs-more .prefs-chips { margin-top: 0; }
.prefs-hint { margin-top: var(--s-5); font-size: 13px; color: var(--text-3); }

/* prefs: Account & security status rows. */
.prefs-status-rows { display: flex; flex-direction: column; gap: var(--s-6); margin: 0; }
.prefs-status-row {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--s-2) var(--s-7);
}
.prefs-status-row dt { font-size: 15px; color: var(--text); }
.prefs-status-row dd { margin: 0; font-size: 14px; text-align: right; }
.prefs-status-row .prefs-status-extra { flex-basis: 100%; text-align: left; }
.prefs-status-row .prefs-status-note { flex-basis: 100%; font-size: 13px; font-weight: 400; }
.prefs-inline-form { display: inline; margin: 0; }
.prefs-details > summary { display: inline-flex; min-height: 44px; align-items: center; list-style: none; font-size: 13px; }
.prefs-details > summary::-webkit-details-marker { display: none; }
.prefs-form { display: flex; flex-direction: column; gap: var(--s-6); margin-top: var(--s-3); }
.prefs-field { display: flex; flex-direction: column; gap: var(--s-2); }
.prefs-field label { font-size: 13px; color: var(--text-2); }
.prefs-field input { width: 100%; }
.prefs-field .prefs-help ul { margin: 0; padding-left: var(--s-7); }
.prefs-form .prefs-btn { align-self: flex-start; }

/* prefs: from 768px the nav becomes a sticky column beside the page, flush with the panel. */
@media (min-width: 768px) {
  .prefs-page .container { display: flex; flex-direction: column; padding: 0; }
  .prefs-page .container > .flash { margin: var(--s-9) var(--page-pad-x) 0; }
  .prefs { flex: 1; display: grid; grid-template-columns: var(--settings-nav-w) minmax(0, 1fr); gap: 0; }
  .prefs-nav {
    --prefs-nav-edge: transparent;
    --prefs-nav-fill: transparent;
    --prefs-nav-fill-current: var(--surface-2);
    margin: 0; overflow: visible; border-right: 1px solid var(--border); background: var(--surface-0);
  }
  .prefs-nav-list {
    position: sticky; top: var(--header-h); flex-direction: column; gap: 0; width: auto; padding: var(--s-10) 0;
  }
  .prefs-nav-title {
    display: block; padding: 0 var(--s-9) var(--s-6);
    font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-4);
  }
  .prefs-nav a { min-height: 0; padding: var(--s-4) var(--s-9); border-width: 0 0 0 2px; border-radius: 0; font-size: 15px; }
  /* prefs: two card columns from 768 (#360 §1, README §3); the cards are about 224px wide at 768. */
  .prefs-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .prefs-main { gap: var(--s-14); padding: var(--s-12) var(--s-12) var(--s-15); }
  .prefs-appearance { gap: var(--s-14); }
  .prefs-title { font-size: 34px; }
  .prefs-picker { padding: var(--s-11); }
  .prefs-swatch-chip { width: 38px; height: 38px; }
  .prefs-btn { min-height: 40px; }
  .prefs-select select, .prefs-field input, .prefs-custom-hue input { min-height: 38px; font-size: 14px; }
  .prefs-more > summary, .prefs-details > summary { min-height: 0; }
}
/* prefs: below 1180px the live preview stacks under the wheel (#360 §1). */
@media (min-width: 1180px) {
  .prefs-picker { grid-template-columns: 392px minmax(0, 1fr); gap: var(--s-12); }
}
@media (prefers-reduced-motion: reduce) {
  .prefs-nav a, .prefs-btn, .prefs-switch-track, .prefs-switch-track::after { transition: none; }
}

/* MFA */
/* The QR block and the code chips inside the auth card (mfa/setup.html and
   mfa/backup_codes.html; the card itself is the next region). The QR keeps a light block so it
   still scans: the SVG draws black modules on a transparent ground with its own 4-module quiet
   zone, and the padding adds to it. forced-color-adjust stops a Windows high-contrast theme from
   repainting the block dark under the black modules. */
.auth-card .auth-qr {
  align-self: flex-start; padding: var(--s-3);
  background: var(--text); border-radius: var(--r-panel);
  forced-color-adjust: none;
}
.auth-card .auth-qr svg { display: block; width: 200px; height: 200px; }
.auth-card .auth-manual { display: flex; flex-direction: column; gap: var(--s-2); }
/* A code chip: the manual key and each backup code. Archivo with tabular figures, not a third
   (monospace) face. */
.auth-card .auth-chip {
  display: block; margin: 0; padding: var(--s-3) var(--s-4);
  background: var(--surface-3); border: 1px solid var(--border); border-radius: var(--r-badge);
  color: var(--text); font-family: var(--font-ui); font-size: 14px; font-weight: 600;
  line-height: 1.4; letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
}
.auth-card .auth-key { overflow-wrap: anywhere; -webkit-user-select: all; user-select: all; }
/* Backup codes: two columns at every width. Each code carries a <wbr> at its midpoint, so on the
   narrowest phones a chip wraps there instead of overflowing. */
.auth-card .auth-codes {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3);
  margin: 0; padding: 0; list-style: none;
}
.auth-card .auth-codes .auth-chip { text-align: center; }

/* ==== Auth card and error pages (redesign, #386) ==== */
/* Sign-in, the three MFA steps and the 404 / 403 / CSRF-failure / 500 pages share one centred
   --surface-2 card (#361, gap-shell §9) inside base.html's header-only auth chrome
   (body.is-auth). 500.html copies that chrome as plain markup because Django renders it with no
   context. Every rule is scoped under .auth-card, so the shared "Auth / preference forms" rules
   don't reach in and these don't leak out. The primary action is --text-filled, not accent (the
   accent keeps its four jobs), and an error is a --danger bar. */
.auth-card {
  display: flex; flex-direction: column; gap: var(--s-7);
  width: 100%; max-width: 400px; margin: 0 auto; padding: var(--s-9);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-app);
}
.auth-card > * { margin: 0; }
.auth-card .auth-head { display: flex; flex-direction: column; gap: var(--s-2); }
.auth-card .auth-eyebrow {
  margin: 0; font-size: 12px; font-weight: 400; line-height: 1.4;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.auth-card .auth-title {
  margin: 0; font-family: var(--font-display); font-weight: 400;
  font-size: 28px; line-height: 1.05; letter-spacing: -0.03em; color: var(--text);
}
.auth-card .auth-lede { font-size: 15px; line-height: 1.55; color: var(--text-2); text-wrap: pretty; }
.auth-card .auth-meta { font-size: 13px; line-height: 1.5; color: var(--text-3); }

/* Bars: an error (--danger, announced with role="alert") and a note (--warn). */
.auth-card .auth-error,
.auth-card .auth-note {
  padding: var(--s-4) var(--s-6); border: 1px solid; border-radius: var(--r-ctl);
  font-size: 14px; line-height: 1.5;
}
.auth-card .auth-error { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger); }
.auth-card .auth-note { background: var(--warn-bg); border-color: var(--warn-border); color: var(--text-body); }
.auth-card .auth-note strong { font-weight: 700; color: var(--warn); }

/* Fields: 44px tall, with 16px text on phones (under 16px, iOS zooms the page on focus). */
.auth-card .auth-fields { display: flex; flex-direction: column; gap: var(--s-6); }
.auth-card .auth-field { display: flex; flex-direction: column; gap: var(--s-2); }
.auth-card .auth-field label { font-size: 13px; font-weight: 600; color: var(--text-2); }
.auth-card .auth-field input {
  width: 100%; height: 44px; margin: 0; padding: 0 var(--s-5);
  font: inherit; font-size: 16px; color: var(--text);
  background: var(--surface-3); border: 1px solid var(--border-input); border-radius: var(--r-ctl);
}
.auth-card .auth-field input:focus { border-color: var(--accent); }
/* The one-time code: one big field, set like the design's numeric display. */
.auth-card .auth-field .auth-otp {
  height: 56px; font-family: var(--font-display); font-size: 26px;
  letter-spacing: 0.2em; font-variant-numeric: tabular-nums;
}

/* Actions. .auth-submit is the --text-filled primary (a <button> or an <a>), .auth-secondary the
   outline button, and .auth-link an accent forward link. */
.auth-card .auth-submit,
.auth-card .auth-secondary {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 var(--s-7); border-radius: var(--r-ctl);
  font: inherit; font-size: 15px; font-weight: 700; line-height: 1.2;
  text-align: center; text-decoration: none; cursor: pointer;
  transition: background-color 120ms ease-out, border-color 120ms ease-out;
}
.auth-card .auth-submit { border: 0; background: var(--text); color: var(--bg); }
.auth-card .auth-submit:hover { background: var(--text-fill-hover); color: var(--bg); text-decoration: none; }
.auth-card .auth-secondary {
  border: 1px solid var(--border-strong); background: none; color: var(--text); font-weight: 600;
}
.auth-card .auth-secondary:hover { border-color: var(--text-3); background: var(--wash-hover); }
.auth-card .auth-actions { display: flex; flex-direction: column; gap: var(--s-3); }
.auth-card .auth-link {
  display: inline-flex; align-items: center; align-self: flex-start; min-height: 44px;
  font-size: 14px; font-weight: 600;
}

/* "Use a backup code instead": a no-JS disclosure under a hairline. */
.auth-card .auth-alt { padding-top: var(--s-3); border-top: 1px solid var(--border); }
.auth-card .auth-alt > summary {
  display: flex; align-items: center; min-height: 44px; list-style: none; cursor: pointer;
  font-size: 14px; font-weight: 600; color: var(--text-2);
  text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 3px;
}
.auth-card .auth-alt > summary::-webkit-details-marker { display: none; }
.auth-card .auth-alt > summary:hover { color: var(--text); }
.auth-card .auth-alt > summary:focus-visible {
  outline: 2px solid var(--accent-strong); outline-offset: 2px; border-radius: 4px;
}
.auth-card .auth-alt[open] > summary { margin-bottom: var(--s-3); }

@media (min-width: 768px) {
  .auth-card { margin-top: var(--s-13); padding: var(--s-12); }
  .auth-card .auth-title { font-size: 30px; }
  .auth-card .auth-field input { font-size: 15px; }
}
@media (prefers-reduced-motion: reduce) {
  .auth-card .auth-submit, .auth-card .auth-secondary { transition: none; }
}

/* Game grid + cards */
.game-grid {
  list-style: none; padding: 0; margin: 0; display: grid; gap: 1.5rem 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
}
.game-card { display: flex; flex-direction: column; gap: 0.55rem; }
.cover-link { display: block; position: relative; overflow: hidden; border-radius: var(--r-panel); }
.cover-link img, .cover-placeholder {
  width: 100%; aspect-ratio: 3 / 4; object-fit: cover; display: block;
  border-radius: var(--r-panel); border: 1px solid var(--border); background: var(--surface-2);
  transition: transform 0.25s var(--ease), border-color 0.2s var(--ease);
}
.cover-link:hover img { transform: scale(1.04); border-color: var(--border-strong); }
.cover-placeholder {
  display: grid; place-items: center; font-family: var(--font-display);
  font-size: 2rem; font-weight: 600; color: var(--accent); background: var(--accent-bg);
}
.game-card .meta { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.8rem; }
/* date-forward card: precision date in the display font + status dot, relative line under it */
.card-date { display: flex; align-items: center; gap: 0.5rem; }
.card-date .when {
  font-family: var(--font-display); font-weight: 600; font-size: 0.92rem;
  letter-spacing: -0.01em; font-variant-numeric: tabular-nums;
}
.card-date .when--released { color: var(--ok); }
.card-date .when--upcoming { color: var(--text); }
.card-date .when--tba { color: var(--unknown); }
.card-rel { font-size: 0.75rem; color: var(--text-3); font-variant-numeric: tabular-nums; }
.card-rel.is-soon { color: var(--accent-strong); }
.plat-row { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.plat {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em; color: var(--text-3);
  background: var(--surface-3); border: 1px solid var(--border); border-radius: 5px; padding: 0.05rem 0.32rem;
  white-space: nowrap; flex: none;
}
/* "+N" overflow chip: same footprint as a platform, dimmer; remaining names in title */
.plat--more { color: var(--text-3); cursor: default; }

/* Pagination */
.pagination { display: flex; gap: 1rem; align-items: center; justify-content: center; margin-top: 2rem; }

/* ==== Watchlist (redesign, #381) ==========================================================
   README §2.4 as amended by the Watchlist decisions (#359). templates/subscriptions/watchlist.html.
   Phone first: hero rows and cards stack; the bottom pair goes side by side from 768px. */
.wl { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-13); }
.wl-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--s-5) var(--s-11); }
.wl-head-text { min-width: 0; }
.wl-title {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 30px;
  line-height: 1.05; letter-spacing: -0.03em; color: var(--text);
}
.wl-summary { margin: var(--s-3) 0 0; font-size: 15px; line-height: 1.5; color: var(--text-3); }
.wl-summary a { color: var(--text-2); }
.wl-pills { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.wl-pill {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--s-7);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 600; color: var(--text-2); white-space: nowrap;
}
.wl-pill:hover { color: var(--text); border-color: var(--border-strong); background: var(--wash-hover); text-decoration: none; }
.wl-pill[aria-current] { color: var(--accent); border-color: var(--accent); background: var(--accent-bg); }

/* Change alert (§2.4 item 2): the seen-state bar (#354), --warn at --r-panel. */
.changes-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-7);
  padding: var(--s-6) var(--s-8);
  background: var(--warn-bg); border: 1px solid var(--warn-border); border-radius: var(--r-panel);
}
.changes-bar[hidden] { display: none; }
.changes-bar-title { margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 15px; color: var(--warn); }
.changes-bar-list { display: flex; flex-wrap: wrap; flex: 1 1 320px; gap: var(--s-1) var(--s-6); min-width: 0; margin: 0; padding: 0; list-style: none; }
.changes-bar-row { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1); font-size: 15px; color: var(--text-body); overflow-wrap: anywhere; }
.changes-bar-row a { color: var(--text); }
.changes-bar-row .glyph { color: var(--warn); }
.changes-bar-more { margin: 0; color: var(--text-3); font-size: 14px; }
.changes-bar form { margin: 0 0 0 auto; }
.changes-bar-seen {
  min-height: 44px; padding: 0 var(--s-3); border: 0; background: none;
  font-size: 14px; font-weight: 600; color: var(--warn);
}
.changes-bar-seen:hover { background: none; color: var(--text); }

/* Groups: an eyebrow (14px Archivo Black, uppercase), its count, and a hairline. */
.wl-group { min-width: 0; }
.wl-group:not(:has(.wl-item)), .wl-year-group:not(:has(.wl-item)) { display: none; }
.wl-eyebrow { display: flex; align-items: center; gap: var(--s-5); margin-bottom: var(--s-7); }
.wl-eyebrow h2 {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 14px; line-height: 1.3;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text);
}
.wl-count { font-size: 14px; color: var(--text-3); }
.wl-eyebrow .rule { flex: 1; min-width: var(--s-11); height: 1px; background: var(--border); }
.wl-earlier > summary { cursor: pointer; list-style: none; min-height: 44px; margin-bottom: 0; }
.wl-earlier > summary::-webkit-details-marker { display: none; }
.wl-earlier[open] > summary { margin-bottom: var(--s-7); }
.wl-year { margin: var(--s-7) 0 var(--s-4); font-size: 12px; font-weight: 600; letter-spacing: 0.14em; color: var(--text-3); }
.wl-year-group:first-of-type .wl-year { margin-top: 0; }

/* Tracked platform chips: the accent means "you watch this". */
.plat-chip--tracked { background: var(--accent); color: var(--accent-fg); }

/* Next 7 days: full-width hero rows. */
.wl-heroes { display: grid; gap: var(--s-5); margin: 0; padding: 0; list-style: none; }
.wl-hero {
  display: grid; align-items: center; gap: var(--s-5) var(--s-7);
  grid-template-columns: 56px minmax(0, 1fr);
  grid-template-areas: "cover main" "when when" "actions actions";
  padding: var(--s-7); background: var(--accent-bg);
  border: 1px solid var(--accent); border-radius: var(--r-panel);
}
.wl-hero-cover { grid-area: cover; align-self: start; display: block; }
.wl-hero-main { grid-area: main; display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-2); min-width: 0; }
.wl-hero-title {
  font-family: var(--font-display); font-weight: 400; font-size: 20px; line-height: 1.15;
  color: var(--text); overflow-wrap: anywhere;
}
.wl-hero-title:hover { color: var(--text); text-decoration: none; }
.wl-meta { margin: 0; font-size: 14px; color: var(--text-3); }
.wl-hero-when { grid-area: when; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-6); min-width: 0; }
.wl-hero-when .rdate-value { font-size: 17px; }
.wl-days { margin: 0; font-size: 13px; color: var(--text-3); }
.wl-days b { margin-right: var(--s-1); font-family: var(--font-display); font-weight: 400; font-size: 26px; line-height: 1; color: var(--accent); }
.wl-hero-actions { grid-area: actions; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3) var(--s-6); min-width: 0; }
.wl-reminder { margin: 0; font-size: 13px; color: var(--text-3); }

/* Actions: the store button and the ··· menu. A hero's store is --text-filled, a table row's outlined. */
.wl-actions { display: inline-flex; align-items: center; gap: var(--s-3); }
.wl-store {
  display: inline-flex; align-items: center; gap: var(--s-1); min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap;
}
.wl-store:hover { color: var(--text); background: var(--wash-hover); border-color: var(--text-3); text-decoration: none; }
.wl-store--fill { padding: 0 var(--s-7); color: var(--bg); background: var(--text); border-color: var(--text); }
.wl-store--fill:hover { color: var(--bg); background: var(--text-fill-hover); border-color: var(--text-fill-hover); }
.wl-menu { position: relative; }
.wl-menu-btn {
  display: grid; place-items: center; width: 44px; height: 44px; list-style: none; cursor: pointer;
  border: 1px solid var(--border-input); border-radius: var(--r-input);
  font-size: 14px; letter-spacing: 0.08em; color: var(--text-2);
}
.wl-menu-btn::-webkit-details-marker { display: none; }
.wl-menu-btn:hover, .wl-menu[open] > .wl-menu-btn { color: var(--text); border-color: var(--border-strong); background: var(--wash-hover); }
.wl-menu-pop {
  position: absolute; right: 0; top: calc(100% + var(--s-1)); z-index: 20;
  display: flex; flex-direction: column; min-width: 232px; padding: var(--s-2);
  background: var(--surface-3); border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
}
.wl-menu-pop form { margin: 0; }
.wl-menu-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  width: 100%; min-height: 44px; padding: 0 var(--s-5); border: 0; border-radius: var(--r-input);
  background: none; font: inherit; font-size: 14px; font-weight: 500; color: var(--text); text-align: left;
}
.wl-menu-item:hover { color: var(--text); background: var(--surface-4); text-decoration: none; }
.wl-menu-item--danger { color: var(--danger); }
.wl-menu-item--danger:hover { color: var(--danger); }

/* Rest of <month> and By platform: the shared release row, watchlist columns (see .release-row--watchlist). */
.rr-note { font-size: 13px; color: var(--text-3); }
/* The ··· menu pops out of its row, so the table can't clip; its rows round their own corners. */
.wl-table { overflow: visible; }
.wl-table > .release-row:first-child { border-top-left-radius: var(--r-panel); border-top-right-radius: var(--r-panel); }
.wl-table > .release-row:last-child { border-bottom-left-radius: var(--r-panel); border-bottom-right-radius: var(--r-panel); }
.wl-table .rr-action { align-self: center; }
.wl-table--undated { border-style: dashed; border-color: var(--border-input); }
.wl-table + .wl-table--undated { margin-top: var(--s-5); }

/* Bottom pair, Out now and Released earlier: small cards, 40px thumb, title over status, date. */
.wl-pair { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-13) var(--s-11); }
.wl-cards { display: grid; gap: var(--s-4); margin: 0; padding: 0; list-style: none; }
.wl-card {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto auto; align-items: center;
  gap: var(--s-5); padding: 11px var(--s-6);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-card);
}
.wl-card-cover { display: block; }
.wl-card-body { min-width: 0; }
.wl-card-title { display: block; font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--text); overflow-wrap: anywhere; }
.wl-card-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.wl-card-status { margin: 2px 0 0; font-size: 13px; color: var(--text-3); overflow-wrap: anywhere; }
.wl-card-date { font-size: 14px; font-weight: 700; color: var(--text); white-space: nowrap; font-variant-numeric: tabular-nums; }
.wl-card-date.is-fuzzy { color: var(--warn); }
.wl-card { column-gap: var(--s-4); }
.wl-card .wl-menu-btn { width: 36px; height: 36px; }
.wl-card--undated { background: none; border-style: dashed; border-color: var(--border-input); }
.wl-card--undated .wl-card-title { color: var(--text-2); }
.wl-card--undated .wl-card-date { color: var(--text-3); }
.wl-change { display: inline-flex; align-items: center; gap: 2px; }
.wl-change--warn { color: var(--warn); }
.wl-change--ok { color: var(--ok); }
.wl-change--danger { color: var(--danger); }

/* Empty state (§2.4): one --surface-2 card. */
.wl-empty { padding: var(--s-11); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-panel); }
.wl-empty-title { margin: 0; font-family: var(--font-display); font-size: 22px; line-height: 1.2; color: var(--text); }
.wl-empty-text { margin: var(--s-3) 0 var(--s-8); font-size: 16px; color: var(--text-3); }
.wl-empty-btn {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--s-8);
  border-radius: var(--r-ctl); background: var(--accent); color: var(--accent-fg); font-size: 15px; font-weight: 700;
}
.wl-empty-btn:hover { color: var(--accent-fg); background: var(--accent-strong); text-decoration: none; }

@media (min-width: 768px) {
  .wl-title { font-size: 40px; }
  .wl-pill { min-height: 36px; }
  .wl-hero {
    grid-template-columns: 76px minmax(0, 1fr) auto;
    grid-template-areas: "cover main main" "cover when actions";
    gap: var(--s-5) var(--s-9); padding: var(--s-7);
  }
  .wl-hero-title { font-size: 22px; }
  .wl-hero-when { flex-direction: column; align-items: flex-start; }
  .wl-hero-actions { flex-direction: column; align-items: flex-end; justify-content: center; }
  .wl-store { min-height: 30px; }
  .wl-menu-btn, .wl-card .wl-menu-btn { width: 30px; height: 30px; }
  .wl-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .wl-cards--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .wl-empty { padding: var(--s-13); }
  .wl-empty-title { font-size: 26px; }
}
@media (min-width: 1180px) {
  .wl-hero { grid-template-columns: 76px minmax(0, 1fr) 260px 190px; grid-template-areas: "cover main when actions"; }
  .wl-cards--grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ==== Game detail page (redesign, #380) ===================================================
   README §2.3 as amended by the Game detail decisions (#358). templates/games/detail.html.
   The masthead is full-bleed in the panel, so the page takes the container's padding away and
   gives each band its own gutter. Phone first (README §2.3 "Mobile"): the art is a 260px band the
   cover overlaps by 40px, every block stacks, release rows are cards, and Watch + Notify me are a
   footer pinned above the tab bar. From 768px the masthead is the 212px / 1fr / auto grid over the
   art. From 1180px the body is 1fr / 344px with the rail beside the main column; between 768 and
   1179 the rail modules drop below it as a 2-up grid. */
.container:has(> .gd) { padding: 0; }
.container:has(> .gd) > .flash { margin: var(--s-9) var(--page-pad-x-m) 0; }
.gd { display: flex; flex-direction: column; min-width: 0; padding-bottom: var(--gd-footer-h); }
:root { --gd-footer-h: 65px; }  /* the phone footer: a 48px button pair, its padding and hairline */

/* Masthead: hero art under the bottom scrim, or a flat --surface-1 band when there is no art. */
.gd-mast { position: relative; display: flex; flex-direction: column; background: var(--surface-1); }
.gd-hero { position: relative; height: 260px; overflow: hidden; }
.gd-hero .cover-slot { border-radius: 0; }
.gd-hero::after { content: ""; position: absolute; inset: 0; background: var(--scrim-bottom); }
.gd-mast-grid {
  position: relative; width: 100%; min-width: 0;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-8); align-items: end;
  padding: 0 var(--page-pad-x-m) var(--s-10);
}
.gd-mast--flat .gd-mast-grid { padding-top: var(--s-11); }
.gd-cover { width: 120px; }
.gd-hero + .gd-mast-grid .gd-cover { margin-top: -40px; }  /* overlaps the art band (§2.3) */
.gd-cover .cover-slot { border: 1px solid var(--border-on-art); border-radius: var(--r-panel); }
.gd-mast-main { min-width: 0; }
.gd-kicker { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); margin: 0 0 var(--s-5); }
.gd-studio { font-size: 13px; color: var(--text-2); }
.gd-title {
  margin: 0 0 var(--s-6); font-family: var(--font-display); font-weight: 400;
  font-size: 30px; line-height: 0.98; letter-spacing: -0.03em; color: var(--text); overflow-wrap: anywhere;
}
.gd-when { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-6); }
.gd-date { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
.gd-date .rdate-value { font-size: 17px; }
.gd-date-source { font-size: 13px; color: var(--text-3); }
.gd-genres { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0; padding: 0; list-style: none; }
.gd-genre {
  display: inline-flex; padding: 5px 9px; border-radius: var(--r-badge);
  background: var(--surface-4); color: var(--text-2); font-size: 12px; font-weight: 600; line-height: 1.2;
}
.gd-genre:hover { color: var(--text); text-decoration: none; }
.gd-side { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
.gd-actions {
  position: fixed; left: 0; right: 0; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); z-index: 9;
  display: flex; gap: var(--s-3); padding: var(--s-3) var(--page-pad-x-m);
  border-top: 1px solid var(--border); background: var(--bg);
}
.gd-actions > * { display: flex; flex: 1 1 0; min-width: 0; }
.gd-actions .notify-slot:empty { display: none; }
.gd-actions .notify-slot > form { display: flex; flex: 1; margin: 0; }
.gd-actions .watch-btn, .gd-actions .notify-btn { width: 100%; min-height: 48px; }
.gd-actions .notify-btn {
  padding: 0 var(--s-8); border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  background: var(--scrim-chip); color: var(--text); font-size: 14px; font-weight: 600;
}
.gd-actions .notify-btn:hover { border-color: var(--text-3); background: var(--wash-hover); }
.gd-actions .notify-btn.is-muted { color: var(--text-3); }
.gd-actions .notify-slot > a { font-size: 14px; }
.gd-hype { display: flex; align-items: baseline; gap: var(--s-3); margin: 0; }
.gd-hype b { font-family: var(--font-display); font-weight: 400; font-size: 22px; line-height: 1; color: var(--accent); }
.gd-hype span { font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-3); }

/* Body: the main column's blocks each open with an eyebrow and a hairline (README §1.2). */
.gd-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-13); padding: var(--s-12) var(--page-pad-x-m) var(--s-16); }
.gd-main { display: flex; flex-direction: column; gap: var(--s-14); min-width: 0; }
.gd-eyebrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-6); margin-bottom: var(--s-6); }
.gd-eyebrow h2, .gd-mod-title {
  margin: 0; font-family: var(--font-ui); font-size: 12px; font-weight: 400; line-height: 1.4;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.gd-eyebrow .rule { flex: 1; min-width: var(--s-11); height: 1px; background: var(--border); }
.gd-eyebrow-meta { font-size: 13px; color: var(--text-3); }
.gd-empty { margin: 0; font-size: 15px; color: var(--text-3); }

/* Media: a 16:9 frame (328px tall from 768) over a thumb strip, trailers first (README §2.3). A
   phone's strip is two rows of three, so the "+N" cell sits at the same count as the 6-up strip. */
.gd-frame {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
}
.gd-frame-link { position: absolute; inset: 0; display: block; color: var(--text); }
.gd-frame-link[data-kind="screenshot"] { cursor: zoom-in; }
.gd-frame-img { display: block; width: 100%; height: 100%; object-fit: cover; }  /* hover scale: the shared no-preference block */
.gd-play {
  position: absolute; inset: 0; margin: auto; width: 62px; height: 62px; padding-left: 3px;
  display: grid; place-items: center; border-radius: 50%;
  background: color-mix(in oklab, var(--accent) 92%, transparent); color: var(--accent-fg);
}
.gd-caption {
  position: absolute; left: var(--s-7); bottom: var(--s-6); max-width: calc(100% - 2 * var(--s-7));
  padding: 5px var(--s-4); border-radius: var(--r-badge); background: var(--scrim-label);
  font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gd-play[hidden], .gd-caption[hidden], .gd-thumb-cell[hidden] { display: none; }
.gd-frame-video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.gd-thumbs {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3);
  margin: var(--s-3) 0 0; padding: 0; list-style: none;
}
.gd-thumbs.is-collapsed .is-overflow { display: none; }
.gd-thumb, .gd-thumb-more {
  display: block; width: 100%; height: 60px; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-input); background: var(--surface-2);
}
.gd-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.gd-thumb:hover { border-color: var(--border-strong); }
.gd-thumb[aria-current="true"] { border: 2px solid var(--accent); }
.gd-thumb-more { padding: 0; background: var(--surface-3); color: var(--text-3); font-size: 13px; font-weight: 600; }
.gd-thumb-more:hover { border-color: var(--border-strong); background: var(--surface-4); color: var(--text); }

/* Synopsis: README body prose, clamped to eight lines by detail.js once it knows it overflows. */
.gd-synopsis {
  margin: 0; max-width: 640px; font-family: var(--font-ui); font-size: 16px; line-height: 1.6;
  color: var(--text-body); text-wrap: pretty; white-space: pre-line;
}
.gd-synopsis.is-clamped { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 8; overflow: hidden; }
.gd-more {
  margin-top: var(--s-3); min-height: 44px; padding: 0; border: 0; background: none;
  color: var(--accent); font-size: 14px; font-weight: 600;
}
.gd-more:hover { background: none; color: var(--accent-strong); }
.gd-more[hidden] { display: none; }

/* Taste card: the Discover enrichment, kept under Synopsis in the same eyebrow style (#358 §7). */
.gd-taste-descriptor { margin: 0; max-width: 640px; font-size: 16px; line-height: 1.55; color: var(--text-body); text-wrap: pretty; }
.gd-taste-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin: var(--s-5) 0 0; }
.gd-taste-label { margin-right: var(--s-1); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-3); }
.gd-taste-caveats { font-size: 14px; line-height: 1.5; color: var(--text-2); }
.gd-taste-provenance { margin: var(--s-5) 0 0; font-size: 12px; color: var(--text-4); }

/* Release table: README §1.5's CSS-grid table. A phone gets one card per release row, with the
   track pill full-width at the bottom (§2.3 "Mobile"). */
.gd-rtable { display: flex; flex-direction: column; gap: var(--s-3); }
.gd-rrow {
  display: grid; align-items: center; gap: var(--s-2) var(--s-5);
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "plat" "meta" "date" "track";
  padding: var(--s-6) var(--s-7); border: 1px solid var(--border); border-radius: var(--r-panel);
  transition: background-color 120ms ease-out;
}
.gd-rrow:not(.gd-rrow--head):hover { background: var(--surface-2); }
.gd-rrow:has(.track-pill.is-tracked) { background: var(--accent-bg); }
.gd-rrow--head { display: none; }
.gd-rplat { grid-area: plat; min-width: 0; font-size: 16px; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.gd-rrow.is-undated .gd-rplat { color: var(--text-2); }
.gd-rregion, .gd-rtype { font-size: 14px; color: var(--text-3); }
.gd-rregion { grid-area: meta; }
.gd-rtype { display: none; }
.gd-rdate { grid-area: date; min-width: 0; }
.gd-rtrack { grid-area: track; display: flex; margin-top: var(--s-3); }
.gd-rrow.no-pill .gd-rtrack, .gd-rtrack:empty { display: none; }
.gd-rtrack .track-form { flex: 1; }
.gd-rtrack .track-pill { width: 100%; min-height: 44px; }

/* Date changes (#358 §3): the headline release's recent change under the table, then the whole
   record in a no-JS <details>. The glyph carries the kind's colour; the words stay quiet. */
/* One wrapping sentence, not a flex row: on a phone the glyph and the link stay in the text. */
.gd-change-note { margin: var(--s-4) 0 0; font-size: 13px; line-height: 1.5; color: var(--text-3); }
.gd-change-note .glyph { margin-right: var(--s-1); vertical-align: -0.15em; }
.gd-change-note--delayed .glyph, .gd-change-note--tba .glyph, .gd-hrow--delayed .gd-hkind, .gd-hrow--tba .gd-hkind { color: var(--warn); }
.gd-change-note--earlier .glyph, .gd-change-note--confirmed .glyph, .gd-hrow--earlier .gd-hkind, .gd-hrow--confirmed .gd-hkind { color: var(--ok); }
.gd-change-note--cancelled .glyph, .gd-hrow--cancelled .gd-hkind { color: var(--danger); }
/* A 44px target inside a line of text: padding grows the hit area, the negative margin keeps the line. */
.gd-history-link { display: inline-block; padding-block: var(--s-5); margin-block: calc(-1 * var(--s-5)); font-weight: 600; }
.gd-history { margin-top: var(--s-4); border: 1px solid var(--border); border-radius: var(--r-panel); }
.gd-history > summary {
  display: flex; align-items: center; gap: var(--s-3); min-height: 44px; padding: 0 var(--s-7);
  cursor: pointer; font-size: 14px; font-weight: 600; color: var(--text-2);
}
.gd-history > summary:hover { color: var(--text); }
.gd-history-count { font-weight: 400; color: var(--text-3); }
.gd-history-list { margin: 0; padding: 0 var(--s-7) var(--s-3); list-style: none; }
.gd-hrow {
  display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 2px var(--s-3);
  padding: var(--s-4) 0; border-top: 1px solid var(--border); font-size: 13px; line-height: 1.45;
}
.gd-hkind { grid-row: span 3; padding-top: 2px; }
.gd-hplat { min-width: 0; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.gd-hchange { color: var(--text-2); font-variant-numeric: tabular-nums; }
.gd-hseen { color: var(--text-3); }

/* Rail modules: --surface-2 cards at 18px (README §2.3). */
.gd-rail { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-8); align-content: start; min-width: 0; }
.gd-mod { min-width: 0; padding: var(--s-8); border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2); }
.gd-mod-title { margin-bottom: var(--s-6); }
.gd-stores { display: flex; flex-direction: column; gap: 9px; margin: 0; padding: 0; list-style: none; }
.gd-store {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-5);
  min-height: 44px; padding: 11px 13px; border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  background: var(--surface-3); color: var(--text);
  transition: background-color 120ms ease-out, border-color 120ms ease-out;
}
.gd-store:hover { border-color: var(--border-strong); background: var(--surface-4); color: var(--text); text-decoration: none; }
.gd-store-name { min-width: 0; font-size: 15px; font-weight: 600; overflow-wrap: anywhere; }
.gd-store-action { display: inline-flex; align-items: center; gap: var(--s-1); font-size: 14px; color: var(--text-3); white-space: nowrap; }
.gd-store--site { border-style: dashed; background: none; }
.gd-store--site .gd-store-name { font-weight: 400; color: var(--text-3); }
.gd-links { margin: var(--s-6) 0 0; font-size: 13px; line-height: 1.7; color: var(--text-3); }
.gd-links a { color: var(--text-2); }
.gd-glance { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-6); margin: 0; }
.gd-glance div { display: flex; flex-direction: column-reverse; gap: 3px; min-width: 0; }
.gd-glance dt { font-size: 12px; color: var(--text-3); }
.gd-glance dd { margin: 0; font-family: var(--font-display); font-size: 20px; line-height: 1.2; color: var(--text); font-variant-numeric: tabular-nums; }
.gd-glance dd.is-accent { color: var(--accent); }
.gd-review-links { display: flex; flex-wrap: wrap; gap: var(--s-6); margin: var(--s-7) 0 0; font-size: 13px; }
.gd-review-links a { display: inline-flex; align-items: center; gap: var(--s-1); min-height: 44px; }
.gd-similar { display: flex; flex-direction: column; gap: var(--s-5); margin: 0; padding: 0; list-style: none; }
.gd-sim { display: grid; grid-template-columns: 44px minmax(0, 1fr); align-items: center; gap: 11px; color: var(--text); }
.gd-sim:hover { color: var(--text); text-decoration: none; }
.gd-sim-thumb { display: block; width: 44px; }
.gd-sim-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.gd-sim-title { font-size: 14px; font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; }
.gd-sim:hover .gd-sim-title { text-decoration: underline; text-underline-offset: 3px; }
.gd-sim-reason { font-size: 12px; line-height: 1.4; color: var(--text-4); overflow-wrap: anywhere; }

@media (max-width: 767px) {
  /* The toast clears the footer pair as well as the tab bar. */
  body:has(.gd-actions) .htmx-toast { bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--gd-footer-h) + var(--s-5)); }
}
@media (min-width: 768px) {
  .container:has(> .gd) > .flash { margin-inline: var(--page-pad-x); }
  .gd { padding-bottom: 0; }
  .gd-mast { flex-direction: row; align-items: flex-end; min-height: 420px; }
  .gd-mast--flat { min-height: 300px; }
  .gd-hero { position: absolute; inset: 0; height: auto; }
  .gd-mast-grid {
    grid-template-columns: 212px minmax(0, 1fr) auto; gap: var(--s-10);
    padding: var(--s-12) var(--page-pad-x) 26px;
  }
  .gd-cover { width: 212px; }
  .gd-hero + .gd-mast-grid .gd-cover { margin-top: 0; }
  .gd-mast-main, .gd-side { padding-bottom: var(--s-2); }
  .gd-title { font-size: 44px; line-height: 0.94; }
  .gd-side { align-items: flex-end; }
  .gd-actions {
    position: static; flex-direction: column; align-items: flex-end; gap: var(--s-4);
    padding: 0; border: 0; background: none;
  }
  .gd-actions > * { flex: none; }
  .gd-actions .watch-btn { width: auto; min-height: 44px; }
  .gd-actions .notify-btn { width: auto; min-height: 40px; }
  .gd-hype { margin-top: var(--s-2); }
  .gd-body { padding: var(--s-13) var(--page-pad-x) 44px; }
  .gd-frame { aspect-ratio: auto; height: 328px; }
  .gd-thumbs { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .gd-synopsis { font-size: 18px; }
  .gd-rtable { display: block; border: 1px solid var(--border); border-radius: var(--r-panel); overflow: hidden; }
  .gd-rrow {
    grid-template-columns: minmax(0, 1fr) 110px 120px 170px 100px;
    grid-template-areas: "plat region type date track";
    gap: var(--s-5); padding: 13px var(--s-7);
    border: 0; border-bottom: 1px solid var(--border); border-radius: 0;
  }
  .gd-rrow:last-child { border-bottom: 0; }
  .gd-rrow--head {
    display: grid; padding-block: var(--s-4); background: var(--surface-0);
    font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-3);
  }
  .gd-rrow--head > :nth-child(1) { grid-area: plat; }
  .gd-rrow--head > :nth-child(2) { grid-area: region; }
  .gd-rrow--head > :nth-child(3) { grid-area: type; }
  .gd-rrow--head > :nth-child(4) { grid-area: date; }
  .gd-rrow--head > :nth-child(5) { grid-area: track; justify-content: flex-end; }
  .gd-rregion { grid-area: region; }
  .gd-rtype { display: block; grid-area: type; }
  .gd-rrow.no-pill .gd-rtrack, .gd-rtrack:empty { display: flex; }
  .gd-rtrack { justify-content: flex-end; margin-top: 0; }
  .gd-rtrack .track-form { flex: none; }
  .gd-rtrack .track-pill { width: auto; min-height: 28px; }
  .gd-hrow { grid-template-columns: 16px minmax(0, 1fr) minmax(0, 1.4fr) auto; align-items: baseline; gap: var(--s-5); }
  .gd-hkind { grid-row: auto; align-self: center; padding-top: 0; }
  .gd-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1180px) {
  .gd-title { font-size: 58px; letter-spacing: -0.035em; }
  .gd-body { grid-template-columns: minmax(0, 1fr) 344px; gap: var(--s-13); }
  .gd-rrow { grid-template-columns: minmax(0, 1fr) 130px 150px 170px 120px; }
  .gd-rail { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  .gd-rrow, .gd-store { transition: none; }
}

/* ==== Radar (redesign, #376) ===============================================================
   README §2.1 as decided by Radar decisions (#356). templates/index.html and
   games/_radar_month.html. Phone first, everything stacked: headline, three stat cells, the lead,
   Most anticipated (three rows), the month block (cards, then a table of two-line rows), then
   the rail modules. From 768px the stat strip is one box and the month cards go three-up; the rail
   modules sit 2-up under the body. From 1180px the hero row is the 1.55fr / 1fr grid with a 2px
   --border gap, and the body is 1fr / the 306px rail. */
.rd { display: flex; flex-direction: column; gap: var(--s-12); min-width: 0; }
.rd-empty { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-3); }
.rd-link { display: inline-flex; align-items: center; gap: var(--s-1); font-size: 14px; font-weight: 600; color: var(--accent); }
.rd-link:hover { color: var(--accent-strong); text-decoration: none; }
.rd-eyebrow { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-5); }
.rd-eyebrow h2, .rd-mod-title {
  margin: 0; font-family: var(--font-ui); font-size: 12px; font-weight: 400; line-height: 1.4;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.rd-eyebrow-note { margin-left: auto; font-size: 13px; color: var(--text-3); }

/* Headline strip: the date eyebrow and the hero statement, then the stat strip. */
.rd-head { display: flex; flex-direction: column; gap: var(--s-8); }
.rd-today {
  display: flex; align-items: center; gap: var(--s-3); margin: 0 0 var(--s-3);
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.rd-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.rd-title {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 34px;
  line-height: 0.95; letter-spacing: -0.03em; color: var(--text);
}
.rd-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3); margin: 0; }
.rd-stat {
  display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: var(--s-1); min-width: 0;
  padding: var(--s-4) var(--s-5); border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
}
.rd-stat dd { margin: 0; font-family: var(--font-display); font-size: 18px; line-height: 1.1; color: var(--text); font-variant-numeric: tabular-nums; }
.rd-stat dt { overflow: hidden; font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-3); text-overflow: ellipsis; white-space: nowrap; }
.rd-stat--warn dd { color: var(--warn); }
.rd-stat--accent dd { color: var(--accent); }
/* A phone shows three cells (§2.1 Mobile): signed in, This week steps aside for You watch. */
.rd-stats:has(.rd-stat--you) .rd-stat--week { display: none; }

/* Hero row: the lead release, then Most anticipated. */
.rd-hero { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-12); min-width: 0; }
.rd-lead {
  position: relative; isolation: isolate; display: flex; align-items: flex-end; min-height: 300px; min-width: 0;
  overflow: hidden; border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
}
.rd-lead--empty { align-items: center; min-height: 160px; padding: var(--s-8); }
.rd-lead-art { position: absolute; inset: 0; z-index: -1; }
.rd-lead-art .cover-slot { border-radius: 0; }
.rd-lead-art::after { content: ""; position: absolute; inset: 0; background: var(--scrim-bottom); }
.rd-lead-body { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-5); width: 100%; min-width: 0; padding: var(--s-8); }
.rd-tag {
  margin: 0; padding: var(--s-1) var(--s-3); border-radius: var(--r-badge); background: var(--accent); color: var(--accent-fg);
  font-size: 11px; font-weight: 700; line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase;
}
.rd-lead-title {
  margin: 0; max-width: 100%; font-family: var(--font-display); font-weight: 400; font-size: 28px;
  line-height: 0.98; letter-spacing: -0.03em; overflow-wrap: anywhere;
}
.rd-lead-title a { color: var(--text); }
.rd-lead-title a:hover { color: var(--text); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.rd-blurb { margin: 0; max-width: 470px; font-size: 15px; line-height: 1.55; color: var(--text-2); text-wrap: pretty; }
.rd-lead-when { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-6); }
.rd-lead-when .rdate-value { font-size: 15px; font-weight: 600; }
.rd-cd .cd-n { font-size: 20px; }
.rd-cd--wide { display: none; }
.rd-lead-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-4); width: 100%; margin-top: var(--s-2); }
.rd-lead-actions .watch-form, .rd-lead-actions .watch-btn { width: 100%; }
.rd-btn-secondary {
  display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 var(--s-8);
  border: 1px solid var(--border-strong); border-radius: var(--r-ctl); background: var(--scrim-chip);
  color: var(--text); font-size: 15px; font-weight: 600; white-space: nowrap;
}
.rd-btn-secondary:hover { border-color: var(--text-3); background: var(--wash-hover); color: var(--text); text-decoration: none; }

.rd-ant { display: flex; flex-direction: column; gap: var(--s-7); min-width: 0; }
.rd-ant-list .rank-row:nth-child(n + 4) { display: none; }  /* three rows on a phone (#356 §7) */
.rd-ant-date--fuzzy { color: var(--warn); }
/* An <svg>, not a styled <span>: the row's percentage rides on the <rect>'s width attribute,
   which CSP does not police, so style-src needs no 'unsafe-inline' (#426). viewBox 0 0 100 3 with
   preserveAspectRatio="none" makes one user unit one percent of whatever width the row gives it.
   overflow:hidden is what lets border-radius clip the rect's square left corner. */
.rd-hype-bar { display: block; width: 100%; height: 3px; margin-top: var(--s-3); overflow: hidden; border-radius: 2px; background: var(--surface-4); }
.rd-hype-bar rect { fill: var(--accent); }
.rd-ant-all { align-self: flex-start; min-height: 44px; }

/* Body: the month block, then the rail. */
.rd-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-13); min-width: 0; }
.rd-month { display: flex; flex-direction: column; gap: var(--s-8); min-width: 0; }
.rd-month-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-6); }
.rd-month-title {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 15px; line-height: 1.2;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text);
}
.rd-month-count { font-size: 13px; color: var(--text-3); }
.rd-month-head .rule { flex: 1; min-width: var(--s-11); height: 1px; background: var(--border); }
.rd-month-next {
  display: inline-flex; align-items: center; gap: var(--s-1); min-height: 44px; padding: 0 var(--s-1);
  border: 0; background: none; color: var(--text-2); font-size: 14px; font-weight: 600;
}
.rd-month-next:hover { background: none; color: var(--text); }

/* Release cards: 126px art with a date corner label; the watched card takes the accent. */
.rd-cards { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); margin: 0; padding: 0; list-style: none; }
.rd-card {
  position: relative; display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
}
.rd-card-art { display: block; }
.rd-card-art .cover-slot { border-radius: 0; }
.rd-card-label {
  position: absolute; top: var(--s-3); left: var(--s-3); padding: var(--s-1) var(--s-2); border-radius: var(--r-badge);
  background: var(--scrim-label); color: var(--text);
  font-size: 11px; font-weight: 700; line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase;
}
.rd-card-body { display: flex; flex-direction: column; gap: var(--s-1); padding: var(--s-5) var(--s-6) var(--s-6); min-width: 0; }
.rd-card-title { margin: 0; font-family: var(--font-ui); font-size: 16px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.rd-card-title a, .rd-row-title { color: var(--text); }
.rd-card-title a:hover, .rd-row-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.rd-card-meta { margin: 0; font-size: 13px; color: var(--text-3); }
.rd-card-badges { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); margin-top: var(--s-3); }
.rd-card.is-watched { border-color: var(--accent); background: var(--accent-bg); }
.rd-card.is-watched .rd-card-label { background: var(--accent); color: var(--accent-fg); }
.rd-prox { display: inline-flex; align-items: center; gap: 2px; font-size: 13px; color: var(--text-3); white-space: nowrap; }
.rd-prox.is-soon, .rd-prox--earlier { color: var(--ok); font-weight: 600; }
.rd-prox.is-watched { color: var(--accent); font-weight: 600; }
.rd-prox--delayed { color: var(--warn); font-weight: 600; }

/* Release table: a CSS-grid row per game (README §1.5); a phone gets two lines per row. */
.rd-table { overflow: hidden; border: 1px solid var(--border); border-radius: var(--r-panel); }
.rd-rows { margin: 0; padding: 0; list-style: none; }
.rd-row {
  display: grid; align-items: center; gap: var(--s-1) var(--s-5);
  grid-template-columns: 22px 44px minmax(0, 1fr) auto;
  grid-template-areas: "day thumb main end" "day thumb chips chips";
  padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--border);
}
.rd-row:last-child { border-bottom: 0; }
.rd-row:first-child { background: var(--surface-2); }
.rd-row.is-delayed { background: var(--warn-row-bg); }
.rd-row-day { grid-area: day; font-family: var(--font-display); font-size: 15px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.rd-row-thumb { grid-area: thumb; width: 44px; }
.rd-row-main { grid-area: main; min-width: 0; }
.rd-row-title { font-size: 16px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.rd-row-meta { margin: 0; font-size: 13px; color: var(--text-3); }
.rd-row-chips { grid-area: chips; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-width: 0; }
.rd-row-end { grid-area: end; display: flex; align-items: center; justify-content: flex-end; gap: var(--s-4); }
.rd-month-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-2) var(--s-5);
  margin: 0; padding: 0 var(--s-6); font-size: 14px; color: var(--text-3);
}
.rd-month-foot .rd-link { min-height: 44px; }
.rd-rows + .rd-month-foot { border-top: 1px solid var(--border); }

/* Rail modules: --surface-2 cards at 18px, 20px apart. */
.rd-rail { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-9); align-content: start; min-width: 0; }
.rd-mod { min-width: 0; padding: var(--s-8); border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2); }
.rd-mod-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--s-1) var(--s-5); margin-bottom: var(--s-6); }
.rd-mod-count { font-size: 12px; font-weight: 700; color: var(--warn); }
.rd-mod-link { font-size: 13px; }
.rd-changes, .rd-thumbs, .rd-dropped { margin: 0; padding: 0; list-style: none; }
.rd-changes { display: flex; flex-direction: column; gap: var(--s-7); }
.rd-change { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: var(--s-2); }
.rd-change-mark { font-size: 15px; font-weight: 700; line-height: 1.45; color: var(--unknown); }
.rd-change--delayed .rd-change-mark { color: var(--warn); }
.rd-change--earlier .rd-change-mark, .rd-change--confirmed .rd-change-mark { color: var(--ok); }
.rd-change--cancelled .rd-change-mark { color: var(--danger); }
.rd-change-body { min-width: 0; }
.rd-change-text { margin: 0; font-size: 14px; line-height: 1.45; color: var(--text-2); overflow-wrap: anywhere; }
.rd-change-text b { font-weight: 600; color: var(--text); }
.rd-was { color: var(--text-3); }
.rd-change-when { margin: 2px 0 0; font-size: 12px; color: var(--text-4); }
.rd-thumbs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); }
.rd-thumb { display: block; }
.rd-thumb--more {
  display: flex; align-items: center; justify-content: center; aspect-ratio: 3 / 4;
  border: 1px dashed var(--border-strong); border-radius: var(--r-badge); color: var(--text-3); font-size: 13px; font-weight: 600;
}
.rd-thumb--more:hover { border-color: var(--text-3); color: var(--text); text-decoration: none; }
.rd-dropped { display: flex; flex-direction: column; gap: var(--s-6); }
.rd-drop { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; align-items: center; gap: var(--s-5); }
.rd-drop-thumb { width: 34px; }
.rd-drop-main { display: flex; flex-direction: column; min-width: 0; }
.rd-drop-title { font-size: 14px; font-weight: 600; line-height: 1.35; color: var(--text); overflow-wrap: anywhere; }
.rd-drop-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.rd-drop-when { font-size: 12px; color: var(--text-4); }
.rd-score { font-family: var(--font-display); font-size: 16px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.rd-score--high { color: var(--ok); }
.rd-score--mid { color: var(--warn); }

@media (min-width: 768px) {
  .rd-head { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: var(--s-11); }
  .rd-title { font-size: 44px; }
  .rd-title-break { display: none; }
  .rd-stats {
    display: flex; flex: none; gap: 0; overflow: hidden;
    border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
  }
  .rd-stat { padding: var(--s-5) var(--s-9); border: 0; border-radius: 0; background: none; }
  .rd-stat + .rd-stat { border-left: 1px solid var(--border); }
  .rd-stats:has(.rd-stat--you) .rd-stat--week { display: flex; }
  .rd-stat dd { font-size: 22px; }
  .rd-stat dt { font-size: 12px; }
  .rd-lead { min-height: 360px; }
  .rd-lead-body { padding: var(--s-13); }
  .rd-tag { font-size: 12px; }
  .rd-lead-title { font-size: 40px; }
  .rd-blurb { font-size: 16px; }
  .rd-cd .cd-n { font-size: 24px; }
  .rd-cd--wide { display: block; }
  .rd-cd--compact { display: none; }
  .rd-lead-actions { display: flex; flex-wrap: wrap; width: auto; }
  .rd-lead-actions .watch-form, .rd-lead-actions .watch-btn { width: auto; }
  .rd-btn-secondary { min-height: 42px; }
  .rd-ant { padding: var(--s-11); border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2); }
  .rd-ant-list .rank-row:nth-child(n + 4) { display: grid; }
  .rd-ant-all { min-height: 0; }
  .rd-month-next { min-height: 32px; }
  .rd-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
  .rd-row {
    grid-template-columns: 52px 44px minmax(0, 1fr) 190px 132px;
    grid-template-areas: "day thumb main chips end";
    gap: var(--s-6); padding: var(--s-5) var(--s-7);
  }
  .rd-month-foot { padding: var(--s-4) var(--s-7); }
  .rd-month-foot .rd-link { min-height: 0; }
  .rd-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1180px) {
  .rd-title { font-size: 52px; }
  .rd-hero {
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 2px; overflow: hidden;
    border: 1px solid var(--border); border-radius: var(--r-app); background: var(--border);
  }
  .rd-lead { min-height: 392px; border: 0; border-radius: 0; }
  .rd-lead-title { font-size: 46px; }
  .rd-ant { border: 0; border-radius: 0; }
  .rd-ant-all { margin-top: auto; }
  .rd-body { grid-template-columns: minmax(0, 1fr) var(--rail-w); gap: var(--s-12); }
  .rd-rail { grid-template-columns: minmax(0, 1fr); }
}

/* Screenshot lightbox (#358 §5): opened from the detail page's media frame. Trailers play inline
   in the frame and never open here. */
.lightbox {
  position: fixed; inset: 0; z-index: 1000; overflow: auto;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-13); background: var(--scrim-lightbox); cursor: zoom-out;
}
.lightbox[hidden] { display: none; }
.lightbox-inner { margin: auto; display: flex; align-items: center; justify-content: center; }
.lightbox-img {
  max-width: 92vw; max-height: 90vh; object-fit: contain;
  border: 1px solid var(--border); border-radius: var(--r-panel); cursor: zoom-in;
}
.lightbox-img.zoomed { max-width: none; max-height: none; cursor: zoom-out; }
body.lightbox-open { overflow: hidden; }

/* Gallery: edge zones over the image page prev/next, and the centre zooms. Each zone darkens
   toward the screenshot's edge. They show on hover, and always where there is no hover. */
.lb-gallery { position: relative; display: inline-flex; }
.lb-nav {
  position: absolute; top: 0; bottom: 0; width: 14%; min-width: 44px; z-index: 2; border: 0; cursor: pointer;
  display: flex; align-items: center; color: var(--text); font-size: 2.6rem; line-height: 1;
  background: transparent; opacity: 0; transition: opacity 120ms ease-out;
}
.lb-prev {
  left: 0; justify-content: flex-start; padding-left: var(--s-4);
  border-radius: var(--r-panel) 0 0 var(--r-panel);
  background: linear-gradient(to right, var(--scrim-label), transparent);
}
.lb-next {
  right: 0; justify-content: flex-end; padding-right: var(--s-4);
  border-radius: 0 var(--r-panel) var(--r-panel) 0;
  background: linear-gradient(to left, var(--scrim-label), transparent);
}
.lb-gallery:hover .lb-nav { opacity: 0.6; }
.lb-nav:hover, .lb-nav:focus-visible { opacity: 1; }
@media (hover: none) { .lb-nav { opacity: 0.6; } }
.lb-gallery.is-zoomed .lb-nav { display: none; }  /* edges become panning room when zoomed */
.lb-count {
  position: absolute; bottom: var(--s-6); left: 50%; transform: translateX(-50%); z-index: 2;
  padding: 3px var(--s-4); border-radius: var(--r-badge); background: var(--scrim-label);
  font-size: 13px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .lb-nav { transition: none; } }

/* Watch button: .watch-btn, in the shared partials region above (#393). */
/* Per-game mute beside the watch button; the empty slot takes no space. */
.notify-slot form { margin: 0; }
.notify-btn.is-muted { color: var(--text-3); }


/* Quiet secondary button: the notify toggle (subscriptions/_notify_button.html). */
.btn-quiet { background: var(--surface-3); color: var(--text); border: 1px solid var(--border-strong); }
.btn-quiet:hover { background: var(--surface-2); border-color: var(--accent); }

/* ==== Browse by month (redesign, #379) ===================================================
   README §2.2 as amended by the Browse decisions (#357). templates/games/list.html and its
   _browse_* partials. Full-bleed in the panel like the detail page, so the page takes the
   container's padding away. The bar sticks under the header at every width, and the jump rail
   sticks under the bar: a sideways scroller below 1180px (README §2.2 "Mobile", §6 tablet), the
   132px column beside the results from 1180px. browse-rail.js measures the bar's live height into
   --bw-bar-h, which is what both the rail's offset and a bucket's scroll-margin are built on.

   The bar is a grid, not a stack, because the phone pass has to move two of its children.
   `.bw-sheet` is `display: contents` at rest, so the view switcher and the filter form are grid
   items of the bar itself; below 768px `.bw.has-sheet` (browse-filters.js, so no-JS keeps the
   plain bar) makes the sheet a real fixed box and both go inside it, behind the "Filters · N"
   trigger, leaving the chip row as the visible bar. */
/* Both are measured live by browse-rail.js; these are the fallbacks a no-JS page scrolls by. */
:root { --bw-bar-h: 128px; --bw-rail-h: 53px; }
.container:has(> .bw) { padding: 0; }
.container:has(> .bw) > .flash { margin: var(--s-9) var(--page-pad-x-m) 0; }
.bw { display: flex; flex-direction: column; min-width: 0; }

/* The filter bar: title, count and view switcher over the chip row and the filter pills. */
.bw-bar {
  display: grid; gap: var(--s-5);
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "top top" "chips chips" "views views" "bar bar";
  position: sticky; top: var(--header-h); z-index: 20;
  padding: var(--s-9) var(--page-pad-x-m) var(--s-8);
  background: var(--surface-0); border-bottom: 1px solid var(--border);
}
.bw-bar-top { grid-area: top; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-3) var(--s-6); }
.bw-title {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 26px;
  line-height: 1.05; letter-spacing: -0.02em; color: var(--text);
}
.bw-count { margin: 0; font-size: 15px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.bw-count b { font-weight: 700; color: var(--text); }
/* A grid item of the bar (area "views"), or a block inside the phone sheet: justify-self puts it
   on the right in the first case and is simply ignored in the second, where margin-left would not
   have been. */
.bw-views {
  grid-area: views; justify-self: end; align-self: center;
  display: inline-flex; width: max-content;
  border: 1px solid var(--border-input); border-radius: var(--r-ctl); overflow: hidden;
}
.bw-view {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--s-6);
  font-size: 14px; font-weight: 600; color: var(--text-3); text-decoration: none;
}
.bw-view:hover { color: var(--text); text-decoration: none; }
.bw-view[aria-current="true"] { background: var(--text); color: var(--bg); font-weight: 700; }

.bw-filters { grid-area: bar; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); margin: 0; }
.bw-pill {
  position: relative; display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: 0 var(--s-5);
  background: var(--surface-2); border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  font-size: 14px; color: var(--text-2); text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: border-color 120ms ease-out;
}
.bw-pill:hover { border-color: var(--border-strong); color: var(--text-2); text-decoration: none; }
.bw-pill b { font-weight: 700; color: var(--text); }
.bw-pill .glyph { color: var(--text-4); }
.bw-pill-any { color: var(--text-4); }
.bw-pill--select select {
  appearance: none; field-sizing: content; margin: 0; padding: 0 var(--s-9) 0 0; border: 0; background: none;
  font: inherit; font-weight: 700; color: var(--text); cursor: pointer;
}
.bw-pill--select select:has(> option[value=""]:checked) { font-weight: 400; color: var(--text-4); }
.bw-pill--select select option { background: var(--surface-2); color: var(--text); font-weight: 400; }
.bw-pill--select .glyph { position: absolute; right: var(--s-5); pointer-events: none; }
.bw-pill--select select:focus-visible { outline: none; }
.bw-pill--select:has(select:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.bw-pill--removable .glyph { color: var(--text-3); }

.bw-dropdown { position: relative; }
.bw-dropdown > summary { list-style: none; }
.bw-dropdown > summary::-webkit-details-marker { display: none; }
.bw-menu {
  position: absolute; top: calc(100% + var(--s-2)); left: 0; z-index: 30;
  display: flex; flex-direction: column; gap: var(--s-3);
  width: max-content; min-width: 200px; max-width: calc(100vw - 2 * var(--page-pad-x-m));
  padding: var(--s-3); background: var(--surface-2);
  border: 1px solid var(--border-strong); border-radius: var(--r-panel);
}
.bw-checks { display: flex; flex-direction: column; max-height: 280px; overflow-y: auto; margin: 0; padding: 0; border: 0; min-width: 0; }
.bw-check {
  display: flex; align-items: center; gap: var(--s-3); min-height: 44px; padding: 0 var(--s-3);
  border-radius: var(--r-input); font-size: 14px; color: var(--text-2); cursor: pointer;
}
.bw-check:hover { background: var(--surface-3); color: var(--text); }
.bw-menu-apply, .bw-apply {
  min-height: 44px; padding: 0 var(--s-6);
  background: none; border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 700; color: var(--text);
}
.bw-menu-apply:hover, .bw-apply:hover { background: var(--wash-hover); border-color: var(--text-3); }
.bw-filters.is-enhanced:not(.is-dirty) .bw-apply { display: none; }

.bw-divider { display: none; width: 1px; height: 26px; background: var(--border); }
.bw-toggle {
  display: inline-flex; align-items: center; gap: var(--s-2); min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 600; color: var(--text-3); text-decoration: none; white-space: nowrap;
  transition: border-color 120ms ease-out, color 120ms ease-out;
}
.bw-toggle:hover { border-color: var(--border-strong); color: var(--text-2); text-decoration: none; }
.bw-toggle.is-on { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); }
.bw-toggle.is-on:hover { color: var(--accent-strong); }
.bw-clear { margin-left: auto; font-size: 14px; color: var(--text-3); }
.bw-clear:hover { color: var(--text); }
.bw-link { color: var(--accent); }
.bw-link:hover { color: var(--accent-strong); }

/* The phone chip row and the Filters sheet (README §2.2 "Mobile", §6).
   `_browse_chips.html` is the filter state as removable chips plus the three toggles, in one
   sideways scroller. It is links only, so it needs no JS; the sheet behind it does, which is why
   `.has-sheet` gates every rule that takes the switcher and the form out of the bar. */
.bw-chips {
  grid-area: chips; display: none; gap: var(--s-3); align-items: center;
  margin-inline: calc(-1 * var(--page-pad-x-m)); padding-inline: var(--page-pad-x-m);
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
}
.bw-chips::-webkit-scrollbar { display: none; }
/* `position` is load-bearing, as it is on .bw-pill: an active chip carries an .sr-only "(on)",
   which is absolutely positioned. Without a positioned chip to contain it, its containing block
   is the page, so it escapes this row's overflow clip — and a chip scrolled off to the right
   drags the whole document's scrollWidth out with it. */
.bw-chip {
  position: relative;
  display: inline-flex; flex: none; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 600; color: var(--text-3); text-decoration: none; white-space: nowrap;
}
.bw-chip:hover { border-color: var(--border-strong); color: var(--text-2); text-decoration: none; }
.bw-chip .glyph { color: currentcolor; }
.bw-chip.is-on { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); }
.bw-chip.is-on:hover { color: var(--accent-strong); }

/* Revealed only where the sheet exists: JS drops its `hidden`, the phone query gives it a box. */
.bw-sheet-btn {
  margin-left: auto; align-self: center;
  display: none; align-items: center; gap: var(--s-2); min-height: 44px; padding: 0 var(--s-5);
  background: none; border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  font-family: inherit; font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap;
}
.bw-sheet-btn b { font-weight: 700; }
/* An active filter count is the one thing on this bar that earns the accent: it is state. */
.bw-sheet-btn:has(b) { border-color: var(--accent); color: var(--accent); }

/* Two `display: contents` wrappers, so at rest the switcher and the form are grid items of the
   bar and the desktop layout is untouched; only the phone sheet gives them boxes of their own. */
.bw-sheet, .bw-sheet-panel { display: contents; }
.bw-sheet-head { display: none; }

/* The jump rail and the results column. */
.bw-body { display: flex; flex-direction: column; min-width: 0; }
.bw-rail {
  padding: var(--s-3) var(--page-pad-x-m); background: var(--surface-0);
  border-bottom: 1px solid var(--border); overflow-x: auto; overscroll-behavior-x: contain;
}
.bw-rail-eyebrow { display: none; }
.bw-rail-list { display: flex; gap: var(--s-1); width: max-content; margin: 0; padding: 0; list-style: none; }
.bw-rail-item {
  display: flex; flex-direction: column; justify-content: center; min-height: 44px;
  padding: var(--s-1) var(--s-5); border-bottom: 2px solid transparent;
  font-size: 14px; font-weight: 600; color: var(--text-2); text-decoration: none; white-space: nowrap;
}
.bw-rail-item:hover { color: var(--text); text-decoration: none; }
.bw-rail-item[aria-current="true"] { border-color: var(--accent); background: var(--surface-2); color: var(--text); font-weight: 700; }
.bw-rail-item--tba .bw-rail-label { color: var(--text-3); }
.bw-rail-count { font-size: 12px; font-weight: 400; color: var(--text-4); font-variant-numeric: tabular-nums; }

.bw-results, .bw-flat {
  display: flex; flex-direction: column; gap: var(--s-12); min-width: 0;
  padding: var(--s-11) var(--page-pad-x-m) var(--s-16);
}
.bw-flat { gap: var(--s-7); }
.bw-previous {
  align-self: flex-start; display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: 0 var(--s-5);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 600; color: var(--text-3); text-decoration: none;
}
.bw-previous:hover { border-color: var(--border-strong); color: var(--text-2); text-decoration: none; }

.bw-bucket { display: flex; flex-direction: column; gap: var(--s-10); min-width: 0; }
.bw-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-7); }
.bw-month {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 30px;
  line-height: 1; letter-spacing: -0.03em; color: var(--text);
}
.bw-year { color: var(--border-strong); }
.bw-rule { flex: 1; min-width: var(--s-11); height: 1px; background: var(--border); }
.bw-relevance { margin: 0; font-size: 14px; color: var(--text-3); }
.bw-group { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
.bw-eyebrow {
  display: flex; align-items: center; gap: var(--s-6); margin: 0;
  font-size: 12px; font-weight: 400; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
}
.bw-eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.bw-group--fuzzy .bw-eyebrow { color: var(--warn); }
.bw-group--fuzzy .release-rows { border-style: dashed; border-color: var(--warn-border); }
.bw-more { margin: 0; font-size: 14px; color: var(--text-3); }

/* Editorial lead pair (#357 §7): 180px art under the bottom scrim, title overlaid, a footer below.
   A phone stacks the cards; from 768px a pair sits side by side and a single lead spans. */
.bw-leads { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-6); min-width: 0; }
.bw-lead {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
}
.bw-lead-art {
  position: relative; isolation: isolate; display: flex; align-items: flex-end; height: 180px;
  padding: var(--s-6) var(--s-6) var(--s-5);
}
.bw-lead-media { position: absolute; inset: 0; z-index: -1; }
.bw-lead-media .cover-slot { border-radius: 0; }
.bw-lead-media::after { content: ""; position: absolute; inset: 0; background: var(--scrim-bottom); }
.bw-lead-label {
  position: absolute; top: var(--s-4); left: var(--s-5); margin: 0; padding: var(--s-1) var(--s-3);
  border-radius: var(--r-badge); background: var(--scrim-label); color: var(--text);
  font-size: 11px; font-weight: 700; line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase;
}
.bw-lead-label--pick { background: var(--accent); color: var(--accent-fg); }
.bw-lead-title {
  margin: 0; min-width: 0; font-family: var(--font-display); font-weight: 400; font-size: 24px;
  line-height: 1.05; letter-spacing: -0.02em; overflow-wrap: anywhere;
}
.bw-lead-title a { color: var(--text); }
.bw-lead-title a:hover { color: var(--text); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.bw-lead-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5); padding: var(--s-5) var(--s-6) var(--s-6); min-width: 0; }
.bw-lead-date { font-size: 15px; font-weight: 700; color: var(--text); white-space: nowrap; }
.bw-lead-watch { margin-left: auto; }

.bw-next {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-6);
  padding-top: var(--s-9); border-top: 1px solid var(--border);
}
.bw-next-name {
  margin: 0; font-family: var(--font-display); font-size: 26px; line-height: 1;
  letter-spacing: -0.02em; color: var(--border-strong);
}
.bw-next-count { margin: 0 auto 0 0; font-size: 14px; color: var(--text-3); }
.bw-next--flat { justify-content: center; padding-top: 0; border-top: 0; }
.bw-continue {
  display: inline-flex; align-items: center; gap: var(--s-2); min-height: 44px; padding: 0 var(--s-6);
  border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  font-size: 14px; font-weight: 700; color: var(--text); text-decoration: none;
  transition: background-color 120ms ease-out, border-color 120ms ease-out;
}
.bw-continue:hover { background: var(--wash-hover); border-color: var(--text-3); color: var(--text); text-decoration: none; }
.bw-continue.htmx-request { opacity: 0.55; pointer-events: none; }
.bw-end { margin: 0; font-size: 13px; color: var(--text-4); text-align: center; }

.bw-empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-4); padding: var(--s-16) var(--page-pad-x-m); }
.bw-empty-title { margin: 0; font-family: var(--font-display); font-size: 22px; letter-spacing: -0.02em; color: var(--text); }

/* Filtering over htmx (README §4 "Filters", "Loading", "Errors"). A swap in flight for 300ms turns
   on .is-loading (browse-filters.js): the results give way to static --surface-2 blocks laid out
   like the rail and a bucket's rows, and the bar stays put. No shimmer, no animation. */
.bw-skeleton { display: none; }
.bw.is-loading > :is(.bw-body, .bw-flat, .bw-empty) { display: none; }
.bw.is-loading > .bw-skeleton { display: flex; flex-direction: column; min-width: 0; }
.bw-skel { display: block; background: var(--surface-2); border-radius: var(--r-ctl); }
.bw-skel-rail {
  display: flex; gap: var(--s-3); overflow: hidden;
  padding: var(--s-3) var(--page-pad-x-m); border-bottom: 1px solid var(--border);
}
.bw-skel--rail { flex: none; width: 76px; height: 44px; }
.bw-skel-results {
  display: flex; flex-direction: column; gap: var(--s-10); min-width: 0;
  padding: var(--s-11) var(--page-pad-x-m) var(--s-16);
}
.bw-skel--head { width: min(260px, 60%); height: 30px; }
.bw-skel-rows { display: flex; flex-direction: column; gap: 1px; overflow: hidden; border-radius: var(--r-panel); }
.bw-skel-rows .bw-skel--row { height: 104px; border-radius: 0; }

.bw-error {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-6);
  margin: var(--s-8) var(--page-pad-x-m) 0; padding: var(--s-5) var(--s-7);
  background: var(--warn-bg); border: 1px solid var(--warn-border); border-radius: var(--r-panel);
}
.bw-error p { margin: 0; font-size: 14px; color: var(--text-body); }
.bw-error-retry { font-size: 14px; font-weight: 700; color: var(--warn); }
.bw-error-retry:hover { color: var(--warn); text-decoration: underline; }
.bw-results > .bw-error, .bw-flat > .bw-error { margin: 0; }

/* Below 1180px the rail is the sideways month scroller, and it sticks under the bar (README §2.2
   "Mobile": "a sticky horizontal month scroller under it"; §6 gives the tablet the same scroller).
   browse-rail.js measures both heights live, so a wrapping bar keeps the rail and every bucket
   heading in step with it. */
@media (max-width: 1179px) {
  .bw-rail { position: sticky; top: calc(var(--header-h) + var(--bw-bar-h)); z-index: 19; }
  .bw-bucket { scroll-margin-top: calc(var(--header-h) + var(--bw-bar-h) + var(--bw-rail-h) + var(--s-2)); }
}

/* The phone bar (README §2.2 "Mobile"): the chip row is the bar, and everything that used to wrap
   below it is behind the Filters trigger. The count keeps its place in the accessibility tree —
   it is the swap's focus target and what htmx-enhance.js announces — but the month header's
   "31 releases" is the count a phone reads, so it is not drawn twice. */
@media (max-width: 767px) {
  .bw-chips { display: flex; }
  .bw.has-sheet .bw-sheet-btn { display: inline-flex; }
  .bw.has-sheet .bw-count {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* A bottom sheet, not a modal: it is the trigger's disclosure (aria-expanded/-controls), so it
     traps nothing and Escape or Done closes it. The scrim only has to stop stray taps. */
  .bw.has-sheet .bw-sheet {
    position: fixed; inset: 0; z-index: 40; display: flex; flex-direction: column; justify-content: flex-end;
    visibility: hidden; pointer-events: none;
  }
  .bw.has-sheet .bw-sheet::before {
    content: ""; position: absolute; inset: 0; background: var(--scrim-bottom); opacity: 0;
    transition: opacity 140ms ease-out;
  }
  .bw.has-sheet .bw-sheet.is-open { visibility: visible; pointer-events: auto; }
  .bw.has-sheet .bw-sheet.is-open::before { opacity: 1; }
  .bw.has-sheet .bw-sheet-panel {
    position: relative; z-index: 1; display: flex; flex-direction: column; min-height: 0;
    max-height: 82vh; overflow-y: auto; overscroll-behavior: contain;
    background: var(--surface-1); border-top: 1px solid var(--border);
    border-top-left-radius: var(--r-panel); border-top-right-radius: var(--r-panel);
    transform: translateY(12px); transition: transform 140ms ease-out;
  }
  .bw.has-sheet .bw-sheet.is-open .bw-sheet-panel { transform: none; }
  .bw.has-sheet .bw-sheet-head {
    position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: var(--s-5);
    padding: var(--s-5) var(--page-pad-x-m);
    background: var(--surface-1); border-bottom: 1px solid var(--border);
  }
  .bw.has-sheet .bw-sheet-title {
    margin: 0; font-size: 11px; font-weight: 400; letter-spacing: 0.18em;
    text-transform: uppercase; color: var(--text-3);
  }
  .bw.has-sheet .bw-sheet-done {
    margin-left: auto; min-height: 44px; padding: 0 var(--s-5);
    background: none; border: 0; border-radius: var(--r-ctl);
    font-family: inherit; font-size: 14px; font-weight: 700; color: var(--accent);
  }
  .bw.has-sheet .bw-views {
    justify-self: stretch; width: auto; margin: var(--s-6) var(--page-pad-x-m) 0;
  }
  .bw.has-sheet .bw-views > .bw-view { flex: 1; justify-content: center; }
  .bw.has-sheet .bw-filters {
    padding: var(--s-6) var(--page-pad-x-m) calc(var(--s-8) + env(safe-area-inset-bottom));
  }
}
@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .bw.has-sheet .bw-sheet::before, .bw.has-sheet .bw-sheet-panel { transition: none; }
  .bw.has-sheet .bw-sheet-panel { transform: none; }
}

@media (min-width: 768px) {
  .container:has(> .bw) > .flash { margin-inline: var(--page-pad-x); }
  .bw-bar {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "top views" "bar bar";
    padding: var(--s-10) var(--page-pad-x) var(--s-8);
  }
  .bw-title { font-size: 30px; }
  .bw-divider { display: block; }
  .bw-view, .bw-pill, .bw-toggle, .bw-check, .bw-menu-apply, .bw-apply, .bw-previous, .bw-continue { min-height: 36px; }
  .bw-view { min-height: 34px; }
  .bw-rail { padding-inline: var(--page-pad-x); }
  .bw-rail-item { min-height: 40px; }
  .bw-results, .bw-flat { padding: var(--s-11) var(--page-pad-x) var(--s-16); }
  .bw-month { font-size: 38px; }
  .bw-leads--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bw-lead-title { font-size: 26px; }
  .bw-empty { padding-inline: var(--page-pad-x); }
  .bw-skel-rail { padding-inline: var(--page-pad-x); }
  .bw-skel--rail { height: 40px; }
  .bw-skel-results { padding-inline: var(--page-pad-x); }
  .bw-skel--head { height: 38px; }
  .bw-skel-rows .bw-skel--row { height: 72px; }
  .bw-error { margin-inline: var(--page-pad-x); }
}
@media (min-width: 1180px) {
  .bw-body { display: grid; grid-template-columns: 132px minmax(0, 1fr); align-items: stretch; }
  .bw-rail { padding: var(--s-10) 0; border-bottom: 0; border-right: 1px solid var(--border); overflow: visible; }
  .bw-rail-inner { position: sticky; top: calc(var(--header-h) + var(--bw-bar-h) + var(--s-10)); }
  .bw-rail-eyebrow {
    display: block; margin: 0 0 var(--s-4); padding: 0 var(--s-8);
    font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3);
  }
  .bw-rail-list { flex-direction: column; gap: 0; width: auto; }
  .bw-rail-item { padding: 9px var(--s-8); border-bottom: 0; border-left: 2px solid transparent; }
  .bw-rail-item[aria-current="true"] { border-color: var(--accent); }
  .bw-bucket { scroll-margin-top: calc(var(--header-h) + var(--bw-bar-h)); }
  .bw.is-loading > .bw-skeleton { display: grid; grid-template-columns: 132px minmax(0, 1fr); }
  .bw-skel-rail { flex-direction: column; gap: var(--s-3); padding: var(--s-10) var(--s-8); border-bottom: 0; border-right: 1px solid var(--border); }
  .bw-skel--rail { width: 100%; height: 36px; }
}
@media (prefers-reduced-motion: reduce) { .bw-pill, .bw-toggle, .bw-continue { transition: none; } }

/* ==== Browse Grid and List views (#379, #357 §6) ========================================== */
/* Two template choices over the same rows, so switching one never changes the order: Grid is the
   README §2.1 release card 4-up inside the same week groups, List a dense one-line table whose
   week labels become sub-header rows. Neither draws the editorial lead pair.
   Phone first: Grid is 2-up with the chips collapsed to a text line, List is two lines behind a
   date gutter with no header row. */

/* --- Grid: release cards ------------------------------------------------ */
.bw-cards {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4);
  list-style: none; margin: 0; padding: 0; min-width: 0;
}
.rcard {
  position: relative; display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-panel); background: var(--surface-2);
  transition: background-color 120ms ease-out;
}
.rcard:hover { background: var(--surface-3); }
/* Watched is the corner label alone (#357 §6): a month of watched games would otherwise put
   about fifteen accent elements on screen, against README §1.4's budget of three to six. */
.rcard.is-watched .rcard-corner { background: var(--accent); color: var(--accent-fg); }
.rcard.is-tba { background: none; border-style: dashed; border-color: var(--border-input); }
.rcard.is-tba .cover-slot img { opacity: 0.75; }
.rcard-art { display: block; }
.rcard-art .cover-slot { border-radius: 0; }
.rcard-corner {
  position: absolute; top: var(--s-3); left: var(--s-3); z-index: 1;
  padding: var(--s-1) var(--s-2); border-radius: var(--r-badge);
  background: var(--scrim-label); color: var(--text);
  font-size: 11px; font-weight: 700; line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase;
}
.rcard-corner--fuzzy { color: var(--warn); }
.rcard-corner--tba { color: var(--text-3); }
.rcard-body {
  flex: 1; display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-4); min-width: 0;
}
.rcard.is-delayed .rcard-body { background: var(--warn-row-bg); }
.rcard-title { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.rcard-title a { color: var(--text); }
.rcard-title a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.rcard-meta { margin: 0; font-size: 13px; color: var(--text-3); display: none; }
.rcard-plats { margin: 0; font-size: 13px; color: var(--text-3); }
.rcard-later { color: var(--warn); }
.rcard .plat-chips { display: none; }
.rcard-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: auto; padding-top: var(--s-1); }
/* A 2-up card has no room for the neutral "Full game" badge; DLC and Early access still show. */
.rcard-foot .type-badge--neutral { display: none; }
.rcard-space { flex: 1; }
.rcard-when { font-size: 13px; color: var(--text-3); white-space: nowrap; }

/* --- List: a dense table, no art ---------------------------------------- */
.ltable {
  list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--border); border-radius: var(--r-panel); overflow: hidden;
}
.ltable--tba { border-style: dashed; border-color: var(--border-input); }
.lhead { display: none; }
.lsub {
  padding: var(--s-2) var(--s-6); border-bottom: 1px solid var(--border); background: var(--surface-1);
  font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-3);
}
.lsub--fuzzy { color: var(--warn); }
.lrow {
  display: grid; align-items: center; font-size: 14px;
  grid-template-columns: 58px minmax(0, 1fr) 44px;
  grid-template-areas: "date game track" "date plat track" "date when track";
  column-gap: var(--s-5); row-gap: var(--s-1);
  padding: var(--s-4) var(--s-6); border-bottom: 1px solid var(--border);
  transition: background-color 120ms ease-out;
}
.lrow--ranked {
  grid-template-columns: 24px 58px minmax(0, 1fr) 44px;
  grid-template-areas: "rank date game track" "rank date plat track" "rank date when track";
}
.ltable > .lrow:last-child { border-bottom: 0; }
.lrow:hover { background: var(--surface-2); }
.lrow.is-delayed { background: var(--warn-row-bg); }
.lrow.is-watched { box-shadow: inset 2px 0 0 var(--accent); }
.lrow.is-tba .l-title { color: var(--text-2); }
.l-rank {
  grid-area: rank; font-family: var(--font-display); font-weight: 400; font-size: 16px;
  line-height: 1.25; font-variant-numeric: tabular-nums; color: var(--text-4);
}
.l-rank.is-first { color: var(--accent); }
/* Archivo 700, not Archivo Black: README §8 never renders the display face below 14px. */
.l-date { grid-area: date; display: flex; flex-direction: column; font-weight: 700; white-space: nowrap; }
.l-date--fuzzy { color: var(--warn); }
.l-date--tba { color: var(--text-3); }
.l-wd { color: var(--text-3); font-weight: 600; }
.l-game { grid-area: game; display: flex; flex-direction: column; min-width: 0; }
.l-title { font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.l-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.l-meta { display: none; }
.l-chips { grid-area: plat; display: none; }
.l-plats { grid-area: plat; font-size: 13px; color: var(--text-3); }
.l-later { color: var(--warn); }
.l-when { grid-area: when; display: flex; align-items: center; gap: var(--s-3); min-width: 0; font-size: 13px; color: var(--text-3); }
.l-hype { font-weight: 700; color: var(--text-2); white-space: nowrap; }
.l-track { grid-area: track; justify-self: end; }

/* --- Skeletons for the two views (README §4 "Loading") ------------------- */
.bw-skel-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
.bw-skel--card { height: 208px; border-radius: var(--r-panel); }
.bw-skeleton--list .bw-skel-rows .bw-skel--row { height: 64px; }
.bw-skeleton--flat .bw-skel-rail { display: none; }

@media (min-width: 768px) {
  .bw-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
  .rcard-body { gap: var(--s-3); padding: var(--s-5) var(--s-6) var(--s-6); }
  .rcard-title { font-size: 16px; }
  .rcard-meta { display: block; }
  .rcard .plat-chips { display: flex; }
  .rcard-plats { display: none; }
  .rcard-foot .type-badge--neutral { display: inline-flex; }

  .lhead {
    display: grid; background: var(--surface-0); padding-block: var(--s-4);
    font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-3);
  }
  .lhead:hover { background: var(--surface-0); }
  .lhead + .lrow { background: var(--surface-2); }
  .lrow {
    grid-template-columns: 104px minmax(0, 1fr) 260px 132px 28px;
    grid-template-areas: "date game plat when track";
    column-gap: var(--s-6); padding: var(--s-3) var(--s-7);
  }
  .lrow--ranked {
    grid-template-columns: 24px 104px minmax(0, 1fr) 260px 132px 28px;
    grid-template-areas: "rank date game plat when track";
  }
  .l-date { flex-direction: row; gap: var(--s-2); }
  .l-game { flex-direction: row; align-items: baseline; gap: var(--s-3); }
  .l-meta { display: block; font-size: 13px; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .l-chips { display: flex; align-items: center; gap: var(--s-2); flex-wrap: nowrap; overflow: hidden; }
  .l-plats { display: none; }
  .l-when { justify-content: flex-end; }

  .bw-skel-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
  .bw-skel--card { height: 232px; }
  .bw-skeleton--list .bw-skel-rows .bw-skel--row { height: 42px; }
}
@media (min-width: 1180px) {
  .bw-cards, .bw-skel-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* A flat sort has no rail, so its skeleton keeps the single column the results use. */
  .bw.is-loading > .bw-skeleton--flat { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) { .rcard, .lrow { transition: none; } }

/* --- Discover: taste profile ------------------------------------------- */
/* Discover in the design language (#361, #385). No Discover screen was designed, so every piece
   is borrowed: the header is the watchlist's (README §2.4), cards are the radar rail's
   --surface-2 modules, taste entries are "Later this year" rows, and the search field is the
   header search. Buttons and links are Discover-scoped, so this region never leans on the shared
   `button` / `.btn` rules. Each page wraps its blocks in .discover-page, which spaces them by gap. */
.discover-page { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-11); }
.discover-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: var(--s-5) var(--s-11);
}
.discover-head-text { min-width: 0; }
.discover-title {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 30px;
  line-height: 1.05; letter-spacing: -0.03em; color: var(--text);
}
.discover-summary { margin: var(--s-3) 0 0; max-width: 640px; font-size: 15px; line-height: 1.5; color: var(--text-3); }
.discover-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-7); }
/* A link to another page is a forward affordance, the accent's fourth job. */
.discover-link {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 14px; font-weight: 600; color: var(--accent); white-space: nowrap;
}
.discover-link:hover { color: var(--accent-strong); text-decoration: none; }
/* Secondary is an outline. Primary is --text-filled, which keeps the accent to its four jobs. */
.discover-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 var(--s-7);
  font: inherit; font-size: 14px; font-weight: 600; line-height: 1.2; white-space: nowrap;
  color: var(--text); background: transparent;
  border: 1px solid var(--border-strong); border-radius: var(--r-ctl);
  text-decoration: none; cursor: pointer;
  transition: background-color 120ms ease-out, border-color 120ms ease-out;
}
.discover-btn:hover { color: var(--text); background: var(--wash-hover); border-color: var(--text-3); text-decoration: none; }
.discover-btn--primary { color: var(--bg); background: var(--text); border-color: var(--text); font-weight: 700; }
.discover-btn--primary:hover { color: var(--bg); background: var(--text-fill-hover); border-color: var(--text-fill-hover); }
.discover-btn--small { padding: 0 var(--s-5); font-size: 13px; color: var(--text-2); border-radius: var(--r-input); }
.htmx-request .discover-btn { opacity: 0.55; pointer-events: none; }

.discover-card {
  padding: var(--s-8); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-panel);
}
/* README §1.2's eyebrow heads a card: 12px uppercase --text-3, then a hairline. */
.discover-eyebrow { display: flex; align-items: center; gap: var(--s-5); margin-bottom: var(--s-7); }
.discover-eyebrow h2 {
  margin: 0; font-family: var(--font-ui); font-size: 12px; font-weight: 400; line-height: 1.3;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3); white-space: nowrap;
}
.discover-eyebrow .rule, .discover-group-head .rule { flex: 1; height: 1px; background: var(--border); }
.discover-eyebrow-meta { font-size: 13px; color: var(--text-3); white-space: nowrap; }
/* README §2.4's group header heads a page section: 14px Archivo Black, a count, a hairline. */
.discover-group-head { display: flex; align-items: center; gap: var(--s-5); margin-bottom: var(--s-7); }
.discover-group-head h2 {
  margin: 0; font-family: var(--font-display); font-size: 14px; font-weight: 400; line-height: 1.3;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text);
}
.discover-group-count { font-size: 14px; color: var(--text-3); }

/* The dated "analyzing" state (ADR-0029): neutral, never accent, beside its re-request control. */
.discover-status {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--s-5) var(--s-7); padding: var(--s-6) var(--s-8);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-panel);
}
.discover-status p { flex: 1 1 18rem; margin: 0; font-size: 15px; line-height: 1.5; color: var(--text-2); }
.discover-analyzing { margin: var(--s-3) 0 0; font-size: 13px; line-height: 1.5; color: var(--text-3); }
.discover-dated { margin: var(--s-5) 0 0; font-size: 12px; color: var(--text-4); }
.discover-note { margin: 0 0 var(--s-7); max-width: 640px; font-size: 13px; line-height: 1.5; color: var(--text-3); }
.discover-hint { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-3); }
.discover-error {
  margin: 0; padding: var(--s-5) var(--s-7); font-size: 15px; color: var(--danger);
  background: var(--danger-bg); border: 1px solid var(--danger-border); border-radius: var(--r-panel);
}
/* README §2.4's empty state: one card, a display line, one sentence, and the next step. */
.discover-empty {
  display: grid; justify-items: start; gap: var(--s-5); padding: var(--s-11) var(--s-8);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-panel);
}
.discover-empty-title {
  margin: 0; font-family: var(--font-display); font-size: 22px; line-height: 1.15;
  letter-spacing: -0.02em; color: var(--text);
}
.discover-empty-text { margin: 0 0 var(--s-3); max-width: 560px; font-size: 16px; line-height: 1.55; color: var(--text-3); }

/* The "Your taste" card: the synthesized summary (Phase 3c) as body prose, then labelled chips. */
.taste-prose { margin: 0; max-width: 640px; font-size: 16px; line-height: 1.6; color: var(--text-body); text-wrap: pretty; }
.aspect-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.aspect-label {
  flex: none; width: 72px; font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-3);
}

/* Taste entries: the watchlist's "Later this year" cards, `40px 1fr auto` with a 52px thumb. */
.taste-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-4); }
.taste-row {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: start;
  gap: var(--s-5); padding: 11px var(--s-6);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-card);
}
.taste-thumb { display: block; width: 40px; height: 52px; object-fit: cover; border-radius: var(--r-badge); }
.taste-row-body { min-width: 0; }
.taste-row-title { font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--text); overflow-wrap: anywhere; }
.taste-row-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.taste-row-note { margin: var(--s-1) 0 0; font-size: 14px; line-height: 1.5; color: var(--text-2); overflow-wrap: anywhere; }
.taste-row-note--empty { color: var(--text-3); }
.taste-remove { margin: 0; align-self: center; }
@media (max-width: 767px) {
  /* A phone can't spare the auto column: Remove drops under the note, which keeps the width. */
  .taste-row { grid-template-columns: 40px minmax(0, 1fr); }
  .taste-remove { grid-column: 2; justify-self: start; }
  .aspect-label { width: auto; margin-right: var(--s-1); }
  /* The eyebrow's meta ("Ranked 2 days ago") takes its own line instead of overflowing the card. */
  .discover-eyebrow { flex-wrap: wrap; row-gap: var(--s-1); }
  .discover-eyebrow-meta { flex-basis: 100%; }
  /* A tighter submit and field padding leave the search placeholder room at 320px. */
  .taste-search .discover-btn { padding: 0 var(--s-5); }
  .taste-search .taste-search-field input[type="search"] { padding-left: 34px; padding-right: var(--s-2); }
}

/* Add and Import: the header search's field, then one --surface-2 card per result. */
.taste-search { display: flex; gap: var(--s-3); max-width: 560px; margin: 0; }
.taste-search-field { position: relative; display: flex; align-items: center; flex: 1; min-width: 0; }
.taste-search-ic {
  position: absolute; left: var(--s-5); display: inline-flex; color: var(--text-3); pointer-events: none;
}
.taste-search input[type="search"] {
  width: 100%; height: 44px; padding: 0 var(--s-5) 0 36px;
  font: inherit; font-size: 16px; /* under 16px, iOS zooms the page on focus */
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border-input); border-radius: var(--r-ctl);
}
.taste-search input::placeholder, .taste-result textarea::placeholder { color: var(--text-3); }
.taste-search input:focus, .taste-result textarea:focus { border-color: var(--accent); }
.taste-results {
  list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-5);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
}
.taste-result {
  padding: var(--s-7); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-panel);
}
.taste-result-form { display: grid; grid-template-rows: auto 1fr auto; gap: var(--s-5); height: 100%; margin: 0; }
.taste-result-head { display: grid; grid-template-columns: 40px minmax(0, 1fr); align-items: center; gap: var(--s-5); }
.taste-result-title { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--text); overflow-wrap: anywhere; }
.taste-result-year { font-weight: 400; color: var(--text-3); }
.taste-result textarea {
  width: 100%; min-height: 76px; padding: var(--s-4) var(--s-5);
  font: inherit; font-size: 16px; line-height: 1.5; color: var(--text); resize: vertical;
  background: var(--surface-2); border: 1px solid var(--border-input); border-radius: var(--r-ctl);
}
.taste-result .discover-btn { justify-self: start; }

@media (min-width: 768px) {
  .discover-page { gap: var(--s-12); }
  .discover-title { font-size: 40px; }
  .discover-link { min-height: 0; }
  .discover-btn { min-height: 38px; }
  .discover-btn--small { min-height: 32px; }
  .discover-card { padding: var(--s-11); }
  .discover-empty { padding: var(--s-13) var(--s-11); }
  .discover-empty-title { font-size: 26px; }
  .taste-prose { font-size: 18px; }
  .taste-search input[type="search"], .taste-result textarea { font-size: 14px; }
}
@media (prefers-reduced-motion: reduce) { .discover-btn { transition: none; } }

/* Parsed-aspect chips on the profile + recommendation reasons. */
/* Discover's chips below: README §1.3's badge shape at chip size, in sentence case because
   these are names.
   A liked aspect is an --ok badge. A disliked aspect, "✦ Fresh find" and "in catalog" are neutral:
   --border-input and no fill (#361). */
/* .aspect-chips stacks the labelled Liked / Disliked rows (_aspect_groups.html). */
.aspect-chips { display: grid; gap: var(--s-2); margin: var(--s-3) 0 0; }
.discover-card .aspect-chips { gap: var(--s-3); margin-top: var(--s-5); }
.aspect-chip, .discover-chip {
  display: inline-flex; align-items: center; padding: var(--s-1) var(--s-3);
  font-size: 11px; font-weight: 600; line-height: 1.2; letter-spacing: 0.06em; white-space: nowrap;
  color: var(--text-2); background: none;
  border: 1px solid var(--border-input); border-radius: var(--r-badge);
}
.aspect-chip--liked { color: var(--ok); background: var(--ok-bg); border-color: var(--ok-border); }

/* Recommendations list. */
/* Rank rows: README §2.1's "Most anticipated" row, built here first. The radar build (#376)
   reuses this family, so it stays free of Discover; page tweaks go on the page's own class
   (.rec-list, below). Markup contract:
     <ol class="rank-list" role="list">
       <li class="rank-row">
         <span class="rank-num" aria-hidden="true">01</span>
         <img class="rank-thumb art-fallback" src="…" alt="" width="44" height="58">
         <div class="rank-body"><a class="rank-title" href="…">…</a><p class="rank-meta">…</p></div>
         <span class="rank-value">4.1k</span>   (optional: the fourth, auto column)
       </li>
     </ol>
   The first row of a .rank-list is rank 01 and takes the accent (ranking is its second job); the
   rest are --text-4. A row with no cover uses <span class="rank-thumb art-fallback">. The hype bar
   is not here: its width is per-row data, which is #376's to deliver. */
.rank-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-6); }
.rank-row {
  display: grid; grid-template-columns: 22px 44px minmax(0, 1fr) auto;
  align-items: center; column-gap: var(--s-5);
}
.rank-num {
  font-family: var(--font-display); font-size: 16px; line-height: 1.25;
  font-variant-numeric: tabular-nums; color: var(--text-4);
}
.rank-list > .rank-row:first-child .rank-num { color: var(--accent); }
.rank-thumb { display: block; width: 44px; height: 58px; object-fit: cover; border-radius: var(--r-badge); }
.rank-body { min-width: 0; }
.rank-title { font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--text); overflow-wrap: anywhere; }
.rank-title:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.rank-meta { margin: 2px 0 0; font-size: 13px; line-height: 1.4; color: var(--text-3); }
.rank-value { font-size: 13px; font-weight: 700; color: var(--text-2); white-space: nowrap; }

/* Discover's rows carry a multi-line "why", so they top-align, rule off from each other, and drop
   the value column they don't use. */
.rec-list { gap: 0; }
.rec-list > .rank-row {
  grid-template-columns: 22px 44px minmax(0, 1fr); align-items: start;
  padding-block: var(--s-7); border-top: 1px solid var(--border);
}
.rec-list > .rank-row:first-child { padding-top: 0; border-top: 0; }
.rec-list > .rank-row:last-child { padding-bottom: 0; }
.rec-list .discover-chip { margin-left: var(--s-2); vertical-align: 1px; }
.rec-why { margin: var(--s-1) 0 0; max-width: 640px; font-size: 14px; line-height: 1.55; color: var(--text-2); text-wrap: pretty; }
