/*
 * Collected — "Ledger" design system (design spec V1, July 2026).
 *
 * A single hand-rolled stylesheet (no framework, no build step) implementing the
 * editorial-archive visual language from the Collected Design Spec:
 *
 *   - warm ink surfaces, dark-first with a first-class light pair
 *   - Newsreader (serif) for page & game titles, Public Sans for UI/body,
 *     IBM Plex Mono for labels, data, counts, and badges
 *   - two accents: leaf (ownership, success, active nav, links) and brass
 *     (in-progress, warnings) — one accent per screen wherever possible
 *   - hairlines over boxes; covers and screenshots supply the color
 *
 * Everything the old Pico.css layer provided (element defaults, buttons, forms,
 * tables) is defined here instead, scoped through design tokens below.
 */

/* ---------------------------------------------------------------------------
 * Fonts (vendored — no CDN)
 * ------------------------------------------------------------------------- */

/* Newsreader — display serif, variable weight (400–700), optical sizing. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/newsreader-latin.0a301b6df4c7.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/newsreader-latin-ext.40f89707c10e.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/newsreader-italic-latin.6d201901085f.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/newsreader-italic-latin-ext.045b9f0ec267.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Public Sans — UI and body, variable weight (400–700). */
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/public-sans-latin.45ac3f4b61c1.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/public-sans-latin-ext.587f1fa8d04e.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — labels, data, counts, badges. */
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/plex-mono-400-latin.814fe1615d4d.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/plex-mono-400-latin-ext.17a5b2a15bf5.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/plex-mono-500-latin.6708425c446b.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/plex-mono-500-latin-ext.216ee1a35b06.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/plex-mono-600-latin.e71a597e376e.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/plex-mono-600-latin-ext.6041b3192dc3.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------------------------------------------------------------------------
 * Design tokens
 * ------------------------------------------------------------------------- */

/* Dark — the primary theme ("warm ink"). Each custom property that carries an
   oklch value is declared twice: the hex line is the fallback for engines
   without oklch support, the spec-canonical oklch line wins where valid. */
:root {
  --bg: #16140f;
  --surface: #1d1a14;
  --surface-2: #211e16;
  --surface-3: #2a261c;
  --border: rgba(236, 232, 221, 0.12);
  --border-strong: rgba(236, 232, 221, 0.25);
  --fill: rgba(236, 232, 221, 0.04);

  --text: #ece8dd;
  --text-muted: #a09a8a;
  --text-faint: #6f6a5e;

  /* Leaf = ownership, success, active nav, links. Fill (--accent) paints leaf
     surfaces (add-to-collection buttons, OWNED badges, checked boxes); text
     (--accent-text) is leaf-colored text/links/focus rings. In dark both roles
     share the same leaf; the light theme darkens only the text role so accent
     text keeps AA contrast on near-white. */
  --accent: #60bb83;
  --accent: oklch(0.72 0.12 155);
  --accent-strong: #6dc88f;
  --accent-strong: oklch(0.76 0.12 155);
  --accent-ink: #16140f;
  --accent-dim: rgba(96, 187, 131, 0.12);
  --accent-text: var(--accent);
  --accent-text-strong: var(--accent-strong);

  /* Brass = in-progress (play state), warnings, secondary highlights. */
  --brass: #d5b36a;
  --brass: oklch(0.78 0.1 85);
  --brass-dim: rgba(213, 179, 106, 0.14);

  --danger: #e08b74;
  --danger-dim: rgba(224, 139, 116, 0.1);
  --success: #60bb83;
  --success: oklch(0.72 0.12 155);

  /* Theme-dependent atmosphere: the sticky header's translucent tint,
     floating-panel shadows, table row hover. */
  --header-tint: rgba(22, 20, 15, 0.85);
  --shadow-panel: 0 12px 32px rgba(0, 0, 0, 0.45);
  --row-hover: rgba(236, 232, 221, 0.025);

  /* Native-select caret: a small filled ▾ matching the spec's dropdown marker.
     A data-URI cannot read CSS custom properties, so each theme supplies its
     own muted-tone image. */
  --select-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M0 .5h9L4.5 6z' fill='%23a09a8a'/%3E%3C/svg%3E");

  --font-display: "Newsreader", "Iowan Old Style", Georgia, serif;
  --font-body: "Public Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --radius: 6px;
  --radius-sm: 4px;
  --container: 76rem;
  --speed: 160ms;

  color-scheme: dark;
}

/* Light — the first-class pair (spec 06: dark first, light always). Same design
   language on warm paper surfaces. Selected by the bootstrap script in
   base.html, which sets data-theme from the stored preference or
   prefers-color-scheme before first paint. */
html[data-theme="light"] {
  --bg: #faf9f5;
  --surface: #ffffff;
  --surface-2: #f2f0e9;
  --surface-3: #e9e6dc;
  --border: rgba(28, 26, 21, 0.12);
  --border-strong: rgba(28, 26, 21, 0.25);
  --fill: rgba(28, 26, 21, 0.04);

  --text: #1c1a15;
  --text-muted: #6f6a5e;
  --text-faint: #8a8478;

  --accent: #267b4c;
  --accent: oklch(0.52 0.11 155);
  --accent-strong: #156f41;
  --accent-strong: oklch(0.48 0.11 155);
  --accent-ink: #faf9f5;
  --accent-dim: rgba(38, 123, 76, 0.1);
  --accent-text: var(--accent);
  --accent-text-strong: var(--accent-strong);

  /* Brass darkens well past the spec's fill value so brass *text* keeps AA
     contrast on the paper background. */
  --brass: #8a6a1e;
  --brass: oklch(0.55 0.1 85);
  --brass-dim: rgba(193, 160, 87, 0.16);

  --danger: #b3402a;
  --danger-dim: rgba(179, 64, 42, 0.08);
  --success: #267b4c;
  --success: oklch(0.52 0.11 155);

  --select-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M0 .5h9L4.5 6z' fill='%236f6a5e'/%3E%3C/svg%3E");

  --header-tint: rgba(250, 249, 245, 0.88);
  --shadow-panel: 0 12px 32px rgba(30, 26, 18, 0.14);
  --row-hover: rgba(28, 26, 21, 0.03);

  color-scheme: light;
}

