/* Card Ladder - mobile first, theme aware, zero external assets.
   The phone is the primary client, so everything below is sized for a thumb
   and reads in daylight or in bed. */

:root {
  --bg: #f7f7f9;
  --panel: #ffffff;
  --ink: #14161a;
  --muted: #6b7280;
  --line: #e4e6eb;
  --accent: #1f4ed8;
  --up: #0f7b4f;
  --down: #b4232a;
  --warn: #9a5b00;
  --warn-bg: #fff5e0;
  --radius: 12px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115; --panel: #171a21; --ink: #e8eaee; --muted: #9aa1ad;
    --line: #262b35; --accent: #7aa2ff; --up: #35c98a; --down: #ff6b73;
    --warn: #f0b357; --warn-bg: #2a2010;
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  padding-bottom: env(safe-area-inset-bottom);
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

header {
  display: flex; gap: 12px; align-items: center; padding: 12px 14px;
  background: var(--panel); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
.brand { font-weight: 700; letter-spacing: -.3px; white-space: nowrap; }
.search { flex: 1; }
.search input {
  width: 100%; padding: 10px 12px; font-size: 16px; /* 16px stops iOS zoom */
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg); color: var(--ink);
}

nav {
  display: flex; gap: 2px; overflow-x: auto; padding: 0 8px;
  background: var(--panel); border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}
nav::-webkit-scrollbar { display: none; }
nav a {
  padding: 11px 14px; color: var(--muted); white-space: nowrap;
  border-bottom: 2px solid transparent; font-size: 15px;
}
nav a.on { color: var(--ink); border-bottom-color: var(--accent); font-weight: 600; }

.asof {
  padding: 7px 14px; font-size: 12.5px; color: var(--muted);
  background: var(--panel); border-bottom: 1px solid var(--line);
}
.asof.stale { background: var(--warn-bg); color: var(--warn); font-weight: 600; }
.pill {
  display: inline-block; margin-left: 8px; padding: 1px 8px; font-size: 11px;
  border: 1px solid var(--line); border-radius: 999px;
}

main { padding: 14px; max-width: 900px; margin: 0 auto; }
footer {
  padding: 22px 14px 40px; color: var(--muted); font-size: 12px;
  text-align: center;
}

h1 { font-size: 21px; margin: 4px 0 14px; letter-spacing: -.3px; }
h2 { font-size: 16px; margin: 22px 0 10px; }
.muted { color: var(--muted); }
.up { color: var(--up); }
.down { color: var(--down); }

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px; margin-bottom: 12px;
}
/* center, not baseline: a row carrying a card photo is as tall as the photo,
   and baseline alignment would drop the price on the right down to meet the
   text sitting beside it. */
.row { display: flex; justify-content: space-between; gap: 12px; align-items: center; }
.big { font-size: 27px; font-weight: 700; letter-spacing: -.6px; }
.tiny { font-size: 12px; }

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 12px; }
.stat b { display: block; font-size: 20px; letter-spacing: -.4px; }
.stat span { color: var(--muted); font-size: 12px; }

