:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel2: #1e222b;
  --border: #2a2f3a;
  --text: #e6e9ef;
  --muted: #8a91a0;
  --accent: #4da3ff;
  --pos: #3ecf8e;
  --neg: #ff6b6b;
  --maxw: 1600px;   /* fixed content width — centred, never full-bleed */
}

* { box-sizing: border-box; }

/* Always reserve the vertical scrollbar gutter so the content width is identical
   on every tab — otherwise a tall tab (e.g. the BVC set-cards table) shows a
   scrollbar and a short one (Compare) doesn't, shifting the chart width ~7px.
   `overflow-y: scroll` is the universal fallback for browsers without gutter. */
html { scrollbar-gutter: stable; overflow-y: scroll; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.45 -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Every top-level block sits in the same centred fixed-width column */
#overview, #sets, #bvc, #compare, #pictures, #deckbuilder, #controls, #table-shell {
  max-width: var(--maxw);
  margin-left: auto;
  margin-right: auto;
}

/* ---- sticky hero header ---- */
header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: linear-gradient(180deg, #141821, #0f1115);
  border-bottom: 1px solid var(--border);
}
.header-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 12px 22px 0;
}
.brand-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.brand { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
/* Right-side header cluster: data timestamp, Discord auth widget, BETA badge */
.header-meta { display: flex; align-items: center; gap: 12px; margin-left: auto; flex-wrap: wrap; }

/* Discord login widget */
#auth { display: flex; align-items: center; gap: 8px; }
.auth-av {
  width: 24px; height: 24px; border-radius: 50%;
  border: 1px solid var(--border); object-fit: cover;
}
.auth-name { font-size: 12px; color: var(--text); max-width: 160px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.auth-btn {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
  text-decoration: none;
}
.auth-btn:hover { color: var(--text); border-color: var(--accent); }
.auth-login { color: #fff; background: #5865F2; border-color: #5865F2; }  /* Discord blurple */
.auth-login:hover { color: #fff; background: #4752c4; border-color: #4752c4; }
header h1 { margin: 0; font-size: 20px; letter-spacing: .3px; }
.tagline { font-size: 12px; color: var(--muted); }
.accent { color: var(--accent); }
.muted { color: var(--muted); font-size: 12px; }

/* ---- tab bar (scrolls horizontally when it overflows) ---- */
#tabs {
  display: flex;
  gap: 4px;
  margin: 9px 0 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  padding-bottom: 2px;
  /* Edge-fade affordance: hint that the tab row scrolls sideways (esp. on
     mobile, where ~7 of the 11 tabs sit off-screen). The fade widths are 0
     at each extreme — JS toggles the classes — so no fade shows when there's
     nothing more to reveal on that side. */
  --tab-fade-l: 0px;
  --tab-fade-r: 0px;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--tab-fade-l), #000 calc(100% - var(--tab-fade-r)), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 var(--tab-fade-l), #000 calc(100% - var(--tab-fade-r)), transparent 100%);
}
#tabs.tab-fade-left  { --tab-fade-l: 24px; }
#tabs.tab-fade-right { --tab-fade-r: 24px; }
#tabs::-webkit-scrollbar { height: 6px; }
#tabs::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.tab { white-space: nowrap; flex: 0 0 auto; }
.tab {
  padding: 7px 16px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.tab:hover { color: var(--text); background: var(--panel); }
.tab.active {
  color: var(--accent);
  background: var(--panel);
  border-color: var(--border);
  font-weight: 600;
}
/* Beta-only tabs (hidden in production) — a dashed amber outline flags them */
.tab.beta { border-color: rgba(240,180,60,0.5); border-style: dashed; }
.tab.beta::after { content: ' β'; color: #f0b43c; font-size: 11px; }

/* Header BETA badge — shown only in beta mode; click to exit beta */
#beta-badge {
  margin-left: 8px;
  padding: 2px 8px;
  border: 1px solid #f0b43c;
  border-radius: 6px;
  background: rgba(240,180,60,0.14);
  color: #f0b43c;
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  cursor: pointer;
}
#beta-badge:hover { background: rgba(240,180,60,0.26); }

/* Lab (beta sandbox) placeholder */
#lab .lab-note {
  max-width: 640px;
  margin: 40px auto;
  padding: 24px 28px;
  border: 1px dashed rgba(240,180,60,0.5);
  border-radius: 12px;
  background: var(--panel);
}
#lab .lab-note h2 { margin: 0 0 12px; }
#lab .lab-note p { margin: 0 0 10px; line-height: 1.5; }
#lab .lab-note code {
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(255,255,255,0.06);
  font-size: 12px;
}

/* ---- filter chips ---- */
#chips { display: flex; gap: 5px; flex-wrap: wrap; }
.chip {
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.chip:hover { color: var(--text); }
.chip.active {
  color: var(--accent);
  border-color: var(--accent);
  background: #16233a;
}

/* ---- controls ---- */
#controls {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 10px 22px;
  flex-wrap: wrap;
}
#search {
  flex: 1 1 260px;
  max-width: 420px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  outline: none;
}
#search:focus { border-color: var(--accent); }
select {
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
}

/* Mobile sort control — hidden on desktop (sorting is via column headers there),
   shown only in the ≤640 card layout where the headers are hidden. */
.msort { display: none; }
/* ...except in gallery view, where there are no column headers to tap */
body.gallery-view .msort { display: flex; align-items: center; gap: 6px; }
.msort label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
#msort-dir {
  padding: 8px 11px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--panel); color: var(--text); font: inherit; cursor: pointer; line-height: 1;
}
#msort-dir:hover { border-color: var(--accent); }

/* Mobile-only "show this stat" pickers (grid tabs + Sets). Hidden on desktop,
   where every column is visible; shown in the ≤640 compact table. */
.mmetric { display: none; }

/* ---- table ---- */
/* #table-shell is the positioning context for the right-edge scroll fade (see
   the bottom of this file) and now carries the centring + width; #table-wrap is
   the scroller itself and simply fills it. */