/* ---------------------------------------------------------------------------
 * Reset + base
 * ------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Page & game titles: Newsreader 500, tight tracking (spec 03). */
h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(1.9rem, 1.3rem + 2.2vw, 2.5rem);
}

h2 {
  font-size: 1.5rem;
}

h3 {
  font-size: 1.15rem;
}

p {
  margin: 0 0 1rem;
}

a {
  color: var(--accent-text);
  text-decoration: none;
  transition: color var(--speed) ease;
}

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

small {
  font-size: 0.85em;
  color: var(--text-muted);
}

strong {
  color: var(--text);
}

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

::selection {
  background: var(--accent);
  color: var(--accent-ink);
}

:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Mono micro-label used for eyebrows, stat captions, table headers (spec 03:
   labels 10–11px, +8–14% tracking, often muted). */
.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--accent-text);
}

/* ---------------------------------------------------------------------------
 * Layout shell
 * ------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2.5rem);
}

main.container {
  flex: 1;
  padding-block: clamp(1.5rem, 4vw, 2.5rem) 4rem;
}

.site-footer {
  border-top: 1px solid var(--border);
  padding-block: 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-faint);
}

.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
}

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}

.footer-legal a {
  color: var(--text-faint);
}

.footer-legal a:hover {
  color: var(--accent-text);
}

/* ---------------------------------------------------------------------------
 * Header + navigation
 * ------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--header-tint);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3.6rem;
}

/* Wordmark (spec 01): always lowercase, italic Newsreader 500 for "collected",
   upright for ".games"; the ".games" takes the leaf accent, never the name.
   Never letter-spaced, never bolded beyond 500. */
.brand {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.2rem;
  letter-spacing: -0.01em;
  color: var(--text);
  white-space: nowrap;
}

.brand .tld {
  font-style: normal;
  color: var(--accent-text);
}

.brand:hover {
  color: var(--text);
  text-decoration: none;
}

/* Right side of the header: inline nav, theme toggle, mobile menu. */
.header-tools {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Icon-only header button: round ghost treatment (the spec's header controls
   are circular). The icon reflects the current theme *mode* (data-theme-mode,
   set by the bootstrap script): half-circle for auto, sun for light, moon for
   dark. */
.theme-toggle {
  width: 2.2rem;
  height: 2.2rem;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--text-muted);
}

.theme-toggle:hover {
  background: var(--fill);
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.theme-toggle svg {
  display: none;
}

html[data-theme-mode="auto"] .theme-toggle .icon-auto,
html[data-theme-mode="light"] .theme-toggle .icon-sun,
html[data-theme-mode="dark"] .theme-toggle .icon-moon {
  display: block;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Flex-center each item so the account button doesn't sit on a shifted
   baseline next to the text links. */
.nav-links li {
  display: flex;
  align-items: center;
}

.nav-links a {
  display: inline-block;
  padding: 0.45rem 0.7rem 0.35rem;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 500;
}

.nav-links a:hover {
  color: var(--text);
  text-decoration: none;
}

/* Active nav link: full text color + a leaf underline (spec 05 screens). */
.nav-links a[aria-current="page"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.nav-links form {
  margin: 0;
}

/* Account dropdown, desktop (issue #150): one avatar button gathers the
   signed-in user's session controls — email, account settings, log out —
   behind a CSS-only <details>. Mirrors the mobile .nav-menu disclosure;
   hidden below the mobile breakpoint, where the hamburger carries the same
   rows. */
.account-menu {
  position: relative;
}

/* Avatar trigger: a round ghost button matching the theme toggle, with a
   chevron that signals the menu and flips when open. */
.account-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  height: 2.2rem;
  padding: 0 0.55rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  color: var(--text-muted);
}

.account-menu > summary::-webkit-details-marker {
  display: none;
}

.account-menu > summary:hover,
.account-menu[open] > summary {
  background: var(--fill);
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.account-menu .account-caret {
  transition: transform var(--speed) ease;
}

.account-menu[open] .account-caret {
  transform: rotate(180deg);
}

.account-menu .account-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.6rem);
  /* Grow to fit the widest row (issue #190: the signed-in email) rather than
     wrapping it inside a fixed width. Clamped between a comfortable minimum and
     a cap so the panel never runs off the viewport; an over-long email gets an
     ellipsis via .account-email below. Anchored right: 0, so it expands left. */
  width: max-content;
  min-width: 14rem;
  max-width: min(26rem, calc(100vw - 2rem));
  padding: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-panel);
  list-style: none;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.account-menu .account-panel a {
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.88rem;
}

.account-menu .account-panel a:hover {
  background: var(--fill);
  text-decoration: none;
}

.account-menu .account-panel form {
  margin: 0;
}

/* The log-out control reads as a full-width menu row, sized to match the link
   rows above it rather than the compact .btn-sm pill it ships elsewhere. */
.account-menu .account-panel .btn-ghost {
  width: 100%;
  justify-content: flex-start;
  padding: 0.55rem 0.75rem;
  font-size: 0.88rem;
  border-radius: var(--radius-sm);
}

/* Account-settings row (icon + text), shared by the desktop dropdown and the
   mobile hamburger panel. */
.account-link {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

/* Signed-in email: a non-interactive caption heading the account rows. In the
   mobile hamburger panel the width is fixed, so a long email breaks onto extra
   lines (break-all) rather than overflowing the narrow screen. */
.account-email {
  padding: 0.35rem 0.75rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-faint);
  word-break: break-all;
}

/* In the desktop dropdown the panel widens to fit the email (issue #190), so
   keep it on one line; only truncate with an ellipsis once it hits the panel's
   max-width. The full address stays available via the trigger's title tooltip. */
.account-menu .account-panel .account-email {
  word-break: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Hairline separating the account rows from the nav links in the mobile panel. */
.nav-divider {
  height: 1px;
  margin: 0.35rem 0.25rem;
  background: var(--border);
}

/* Mobile menu: a CSS-only <details> disclosure that drops a panel below the
   header. Hidden on desktop, where the inline .nav-links list shows instead. */
.nav-menu {
  position: relative;
  display: none;
}

.nav-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--text);
}

