:root {
  color-scheme: light dark;
  --bg: #f6f7fb;
  --surface: #ffffff;
  --surface-alt: #eef0f5;
  --surface-raised: #ffffff;
  --border: #e0e3ea;
  --border-strong: #cbd0db;
  --text: #14161c;
  --muted: #666c7a;
  --accent: #3652d9;
  --accent-hover: #2a41b8;
  --accent-soft: #e8ecff;
  --accent-text-on: #ffffff;
  --fragile: #c0562a;
  --presumed: #96791a;
  --success-bg: #e3f6e6;
  --success-text: #197234;
  --success-border: #a9dfb4;
  --danger-bg: #fbe4e4;
  --danger-text: #a3232f;
  --danger-border: #f0b3b8;

  /* type scale - 1.2 ratio, rounded to clean px-equivalents */
  --text-xs: 0.72rem;
  --text-sm: 0.8125rem;
  --text-base: 0.875rem;
  --text-md: 0.95rem;
  --text-lg: 1.125rem;
  --text-xl: 1.45rem;
  --text-2xl: 2.1rem;
  --font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, system-ui, sans-serif;

  /* spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2.25rem;

  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(20, 24, 33, 0.05);
  --shadow: 0 1px 2px rgba(20, 24, 33, 0.04), 0 4px 16px rgba(20, 24, 33, 0.07);
  --shadow-lg: 0 4px 10px rgba(20, 24, 33, 0.06), 0 16px 40px rgba(20, 24, 33, 0.12);
  --focus-ring: 0 0 0 3px var(--accent-soft);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101217;
    --surface: #1a1d25;
    --surface-alt: #21252f;
    --surface-raised: #232733;
    --border: #2e323e;
    --border-strong: #3c4152;
    --text: #edeef2;
    --muted: #9298a8;
    --accent: #7c95ff;
    --accent-hover: #97acff;
    --accent-soft: #262c4d;
    --accent-text-on: #101217;
    --fragile: #e6935f;
    --presumed: #e0c15c;
    --success-bg: #15301f;
    --success-text: #7fe0a0;
    --success-border: #2b5e3c;
    --danger-bg: #391c1f;
    --danger-text: #f3a3ab;
    --danger-border: #6b3138;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.28), 0 8px 24px rgba(0, 0, 0, 0.38);
    --shadow-lg: 0 6px 16px rgba(0, 0, 0, 0.35), 0 24px 56px rgba(0, 0, 0, 0.5);
  }
}

* { box-sizing: border-box; }

html { scrollbar-color: var(--border-strong) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

body {
  font-family: var(--font-display);
  margin: 0;
  font-size: var(--text-base);
  background:
    radial-gradient(1100px 500px at 15% -10%, color-mix(in srgb, var(--accent) 7%, transparent), transparent 60%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); text-underline-offset: 0.15em; }

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(10px) saturate(1.4);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  border-bottom: 1px solid var(--border);
}

nav {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: 0.7rem var(--space-5);
  max-width: 1400px;
  margin: 0 auto;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: var(--text-md);
  letter-spacing: -0.01em;
  font-weight: 600;
}
.brand-mark {
  color: var(--accent);
  display: inline-flex;
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--accent) 55%, transparent));
  transition: transform 0.2s ease;
}
.brand:hover .brand-mark { transform: rotate(90deg); }

.nav-links { display: flex; gap: var(--space-1); flex-wrap: wrap; }
.nav-links a {
  position: relative;
  text-decoration: none;
  color: var(--muted);
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius-sm);
  font-weight: 500;
  font-size: var(--text-sm);
  transition: background 0.15s ease, color 0.15s ease;
}
.nav-links a:hover { background: var(--surface-alt); color: var(--text); }
.nav-links a.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

.nav-right { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }
.nav-right a { text-decoration: none; font-weight: 500; font-size: var(--text-sm); }
.nav-right a:hover { color: var(--accent-hover); }

/* --- burger menu (narrow viewports only, see the nav media query below) ---
   CSS-only toggle: a clipped checkbox in base.html drives the panel through
   `:checked ~`. The checkbox is clipped rather than display:none so it keeps
   its place in the tab order - a display:none control is unreachable by
   keyboard, which would make the whole nav keyboard-inaccessible on a phone. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.nav-toggle { position: absolute; opacity: 0; width: 1px; height: 1px; }
.nav-toggle, .burger { display: none; }
.burger {
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  margin-left: auto;
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s ease;
}
.burger:hover { background: var(--surface-alt); }
.nav-toggle:focus-visible + .burger { outline: none; box-shadow: var(--focus-ring); }
.burger-bars { position: relative; }
.burger-bars,
.burger-bars::before,
.burger-bars::after {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s ease, background 0.2s ease;
}
.burger-bars::before, .burger-bars::after { content: ""; position: absolute; left: 0; }
.burger-bars::before { transform: translateY(-6px); }
.burger-bars::after { transform: translateY(6px); }
/* open state: the three bars fold into a close cross */
.nav-toggle:checked + .burger .burger-bars { background: transparent; }
.nav-toggle:checked + .burger .burger-bars::before { transform: rotate(45deg); }
.nav-toggle:checked + .burger .burger-bars::after { transform: rotate(-45deg); }

/* The whole nav - page links AND the account/language row - collapses behind
   the burger. The breakpoint is deliberately far wider than the 640px used
   for page content further down, and it is measured, not guessed: holding the
   links plus the lang switch, role badge and logout on ONE line needs 1029px
   for a viewer and 1161px for an editor in French (1025 / 1081 in English -
   French is the long one). Below that the account row drops onto a second
   line under the links, stranded against the right edge - the layout this
   replaces. So the burger takes over at 1160px, i.e. everything up to and
   including a tablet in landscape or a half-width laptop window; a
   full-screen laptop keeps the bar it has today. */
@media (max-width: 1160px) {
  /* `header nav`, not `nav`: .pagination is a <nav> too (sales.html,
     pop_crossing.html...) and a bare selector would gap that as well. */
  header nav { row-gap: var(--space-3); }
  .nav-toggle { display: block; }
  .burger { display: flex; }
  .nav-links, .nav-right { display: none; }
  /* flex-basis:100% + the nav's flex-wrap puts each open panel on its own
     full-width row, expanding the sticky header downwards. No absolute
     positioning: nav has no positioning context of its own. */
  .nav-toggle:checked ~ .nav-links,
  .nav-toggle:checked ~ .nav-right {
    display: flex;
    flex: 0 0 100%;
    margin-left: 0;
  }
  .nav-toggle:checked ~ .nav-links {
    flex-direction: column;
    gap: 2px;
    /* a very long menu (editor role, small phone) scrolls inside the header
       instead of pushing the page content off-screen */
    max-height: calc(100vh - 10rem);
    overflow-y: auto;
  }
  /* Full-width rows, taller than the desktop links - these are thumb targets
     now, not hover targets. */
  .nav-toggle:checked ~ .nav-links a {
    padding: 0.6rem 0.7rem;
    font-size: var(--text-base);
  }
  .nav-toggle:checked ~ .nav-right {
    flex-wrap: wrap;
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
  }
}

.lang-switch {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
  font-size: var(--text-xs);
  font-weight: 700;
  background: var(--surface-alt);
}
.lang-switch a {
  padding: 0.25rem 0.6rem;
  text-decoration: none;
  color: var(--muted);
  transition: background 0.15s ease, color 0.15s ease;
}
.lang-switch a.active { background: var(--accent); color: var(--accent-text-on); }
.lang-switch a:not(.active):hover { background: var(--border); color: var(--text); }

.badge {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.18rem 0.7rem;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--muted);
}
.role-badge.role-admin { color: var(--success-text); background: var(--success-bg); border-color: var(--success-border); }
.role-badge.role-manager { color: var(--accent); background: var(--accent-soft); border-color: var(--accent); }
.role-badge.role-viewer { color: var(--muted); background: var(--surface-alt); border-color: var(--border); }