#table-shell {
  position: relative;
  margin: 0 auto 30px;
  width: calc(100% - 44px);
  max-width: calc(var(--maxw) - 44px);   /* align edges with padded sections */
}
#table-wrap {
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow-x: auto;
}
table { border-collapse: collapse; width: 100%; }
thead th {
  position: sticky; top: 0;
  background: var(--panel2);
  text-align: left;
  padding: 9px 9px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
thead th.sorted { color: var(--accent); }
tbody td {
  padding: 7px 9px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
td.name-cell {
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Name cell with a leading card thumbnail */
.name-wrap { display: flex; align-items: center; gap: 9px; min-width: 0; }
.row-thumb {
  width: 30px;
  height: 42px;
  object-fit: cover;
  object-position: top;
  border-radius: 3px;
  flex: 0 0 auto;
  background: var(--panel2);
  border: 1px solid var(--border);
}
.name-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* ---- momentum signal badges ---- */
.sig-cell { text-align: center; }
.sig {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .2px;
  white-space: nowrap;
  border: 1px solid transparent;
}
.sig-hot { color: #0b1a12; background: var(--pos); }
.sig-up { color: var(--pos); background: rgba(62,207,142,0.14); border-color: rgba(62,207,142,0.4); }
.sig-high { color: #4da3ff; background: rgba(77,163,255,0.14); border-color: rgba(77,163,255,0.4); }
.sig-flat { color: var(--muted); background: var(--panel2); border-color: var(--border); }
.sig-low { color: #f39c12; background: rgba(243,156,18,0.14); border-color: rgba(243,156,18,0.4); }
.sig-down { color: var(--neg); background: rgba(255,107,107,0.13); border-color: rgba(255,107,107,0.4); }
.sig-drop { color: #fff; background: var(--neg); }
.sig-thin { color: var(--muted); background: none; font-weight: 400; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.015); }
tbody tr { cursor: pointer; }
tbody tr:hover { background: var(--panel2); }
tbody tr.selected { background: #20304a; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.pos { color: var(--pos); }
.neg { color: var(--neg); }
.dim { color: var(--muted); }

/* ---- market overview ---- */
#overview { padding: 4px 22px 0; }
#overview.hidden { display: none; }
/* Sits below the charts, delineating the tiers/tables it drives */
#ov-controls {
  display: flex;
  gap: 16px;
  margin: 2px 0 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  align-items: center;
}
#ov-controls label {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
}
/* ---- overview stat strip ----
   One compact text row above the charts: $ value + unit volume (each split
   Sealed/Singles) plus market cap. */
#ov-cards {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  font-size: 12.5px;
  color: var(--text);
  line-height: 1.9;
  margin: 6px 0 12px;
}
.ov-stat { white-space: nowrap; }
.ov-stat-l { color: var(--muted); }
.ov-stat-v { font-weight: 700; margin-left: 5px; }
.ov-stat-bd { color: var(--muted); }   /* the "(Sealed …, Singles …)" split */
.ov-sep { color: #3a4150; }
.ov-caption { margin: 10px 0 8px; }

/* ---- overview area charts (market cap + weekly value traded) ---- */
#ov-charts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  /* Sits at the BOTTOM of #overview (moved 2026-09-06), so the top margin
     separates it from the buyouts table rather than from #ov-cards. */
  margin: 18px 0 6px;
}
.ov-chart {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  height: 340px;
  padding: 6px 6px 4px;
}

/* Market Overview tables (increases / falls / buyouts) */
.ov-tables { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
.ov-tables > div {
  flex: 1 1 380px;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 10px 14px 4px;
  overflow-x: auto;
}
.ov-tables h3 {
  margin: 2px 0 8px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
}
.ov-tables h3 span { color: var(--text); }
.ov-tables table { border-collapse: collapse; width: 100%; }
.ov-tables thead th {
  position: static;
  background: none;
  text-align: left;
  font-size: 11px;
  color: var(--muted);
  padding: 4px 9px;
  border-bottom: 1px solid var(--border);
  cursor: default;
}
.ov-tables thead th.num { text-align: right; }
.ov-tables td {
  padding: 6px 9px;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.ov-tables td.name-cell { max-width: 210px; overflow: hidden; text-overflow: ellipsis; }
.ov-tables tbody tr:last-child td { border-bottom: none; }

/* ---- sets browser ---- */
#sets { padding: 4px 22px 30px; }
#sets.hidden { display: none; }
#sets-intro { margin: 2px 0 12px; }
#sets-wrap {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow-x: auto;
}
#sets-table { border-collapse: collapse; width: 100%; }
#sets-table thead th {
  position: sticky; top: 0;
  background: var(--panel2);
  text-align: left;
  padding: 9px 11px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
#sets-table thead th.num { text-align: right; }
#sets-table thead th.sorted { color: var(--accent); }
#sets-table tbody td {
  padding: 9px 11px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
#sets-table tbody tr { cursor: pointer; }
#sets-table tbody tr:hover { background: var(--panel2); }
#sets-table tbody tr:last-child td { border-bottom: none; }
.set-name b { font-size: 14px; }
#sets-table td.name-cell { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }

/* ---- box vs cards ---- */
#bvc { padding: 4px 22px 0; }
#bvc.hidden { display: none; }
#bvc-controls {
  display: flex;
  gap: 16px;
  margin-bottom: 10px;
  align-items: center;
  flex-wrap: wrap;
}
#bvc-controls label {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
}
#bvc-controls label.hidden { display: none; }
/* Chart on the left, stat cards + caption stacked on the right (like Compare) */
#bvc-main { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
#bvc-side { flex: 2 1 300px; min-width: 0; display: flex; flex-direction: column; gap: 12px; }
#bvc-stats { display: flex; gap: 10px; flex-wrap: wrap; }
#bvc-stats:empty { display: none; }
/* Rarity-mix pie (single-set mode); Plotly.purge empties it in all-sets mode.
   Fixed-width and left-aligned so it doesn't stretch with the wide side panel,
   with room around the donut for the outside labels (automargin in the trace). */
#bvc-pie {
  align-self: flex-start;
  width: 360px;
  max-width: 100%;
  height: 340px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
}
#bvc-pie:empty { display: none; }
/* Rarity mix block: the pie ⇄ table toggle plus whichever view is showing */
#bvc-mix { display: flex; flex-direction: column; align-items: flex-start; }
/* Compact segmented control — the EV Tracker's Pie/Table and the grid tabs'
   Table/Gallery both use it (.cmp-mode gives it the pill frame). Own class, NOT
   .mode-btn: that one is wired globally to the Compare/EV Tracker tab switch. */
.mix-mode { margin-bottom: 8px; }
.seg-btn {
  padding: 4px 12px;
  background: var(--panel);
  color: var(--muted);
  border: 0;
  font-size: 12px;
  cursor: pointer;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--border); }
.seg-btn:hover:not(.active) { color: var(--text); }
.seg-btn.active { background: var(--accent); color: #0e1116; font-weight: 600; }
#bvc-pie.hidden, #bvc-rarity.hidden { display: none; }
/* Table view sits in the same 360px slot as the donut */
#bvc-rarity {
  width: 360px;
  max-width: 100%;
}
#bvc-rarity:empty { display: none; }
/* Rarity table headers are clickable sorts (the .bvc-summary base style is
   shared with the non-sortable Compare EV summary, so scope this to #bvc-rarity) */
#bvc-rarity th { cursor: pointer; user-select: none; }
#bvc-rarity th:hover { color: var(--text); }
#bvc-rarity th.sorted { color: var(--accent); }
.bvc-mix-title {
  font-size: 13px;
  color: var(--text);
  text-align: center;
  margin-bottom: 8px;
}
/* Set-cards caption: align its text with the table + search box (both inset 22px) */
#bvc-table-caption {
  max-width: var(--maxw);
  margin: 10px auto 8px;
  padding: 0 22px;
}
/* All-sets mode per-set summary table (fills the side panel like the stat cards) */
.bvc-summary {
  border-collapse: collapse;
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
.bvc-summary th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
}
.bvc-summary td {
  padding: 7px 12px;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.bvc-summary tr:last-child td { border-bottom: none; }
.bvc-summary th.num, .bvc-summary td.num { text-align: right; }
.bvc-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-right: 7px;
  vertical-align: middle;
}
.stat {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 14px;
  min-width: 110px;
}
.stat-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
}
.stat-value { font-size: 17px; font-weight: 600; margin-top: 2px; }
#bvc-chart {
  flex: 2 1 320px;
  min-width: 0;
  height: 480px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
}