/* Charts */
.chart { margin: 0; }
.chart svg { display: block; overflow: visible; }
.chart .line { stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.chart .line.up { stroke: var(--up); }
.chart .line.down { stroke: var(--down); }
.chart .area { fill: var(--accent); opacity: .10; }
.chart .area.up { fill: var(--up); }
.chart .area.down { fill: var(--down); }
.chart .last { fill: var(--accent); }
.chart .last.up { fill: var(--up); }
.chart .last.down { fill: var(--down); }
.chart .dot { fill: var(--muted); }
.chart .mark, .chart .axis { fill: var(--muted); font-size: 11px; }
.chart figcaption { display: flex; gap: 10px; align-items: baseline; padding-top: 6px; }
.chart figcaption > span { display: flex; gap: 10px; align-items: baseline; }

/* Cursor. pan-y, not none: dragging sideways reads the chart, dragging up and
   down still scrolls the page - a chart that traps the scroll on a phone is
   worse than one that does nothing. */
.chart svg { touch-action: pan-y; }
.chart .hit { cursor: crosshair; }
.crosshair { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: .75; }
.cursordot { fill: var(--accent); }
.cursordot.up { fill: var(--up); }
.cursordot.down { fill: var(--down); }
/* [hidden] loses to the display rules above unless it is stated again. */
.chart [hidden] { display: none; }
.readout .rv { font-variant-numeric: tabular-nums; }
.spark .line { stroke: var(--accent); stroke-width: 1.5; }
.spark .line.up { stroke: var(--up); }
.spark .line.down { stroke: var(--down); }

/* Population bars */
.barrow { display: flex; align-items: center; gap: 8px; margin: 3px 0; font-size: 13px; }
.barlabel { width: 82px; color: var(--muted); flex: none; }
/* The track is an <svg> and the fill a <rect>, so the fill is painted with
   `fill` rather than `background` - see bar_row in chart.py for why the bar is
   drawn rather than sized with an inline style. */
.bartrack { flex: 1; background: var(--line); border-radius: 4px; height: 9px; display: block; }
.barfill { fill: var(--accent); }
.barval { width: 66px; text-align: right; flex: none; font-variant-numeric: tabular-nums; }

/* Tables scroll inside themselves - the page body must never scroll sideways */
.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* A card's grades read as one block; this line separates one card from the
   next without adding a whole heading row per card. */
tr.groupstart td { border-top: 2px solid var(--line); }

.badge {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; font-weight: 700; border: 1px solid var(--line);
}
.badge.exact { color: var(--up); border-color: var(--up); }
.badge.verify { color: var(--warn); border-color: var(--warn); }
.badge.gone { color: var(--muted); }

.note {
  background: var(--warn-bg); color: var(--warn); border-radius: 8px;
  padding: 9px 11px; font-size: 13px; margin: 10px 0;
}
.conf { display: inline-flex; gap: 2px; vertical-align: middle; }
.conf i { width: 7px; height: 7px; border-radius: 50%; background: var(--line); display: block; }
.conf i.on { background: var(--up); }

.empty { color: var(--muted); text-align: center; padding: 40px 10px; }

/* These were style="" attributes. The CSP sends style-src 'self' with no
   'unsafe-inline', so the browser refused every one of them and the effect
   simply never happened - the selected subject and the current grade row were
   not actually bold on the live site. */
.sel, tr.sel td { font-weight: 700; }
.pastebox {
  width: 100%; padding: 10px; font: 14px ui-monospace, monospace;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--ink);
}
form.inline { display: flex; gap: 8px; }
form.inline.spaced { margin-top: 8px; }
select.grow {
  flex: 1; padding: 9px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--bg); color: var(--ink);
}
input[type=password], input[type=text] {
  padding: 10px 12px; font-size: 16px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--bg); color: var(--ink); width: 100%;
}
button {
  padding: 10px 16px; font-size: 15px; border-radius: 8px; border: 0;
  background: var(--accent); color: #fff; font-weight: 600;
}

/* Card photos. Cached locally and served from our own origin, so the page
   keeps img-src 'self'. Sized so a slab reads at a glance on a phone without
   pushing the price below the fold. */
.cardphoto {
  width: 132px; height: auto; border-radius: 8px; flex: none;
  border: 1px solid var(--line); background: var(--bg);
}
/* Source photos run anywhere from 0.52 to 1.0 wide/tall: a slab shot is much
   taller than the card, a raw scan is card-shaped, and some arrive square and
   padded. `cover` at one fixed ratio therefore cropped the PSA label off the
   tall ones, which is the half worth seeing. `contain` letterboxes instead, so
   the box stays a consistent size and nothing is cut. */
.thumb {
  width: 70px; height: 100px; object-fit: contain; border-radius: 5px;
  vertical-align: middle; margin-right: 11px; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  flex-shrink: 0;          /* never squash on a narrow phone */
}
/* Constituent tables are dense - one row per grade - so they take a smaller
   box than the list views. */
td .thumb { width: 50px; height: 72px; margin-right: 9px; }

/* Photo on the left, a column of text beside it, price on the right. Laying
   this out inline instead let the metadata wrap underneath the photo once the
   photo got big, which is what made the larger size look broken. */
.idline { display: flex; align-items: center; min-width: 0; }
.idtext { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.idtext > * { overflow: hidden; text-overflow: ellipsis; }
.row .big { white-space: nowrap; flex-shrink: 0; }
/* A date or figure may sit on its own line, but must never split across two. */
.nowrap { white-space: nowrap; }

/* Table cells are nowrap so figures never split, but a card name plus a photo
   is wide enough to push the Grade column off a phone screen. This one column
   wraps instead, beside the photo rather than under it. */
td.cardcell { white-space: normal; min-width: 212px; }
/* min-width, not just max: the table's auto layout will otherwise squeeze this
   column down to one word per line, since max-width is only a ceiling. */
.cardcell .idtext { min-width: 142px; max-width: 200px; }
.cardcell .idtext > * { overflow: visible; }

/* On a phone the price and a 70px photo cannot both have their full width, and
   the price is the thing that must never wrap mid-figure. */
@media (max-width: 460px) {
  .row .big { font-size: 20px; }
  .thumb { width: 62px; height: 88px; margin-right: 9px; }
}