main { padding: var(--space-5); max-width: 1400px; margin: 0 auto; }

h1 { font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--space-4); }
h2 { font-size: var(--text-lg); font-weight: 650; margin-top: var(--space-6); margin-bottom: var(--space-3); letter-spacing: -0.01em; }

form.filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  padding: var(--space-3);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
form.filters input[type="text"],
form.filters input[type="date"],
form.filters select {
  padding: 0.42rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: inherit;
  font-size: var(--text-sm);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
form.filters input[type="text"]:focus,
form.filters input[type="date"]:focus,
form.filters select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}
form.filters button {
  padding: 0.42rem 1.1rem;
  cursor: pointer;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-text-on);
  font-weight: 600;
  font-size: var(--text-sm);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;
}
form.filters button:hover { background: var(--accent-hover); border-color: var(--accent-hover); box-shadow: var(--shadow-sm); }
form.filters button:active { transform: translateY(1px); box-shadow: none; }
form.filters a { font-size: var(--text-sm); color: var(--muted); text-decoration: none; }
form.filters a:hover { color: var(--accent); text-decoration: underline; }
form.filters label { font-size: var(--text-sm); display: inline-flex; align-items: center; gap: 0.3rem; }

.count { color: var(--muted); margin-bottom: var(--space-3); font-size: var(--text-sm); }

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin: var(--space-2) 0 var(--space-5);
  font-size: var(--text-sm);
}
.pagination a {
  text-decoration: none;
  font-weight: 600;
  color: var(--accent);
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius-sm);
  transition: background 0.15s ease;
}
.pagination a:hover { background: var(--accent-soft); }
.pagination-disabled { color: var(--muted); opacity: 0.5; padding: 0.3rem 0.6rem; }
.pagination-status { color: var(--muted); font-variant-numeric: tabular-nums; }