/* ---- gallery view (Sealed / Singles / Buyouts) ---- */
/* Same rows as the table, drawn as picture + name tiles. The toggle lives in
   #controls; .view-mode just drops .cmp-mode's bottom margin so it lines up
   with the filter row. */
.view-mode { margin-bottom: 0; }
#view-toggle.hidden { display: none; }
#gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  width: calc(100% - 44px);
  max-width: calc(var(--maxw) - 44px);
  margin: 0 auto 30px;
}
#gallery.hidden { display: none; }
/* .hidden isn't global in this stylesheet — each hideable block declares its own */
#table-shell.hidden { display: none; }
.gal-tile {
  margin: 0;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
}
.gal-tile:hover { border-color: var(--accent); }
.gal-tile.selected { border-color: var(--accent); background: var(--panel2); }
/* contain, not cover: sealed boxes aren't card-shaped and shouldn't be cropped */
.gal-tile img {
  display: block;
  width: 100%;
  aspect-ratio: 5 / 7;
  object-fit: contain;
  background: #1a1d24;
  border-radius: 6px;
}
.gal-tile figcaption {
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--text);
  /* two lines, then ellipsis — keeps every tile the same height */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---- compare ---- */
#compare { padding: 4px 22px 30px; }
#compare.hidden { display: none; }
/* Compare/EV Tracker mode toggle (segmented radio at the top of the merged tab) */
.cmp-mode {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 14px;
}
.mode-btn {
  padding: 7px 16px;
  background: var(--panel);
  color: var(--muted);
  border: 0;
  font-size: 13px;
  cursor: pointer;
}
.mode-btn + .mode-btn { border-left: 1px solid var(--border); }
.mode-btn:hover:not(.active) { color: var(--text); }
.mode-btn.active { background: var(--accent); color: #0e1116; font-weight: 600; }

#cmp-controls {
  display: flex;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
#cmp-picker { position: relative; flex: 1 1 300px; max-width: 480px; }
#cmp-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  outline: none;
}
#cmp-input:focus { border-color: var(--accent); }
#cmp-suggest {
  position: absolute;
  top: 100%; left: 0; right: 0;
  z-index: 10;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 8px;
  max-height: 300px;
  overflow-y: auto;
}
#cmp-suggest.hidden { display: none; }
.cmp-opt { padding: 7px 12px; cursor: pointer; font-size: 13px; }
.cmp-opt:hover { background: var(--panel); }
.cmp-opt .dim { float: right; }
.cmp-check {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
}
#cmp-pills { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.cmp-pill {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
}
.cmp-x {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 14px;
  padding: 0;
}
.cmp-x:hover { color: var(--neg); }
/* Chart on the left, the returns+volume tables stacked on the right */
#cmp-main { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
#cmp-chart {
  flex: 2 1 320px;
  min-width: 0;
  height: 480px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.cmp-tables {
  display: flex;
  flex-direction: column;
  gap: 18px;
  flex: 2 1 300px;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
}
.cmp-tables > div { overflow-x: auto; }
.cmp-tables h3 {
  margin: 2px 0 8px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
}
.cmp-tables table { border-collapse: collapse; width: 100%; }
.cmp-tables th {
  text-align: left;
  font-size: 11px;
  color: var(--muted);
  padding: 4px 10px;
  border-bottom: 1px solid var(--border);
}
.cmp-tables td {
  padding: 5px 10px;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
/* Cap the product-name column so the numeric columns always fit without scroll */
.cmp-tables th:first-child, .cmp-tables td:first-child {
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Centre every column's data (headers + cells, overriding the .num right-align) */
.cmp-tables th, .cmp-tables td, .cmp-tables td.num { text-align: center; }

/* ---- sparkline cells ---- */
.spark-cell { padding: 2px 12px; }
.spark-cell svg { display: block; }

/* ---- watch column ---- */
.watch-cell {
  text-align: center;
  color: var(--muted);
  cursor: pointer;
  width: 34px;
  font-size: 15px;
}
.watch-cell:hover { color: var(--accent); }
.watch-cell.on { color: #f5c518; }

/* ---- pictures ---- */
#pictures { padding: 4px 22px 30px; }
#pictures.hidden { display: none; }
#pic-controls {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-bottom: 12px;
}
#pic-picker { position: relative; flex: 1 1 300px; max-width: 480px; }
#pic-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  outline: none;
}
#pic-input:focus { border-color: var(--accent); }
#pic-suggest {
  position: absolute;
  top: 100%; left: 0; right: 0;
  z-index: 10;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 8px;
  max-height: 300px;
  overflow-y: auto;
}
#pic-suggest.hidden { display: none; }
#pic-gallery {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}
.pic-card {
  position: relative;
  margin: 0;
  width: 200px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px;
}
.pic-card img { width: 100%; border-radius: 6px; display: block; }
.pic-card figcaption {
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pic-x {
  position: absolute;
  top: 4px; right: 8px;
  background: rgba(0,0,0,.55);
  border: none;
  border-radius: 50%;
  width: 22px; height: 22px;
  color: #fff;
  cursor: pointer;
  font-size: 13px;
}
.pic-x:hover { background: var(--neg); }

/* ---- deck builder ---- */
#deckbuilder { padding: 4px 22px 30px; }
#deckbuilder.hidden { display: none; }
#deck-cols { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
#deck-left { flex: 2 1 380px; min-width: 0; }
#deck-right {
  flex: 3 1 460px;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
}
.deck-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.deck-row > input, .deck-row > select { flex: 1 1 auto; min-width: 0; }
#deck-name {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  outline: none;
  font: inherit;
}
#deck-name:focus { border-color: var(--accent); }
#deck-status { min-height: 16px; margin-bottom: 6px; }
#deck-search {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  outline: none;
  font: inherit;
}
#deck-search:focus { border-color: var(--accent); }
#deck-count { margin: 8px 0 6px; }
#deck-more { margin-top: 8px; }

/* Hover preview for a search result (positioned by showDeckPreview) */
#deck-preview {
  position: fixed; left: 0; top: 0; z-index: 900;
  width: min(300px, 40vw);
  padding: 8px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