.nav-menu > summary::-webkit-details-marker {
  display: none;
}

.nav-menu[open] > summary {
  background: var(--fill);
}

.nav-menu .nav-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.6rem);
  min-width: 13rem;
  padding: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-panel);
  list-style: none;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.nav-menu .nav-panel a {
  display: block;
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.88rem;
}

.nav-menu .nav-panel a:hover {
  background: var(--fill);
  text-decoration: none;
}

/* In the mobile panel the account-settings link reads as a normal row: icon +
   text. Scoped here so it wins over the .nav-panel a { display: block } above. */
.nav-menu .nav-panel .account-link {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.nav-menu .nav-panel form {
  margin: 0;
}

.nav-menu .nav-panel button {
  width: 100%;
  text-align: left;
}

@media (max-width: 720px) {
  .site-header .nav-links {
    display: none;
  }

  .nav-menu {
    display: block;
  }
}

/* ---------------------------------------------------------------------------
 * Buttons
 * ------------------------------------------------------------------------- */

button,
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.55rem 1.15rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.84rem;
  line-height: 1.3;
  cursor: pointer;
  text-decoration: none;
  transition:
    background var(--speed) ease,
    border-color var(--speed) ease,
    color var(--speed) ease,
    opacity var(--speed) ease,
    transform var(--speed) ease;
}

button:hover,
.btn:hover {
  background: var(--fill);
  border-color: var(--border-strong);
  color: var(--text);
  text-decoration: none;
}

button:active,
.btn:active {
  transform: translateY(1px);
}

/* Primary neutral (spec 04): text-color fill — the default emphasis everywhere
   except add-to-collection actions, which use the leaf .btn-accent below. */
button[type="submit"],
.btn-primary {
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
  font-weight: 600;
}

button[type="submit"]:hover,
.btn-primary:hover {
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
  opacity: 0.9;
}

/* Leaf fill (spec 04): "＋ Add to collection" and other ownership actions.
   Declared after the submit styling so it wins on submit buttons too. */
.btn-accent,
button.btn-accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

.btn-accent:hover,
button.btn-accent:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--accent-ink);
  opacity: 1;
}

/* Quiet button: hairline outline, no fill. Wins over the submit styling above
   so secondary submit-buttons (e.g. "Log out", "Remove") stay quiet. */
.btn-ghost,
button.btn-ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-muted);
  font-weight: 500;
}

.btn-ghost:hover,
button.btn-ghost:hover {
  border-color: var(--accent-text);
  color: var(--accent-text);
  background: var(--accent-dim);
  opacity: 1;
}

/* Destructive button: danger-tinted outline. Wins over the submit styling so
   destructive submit-buttons (e.g. "Delete account") read as warnings. */
.btn-danger,
button.btn-danger {
  background: transparent;
  border-color: var(--danger);
  color: var(--danger);
  font-weight: 600;
}

.btn-danger:hover,
button.btn-danger:hover {
  background: var(--danger-dim);
  border-color: var(--danger);
  color: var(--danger);
  opacity: 1;
}

.btn-sm,
button.btn-sm {
  padding: 0.28rem 0.65rem;
  font-size: 0.74rem;
  border-radius: 3px;
}

/* ---------------------------------------------------------------------------
 * Forms
 * ------------------------------------------------------------------------- */

label {
  display: block;
  margin-bottom: 1rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
}

input,
select,
textarea {
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.75rem;
  width: 100%;
  margin-top: 0.35rem;
  transition: border-color var(--speed) ease, background var(--speed) ease;
}

input::placeholder {
  color: var(--text-faint);
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent-text);
}

select {
  appearance: none;
  background-image: var(--select-caret);
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  padding-right: 2rem;
}

input[type="checkbox"] {
  width: 0.9rem;
  height: 0.9rem;
  margin: 0;
  accent-color: var(--accent);
}

/* Inline checkbox label (e.g. "Show unreleased games"). */
label.check {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  font-size: 0.82rem;
  cursor: pointer;
}

.field-error {
  display: block;
  margin: -0.5rem 0 1rem;
  color: var(--danger);
  font-size: 0.85rem;
}

.help-text {
  display: block;
  margin: -0.5rem 0 1rem;
  color: var(--text-faint);
  font-size: 0.82rem;
}

/* ---------------------------------------------------------------------------
 * Cards
 * ------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 1.75rem);
  margin-bottom: 1.5rem;
}

.card > header {
  margin-bottom: 1rem;
}

.card > footer {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

/* Narrow centered card for auth and other single-purpose forms. */
.auth-card {
  max-width: 26rem;
  margin: clamp(1rem, 6vh, 4rem) auto 0;
}

.auth-card h1 {
  font-size: 1.6rem;
}

/* Social login (issue #198): provider buttons under the auth forms. */
.social-login {
  margin: 1rem 0;
}

.social-login form {
  margin: 0 0 0.5rem;
}

.social-login button {
  width: 100%;
}

/* "── or ──" rule between the password form and the provider buttons. */
.social-login-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 1rem;
}

.social-login-divider::before,
.social-login-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--border);
}

/* Connected third-party accounts on the connections page. */
.connections-list {
  list-style: none;
  padding: 0;
}

.connections-list li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.connections-list li form {
  margin: 0 0 0 auto;
}

/* OAuth 2.1 consent screen (issue #213): the MCP/agent authorization prompt at
   /identity/o/authorize, on the shared auth-card chrome. The consent + error
   templates live in apps/accounts/templates/idp/oidc/. */
.consent-card {
  max-width: 28rem;
}

.consent-head {
  text-align: center;
  margin-bottom: 1.25rem;
}

/* The "c." app-icon mark as a centered focal point (spec 01: the italic "c."
   carries the identity at small sizes; the green period is the constant).
   Fixed ink colors — the tile is the dark app icon in both themes. */
.consent-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: 0 auto 0.9rem;
  background: #16140f;
  border: 1px solid var(--border-strong);
  border-radius: 9px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1;
  color: #ece8dd;
}

.consent-mark .tld {
  font-style: normal;
  color: #60bb83;
  color: oklch(0.72 0.12 155);
}