.table-scroll {
  overflow-x: auto;
  margin-bottom: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  background: var(--surface);
}
table { border-collapse: collapse; width: 100%; min-width: 900px; }
th, td { border-bottom: 1px solid var(--border); padding: 0.65rem 0.75rem; text-align: left; vertical-align: top; }
th {
  background: var(--surface-alt);
  position: sticky;
  top: 0;
  white-space: nowrap;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  font-weight: 700;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr { transition: background 0.1s ease; }
tbody tr:hover { background: var(--surface-alt); }
tbody tr:nth-child(even) { background: color-mix(in srgb, var(--surface-alt) 45%, transparent); }
tbody tr:nth-child(even):hover { background: var(--surface-alt); }

/* --- one card component, three tables: /sales, /pop and /pop/crossing each
   render BOTH a table and this list, and style.css picks one (the swap lives
   in the 640px block at the bottom of this file). Tokens are the feed card's,
   so every card on the site reads as the same component family.
   Not every part is used by every page - /pop has no price, /sales has no
   stats grid - which is the point of keeping it one vocabulary. */
.data-cards { display: none; list-style: none; margin: 0 0 var(--space-5); padding: 0; }
.data-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: var(--space-3);
}
.data-card-head { display: flex; align-items: flex-start; gap: var(--space-2); }
/* overflow-wrap, not ellipsis: a card name is the one thing on the card the
   reader is scanning for, so a long one wraps rather than being cut. */
.data-card-name {
  flex: 1;
  min-width: 0;
  font-size: var(--text-md);
  font-weight: 650;
  line-height: 1.3;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.data-card-head .share-icon-btn { flex: none; margin-top: -0.15rem; }
/* Set, rarity, edition, grade, region - the four table columns that qualify
   WHICH printing this price is for, on one wrapping line separated by dots. */
.data-card-attrs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem var(--space-2);
  font-size: var(--text-sm);
  color: var(--muted);
}
/* Separator trails its item (::after) rather than leading the next one: when
   the line wraps, the dot ends the previous line instead of dangling at the
   start of the new one. */
.data-card-attrs > span:not(:last-child)::after { content: "·"; margin-left: var(--space-2); color: var(--border-strong); }
.data-card-set { font-weight: 650; color: var(--text); }
.data-card-attrs .edition-tag, .data-card-attrs .tag { margin: 0; }
.data-card-price { font-size: var(--text-md); font-weight: 650; margin-top: var(--space-1); }
/* the conversion line only - not the 130pts tags, which keep their own weight */
.data-card-price > div.small { font-weight: 500; }
.data-card-price br + .pts130-tag { display: inline-block; margin-top: 0.15rem; }
/* The raw marketplace title is context for the parsed name above it, not a
   heading - two lines at most, clamped. */
.data-card-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.data-card-internal { display: flex; flex-wrap: wrap; gap: 0.1rem var(--space-3); }
/* Numeric columns that only mean something next to their header - pop counts
   per grader, a crossing row's sales/population/ratio. A two-column dl keeps
   the header attached to its figure, which a dot-separated line cannot. */
.data-card-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3);
  margin: var(--space-1) 0 0;
}
.data-card-stats > div { min-width: 0; }
/* .data-card-price-label rides the same rule: on /pop/crossing the big figure
   is a MEDIAN in dollars, which the number alone does not say. */