#deck-preview.hidden { display: none; }
#deck-preview img {
  display: block; width: 100%; aspect-ratio: 358 / 500;
  object-fit: contain; border-radius: 8px; background: var(--panel);
}
#deck-preview.dp-noimg img { display: none; }
.dp-cap { display: flex; flex-direction: column; gap: 2px; margin-top: 7px; font-size: 12px; }
.dp-cap strong { font-size: 13px; }
.dp-text {
  margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--border);
  white-space: pre-line; line-height: 1.4;
}
#deck-filters .df-row.hidden { display: none; }
#deck-more.hidden { display: none; }

/* Deck search filters — colour/type chips, trait/set selects, stat ranges */
#deck-filters {
  margin-top: 8px;
  padding: 8px 12px 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
}
#deck-filters summary { cursor: pointer; font-size: 13px; font-weight: 600; }
#deck-filters[open] summary { margin-bottom: 8px; }
.df-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
.df-label { font-size: 12px; color: var(--muted); min-width: 44px; }
.df-chips { display: flex; gap: 5px; flex-wrap: wrap; flex: 1 1 0; min-width: 0; }
.df-chips .chip { padding: 4px 10px; display: inline-flex; align-items: center; gap: 5px; }
.df-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 5px; vertical-align: 1px; flex: none;
}
.df-chips .df-dot { margin-right: 0; }
.df-selects select { flex: 1 1 160px; min-width: 0; }
#df-ranges {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 14px;
  margin-top: 8px;
}
.df-range { display: flex; align-items: center; gap: 5px; }
.df-range .df-label { min-width: 32px; }
.df-range input {
  width: 0; flex: 1 1 0;
  padding: 4px 6px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel2);
  color: var(--text);
  font: inherit; font-size: 12px;
}
.df-range input:focus { outline: none; border-color: var(--accent); }
.df-foot { justify-content: space-between; margin-top: 10px; font-size: 12px; }
.df-foot label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
#deck-results-wrap {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  max-height: 64vh;
  overflow-y: auto;
  overflow-x: auto;
}
#deck-results td.deck-add {
  width: 26px;
  text-align: center;
  color: var(--pos);
  font-weight: 700;
  font-size: 15px;
}
#deck-results td.deck-inq {
  width: 30px;
  text-align: center;
  color: var(--accent);
  font-weight: 700;
}
.deck-code { font-family: ui-monospace, Consolas, monospace; font-size: 12px; }
#deck-results td.name-cell { max-width: 200px; }
#deck-summary { font-size: 14px; font-weight: 600; margin: 2px 0 12px; }

/* Deck analytics — cost curve + type/colour splits */
#deck-analytics { margin: 14px 0 16px; }
#deck-analytics:empty { display: none; }
.da-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.da-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
}
.da-head {
  font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); margin-bottom: 10px;
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
}
.da-sub { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 11px; }
/* Proportion bars (type / colour) */
.da-row { display: flex; align-items: center; gap: 8px; margin: 5px 0; font-size: 12px; }
.da-label {
  flex: 0 0 96px; display: flex; align-items: center; gap: 6px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.da-foot { margin-top: 8px; font-size: 11px; color: var(--muted); }
.da-swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; border: 1px solid var(--border); }
.da-track { flex: 1; height: 8px; background: var(--panel2); border-radius: 4px; overflow: hidden; }
.da-fill { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.da-val { flex: 0 0 64px; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Cost-curve histogram */
.da-curve { display: flex; align-items: flex-end; gap: 4px; height: 96px; }
.da-col {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: flex-end; height: 100%; gap: 3px; min-width: 14px;
}
.da-colcount { font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums; height: 12px; }
.da-colbar {
  width: 100%; background: var(--accent); border-radius: 3px 3px 0 0;
  min-height: 2px; transition: height .15s ease;
}
.da-collabel { font-size: 11px; color: var(--muted); }
#deck-gallery { display: flex; gap: 12px; flex-wrap: wrap; align-content: flex-start; }
.deck-tile { width: 132px; }
.deck-tile-img { position: relative; }
.deck-tile-img img {
  width: 100%;
  border-radius: 8px;
  display: block;
  border: 1px solid var(--border);
  background: var(--panel2);
}
.deck-tile-img img.chartable { cursor: pointer; }
.deck-qty {
  position: absolute;
  top: 5px; right: 5px;
  min-width: 24px; height: 24px;
  line-height: 22px;
  text-align: center;
  border-radius: 12px;
  padding: 0 6px;
  background: #111;
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
}
.deck-tile .deck-code { text-align: center; margin-top: 3px; color: var(--muted); }
.deck-tile-btns { display: flex; justify-content: center; gap: 4px; margin-top: 4px; }
.deck-tile-btns button {
  padding: 1px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel2);
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.deck-tile-btns button:hover:not(:disabled) { color: var(--text); border-color: var(--muted); }
.deck-tile-btns .deck-x:hover:not(:disabled) { color: var(--neg); border-color: var(--neg); }
.deck-tile-btns .deck-star.on { color: #f5c518; border-color: #f5c518; }
.deck-tile-btns .deck-star:hover:not(:disabled) { color: #f5c518; }
.deck-tile-btns button:disabled { opacity: .35; cursor: default; }
.deck-export-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0 6px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.deck-export-head h3 {
  margin: 0;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
}
#deck-msa-link { margin-left: auto; text-decoration: none; }
#deck-msa-link[aria-disabled="true"] {
  opacity: .4;
  pointer-events: none;
  cursor: default;
}
.deck-export-opts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 8px 0;
  font-size: 13px;
  color: var(--text);
}
.deck-export-opts label {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
}
.deck-export-opts label.disabled { opacity: .4; cursor: default; }
.deck-export-opts .deck-fmt-qty { margin-left: auto; }
#deck-export {
  width: 100%;
  height: 150px;
  resize: vertical;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel2);
  color: var(--text);
  font: 13px/1.5 ui-monospace, Consolas, monospace;
  outline: none;
}

/* ---- decklist import (paste-to-load) ---- */
#deck-import-panel { margin: 8px 0 4px; }
#deck-import-panel.hidden { display: none; }
#deck-import {
  width: 100%;
  height: 120px;
  resize: vertical;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel2);
  color: var(--text);
  font: 13px/1.5 ui-monospace, Consolas, monospace;
  outline: none;
}
#deck-import:focus { border-color: var(--accent); }
.deck-import-btns { display: flex; gap: 8px; margin-top: 8px; }

/* ---- detail panel (modal popup) ---- */
#detail-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.62);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  overflow-y: auto;
}
#detail-overlay.hidden { display: none; }
#detail {
  /* Single source of truth for the media row's height: the chart is fixed at
     this, the card image caps at it. The vh term keeps a tall popup inside a
     short window. */
  --detail-media-h: min(460px, 48vh);
  /* #chart-range's height + margin: the right column drops by this so the image
     bottom lines up with the chart bottom, and the pills with the price stats */
  --chart-range-h: 28px;
  position: relative;
  width: min(1080px, 94vw);
  max-height: 90vh;
  overflow-y: auto;
  padding: 18px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