.consent-lede {
  margin: 0.4rem 0 0;
  color: var(--text-muted);
  font-size: 0.92rem;
}

/* Which account the grant is bound to — shown small and monospaced under the lede. */
.consent-account {
  display: block;
  margin-top: 0.35rem;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.78rem;
}

.consent-scopes {
  background: var(--fill);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1rem 1.1rem;
  margin-bottom: 1.25rem;
}

.consent-scopes-title {
  margin: 0 0 0.75rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.consent-scope-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

/* Each requested permission: a pre-checked, deselectable row. Overrides the
   block-label defaults so the checkbox sits inline with its description. */
.consent-scope {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 0;
  color: var(--text);
  font-size: 0.88rem;
  font-weight: 400;
  cursor: pointer;
}

.consent-scope input[type="checkbox"] {
  margin-top: 0.15rem;
  flex: none;
}

.consent-scope-text {
  line-height: 1.4;
}

.consent-email {
  margin-bottom: 1.25rem;
}

.consent-actions {
  display: flex;
  gap: 0.75rem;
}

.consent-actions button {
  flex: 1;
}

.consent-foot {
  margin: 1.25rem 0 0;
  text-align: center;
}

/* ---------------------------------------------------------------------------
 * Page furniture
 * ------------------------------------------------------------------------- */

.page-head {
  margin-bottom: 1.5rem;
}

.page-head h1 {
  margin-bottom: 0.25rem;
}

.page-head p {
  color: var(--text-muted);
  margin: 0;
}

/* Heading + mono note sharing one baseline (design 05.2/05.4). */
.page-head--row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
}

.page-head--row h1 {
  margin-bottom: 0;
}

/* Mono annotation beside a page title (spec 05.2: "42,793 games · built
   from Wikidata & Wikipedia"). */
.page-head .head-note {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.back-link {
  display: inline-block;
  margin-bottom: 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.back-link:hover {
  color: var(--accent-text);
}

/* Long-form legal/prose pages (Terms of Service #153, Privacy Policy #154).
 * Constrains the measure for readability and gives headings/lists comfortable
 * rhythm. */
.legal {
  max-width: 70ch;
  color: var(--text);
  line-height: 1.7;
}

.legal h2 {
  margin-top: 2rem;
  margin-bottom: 0.5rem;
  font-size: 1.2rem;
}

.legal p,
.legal ul {
  margin-bottom: 1rem;
  color: var(--text-muted);
}

.legal ul {
  padding-left: 1.25rem;
}

.legal li {
  margin-bottom: 0.4rem;
}

.legal a {
  color: var(--accent-text);
}

/* Bold inline labels (e.g. the Privacy Policy's "What we collect" list) lift
 * back to full-strength text against the muted body copy. */
.legal strong {
  color: var(--text);
}

/* Flash messages, styled like the spec's post-add toast: hairline box with a
   3px leaf lead edge (danger red for errors). */
.alert {
  display: block;
  padding: 0.7rem 1rem;
  margin-bottom: 1.25rem;
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.88rem;
}

.alert.message-success {
  border-left-color: var(--accent);
}

.alert.message-error,
.alert.alert-danger {
  border-left-color: var(--danger);
  background: var(--danger-dim);
}

.text-danger {
  color: var(--danger);
}

/* Facts rows (spec 04): mono label left, value right, hairline separators.
   Rendered from the existing <dl class="meta-list"> markup — grid places each
   dt/dd pair on one row, and both cells carry the row's bottom hairline so the
   rule runs the full width. */
.meta-list {
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  margin: 1.5rem 0 0;
  border-top: 1px solid var(--border-strong);
  max-width: 44rem;
}

.meta-list dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 0.95rem 1.5rem 0.78rem 0;
  border-bottom: 1px solid var(--border);
}

.meta-list dd {
  margin: 0;
  padding: 0.78rem 0;
  border-bottom: 1px solid var(--border);
  text-align: right;
  font-size: 0.88rem;
}

@media (max-width: 540px) {
  .meta-list {
    grid-template-columns: 1fr;
  }

  .meta-list dt {
    padding: 0.75rem 0 0.1rem;
    border-bottom: 0;
  }

  .meta-list dd {
    padding-top: 0;
    text-align: left;
  }
}

/* Two-column facts grid on the game detail page (design 05.3): label/value
   pairs flow into left and right columns with a wide gutter. */
@media (min-width: 800px) {
  .meta-list--cols {
    grid-template-columns: max-content 1fr max-content 1fr;
    max-width: none;
  }

  .meta-list--cols dd:nth-of-type(odd) {
    padding-right: 3rem;
  }

  .meta-list--cols dt:nth-of-type(even) {
    padding-left: 0;
  }
}

/* ---------------------------------------------------------------------------
 * Catalog search bar (spec 04: one line + advanced drawer) + filter bars
 * ------------------------------------------------------------------------- */

.filter-panel {
  margin-bottom: 1.5rem;
}

/* The one-line search bar: text field + filter segments + submit, separated by
   hairlines inside a single bordered row. On narrow viewports each segment
   becomes its own full-width row. */
.filter-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 0;
  margin: 0;
  overflow: hidden;
}

/* Magnifying glass + text input (spec 04). The field takes the leftover
   width; the segments and Search stay content-sized. */
.search-field {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  flex: 1 1 10rem;
  min-width: 8rem;
  padding-left: 1rem;
  color: var(--text-muted);
}

.search-field svg {
  flex: none;
}

.search-field input[type="search"] {
  flex: 1;
  min-width: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0.7rem 1rem 0.7rem 0;
  font-size: 0.88rem;
}

.search-field input[type="search"]:focus {
  background: transparent;
}

/* Filter segment (spec 04): descriptive label + current value in leaf mono +
   caret, with the native <select> stretched invisibly across the whole
   segment so interaction and accessibility stay native. */
.search-seg {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0 0.95rem;
  border-left: 1px solid var(--border);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  cursor: pointer;
}

.search-seg:focus-within {
  outline: 2px solid var(--accent-text);
  outline-offset: -2px;
}

.search-seg .seg-value {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--accent-text);
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-seg .seg-value:empty {
  display: none;
}