.data-card-stats dt,
.data-card-price-label {
  display: block;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  font-weight: 700;
}
.data-card-stats dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.data-card-stats .wide { grid-column: 1 / -1; }
/* fragile / region-source markers, the crossing table's "notes" column */
.data-card-tags { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
.data-card-tags .tag { margin: 0; }
.data-card-foot {
  margin-top: var(--space-1);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: var(--text-xs);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.thumb {
  max-width: 90px;
  max-height: 90px;
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.thumb:hover { transform: scale(1.06); box-shadow: var(--shadow-lg); border-color: var(--border-strong); position: relative; z-index: 5; }
.thumb-wrap { display: flex; gap: var(--space-2); }
.no-shot {
  color: var(--muted);
  font-style: italic;
  font-size: var(--text-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 90px;
  height: 56px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  text-align: center;
  padding: 0 var(--space-2);
}

.tag {
  display: inline-block;
  padding: 0.12rem 0.6rem;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 650;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  margin: 0 0.2rem 0.2rem 0;
}
.tag.fragile { color: var(--fragile); border-color: var(--fragile); background: transparent; }
.tag.presumed { color: var(--presumed); border-color: var(--presumed); background: transparent; }
.tag.read { color: var(--success-text); border-color: var(--success-border); background: transparent; }

.card-name-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.card-name-row h1 { margin: 0; }
.share-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 600;
  text-decoration: none;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.share-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.share-btn svg { flex-shrink: 0; }

.share-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: var(--radius);
  color: var(--muted);
  text-decoration: none;
  transition: color 0.15s ease, background 0.15s ease;
}
.share-icon-btn:hover { color: var(--accent); background: var(--accent-soft); }

.note { color: var(--muted); font-size: var(--text-sm); max-width: 320px; }
.small { font-size: var(--text-sm); color: var(--muted); }
.empty {
  color: var(--muted);
  font-style: italic;
  padding: var(--space-6);
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

.candidates { font-size: var(--text-sm); }
.candidates strong {
  display: inline-block;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  font-weight: 700;
  margin-top: var(--space-2);
}
.candidates ul { margin: 0.2rem 0 0.4rem; padding-left: 1.1rem; }
.candidates li.would-accept { font-weight: 700; color: var(--success-text); }
.candidates li { margin-bottom: 0.2rem; }

.diff-pos, .diff-neg { font-weight: 700; padding: 0.05rem 0.35rem; border-radius: 4px; }
.diff-pos { color: var(--success-text); background: var(--success-bg); }
.diff-neg { color: var(--danger-text); background: var(--danger-bg); }

/* Plain colored text (no badge box) - price vs the last comparable sale
   of the same card/grade/rarity/region, see queries_sales.py's
   _price_trend_lookup. "similar" (within +/-3%) stays the default text
   color rather than an explicit "white", so it still reads correctly in
   light mode. */
.price-up { color: var(--success-text); font-weight: 650; }
.price-down { color: var(--danger-text); font-weight: 650; }

.pts130-tag { display: inline-block; padding: 0.08rem 0.45rem; border-radius: 4px; margin: 0.15rem 0.2rem 0.15rem 0; font-weight: 600; }
.pts130-confirmed { color: var(--success-text); background: var(--success-bg); }
.pts130-unconfirmed { color: var(--muted); background: var(--surface-alt); }

.flash { padding: 0.7rem 1rem; border-radius: var(--radius-sm); margin-bottom: var(--space-4); font-weight: 600; font-size: var(--text-md); box-shadow: var(--shadow-sm); }
.flash.ok { background: var(--success-bg); color: var(--success-text); border: 1px solid var(--success-border); }
.flash.err { background: var(--danger-bg); color: var(--danger-text); border: 1px solid var(--danger-border); }

.inline-form { display: inline-flex; gap: 0.3rem; align-items: center; margin: 0.1rem 0; }
.action-box {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
button.mini {
  font-size: var(--text-xs);
  padding: 0.2rem 0.6rem;
  cursor: pointer;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
button.mini:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
button.danger {
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  color: var(--danger-text);
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.8rem;
  font-weight: 650;
  font-size: var(--text-sm);
  transition: filter 0.15s ease, transform 0.1s ease;
}
button.danger:hover { filter: brightness(1.08); }
button.danger:active { transform: translateY(1px); }
.action-box input[type="text"] {
  padding: 0.35rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  font-size: var(--text-sm);
}
.action-box input[type="text"]:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.action-box button {
  padding: 0.4rem 0.85rem;
  cursor: pointer;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--accent-text-on);
  font-weight: 650;
  font-size: var(--text-sm);
  transition: background 0.15s ease, transform 0.1s ease;
}
.action-box button:hover { background: var(--accent-hover); }
.action-box button:active { transform: translateY(1px); }
.action-box button.danger { background: var(--danger-bg); color: var(--danger-text); border-color: var(--danger-border); }
.action-box button.danger:hover { background: var(--danger-bg); filter: brightness(1.08); }

/* --- login landing page --- */
.hero {
  max-width: 680px;
  margin: 0 auto;
  min-height: calc(100vh - 220px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  padding: 2rem 1rem;
}
.hero-mark {
  font-size: 2.4rem;
  color: var(--accent);
  margin-bottom: var(--space-3);
  filter: drop-shadow(0 0 22px color-mix(in srgb, var(--accent) 45%, transparent));
  animation: hero-mark-in 0.5s ease both;
}
@keyframes hero-mark-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.9); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.hero-title {
  font-size: var(--text-2xl);
  font-weight: 750;
  letter-spacing: -0.025em;
  margin: 0 0 0.7rem;
}
.hero-subtitle {
  color: var(--muted);
  font-size: var(--text-md);
  line-height: 1.65;
  margin: 0 0 var(--space-6);
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}
.hero-features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  text-align: left;
}
@media (max-width: 640px) {
  .hero-features { grid-template-columns: 1fr; }
  .hero { min-height: auto; }
}
.hero-feature {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.1rem;
  box-shadow: var(--shadow-sm);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.hero-feature:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
.hero-feature-title {
  font-weight: 650;
  font-size: var(--text-sm);
  margin-bottom: 0.35rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.hero-feature-title::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--accent);
  flex-shrink: 0;
}
.hero-feature-text { color: var(--muted); font-size: var(--text-sm); line-height: 1.55; }

.discord-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: #5865f2;
  color: #fff;
  text-decoration: none;
  font-weight: 650;
  font-size: var(--text-md);
  padding: 0.75rem 1.6rem;
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(88, 101, 242, 0.4);
  transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.discord-cta:hover { background: #4752c4; transform: translateY(-1px); box-shadow: 0 8px 26px rgba(88, 101, 242, 0.5); }
.discord-cta:active { transform: translateY(1px); }

.hero-note {
  color: var(--muted);
  font-size: var(--text-xs);
  margin-top: var(--space-4);
}

/* --- autocomplete --- */
.autocomplete { position: relative; }
.autocomplete-list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  z-index: 30;
}
.autocomplete-item { padding: 0.5rem 0.75rem; cursor: pointer; font-size: var(--text-sm); }
.autocomplete-item:hover, .autocomplete-item.active { background: var(--accent-soft); color: var(--accent); }

/* --- best-pic hall of fame (contest_hall.html) ---
   A single centred column of posts rather than a table: these are photos
   people entered to be looked at, and the site's table layout would shrink
   them to thumbnails. Colours come from the :root tokens only, so the
   dark-scheme block above applies here with no extra rules. */
.feed {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin: 0 0 var(--space-6);
}
.feed-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.feed-item:hover {
  box-shadow: var(--shadow);
  border-color: var(--border-strong);
  transform: translateY(-1px);
}
.feed-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}
.feed-avatar-img {
  object-fit: cover;
  background: var(--surface-alt);
  /* inset ring instead of a border so the picture keeps its full size */
  box-shadow: 0 0 0 1px var(--border) inset, 0 0 0 2px var(--surface);
}
.feed-avatar {
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}
/* min-width:0 so a long username ellipsises instead of shoving the rank
   badge out of the card. */
.feed-head-text { display: flex; flex-direction: column; min-width: 0; }
.feed-user {
  font-size: var(--text-base);
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-theme {
  font-size: var(--text-xs);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-rank {
  flex: none;
  margin-left: auto;
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent);
  font-weight: 700;
}
.feed-media {
  display: block;
  background: var(--surface-alt);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
/* contain, not cover: a contest entry is judged as composed, so the whole
   frame is shown rather than centre-cropped to a square. max-height stops a
   very tall portrait shot from pushing the next post off the screen. */
.feed-media img {
  display: block;
  width: 100%;
  height: auto;
  max-height: min(62vh, 30rem);
  object-fit: contain;
}
.feed-media-missing {
  padding: var(--space-6);
  text-align: center;
  color: var(--muted);
  font-style: italic;
  font-size: var(--text-sm);
}
.feed-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  color: var(--muted);
}

/* --- locked nav tabs (visible, not hidden - they are the offer) --- */
.nav-links a.locked { opacity: 0.62; }
.nav-links a.locked:hover { opacity: 1; }
.lock { font-size: 0.72em; margin-left: 0.3em; vertical-align: 1px; }
.badge.role-member { background: var(--surface-alt); color: var(--muted); }

/* --- paywall --- */
.paywall {
  display: flex;
  justify-content: center;
  padding: var(--space-5) var(--space-3) var(--space-6);
}
.paywall-card {
  width: 100%;
  max-width: 520px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-6) var(--space-5);
  text-align: center;
  /* A single accent wash rather than a loud gradient - the page still has
     to look like the rest of the site, not like an ad. */
  background-image: radial-gradient(120% 80% at 50% 0%, var(--accent-soft) 0%, transparent 62%);
}
.paywall-badge {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-text-on);
  border-radius: 999px;
  padding: 0.22rem 0.8rem;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.paywall-title {
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: var(--space-4) 0 var(--space-2);
}
.paywall-lead { color: var(--muted); font-size: var(--text-md); margin: 0 auto var(--space-5); max-width: 42ch; }
.paywall-perks { list-style: none; padding: 0; margin: 0 0 var(--space-5); text-align: left; display: inline-block; }
.paywall-perks li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
}
.paywall-check { color: var(--success-text); font-weight: 700; flex: none; }
.patreon-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--accent);
  color: var(--accent-text-on);
  text-decoration: none;
  font-weight: 650;
  padding: 0.7rem 1.5rem;
  border-radius: 999px;
  box-shadow: var(--shadow);
  transition: transform 0.12s ease, background 0.12s ease;
}
.patreon-cta:hover { background: var(--accent-hover); transform: translateY(-1px); }
.patreon-cta:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.paywall-alt { color: var(--muted); font-size: var(--text-sm); margin: var(--space-4) 0 0; }
.paywall-free { color: var(--muted); font-size: var(--text-xs); margin: var(--space-4) 0 0; }