/* chart on the left (capped width), card image to its right at chart height */
#detail-main {
  display: flex;
  gap: 18px;
  /* flex-start, not flex-end: the right column is image + pills, so its height
     no longer tracks the chart's and bottom-aligning would shove the chart down */
  align-items: flex-start;
  margin: 0;
}
/* Right column — product image with the card-info pills beneath it. Sized to the
   image's own cap so the pills wrap in the image's column, not the chart's. */
#img-slot {
  height: var(--detail-media-h);
  display: flex;
  align-items: flex-end;
}
#img-col {
  flex: 0 0 auto;
  max-width: min(400px, 34vw);
  margin-top: var(--chart-range-h);
  display: flex;
  flex-direction: column;
  gap: 16px;              /* matches #detail-stats' margin-top, so the pills and
                             the price stats start on the same line */
}
/* Capped so the chart can't grow into the card image (which loads late, so
   its width isn't reserved at Plotly-render time) */
#chart-col { flex: 1 1 auto; min-width: 0; max-width: 600px; }
/* Price stats sit directly under the chart, level with the right column's
   pills — the two columns flow independently. */
#detail-stats { margin-top: 16px; }
#detail-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;   /* white space under the title (user request) */
  padding-right: 26px;   /* keep clear of the close button */
}
#detail-watch {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  padding: 0;
  color: var(--muted);
}
#detail-watch:hover { color: #f5c518; }
#detail-watch.on { color: #f5c518; }
#detail-link {
  font-size: 12px;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
#detail-link:hover { text-decoration: underline; }
#detail-stats { display: flex; flex-wrap: wrap; gap: 22px; margin-bottom: 10px; }

/* 52-week range bar */
#detail-range { margin: 2px 0 14px; max-width: 460px; }
#detail-range:empty { display: none; }
.rng-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.rng-ath { font-size: 11px; color: var(--muted); }
.rng-ath.at-high { color: var(--pos); font-weight: 600; }
.rng-bar {
  position: relative;
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--neg), #f39c12 55%, var(--pos));
  opacity: .85;
}
.rng-fill { display: none; }
.rng-marker {
  position: absolute;
  top: -3px;
  width: 4px;
  height: 14px;
  border-radius: 2px;
  background: #fff;
  transform: translateX(-50%);
  box-shadow: 0 0 0 2px var(--panel), 0 1px 4px rgba(0,0,0,.5);
}
.rng-ends {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  margin-top: 5px;
  font-variant-numeric: tabular-nums;
}
.rng-ends .accent { font-weight: 700; }

/* PSA population panel (detail modal) */
#detail-psa {
  margin: 0 0 14px;
  max-width: 460px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel2);
}
#detail-psa:empty { display: none; }
.psa-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.psa-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .5px;
  color: #fff;
  background: #c0392b;
  padding: 2px 6px;
  border-radius: 4px;
}
.psa-stats { display: flex; flex-wrap: wrap; gap: 22px; }
.psa-stats .dstat-value { font-size: 16px; }
.gem-cell { font-variant-numeric: tabular-nums; }

.dstat-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
  font-weight: 600;
}
.dstat-value { font-size: 18px; font-weight: 700; }
.dstat-value.accent { color: var(--accent); }
.pill-label {
  color: var(--muted);
  font-weight: 600;
  margin-right: 5px;
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: .4px;
}
.pill-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  display: inline-block;
  margin-right: 5px;
}
#detail-img {
  /* As big as the mirrored art actually is: images/<pid>.jpg is fit-in 500x500
     (download_images.py --size 500), so a portrait card maxes out at 358x500 —
     going past that just upscales and blurs the card text. Re-mirror at a
     bigger --size to go larger.
     BOTH axes are capped, via max-* with auto sizing so the aspect ratio is
     kept and nothing is letterboxed: capping the height alone let LANDSCAPE art
     (sealed boxes, promo sets — 500px on their long edge) render ~700px wide
     and cover the chart. The vh/vw terms keep it inside the popup on small
     windows; #chart-col's own 600px cap does the rest. */
  max-height: var(--detail-media-h);
  max-width: min(400px, 34vw);
  height: auto;
  width: auto;
  border-radius: 8px;
  background: var(--panel2);
}
/* Narrower than a card (booster packs etc — see fitDetailImg): the full 500px
   makes them read as lanky strips beside the chart, so they cap shorter. */
#detail-img.tall-art { max-height: calc(var(--detail-media-h) * 0.84); }
#detail-body { flex: 1; min-width: 0; }
#detail-title { font-size: 17px; font-weight: 600; }
#detail-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.pill {
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--panel2);
  border: 1px solid var(--border);
  color: var(--muted);
}
/* Fixed height so #img-col can offset by exactly this much and line the two
   columns up (see --chart-range-h) — don't make this row auto-height. */
#chart-range { display: flex; align-items: center; gap: 5px; height: 24px; margin-bottom: 4px; }
#chart-range .chip { padding: 3px 10px; font-size: 11px; }
/* The chart is always exactly --detail-media-h tall (see #detail); the image
   caps at the same value, so the two line up whatever shape the art is.
   Plotly's responsive:true re-fits the chart when the vh term changes. */
#chart { width: 100%; height: var(--detail-media-h); }
#detail-close {
  position: absolute;
  top: 8px; right: 12px;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 20px;
  cursor: pointer;
}
#detail-close:hover { color: var(--text); }

/* Prev/next arrows flank the modal, pinned to the viewport centre so they don't
   scroll with the card's content (they sit on the overlay, not inside #detail) */
.detail-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
#detail-prev { left: 16px; }
#detail-next { right: 16px; }
.detail-nav:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.detail-nav:disabled { opacity: .3; cursor: default; }
@media (max-width: 760px) {
  .detail-nav { width: 36px; height: 36px; font-size: 22px; }
  #detail-prev { left: 6px; }
  #detail-next { right: 6px; }
}

@media (max-width: 760px) {
  /* Stack chart over image. `align-items: stretch` is essential: without it the
     column flex sizes #chart-col to its content width (~160px), leaving a ~60px
     plot area after the y-axis margins — the chart looked squashed on the x-axis.
     The image opts out of the stretch so it keeps its natural aspect ratio. */
  #detail-main { flex-direction: column; align-items: stretch; }
  #chart-col { max-width: 100%; }
  /* phones get a shorter media height; #chart and the image both follow it */
  #detail { --detail-media-h: min(320px, 38vh); }
  #img-col { align-self: center; max-width: min(320px, 78vw); margin-top: 14px; }
  #img-slot { height: auto; justify-content: center; }
  #detail-img { max-width: 100%; height: auto; width: auto; align-self: center; }
}

/* ---- responsive / mobile ---- */
@media (max-width: 900px) {
  #cmp-main { flex-direction: column; }
  #cmp-chart, .cmp-tables { flex-basis: auto; width: 100%; }
  #bvc-main { flex-direction: column; }
  #bvc-chart, #bvc-side { flex-basis: auto; width: 100%; }
}