/* Drawer segments show a muted "any" when no value is applied (spec 04). */
.search-seg--any .seg-value:empty {
  display: inline;
}

.search-seg--any .seg-value:empty::before {
  content: "any";
  color: var(--text-faint);
}

.search-seg .seg-caret {
  font-size: 0.6rem;
  color: var(--text-muted);
}

.search-seg select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
}

.filter-row button {
  flex: none;
  border: 0;
  border-left: 1px solid var(--border);
  border-radius: 0;
  padding-inline: 1.3rem;
}

@media (max-width: 860px) {
  .filter-row {
    flex-direction: column;
    flex-wrap: nowrap;
  }

  .search-field,
  .search-seg,
  .filter-row button {
    width: 100%;
    border-left: 0;
    border-top: 1px solid var(--border);
  }

  .search-field {
    border-top: 0;
  }

  .search-seg {
    padding-block: 0.6rem;
  }
}

/* Advanced drawer (spec 04): a <details> attached under the search bar. Closed,
   the summary reads as a "＋ Advanced: …" leaf link; open, the summary hides and
   the attached row holds the long-tail filters and opt-in toggles (the drawer
   stays open whenever any of its filters is active — the template opens it
   server-side, so state is visible without a click). */
.advanced-drawer {
  margin: 0;
}

.advanced-drawer {
  position: relative;
}

.advanced-drawer > summary {
  list-style: none;
  cursor: pointer;
  display: inline-block;
  margin-top: 0.6rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--accent-text);
}

.advanced-drawer > summary::-webkit-details-marker {
  display: none;
}

.advanced-drawer > summary .drawer-open-label {
  border-bottom: 1px dotted var(--accent-text);
}

.advanced-drawer > summary .drawer-close-label {
  display: none;
  font-size: 0.74rem;
}

/* Open: the "＋ Advanced" teaser gives way to a right-aligned "Hide advanced ▴"
   sitting inside the attached row (spec 04). */
.advanced-drawer[open] > summary {
  position: absolute;
  top: 0.6rem;
  right: 1rem;
  z-index: 1;
  margin: 0;
  line-height: 2rem;
}

.advanced-drawer[open] > summary .drawer-open-label {
  display: none;
}

.advanced-drawer[open] > summary .drawer-close-label {
  display: inline;
}

/* The open drawer docks onto the search bar: the bar squares its bottom
   corners and the row carries no top border of its own. */
.filter-row:has(+ .advanced-drawer[open]) {
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}

.advanced-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--fill);
  padding: 0.4rem 8rem 0.4rem 1rem;
  min-height: 2.5rem;
}

.advanced-row .search-seg {
  border-left: 0;
  padding: 0.35rem 0;
}

.advanced-tag {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  color: var(--accent-text);
}

.advanced-row label {
  margin: 0;
}

.advanced-row .check {
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* Secondary row under the search bar: column picker, view toggle, reset. */
.filter-extras {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin-top: 0.85rem;
}

.filter-extras .spacer {
  flex: 1;
}

/* Column picker: <details> disclosure as a popover of checkboxes. */
.column-picker {
  position: relative;
}

.column-picker > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  color: var(--text-muted);
}

.column-picker > summary::-webkit-details-marker {
  display: none;
}

.column-picker > summary::after {
  content: "";
  width: 8px;
  height: 5px;
  background: currentColor;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  transition: transform var(--speed) ease;
}

.column-picker[open] > summary {
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.column-picker[open] > summary::after {
  transform: rotate(180deg);
}

/* The popover panel, styled like the spec's multi-select filter menu. */
.column-picker .picker-panel {
  position: absolute;
  left: 0;
  top: calc(100% + 0.5rem);
  z-index: 30;
  min-width: 13rem;
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-panel);
}

.column-picker .picker-panel label.check {
  display: flex;
  margin-bottom: 0.45rem;
}

.column-picker .picker-panel label.check:last-child {
  margin-bottom: 0;
}

/* "Reset columns to default" link at the bottom of the picker (issue #174). */
.column-picker .picker-panel .picker-reset {
  display: block;
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  text-align: center;
  white-space: nowrap;
}

/* The column picker configures the list table's columns, so it is meaningless in
   grid mode — hide it whenever the grid radio is checked (the mirror image of the
   grid-only Sort menu, which the swapped results partial omits in list mode). The
   picker sits in the static filter form OUTSIDE the HTMX swap region, so a template
   conditional can't re-evaluate it on a live toggle; keying off the swapped
   toolbar's data-view attribute covers first paint, live swaps, and the no-JS
   path alike. Scoped to .filter-extras so the toolbar's Sort menu (also a
   .column-picker) is unaffected, and display:none (not removal) keeps the column
   checkboxes serializing so the selection survives a round-trip through grid
   mode. */
main:has(.results-toolbar[data-view="grid"]) .filter-extras > .column-picker {
  display: none;
}

/* List/grid view toggle (issue #240): radios styled as the spec's mono segmented
   control (GRID | LIST). The inputs stay in the document (visually hidden, not
   display:none) so they remain focusable and the form still serializes the
   checked mode. */