/* --- hall of fame: hearts + comments --- */
/* Count and filters share one row; both wrap onto their own line when the
   viewport can't hold them side by side. */
/* Title, filter row and feed all share one column width, so nothing sits
   off-axis from the cards. */
.hof-page { max-width: 720px; margin: 0 auto; }
.hof-page h1 { text-align: left; }

/* The count is INSIDE the filter bar, so the two can't wrap apart from each
   other - they are one control strip, not two blocks that happen to fit. */
form.filters.hof-bar { gap: var(--space-2); }
.hof-count {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  line-height: 1.25;
}
form.filters.hof-bar input[type="text"] { flex: 0 1 9rem; min-width: 5rem; }
form.filters.hof-bar select { flex: 0 1 auto; }

.hof-signin { max-width: none;
  background: linear-gradient(var(--surface), var(--surface)) padding-box,
              linear-gradient(120deg, var(--accent-soft), var(--surface-alt)) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: var(--space-3);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--muted);
  margin: 0 0 var(--space-5);
}
/* Instagram post anatomy: action bar, like count, caption, comments, then
   the timestamp last and smallest. */
.feed-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4) var(--space-1);
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--text);
  text-decoration: none;
  line-height: 0;
  transition: color 0.12s ease, transform 0.12s ease;
}
.icon-btn:hover { transform: scale(1.08); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.icon-btn-static { cursor: default; color: var(--muted); }
.icon-btn-static:hover { transform: none; }
.icon-count { font-size: var(--text-sm); line-height: 1; font-variant-numeric: tabular-nums; }
/* Share is the site's shared .share-icon-btn (same as /sales); it only
   needs pushing to the right here, because heart and comment act on this
   post while share takes the reader out of it. */
.feed-actions .share-icon-btn { margin-left: auto; }
.heart-btn:hover { color: #e0245e; }
.heart-btn.hearted { color: #e0245e; }
.feed-likes {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3em;
  margin: 0;
  padding: 0 var(--space-4);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--muted);
  letter-spacing: -0.01em;
}
.feed-likes strong { color: var(--text); font-weight: 650; }
.liker-names { min-width: 0; }
/* Overlapping avatar stack, the way a "liked by" row usually reads. Each
   avatar gets a ring in the card colour so the overlap stays legible. */
.liker-stack { display: inline-flex; align-items: center; flex: none; }
.liker-stack > * + * { margin-left: -0.45rem; }
.liker-avatar {
  width: 1.3rem;
  height: 1.3rem;
  border-radius: 999px;
  object-fit: cover;
  background: var(--surface-alt);
  box-shadow: 0 0 0 2px var(--surface);
}
.liker-avatar-letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
}
.feed-caption {
  margin: var(--space-1) 0 0;
  padding: 0 var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.45;
}
.feed-caption-user { font-weight: 650; margin-right: 0.35em; }
.feed-caption-text { color: var(--text); }
/* The theme is the whole point of a contest entry, so it gets its own band
   straight under the photo rather than a hashtag buried in the caption. */
.feed-theme-band {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) var(--space-4) 0;
  /* a hairline that stops short of the edges, so the band reads as part of
     the card rather than a full-width divider slicing it in two */
  background:
    linear-gradient(var(--border), var(--border)) no-repeat
    var(--space-4) 0 / calc(100% - var(--space-4) * 2) 1px;
}
.feed-theme-label {
  flex: none;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.feed-theme-value {
  font-size: var(--text-md);
  font-weight: 650;
  color: var(--accent);
  overflow-wrap: anywhere;
}
.feed-score {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3em;
  margin-left: 0.4em;
  padding: 0.08rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.feed-score-pct { color: var(--muted); font-weight: 600; }
.feed-time {
  display: block;
  padding: var(--space-2) var(--space-4) var(--space-3);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.comments { list-style: none; margin: var(--space-2) 0 0; padding: 0 var(--space-4); }
/* The avatar and the comment's first line are given the SAME height, so
   flex-start lines them up exactly - no magic margin that drifts the moment
   the font size changes. Multi-line comments keep the avatar on line one,
   which is what a threaded comment should do. */
.comment {
  --comment-line: 1.6rem;
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-sm);
  line-height: var(--comment-line);
  padding: 0.15rem 0;
}
.comment-avatar {
  flex: none;
  width: var(--comment-line);
  height: var(--comment-line);
  border-radius: 999px;
  object-fit: cover;
  background: var(--surface-alt);
  box-shadow: 0 0 0 1px var(--border) inset;
}
.comment-avatar-letter {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1;
}
/* min-width:0 so a long comment wraps under the name instead of widening
   the card past the feed column. */
.comment-text { min-width: 0; }
.comment-user { font-weight: 650; margin-right: 0.4em; }
.comment-body { color: var(--text); }
.comment-del { margin-left: auto; opacity: 0; transition: opacity 0.12s ease; }
.comment:hover .comment-del,
.comment-del:focus-within { opacity: 1; }
.link-btn {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: var(--text-md);
  line-height: 1;
  padding: 0 0.2rem;
}
.link-btn:hover { color: var(--danger-text); }
/* Instagram's comment box: a borderless field flush inside the card with a
   plain accent-coloured "Post". The site's bordered input style lives under
   form.filters only, so without this the field fell back to raw browser
   chrome, which looked pasted-in against the card. */
.comment-form {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1) var(--space-4) var(--space-2);
  border-top: 1px solid var(--border);
  margin-top: var(--space-3);
  background: color-mix(in srgb, var(--surface-alt) 45%, transparent);
}
.comment-form input[type="text"] {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: var(--text-sm);
  padding: var(--space-3) 0;
}
.comment-form input[type="text"]::placeholder { color: var(--muted); }
/* No :focus-visible override here on purpose - the global focus ring is the
   keyboard affordance and removing it would strip it from this field only. */
.comment-form button {
  flex: none;
  background: none;
  border: none;
  padding: var(--space-2) 0;
  color: var(--accent);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 650;
  cursor: pointer;
  transition: color 0.12s ease, opacity 0.12s ease;
}
.comment-form button:hover { color: var(--accent-hover); }
.flash-ok { background: var(--success-bg); color: var(--success-text); border: 1px solid var(--success-border); }
.flash-err { background: var(--danger-bg); color: var(--danger-text); border: 1px solid var(--danger-border); }
.flash-ok, .flash-err {
  margin: 0 0 var(--space-4);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  text-align: center;
}

/* --- share pages (public, unauthenticated - no base.html chrome) --- */
.share-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.share-card {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-5);
  max-width: 420px;
  width: 100%;
  text-align: center;
}
.share-card-wide { max-width: 720px; text-align: left; }
.share-image { width: 100%; border-radius: var(--radius-sm); margin-bottom: var(--space-4); }
.share-price { font-size: var(--text-xl); font-weight: 700; color: var(--accent); margin: var(--space-2) 0; }
.share-win .share-image { max-height: 60vh; object-fit: contain; }
.share-win-who {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.share-win-who h1 { margin: 0; }
.share-win-line { color: var(--muted); margin: var(--space-2) 0 0; }
/* A real button, not a tinted link: this is the one action the share page
   offers, so it gets the site's primary button treatment. */
.share-win-more { margin: var(--space-5) 0 0; }
.share-win-more a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.55rem 1.25rem;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--accent-text-on);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 650;
  box-shadow: var(--shadow-sm);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.share-win-more a:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  transform: translateY(-1px);
}
.share-win-more a:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.share-attrs { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center; margin: var(--space-3) 0; }
.share-footer { color: var(--muted); margin-top: var(--space-4); }