@media (max-width: 640px) {
  header h1 { font-size: 17px; }
  .tagline { display: none; }
  .header-inner { padding: 10px 14px 0; }
  #generated { font-size: 11px; }
  #overview, #sets, #bvc, #compare, #pictures, #deckbuilder { padding-left: 14px; padding-right: 14px; }
  #controls { padding: 10px 14px; }
  #table-shell { width: calc(100% - 28px); max-width: calc(var(--maxw) - 28px); }
  /* stat strips wrap naturally; stack the two charts on phones */
  #ov-cards { font-size: 12px; gap: 3px 10px; }
  #ov-charts { grid-template-columns: 1fr; }
  .ov-chart { height: 300px; }
  .stat-value { font-size: 15px; }
  /* drop the Units column from the increases/falls tables on phones */
  .ov-hide-sm { display: none; }
  /* narrower product-name column so the overview tables fit without scrolling */
  .ov-tables td.name-cell { max-width: 120px; }
  #bvc-chart, #cmp-chart { height: 360px; }
  .row-thumb { width: 26px; height: 36px; }
  td.name-cell { max-width: 190px; }
  #detail-overlay { padding: 10px; }
  #detail { padding: 14px; width: 96vw; }
  .dstat-value { font-size: 16px; }
  #detail-stats { gap: 16px; }
  #deck-cols { gap: 12px; }

  /* Touch targets: bump tabs + selects to the ~44px guideline on phones
     (they sit ~35px at desktop density, fiddly to tap). Desktop unchanged. */
  .tab { min-height: 44px; display: inline-flex; align-items: center; }
  select { min-height: 44px; }

  /* ---- main grid → compact table on mobile (Card · Price · % move) ---- */
  /* It stays a real <table>; the noisier columns are tagged .sm-hide in JS and
     dropped here, so it fits a phone with no 4× sideways scroll. Full stats are
     one tap away in the detail modal, and any shown column sorts by tapping its
     header — so the separate #msort control isn't needed (stays display:none). */
  #grid { width: 100%; }
  #grid .sm-hide { display: none; }
  #grid thead th { padding: 9px 6px; font-size: 10px; }
  #grid tbody td { padding: 9px 6px; font-size: 12.5px; }
  #grid td.name-cell { max-width: 60vw; white-space: normal; }
  #grid td.name-cell .name-txt { white-space: normal; }

  /* show the mobile metric pickers */
  .mmetric { display: inline-block; font-size: 12px; }
  #sets-metric { display: block; margin: 2px 0 12px; }

  /* ---- Sets tab → compact table on mobile (Set · Set Value · metric) ---- */
  /* Same treatment as the grid tabs: hide the .sm-hide columns so it fits with
     no sideways scroll; tap a set to open it in Singles. */
  #sets-table .sm-hide { display: none; }
  #sets-table thead th { padding: 9px 6px; font-size: 10px; }
  #sets-table tbody td { padding: 9px 6px; font-size: 12.5px; }
  #sets-table td.set-name { max-width: 46vw; white-space: normal; }
  #sets-table .set-name b { font-size: 13px; }
}

/* ============================ Events map tab ============================ */
#events.hidden { display: none; }   /* per-section hide rule, like the other tabs */
#ev-wrap {
  display: flex;
  height: 78vh;
  min-height: 520px;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  background: var(--panel);
}
#ev-side {
  width: 340px;
  flex: none;
  overflow-y: auto;
  border-right: 1px solid var(--border);
}
#ev-map { flex: 1; min-width: 0; background: var(--panel2); }
#ev-map.leaflet-container { font: inherit; background: var(--panel2); }

#ev-sub { padding: 12px 16px; border-bottom: 1px solid var(--border); font-size: 12px; }
.ev-filter { padding: 10px 16px 12px; border-bottom: 1px solid var(--border); }
.ev-hd { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 7px; }
.ev-hd strong { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.ev-hd button, #ev-toggle {
  background: none; border: 0; padding: 0; font: inherit; font-size: 12px;
  color: var(--accent); cursor: pointer; text-decoration: underline;
}
#ev-state {
  width: 100%; font: inherit; font-size: 13px; padding: 6px 9px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--panel2); color: var(--text); cursor: pointer;
}
#ev-state:hover { border-color: var(--accent); }
#ev-near {
  margin-top: 8px; width: 100%; font: inherit; font-size: 13px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--panel2); color: var(--text); cursor: pointer;
}
#ev-near:hover { border-color: var(--accent); }
#ev-near.on { background: var(--accent); border-color: var(--accent); color: #fff; }
#ev-near:disabled { opacity: .6; cursor: default; }

#ev-dbox { display: flex; flex-wrap: wrap; gap: 6px; }
#ev-dbox button {
  font: inherit; font-size: 12px; padding: 4px 10px; border: 1px solid var(--border);
  border-radius: 999px; background: none; color: var(--text); cursor: pointer;
}
#ev-dbox button:hover { border-color: var(--accent); }
#ev-dbox button.on { background: var(--accent); border-color: var(--accent); color: #fff; }
#ev-date {
  font: inherit; font-size: 12px; padding: 3px 8px; border: 1px solid var(--border);
  border-radius: 999px; background: var(--panel2); color: var(--text); cursor: pointer;
}
#ev-date.on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.ev-f { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 13px; cursor: pointer; }
.ev-f input { margin: 0; flex: none; accent-color: var(--accent); }
.ev-f .ev-fl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ev-f .ev-n { margin-left: auto; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; padding-left: 8px; }
.ev-f .ev-sw { width: 9px; height: 9px; border-radius: 50%; flex: none; }

.ev-store { padding: 11px 16px; border-bottom: 1px solid var(--border); cursor: pointer; }
.ev-store:hover { background: var(--panel2); }
.ev-store h2 { margin: 0; font-size: 13.5px; font-weight: 600; }
.ev-meta { color: var(--muted); font-size: 12px; margin-top: 2px; }
.ev-count { color: var(--accent); font-weight: 600; }
.ev-dist { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }
.ev-empty { padding: 22px 16px; color: var(--muted); font-size: 13px; }

/* Leaflet popup — themed dark to match the site */
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--panel); color: var(--text); }
.leaflet-popup-content-wrapper { border: 1px solid var(--border); border-radius: 10px; }
.leaflet-popup-content { margin: 11px 13px; font: 13px/1.45 inherit; max-height: 280px; overflow-y: auto; }
.leaflet-container a.leaflet-popup-close-button { color: var(--muted); }
.ev-pop h2 { margin: 0 0 4px; font-size: 14px; }
.ev-pop .ev-addr { color: var(--muted); font-size: 12px; margin-bottom: 7px; }
.ev-pop .ev-addr a { color: var(--accent); }
.ev-ev { padding: 5px 0; border-top: 1px solid var(--border); }
.ev-ev .t { font-weight: 600; }
.ev-ev .d { color: var(--muted); font-size: 12px; margin-left: 14px; }
.ev-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
.ev-closed { color: var(--neg); }

@media (max-width: 760px) {
  #ev-wrap { flex-direction: column; height: auto; }
  #ev-side { width: auto; max-height: 42vh; border-right: 0; border-bottom: 1px solid var(--border); }
  #ev-map { height: 60vh; }
}