.view-toggle {
  display: inline-flex;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.view-toggle .seg {
  position: relative;
  margin: 0; /* cancel the global label margin-bottom */
}

.view-toggle .seg input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.view-toggle .seg span {
  display: block;
  padding: 0.3rem 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  transition:
    background var(--speed) ease,
    color var(--speed) ease;
}

.view-toggle .seg + .seg span {
  border-left: 1px solid var(--border);
}

.view-toggle .seg input:checked + span {
  background: var(--border);
  color: var(--text);
}

.view-toggle .seg input:focus-visible + span {
  outline: 2px solid var(--accent-text);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
 * Results toolbar + tables + pagination
 * ------------------------------------------------------------------------- */

.results-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.85rem;
}

.results-toolbar > p {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.results-toolbar select {
  width: auto;
  margin: 0;
  border: 0;
  padding-block: 0.3rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  background-color: transparent;
}

/* Right-hand toolbar cluster: the grid-mode Sort menu + the page-size selector. */
.toolbar-controls {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* "Sort" menu (issue #240): quiet text control — "Sort Title A–Z ▾" — whose
   <details> popover lists the sort links (design 05.2). */
.sort-picker > summary {
  border: 0;
  padding: 0.3rem 0;
  color: var(--text-muted);
}

.sort-picker .sort-current {
  color: var(--text);
}

.sort-picker .picker-panel {
  left: auto;
  right: 0;
  min-width: 10.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.sort-picker .picker-panel a {
  font-size: 0.82rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.sort-picker .picker-panel a:hover {
  color: var(--accent-text);
  text-decoration: none;
}

.sort-picker .picker-panel a[aria-current="true"] {
  color: var(--accent-text);
  font-weight: 600;
}

/* Results table: hairlines only — no surrounding box (spec 06: hairlines over
   boxes, alignment over decoration). */
.table-wrap {
  overflow-x: auto;
  border-top: 1px solid var(--border-strong);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

thead th {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  text-align: left;
  color: var(--text-muted);
  padding: 0.75rem 0.9rem 0.75rem 0;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

/* Sort-header links fill the whole header cell so a click anywhere on the
   column header hits the link — the mono header text is narrow, and a
   text-width anchor would leave most of the cell inert. */
thead th a {
  display: block;
  color: inherit;
}

thead th a:hover {
  color: var(--accent-text);
  text-decoration: none;
}

thead th[aria-sort="ascending"] a,
thead th[aria-sort="descending"] a {
  color: var(--accent-text);
}

tbody td {
  padding: 0.68rem 0.9rem 0.68rem 0;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

tbody tr {
  transition: background var(--speed) ease;
}

tbody tr:hover {
  background: var(--row-hover);
}

tbody td a {
  color: var(--text);
  font-weight: 500;
}

tbody td a:hover {
  color: var(--accent-text);
}

/* Physical/digital availability badge in the catalog's Availability column. */
.availability-badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  white-space: nowrap;
}

/* No distribution-format evidence (issue #222): visually de-emphasized so an
   honest "we don't know" reads differently from an evidence-backed format. */
.availability-badge--unknown {
  border-style: dashed;
  color: var(--text-faint);
}

/* Release-class badge next to the title (issue #249): marks a homebrew/unlicensed/
   aftermarket row when the "Show homebrew" opt-in reveals the otherwise-hidden tail.
   Subdued by default; the homebrew variant takes the brass tint (spec 02: brass =
   secondary highlights/warnings). */
.release-class-badge {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  white-space: nowrap;
  vertical-align: middle;
}

.release-class-badge--homebrew,
.release-class-badge--aftermarket_repro {
  border-color: var(--brass);
  color: var(--brass);
}

/* Copy-condition badge next to a collection entry's title (issues #226/#229):
   a subdued pill marking loose / complete (CIB) / sealed copies in the list. */
.condition-badge {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  white-space: nowrap;
  vertical-align: middle;
}

/* "Collector details" disclosure on the entry edit form (issues #226/#229). */
.collector-details {
  margin: 0.5rem 0 1rem;
}

.collector-details > summary {
  cursor: pointer;
  color: var(--text-muted);
  font-size: 0.9rem;
}

/* Inline action cluster inside table cells (Edit / Remove …). */
.row-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}

.row-actions form {
  display: inline;
  margin: 0;
}

/* Pagination: bare mono line — "← Previous · Page 1 of 12 · Next →" (spec 05.2:
   no boxes). */
.pagination ul {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  list-style: none;
  margin: 1.75rem 0 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--text-muted);
}

.pagination a {
  display: inline-block;
  padding: 0.25rem 0.2rem;
  color: var(--text-muted);
}

.pagination a:hover {
  color: var(--accent-text);
  text-decoration: none;
}

.empty-state {
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--text-muted);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}

/* BYO-LLM guidance panel (#234): the empty-state shell with readable,
 * left-aligned steps for the agent workflow. */
.byo-guidance {
  text-align: left;
  max-width: 46rem;
}

.byo-guidance h2 {
  margin-top: 0;
}

.byo-steps {
  margin: 0.75rem 0;
  padding-left: 1.4rem;
  display: grid;
  gap: 0.5rem;
}

/* ---------------------------------------------------------------------------
 * Quick-add control (catalog rows + game detail) + status pills
 * ------------------------------------------------------------------------- */

.quick-add {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  white-space: nowrap;
}

.quick-add-form {
  display: inline;
  margin: 0;
}

/* Tracked state ("In collection ✓" / "On wishlist ✓"): leaf, in the mono data
   voice (spec 04: statuses and play state render in mono; leaf = ownership). */
.quick-add-done {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--accent-text);
}

.quick-add-done a {
  font-size: 0.9em;
}

/* The shared control rendered above the add-to-collection form on the game
   detail page needs block spacing it doesn't get inside a table cell. */
.card .quick-add {
  margin-bottom: 1.1rem;
}

/* Status badges (spec 04): OWNED = leaf fill; WISHLIST = outlined on a dim ink
   scrim so it stays readable over cover art. Fixed colors — the scrim reads
   the same over covers in both themes. */
.status-pill {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 3px;
  padding: 0.15rem 0.4rem;
  background: var(--accent);
  color: var(--accent-ink);
  white-space: nowrap;
}

.status-pill--wishlist {
  background: rgba(22, 20, 15, 0.8);
  color: #ece8dd;
  border: 1px solid rgba(236, 232, 221, 0.35);
}

/* Cover overlay placement on browse cards. */
.cover-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
}

/* ---------------------------------------------------------------------------
 * Genre / metadata chips (game detail)
 * ------------------------------------------------------------------------- */

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
}

.chip {
  display: inline-block;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-size: 0.74rem;
  color: var(--text);
  white-space: nowrap;
}

/* Brass chip: availability / play-state highlights ("Physical only"). */
.chip--brass {
  border-color: var(--brass);
  color: var(--brass);
}

/* ---------------------------------------------------------------------------
 * Home / landing page (spec 05.1)
 * ------------------------------------------------------------------------- */

.hero-grid {
  display: grid;
  grid-template-columns: 1fr minmax(0, 23rem);
  gap: clamp(2rem, 5vw, 3rem);
  align-items: center;
  padding-block: clamp(2rem, 6vw, 4rem) clamp(2rem, 5vw, 3rem);
}

@media (max-width: 860px) {
  .hero-grid {
    grid-template-columns: 1fr;
  }
}

.hero .eyebrow {
  margin-bottom: 1.1rem;
  letter-spacing: 0.14em;
}

.hero h1 {
  font-size: clamp(2.4rem, 1.4rem + 4.5vw, 3.4rem);
  line-height: 1.05;
  margin-bottom: 1.25rem;
}

.hero h1 .accent {
  font-style: italic;
  color: var(--accent-text);
}

.hero .lede {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 30rem;
  margin-bottom: 1.75rem;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* The librarian panel (spec 05.1): a worked example of the BYO-LLM workflow
   (#234) — the user's own assistant filing games over the API/MCP server.
   Illustrative markup, not live data. */
.librarian-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.35rem;
  box-shadow: var(--shadow-panel);
}

.librarian-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}

.librarian-head::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

.librarian-sub {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  color: var(--text-faint);
  margin: 0.3rem 0 0.9rem;
}

.librarian-rows {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.librarian-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem;
  background: var(--fill);
  border-radius: var(--radius-sm);
}

/* Tiny cover placeholder: the spec's diagonal-stripe stand-in. */
.librarian-row .mini-cover {
  flex: none;
  width: 30px;
  height: 40px;
  border-radius: 2px;
  background: repeating-linear-gradient(
    45deg,
    var(--border) 0 5px,
    var(--fill) 5px 10px
  );
}

.librarian-row .row-title {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
}

.librarian-row .row-note {
  display: block;
  font-size: 0.72rem;
  color: var(--text-muted);
}

.librarian-row .row-verb {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.04em;
  color: var(--accent-text);
}

.librarian-row .row-verb--brass {
  color: var(--brass);
}

.works-with {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  color: var(--text-faint);
}

.works-with .mcp-chip {
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.1rem 0.45rem;
  color: var(--text-muted);
}

/* Staggered load-in for the hero (motion-safe only). */
@media (prefers-reduced-motion: no-preference) {
  .hero > * {
    animation: rise 0.55s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }

  .hero > *:nth-child(2) {
    animation-delay: 70ms;
  }

  .hero > *:nth-child(3) {
    animation-delay: 140ms;
  }

  .hero > *:nth-child(4) {
    animation-delay: 210ms;
  }
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* Stat strip (spec 05.1): hairline rules above and below, Newsreader numerals,
   mono captions — no boxes. */
.stat-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--border);
  border-block: 1px solid var(--border-strong);
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
}

@media (max-width: 640px) {
  .stat-strip {
    grid-template-columns: 1fr;
  }
}

.stat {
  background: var(--bg);
  padding: 1.1rem 1.5rem;
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.stat .stat-value {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 500;
  line-height: 1.1;
  color: var(--text);
}

/* The personal stat ("in your collection") takes the leaf. */
.stat--mine .stat-value {
  color: var(--accent-text);
}

.stat .stat-label {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}

/* Collection stats dashboard: muted card sub-heading + distribution bars. */

.card-note {
  margin: 0.35rem 0 0;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.dist-cell {
  width: 45%;
  min-width: 10rem;
}

.dist-bar {
  display: inline-block;
  vertical-align: middle;
  width: calc(100% - 4rem);
  height: 0.4rem;
  margin-right: 0.6rem;
  background: var(--surface-3);
  border-radius: 999px;
  overflow: hidden;
}

.dist-bar span {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: inherit;
}

.home-section {
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
}

.home-section > .section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.home-section > .section-head h2 {
  margin: 0;
}

.home-section > .section-head a {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
}

/* Recently-added strip on home: quiet hairline tiles. */
.game-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
  gap: 0.9rem;
}

.game-card {
  display: block;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  color: var(--text);
  transition:
    border-color var(--speed) ease,
    background var(--speed) ease;
}

.game-card:hover {
  border-color: var(--border-strong);
  background: var(--fill);
  text-decoration: none;
  color: var(--text);
}

.game-card .game-title {
  display: block;
  font-weight: 600;
  font-size: 0.85rem;
  line-height: 1.35;
  margin-bottom: 0.3rem;
}

.game-card .game-meta {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
 * Game detail (spec 05.3)
 * ------------------------------------------------------------------------- */

/* The cover stretches to the info column's height so its bottom edge aligns
   with the Add/Wishlist row (design 05.3); object-fit/slice absorb the small
   aspect difference. min-height keeps a sparse page from squashing it. */
.detail-head {
  display: grid;
  grid-template-columns: minmax(9rem, 12rem) 1fr;
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
  min-height: 15rem;
  margin-bottom: 1rem;
}

.detail-head > .game-cover {
  height: 100%;
  max-width: none;
  aspect-ratio: auto;
  object-fit: cover;
}

@media (max-width: 600px) {
  .detail-head {
    grid-template-columns: 1fr;
  }
}

.detail-head h1 {
  margin-bottom: 0.35rem;
}

/* Mono release line under the title: "RELEASED JULY 21, 1995 · VIRTUAL BOY". */
.release-line {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 1rem;
}

.detail-head .description {
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--text-muted);
  max-width: 38rem;
}

.detail-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.65rem;
  margin-top: 1.25rem;
}

/* The shared quick-add control renders compact (.btn-sm) for table rows and
   cards; the detail header promotes it to full-size primary actions. */
.detail-actions .quick-add {
  gap: 0.65rem;
  font-size: 0.84rem;
}

.detail-actions .quick-add button {
  padding: 0.55rem 1.15rem;
  font-size: 0.84rem;
  border-radius: var(--radius-sm);
}

/* Tracked state in the header: a leaf-tinted pill in the data voice. */
.detail-actions .quick-add-done {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-sm);
  background: var(--accent-dim);
  font-size: 0.76rem;
}