/* --- mobile (phones, narrow viewports) --- */
@media (max-width: 640px) {
  main { padding: var(--space-3); }
  nav { padding: 0.6rem var(--space-3); gap: var(--space-3); }
  .nav-right { gap: var(--space-2); }

  h1 { font-size: var(--text-lg); }

  /* Filter bars: a two-column grid rather than one full-width field per row.
     Stacking them all was still eight to eleven rows on /sales - the reader
     scrolled past the whole control panel before reaching a single result.
     The selects are short, self-labelling ("Set (all)", "PSA 10"...) and pair
     up fine; only the search field, the submit button and the reset link get
     a full row, because those are the wide/primary ones.
     .hof-bar is excluded on purpose: its count sits INSIDE the bar and is
     flex-sized (see form.filters.hof-bar above), which a grid would undo. */
  form.filters { padding: var(--space-3); }
  form.filters:not(.hof-bar) {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: end;
  }
  form.filters:not(.hof-bar) > .autocomplete,
  form.filters:not(.hof-bar) > input[type="text"],
  form.filters:not(.hof-bar) > button,
  form.filters:not(.hof-bar) > a { grid-column: 1 / -1; }
  form.filters input[type="text"],
  form.filters input[type="date"],
  form.filters select,
  form.filters button { width: 100%; }
  form.filters .autocomplete { width: 100%; }
  /* .autocomplete-list is min-width:220px, so its input has to hold a full
     row or the suggestions overflow the card on a 390px screen. */
  form.filters .autocomplete-list { min-width: 100%; }
  /* pop_crossing wraps its date inputs in a <label>; caption above the field
     instead of beside it, so the pair fits a half-width cell. Scoped with
     :has() rather than applied to every label - quarantine's status filters
     are checkbox labels, and those have to stay a checkbox-then-text ROW. */
  form.filters label:has(input[type="date"]) {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    font-size: var(--text-xs);
  }
  form.filters button { padding: 0.6rem 1.1rem; margin-top: var(--space-1); }
  form.filters a { justify-self: center; }

  /* .hof-bar keeps its flex layout, but the count spans the full width so the
     three controls below it can share the row. */
  form.filters.hof-bar input[type="text"] { flex: 1 1 100%; }
  form.filters.hof-bar select { flex: 1 1 8rem; }
  form.filters.hof-bar button { flex: 1 1 100%; }

  th, td { padding: 0.5rem 0.6rem; }
  .table-scroll { -webkit-overflow-scrolling: touch; }

  /* /sales, /pop and /pop/crossing swap their table for the card list.
     /quarantine does not - it is an editor triage view whose rows are
     screenshot-led, so it still scrolls horizontally; that is why the swap
     is keyed on .has-cards and not on .table-scroll. */
  .table-scroll.has-cards { display: none; }
  .data-cards { display: flex; flex-direction: column; gap: var(--space-3); }

  /* Bigger, easier-to-tap targets - the desktop sizing is comfortable
     with a mouse but tight for a thumb. */
  .share-btn { padding: 0.35rem 0.7rem; }
  /* on a card this is the only control, not one cell among twelve */
  .share-icon-btn { width: 2.4rem; height: 2.4rem; }
  .pagination a, .pagination-disabled { padding: 0.5rem 0.8rem; }
  button.mini { padding: 0.35rem 0.75rem; }

  .card-name-row { flex-wrap: wrap; row-gap: 0.3rem; }

  .share-page { padding: var(--space-3); }
  .share-card { padding: var(--space-4); }

  /* Edge-to-edge posts on a phone, the way a photo feed is normally read. */
  .feed { gap: var(--space-4); }
  .feed-item { border-radius: var(--radius); }
  .feed-media img { max-height: min(60vh, 24rem); }

  .paywall-card { padding: var(--space-5) var(--space-4); }
  .patreon-cta { width: 100%; justify-content: center; }
  /* Stays a single row - the field is borderless and full-width already,
     and a taller tap target is enough. */
  .comment-form input[type="text"] { padding: var(--space-3) 0; }
}

/* 1st Edition marker (sales + crossing). Only 1st Ed is ever badged -
   Unlimited is the default reading of a silent title, so it needs no mark. */
.edition-tag {
  display: inline-block; padding: 0.08rem 0.45rem; border-radius: 4px;
  margin-left: 0.3rem; font-weight: 650;
  color: var(--accent-text, var(--success-text)); background: var(--surface-alt);
  border: 1px solid var(--border);
}