/* About tab — static informational page */
#about.hidden { display: none; }   /* per-section hide rule, like the other tabs */
/* The EV-Tracker table caption is a shared element toggled with .hidden but had
   no display:none rule, so it leaked onto grid-less tabs (e.g. About). */
#bvc-table-caption.hidden { display: none; }
#about-body { max-width: 760px; padding: 8px 22px 40px; }
#about-body h2 { font-size: 22px; margin: 8px 0 14px; }
#about-body p { color: var(--text); line-height: 1.6; margin: 0 0 14px; }
.about-lead { font-size: 16px; color: var(--muted) !important; }
.about-list { list-style: none; padding: 0; margin: 0 0 8px; }
.about-list li {
  color: var(--text); line-height: 1.55; padding: 10px 0 10px 16px;
  border-bottom: 1px solid var(--border); position: relative;
}
.about-list li:last-child { border-bottom: 0; }
.about-list li::before {
  content: ""; position: absolute; left: 0; top: 18px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}
.about-list strong { color: var(--text); }
.about-updates-title { font-size: 18px; margin: 28px 0 14px; }
.updates .update { margin: 0 0 20px; }
.updates .update:last-child { margin-bottom: 0; }
.update-date { font-weight: 600; color: var(--text); font-size: 14px; margin: 0 0 6px; }

/* ---------------------------------------------------------------------------
   Frozen name column on the wide grid tables (added 2026-08-22)

   Singles is 18 columns / ~1530px, so #table-wrap scrolls horizontally — and
   the card name used to scroll away with it, leaving you reading "PSA Pop /
   Gem %" with no idea which row you were on. Pinning the name column keeps the
   row identifiable no matter how far right you scroll.

   Only the NAME column is pinned, not the watch (+) column in front of it. The
   watch column renders ~28px wide despite its `width: 34px` (table layout
   treats width as a suggestion), so any hardcoded left offset for the name
   column would be off by a few px. Pinned at left:0 the name needs no such
   number, and the watch column simply slides underneath as you scroll.

   Two things this depends on:
   - The pinned cell uses `background: inherit` so it keeps its ROW's current
     colour (stripe / hover / selected). That needs the row to paint an OPAQUE
     background — the default row was transparent and the even stripe was
     translucent white, either of which would let scrolling columns show through
     the pinned cell. The four tr rules below restate the existing colours
     opaquely; id-scoped so they beat the unscoped rules without changing any
     shade (#1b1d24 is that 1.5% white stripe flattened onto --panel).
   - `border-collapse: collapse` paints borders on the TABLE rather than the
     cell, so a sticky cell can lose its border-bottom once it detaches. The
     inset shadows redraw it (a harmless exact overlap if the browser paints the
     border anyway) and add a right edge marking where the frozen column ends.

   Desktop only: under 640px the table drops to 4 columns and never overflows.
--------------------------------------------------------------------------- */
@media (min-width: 641px) {
  #table-wrap tbody tr { background: var(--panel); }
  #table-wrap tbody tr:nth-child(even) { background: #1b1d24; }
  #table-wrap tbody tr:hover { background: var(--panel2); }
  #table-wrap tbody tr.selected { background: #20304a; }

  #table-wrap tbody td.name-cell {
    position: sticky; left: 0; z-index: 1;
    background: inherit;
    box-shadow: inset -1px 0 0 var(--border), inset 0 -1px 0 var(--border);
  }
  /* last row carries no separator (matches `tbody tr:last-child td` above) */
  #table-wrap tbody tr:last-child td.name-cell { box-shadow: inset -1px 0 0 var(--border); }

  /* Header cells are already sticky vertically; pin the name header
     horizontally too and layer the row above the cells scrolling under it. */
  #table-wrap thead th { z-index: 2; }
  #table-wrap thead th:nth-child(2) {
    position: sticky; left: 0; z-index: 3;
    background: var(--panel2);
    box-shadow: inset -1px 0 0 var(--border);
  }
}

/* ---------------------------------------------------------------------------
   Focus rings (added 2026-08-22)

   All seven of these controls set `outline: none`, which hands us full
   responsibility for the focus indicator. #deck-export had NO :focus rule at
   all, so keyboard users got no indication whatsoever that it was focused. The
   rest changed border-colour only — a 1px cue. The added ring makes focus
   unmistakable (WCAG 2.2 focus-appearance) without touching the resting look of
   any control.
--------------------------------------------------------------------------- */
#search:focus, #cmp-input:focus, #pic-input:focus, #deck-name:focus,
#deck-search:focus, #deck-import:focus, #deck-export:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(77, 163, 255, 0.35);
}

/* ---------------------------------------------------------------------------
   iOS zoom guard (added 2026-08-22)

   Mobile Safari zooms the page in whenever a control under 16px takes focus,
   and never zooms back out — so tapping the deck or compare search box left the
   whole page stuck magnified. 16px is Safari's threshold, not a design
   preference; at phone sizes these controls barely change but the zoom-lock
   disappears. (The other fix, maximum-scale=1 on the viewport meta, works by
   disabling pinch-zoom for everyone — a bad trade.)
--------------------------------------------------------------------------- */
@media (max-width: 640px) {
  /* !important is deliberate. Several of these controls carry id-level rules
     (#deck-name, #ev-date, #sets-metric ...) that outrank any selector we could
     reasonably write here, and 16px is a hard Safari threshold rather than a
     style preference — a control that misses it re-introduces the zoom trap. An
     explicit id list would work today and silently regress the moment a new
     control gets its own rule. */
  input, select, textarea { font-size: 16px !important; }
}

/* ---------------------------------------------------------------------------
   Pulse-stat separators on mobile (added 2026-08-22)
   The inline "·" dividers between the Market Overview pulse stats are laid out
   as blocks once the stats stack at phone widths, so each wrapped line ended
   with a stranded dot that read as a typo. They're decorative (aria-hidden in
   app.js), so on mobile they simply go away — the stacking already separates
   the stats. This also retires the page's only WCAG contrast failure, which was
   this dot at 1.85:1.
--------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .ov-sep { display: none; }
}

/* ---------------------------------------------------------------------------
   Grid right-edge scroll fade (added 2026-08-22)

   The wide tables scroll horizontally, but #table-wrap is as TALL as the whole
   table — 100,000+ px at 1,800 rows — so its horizontal scrollbar sits below
   every single row and is, in practice, unreachable. With no scrollbar in view
   and no other cue, columns past the right edge (PSA Pop, Gem %) were simply
   invisible features: the header just stopped mid-word.

   This paints a fade over the right edge whenever there is more to scroll
   toward (app.js's updateGridFade toggles .can-scroll-x on scroll/resize/render
   and clears it at the end of the scroll, so a fully-scrolled or non-scrolling
   table shows nothing). It has to be an OVERLAY rather than a background on the
   scroller: the rows carry opaque backgrounds — needed so the frozen name
   column can inherit them — and would paint straight over any background layer.
--------------------------------------------------------------------------- */
#table-shell::after {
  content: '';
  position: absolute;
  top: 1px; right: 1px; bottom: 1px;     /* sit inside the wrap's 1px border */
  width: 48px;
  pointer-events: none;                  /* never eat a click or a scroll */
  border-radius: 0 12px 12px 0;          /* follow the wrap's rounded corner */
  background: linear-gradient(to right, rgba(15, 17, 21, 0), rgba(15, 17, 21, 0.92));
  opacity: 0;
  transition: opacity .15s ease;
}
#table-shell.can-scroll-x::after { opacity: 1; }