/* Source links (facts SOURCES row): external references, in leaf. */
.source-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  font-size: 0.85rem;
}

/* Right-align inside the facts ledger (flex ignores text-align). */
.meta-list .source-links {
  justify-content: flex-end;
}

@media (max-width: 540px) {
  .meta-list .source-links {
    justify-content: flex-start;
  }
}

/* The optional add-details form on the detail page sits behind a quiet
   disclosure — adding is one click; details are optional metadata (spec 06). */
.add-details {
  margin-top: 1.5rem;
  max-width: 30rem;
}

.add-details > summary {
  list-style: none;
  cursor: pointer;
  display: inline-block;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent-text);
}

.add-details > summary::-webkit-details-marker {
  display: none;
}

.add-details > summary::before {
  content: "＋ ";
}

.add-details[open] > summary::before {
  content: "－ ";
}

.add-details form {
  margin-top: 1rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--fill);
}

/* ---------------------------------------------------------------------------
 * HTMX swap polish + motion preferences
 * ------------------------------------------------------------------------- */

#catalog-results.htmx-swapping,
#collection-results.htmx-swapping {
  opacity: 0.4;
  transition: opacity 120ms ease;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/*
 * Game cover art on detail pages (spec 180).
 *
 * A fixed aspect ratio + object-fit keeps the footprint identical whether a real
 * cover or the neutral placeholder is shown, so the layout never shifts between
 * games with and without art. The image is capped so a large source image cannot
 * dominate the page, and aspect ratio is preserved (never stretched/cropped).
 */
.game-cover {
  display: block;
  width: 100%;
  max-width: 12rem;
  aspect-ratio: 3 / 4;
  height: auto;
  object-fit: contain;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* ---------------------------------------------------------------------------
 * Cover-art browse grid (issue #240) — the catalog/collection "Grid" view
 * ------------------------------------------------------------------------- */

/* Fixed column counts at breakpoints (issue #264), not auto-fill: every count
   (2/3/4/6) divides the grid page sizes (24/48/96), so each page renders only
   complete rows at any viewport width — no orphan one-card last row. Keep the
   counts and the page sizes in apps/catalog/pagination.py in sync. */
.browse-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 0.9rem;
}

@media (min-width: 40rem) {
  .browse-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 56rem) {
  .browse-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (min-width: 75rem) {
  .browse-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

/* Spec game card (04): no surrounding box — the cover carries a hairline edge,
   title and mono meta sit below, actions pin to the bottom (hairlines over
   boxes; the art supplies the color). */
.browse-card {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* Cover wrapper: positioning context for the status pill overlay. */
.browse-card .browse-card-cover {
  position: relative;
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

/* The shared cover partial is sized for the detail page (capped width, contain).
   Inside a card it fills the full card width edge to edge; object-fit: cover
   keeps the 3:4 tiles uniform across covers whose source ratios vary slightly
   (IGDB art is 3:4 already, Commons art is close). */
.browse-card .game-cover {
  max-width: none;
  margin: 0;
  object-fit: cover;
  transition: border-color var(--speed) ease;
}

.browse-card:hover .game-cover {
  border-color: var(--border-strong);
}

.browse-card .browse-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 0.3rem;
}

.browse-card .browse-card-title {
  font-weight: 600;
  font-size: 0.85rem;
  line-height: 1.3;
  color: var(--text);
}

.browse-card .browse-card-title:hover {
  color: var(--accent-text);
  text-decoration: none;
}

.browse-card .browse-card-meta {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

/* Pin the quick-add / Edit-Remove cluster to the card's bottom edge so action
   rows align across a row of cards with different title lengths. */
.browse-card .browse-card-actions {
  margin-top: auto;
  padding-top: 0.45rem;
}

/* The shared quick-add and Edit/Remove controls are laid out for a table cell
   (nowrap); let them wrap inside a narrow card instead of overflowing. */
.browse-card .quick-add,
.browse-card .row-actions {
  flex-wrap: wrap;
  white-space: normal;
}

/* Card actions span the card: Add and Wishlist split the row (design 05.2). */
.browse-card .quick-add {
  width: 100%;
  gap: 0.4rem;
}

.browse-card .quick-add .quick-add-form {
  flex: 1 1 0;
  display: block;
}

.browse-card .quick-add .quick-add-form button {
  width: 100%;
}

/* Tracked state reads as one full-width quiet pill: "In collection ✓ Manage". */
.browse-card .quick-add-done {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  padding: 0.32rem 0.5rem;
  color: var(--text-muted);
}

/* Owned covers take a leaf outline as well as the badge (design 05.2). */
.browse-card-cover--owned .game-cover {
  border-color: var(--accent);
}

/* ---------------------------------------------------------------------------
 * Recommendations page (issues #21 + #22)
 * ------------------------------------------------------------------------- */

.recs-trigger {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.5rem;
}

/* htmx ships the .htmx-indicator visibility toggling; this just styles the note. */
.recs-loading {
  color: var(--text-muted);
  font-size: 0.9rem;
}

.recs-meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
  color: var(--text-muted);
  font-size: 0.82rem;
}

.recs-badge {
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
}

/* Brass: the deterministic fallback ranking is a "heads-up" state (spec 02:
   brass = warnings/secondary highlights). */
.recs-badge-fallback {
  color: var(--brass);
  border-color: var(--brass);
}

.recs-badge-agent {
  color: var(--accent-text);
}

.recs-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.9rem;
}

.recs-card {
  display: flex;
  gap: 1rem;
  padding: 1rem 1.2rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.recs-rank {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--text-muted);
  min-width: 2rem;
  text-align: center;
}

.recs-body {
  flex: 1;
  min-width: 0;
}

.recs-title-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

.recs-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
}

.recs-score {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--accent-text);
}

.recs-genres {
  margin: 0.15rem 0 0;
  color: var(--text-muted);
  font-size: 0.8rem;
}

.recs-rationale {
  margin: 0.5rem 0 0.75rem;
}

.recs-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.recs-feedback {
  display: inline-flex;
  gap: 0.25rem;
}

.recs-feedback form {
  display: inline;
  margin: 0;
}

.recs-thumb {
  filter: grayscale(1);
  opacity: 0.65;
}

.recs-thumb:hover {
  opacity: 1;
}

.recs-thumb-active {
  filter: none;
  opacity: 1;
  border-color: var(--accent-text);
}

.recs-error {
  padding: 1rem 1.2rem;
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  color: var(--danger);
}

.recs-error p {
  margin: 0.35rem 0 0;
  color: var(--text);
}