/* ---- Deal sheet (beta) ---- */
#deal.hidden, #deal-results.hidden { display: none; }
#deal { max-width: var(--maxw); margin: 0 auto; padding: 18px 22px 40px; }
.deal-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 14px; }
.deal-head h2 { margin: 0; font-size: 20px; }
.deal-layout { display: flex; gap: 18px; align-items: flex-start; }
.deal-main { flex: 1 1 auto; min-width: 0; }
.deal-side { flex: 0 0 320px; position: sticky; top: 108px; }
.deal-panel {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px;
}
.deal-panel-head { display: flex; justify-content: space-between; margin-bottom: 10px; }
#deal-items { overflow-x: auto; }

.deal-search-wrap { position: relative; margin-bottom: 14px; }
#deal-search {
  width: 100%; padding: 10px 14px; font: inherit; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
#deal-search:focus { outline: none; border-color: var(--accent); }
#deal-results {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 20;
  background: var(--panel2); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.45); overflow: hidden;
}
.deal-hit {
  display: grid; grid-template-columns: 34px 1fr auto 80px 80px; align-items: center; gap: 10px;
  width: 100%; padding: 6px 12px; background: none; border: none; border-bottom: 1px solid var(--border);
  color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.deal-hit:hover { background: rgba(77,163,255,0.08); }
.deal-hit-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deal-hit .num { text-align: right; }
.deal-hit-add { color: var(--accent); text-align: right; font-size: 12px; font-weight: 600; }
.deal-more { padding: 8px 12px; font-size: 12px; }

.deal-table { width: 100%; border-collapse: collapse; }
.deal-table th {
  text-align: left; font-size: 11px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em; padding: 6px 8px; border-bottom: 1px solid var(--border);
}
.deal-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.deal-table tr:last-child td { border-bottom: none; }
.deal-table .num { text-align: right; white-space: nowrap; }
.deal-table .name-cell { max-width: 300px; }
.deal-open { cursor: pointer; }
.deal-open:hover { color: var(--accent); text-decoration: underline; }
.deal-qty { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.deal-qty span { min-width: 26px; text-align: center; font-variant-numeric: tabular-nums; }
.deal-qty button, .deal-x {
  background: var(--panel2); border: none; color: var(--text); font: inherit; width: 26px; height: 26px; cursor: pointer;
}
.deal-qty button:hover { background: var(--border); }
.deal-x { background: none; color: var(--muted); font-size: 18px; border-radius: 6px; }
.deal-x:hover { color: var(--neg); background: rgba(255,107,107,0.1); }
.deal-price-wrap { color: var(--muted); font-size: 12px; }
.deal-price {
  width: 78px; margin-left: 2px; padding: 3px 6px; text-align: right; font: inherit; font-size: 13px;
  color: var(--text); background: var(--panel2); border: 1px solid transparent; border-radius: 6px;
}
.deal-price:hover, .deal-price:focus { border-color: var(--border); outline: none; }
.deal-price.over { border-color: rgba(240,180,60,0.6); color: #f0b43c; }
.deal-mkt { font-size: 11px; color: var(--muted); }
.deal-mkt button { background: none; border: none; color: var(--accent); cursor: pointer; padding: 0 2px; }
.deal-empty { padding: 34px 10px; text-align: center; color: var(--muted); }

.deal-field { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-weight: 600; margin-top: 4px; }
.deal-field + .deal-chips, .deal-field-label + .deal-chips { margin-top: 8px; }
.deal-field-label { font-weight: 600; margin-top: 16px; }
.deal-field input {
  width: 96px; padding: 6px 8px; text-align: right; font: inherit; color: var(--text);
  background: var(--panel2); border: 1px solid var(--border); border-radius: 7px;
}
.deal-field input:focus { outline: none; border-color: var(--accent); }
.deal-input-suffix { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); }
.deal-input-suffix input { width: 84px; }
#deal-rate-field.hidden, #deal-rate-src.hidden { display: none; }
#deal-cur {
  padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border);
  background: var(--panel2); color: var(--text); font: inherit;
}
.deal-panel .deal-field:not(:first-child) { margin-top: 16px; }
.deal-chips { display: flex; gap: 5px; flex-wrap: wrap; }
.deal-chips .chip { padding: 4px 10px; }
.deal-hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.linkbtn { background: none; border: none; padding: 0; color: var(--accent); font: inherit; cursor: pointer; }
.linkbtn:hover { text-decoration: underline; }

#deal-totals { margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--border); }
.deal-line { display: flex; justify-content: space-between; padding: 3px 0; font-variant-numeric: tabular-nums; }
.deal-grand {
  display: flex; justify-content: space-between; align-items: baseline; margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border); font-weight: 700;
}
.deal-grand span:last-child { font-size: 26px; color: var(--pos); font-variant-numeric: tabular-nums; }
.deal-actions { display: flex; gap: 8px; margin-top: 14px; }
.deal-btn {
  flex: 1; padding: 8px 12px; font: inherit; font-weight: 600; color: var(--text); cursor: pointer;
  background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
}
.deal-btn.primary { background: var(--accent); border-color: var(--accent); color: #0b1220; flex: 2; }
.deal-btn:disabled { opacity: 0.4; cursor: default; }

@media (max-width: 900px) {
  .deal-layout { flex-direction: column; align-items: stretch; }
  .deal-side { position: static; flex-basis: auto; width: 100%; }
}
@media (max-width: 640px) {
  #deal { padding: 12px 10px 30px; }
  .deal-table th:nth-child(2), .deal-table td:nth-child(2),
  .deal-table th:nth-child(5), .deal-table td:nth-child(5) { display: none; }
  .deal-table .name-cell { max-width: 80px; }
  .deal-table td, .deal-table th { padding: 6px 3px; }
  .deal-table th { font-size: 10px; letter-spacing: 0; }
  .deal-x { width: 20px; }
  .deal-table .row-thumb { display: none; }
  .deal-price-wrap { font-size: 0; }          /* drop the "US$" prefix, keep the input */
  .deal-price { width: 60px; padding: 3px 4px; font-size: 13px; }
  .deal-qty button { width: 22px; }
  .deal-qty span { min-width: 18px; }
  .deal-panel { padding: 12px 10px; }
  .deal-hit { grid-template-columns: 34px 1fr 70px 56px; }
  .deal-hit > .dim { display: none; }
}
.about-sources { margin-top: 24px; }
