/* ═══════════════════════════════════════════════════════════
   BOARD GAMES — shared styles for the browse & analytics pages

   Loaded AFTER main.css, which supplies the site's tokens
   (--color-bg / --color-surface / --color-text / --color-accent
   / --radius / --gap / --padding / --font-body / --font-display).
   Only board-game-specific additions live here.

   Light-only, deliberately: the rest of bjoebarnhart.com has no
   prefers-color-scheme handling, so a dark page reached from a
   light Projects page would read as broken. If dark mode is ever
   added site-wide, it belongs in main.css and this file inherits.
═══════════════════════════════════════════════════════════ */

:root {
  /* ── Data-viz tokens ──────────────────────────────────────
     Validated with the dataviz skill's validator against the
     card surface (#ffffff), all-pairs, light mode:
       CVD worst pair    9.2  (>= 8 target)
       normal-vision     24.0 (>= 15 floor)
     --viz-series-3 sits at 2.82:1 contrast, under the 3:1 line,
     so anything using it ships visible labels AND a table view.
     Most charts here are single-series and use only series-1
     plus the de-emphasis gray.

     Series-1 is terracotta rather than the site's blue accent: the
     page was reading as one colour, and warm sits better on the
     #f8f7f4 page than another blue. Each chart below picks its own
     hue by role, so no two adjacent cards repeat. */
  --viz-series-1: #b8633a;
  --viz-series-2: #1baf7a;
  --viz-series-3: #4a3aa7;
  --viz-series-4: #2a78d6;

  /* ── Year ramp (ordinal) ──────────────────────────────────
     Year is ordered, so it takes a one-hue ramp, not categorical
     hues: light = older, dark = recent, and chronology is legible
     inside a cluster without reading the legend. Validated with
     --ordinal against #ffffff: monotone L, all adjacent ΔL >= 0.06,
     light end 2.01:1 (>= 2.0 floor), hue spread 2°. */
  --viz-year-1: #ff9f6f;
  --viz-year-2: #ec8a5d;
  --viz-year-3: #d2764b;
  --viz-year-4: #b8633a;
  --viz-year-5: #9e5029;
  --viz-year-6: #863d18;
  --viz-year-7: #6e2b04;

  /* Emphasis pair: the point vs. the context. */
  --viz-muted:    #b8b6ae;
  --viz-muted-in: #d7d5cc;

  /* Chart chrome — recessive, hairline, never dashed. */
  --viz-grid:     #ecece6;
  --viz-axis:     #d6d4cb;
  --viz-label:    #83817b;

  --viz-surface:  var(--color-surface);
}

/* ═══════════════════════════════════════════
   PAGE SHELL
═══════════════════════════════════════════ */

.bg-page {
  max-width: 1180px;
}

.bg-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 2rem;
}

.bg-header .page-subtitle {
  margin-bottom: 0;
}

/* Tab switch between the two pages */
.bg-tabs {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.25rem;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 99px;
  box-shadow: 0 2px 8px var(--color-border);
}

.bg-tab {
  padding: 0.45rem 1.1rem;
  border-radius: 99px;
  font-size: 0.875rem;
  color: var(--color-text-muted);
  transition: background-color 0.2s, color 0.2s;
  white-space: nowrap;
}

.bg-tab:hover { color: var(--color-text); }

.bg-tab[aria-current='page'] {
  background-color: var(--color-accent);
  color: #fff;
}

/* ═══════════════════════════════════════════
   BUILD BANNER — data provenance, stated plainly
═══════════════════════════════════════════ */

.bg-note {
  display: flex;
  gap: 0.6rem;
  padding: 0.85rem 1.1rem;
  margin-bottom: 2rem;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--viz-series-2);
  border-radius: var(--radius-sm);
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--color-text-muted);
}

.bg-note strong { color: var(--color-text); font-weight: 600; }

/* ═══════════════════════════════════════════
   STAT TILES / KPI ROW
═══════════════════════════════════════════ */

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--gap);
  margin-bottom: 2.5rem;
}

.stat-tile {
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 1.15rem 1.25rem;
  box-shadow: 0 2px 8px var(--color-border);
}

.stat-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--color-text-muted);
  margin-bottom: 0.45rem;
}

/* Sans, never the display serif; proportional figures, not tabular. */
.stat-value {
  font-family: var(--font-body);
  font-size: 1.9rem;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.stat-hero .stat-value { font-size: 3rem; }

.stat-sub {
  margin-top: 0.4rem;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  line-height: 1.45;
}

/* ═══════════════════════════════════════════
   CHART CARD
═══════════════════════════════════════════ */

.chart-card {
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 1.35rem 1.5rem 1.5rem;
  box-shadow: 0 2px 8px var(--color-border);
  margin-bottom: var(--gap);
  /* Height is never fixed: the container grows to include the
     x-axis band, so axis labels can't be cropped into a nested
     scrollbar. */
}

.chart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.25rem;
}

.chart-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: normal;
  line-height: 1.25;
}

.chart-note {
  font-size: 0.82rem;
  color: var(--color-text-muted);
  line-height: 1.5;
  margin-bottom: 1.1rem;
  max-width: 68ch;
}

.chart-body { position: relative; }

/* Wide charts scroll inside their own card, never the page. */
.chart-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 0.25rem;
}

.chart-svg { display: block; width: 100%; height: auto; }

/* Inside a scroller the chart must keep its natural width and scroll, not
   obediently shrink to the container -- 73 months squeezed into a phone-width
   card is 4px per bar, which is not a chart. min-width keeps it filling the
   card on wide screens. */
.chart-scroll .chart-svg {
  width: auto;
  max-width: none;
  min-width: 100%;
}

/* ── Chart chrome primitives ── */
.viz-grid-line { stroke: var(--viz-grid); stroke-width: 1; shape-rendering: crispEdges; }
.viz-axis-line { stroke: var(--viz-axis); stroke-width: 1; shape-rendering: crispEdges; }

.viz-tick {
  fill: var(--viz-label);
  font-size: 11px;
  font-variant-numeric: tabular-nums; /* axis ticks align vertically */
}

.viz-label-value {
  fill: var(--color-text);
  font-size: 11px;
  font-weight: 600;
}

.viz-label-cat { fill: var(--color-text-muted); font-size: 11.5px; }

/* Each card sets --viz-mark-color, so a chart's hue is a property of the
   card rather than a global. Falls back to series-1 for anything unset. */
.viz-mark { fill: var(--viz-mark-color, var(--viz-series-1)); }
.viz-mark-muted { fill: var(--viz-muted); }

/* The 2px surface ring that keeps overlapping dots legible. */
.viz-dot { stroke: var(--viz-surface); stroke-width: 2; }

.viz-hit { fill: transparent; cursor: pointer; }
.viz-hit:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

/* Hover darkens the mark's own hue rather than repainting it.
   It used to jump to the site's blue accent, which read as "this bar is a
   different thing" instead of "this bar is under the cursor" -- and after the
   palette moved off blue, as a colour from nowhere. Browsers without
   color-mix simply keep the base fill, which is a fine no-op. */
.viz-band:hover .viz-mark,
.viz-band.is-active .viz-mark {
  fill: color-mix(in oklab, var(--viz-mark-color, var(--viz-series-1)) 82%, #000);
}

.viz-cluster-bar:hover { opacity: 1; filter: brightness(0.86); }

/* ── Legend (only rendered for >= 2 series) ── */
.viz-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin-bottom: 0.9rem;
  font-size: 0.8rem;
  color: var(--color-text-muted); /* text wears text tokens, never the data color */
}

.viz-legend-item { display: inline-flex; align-items: center; gap: 0.4rem; }

.viz-swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════
   TOOLTIP
═══════════════════════════════════════════ */

.viz-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity 0.12s ease;
  background-color: #1c1c1e;
  color: #fff;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  line-height: 1.45;
  white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
}

.viz-tip.is-visible { opacity: 1; }
.viz-tip-title { font-weight: 600; margin-bottom: 0.1rem; }
.viz-tip-row { color: rgba(255, 255, 255, 0.78); }

/* ═══════════════════════════════════════════
   TABLE VIEW — every chart's accessible twin
═══════════════════════════════════════════ */

/* The table is the screen-reader path through each chart, so it stays in the
   DOM and stays reachable — it is only removed from sight. `clip` rather than
   `display:none`, because a hidden table is not announced at all. Sighted
   readers get the same values from the hover tooltip and direct labels. */
.viz-table-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.table-wrap { margin-top: 0.9rem; overflow-x: auto; }
.table-wrap[hidden] { display: none; }

/* ── multi-series legend ─────────────────────────────────── */

.viz-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.85rem;
  margin: 0 0 0.85rem;
}

.viz-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.76rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.viz-legend-item i {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  display: inline-block;
  flex: 0 0 auto;
}

.viz-cluster-bar { transition: filter 0.12s ease; }

/* ── in-card control (year picker, range switch) ─────────── */

.chart-control {
  margin-left: auto;
  font-family: var(--font-body);
  font-size: 0.78rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  cursor: pointer;
}

.chart-head { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }

.viz-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
}

.viz-table th,
.viz-table td {
  padding: 0.4rem 0.7rem 0.4rem 0;
  text-align: left;
  border-bottom: 1px solid var(--color-border);
}

.viz-table th {
  font-weight: 600;
  color: var(--color-text-muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.viz-table td.num,
.viz-table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ═══════════════════════════════════════════
   SECTION + GRID SCAFFOLD
═══════════════════════════════════════════ */

.bg-section-title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: normal;
  margin: 2.5rem 0 0.4rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--color-border);
}

.bg-section-note {
  font-size: 0.875rem;
  color: var(--color-text-muted);
  line-height: 1.6;
  max-width: 72ch;
  margin-bottom: 1.25rem;
}

.chart-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--gap);
  align-items: start;
}

/* Empty state, e.g. shelf of shame with no real owned list yet. */
.bg-empty {
  padding: 1.5rem;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius);
  font-size: 0.875rem;
  color: var(--color-text-muted);
  line-height: 1.6;
}

/* ═══════════════════════════════════════════
   BROWSE — CONTROLS
═══════════════════════════════════════════ */

/* One filter row above everything it scopes; never per-card. */
.browse-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  align-items: center;
  padding: 0.9rem 1rem;
  margin-bottom: 1.25rem;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 2px 8px var(--color-border);
  position: sticky;
  top: 0;
  z-index: 4;
}

.control-search {
  flex: 1 1 220px;
  min-width: 180px;
}

.control-search input,
.browse-controls select {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.875rem;
  color: var(--color-text);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem;
}

.browse-controls select { width: auto; cursor: pointer; }

.control-search input:focus-visible,
.browse-controls select:focus-visible,
.chip:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.control-group {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

.chip {
  font-family: var(--font-body);
  font-size: 0.78rem;
  padding: 0.35rem 0.75rem;
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 99px;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.chip:hover { color: var(--color-text); }

.chip[aria-pressed='true'] {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.browse-count {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin-bottom: 1rem;
}

/* ── owned / played-elsewhere toggle ───────────────────────────
   Sits above the filter bar and is styled heavier than the chips
   inside it, because it answers a different kind of question: the
   chips narrow a list, this one chooses which list. */
.shelf-toggle {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}

.shelf-option {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background-color: var(--color-surface);
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.shelf-option:hover { color: var(--color-text); }

.shelf-option[aria-pressed='true'] {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.shelf-option:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.shelf-count {
  font-size: 0.78rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}

/* ═══════════════════════════════════════════
   BROWSE — CARD GRID
═══════════════════════════════════════════ */

.game-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--gap);
}

.game-card {
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 2px 8px var(--color-border);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
  padding: 0;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.game-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.1);
}

.game-card:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* A fixed square frame, whatever the cover's own proportions are, so the
   grid stays aligned. Square because it is the median of the real covers:
   across the 57 mirrored images the ratio runs 0.33 to 2.06 with a median
   of 1.00 — 25 portrait, 21 square, 11 landscape. That spread is also why
   the image is `contain` rather than `cover`: cropping to a common frame
   sliced the titles off the portrait boxes, and the title is exactly how
   you recognise a game on a shelf.
   The gradient is the placeholder for games with no mirrored art. Its hue
   is derived from the game's name so a card keeps the same colour between
   builds — identity, not decoration. */
.game-art {
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem;
  position: relative;
  overflow: hidden;
}

/* Letterbox bars read as a quiet mat around the cover, not as a second
   colour competing with the art. */
.game-art.has-art {
  background: var(--color-bg);
  padding: 0;
}

.game-art-initials {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 5vw, 2.4rem);
  line-height: 1;
  color: rgba(255, 255, 255, 0.92);
  letter-spacing: 0.02em;
}

.game-art img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.game-body {
  padding: 0.8rem 0.9rem 0.95rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  flex: 1;
}

.game-name {
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-text);
}

.game-meta {
  font-size: 0.76rem;
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.6rem;
  font-variant-numeric: tabular-nums;
  margin-top: auto;
}

.game-flag {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--viz-series-2);
}

/* BGG rank, over the cover. Its own dark chip rather than plain text,
   because it sits on box art whose colours cannot be predicted — the
   contrast has to come from the badge, not from the image behind it. */
.game-rank {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  padding: 0.12rem 0.4rem;
  border-radius: 999px;
  background-color: rgba(20, 20, 22, 0.78);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  line-height: 1.5;
  backdrop-filter: blur(2px);
}

/* ═══════════════════════════════════════════
   BROWSE — DETAIL DRAWER
═══════════════════════════════════════════ */

.drawer-scrim {
  position: fixed;
  inset: 0;
  background-color: rgba(28, 28, 30, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 20;
}

.drawer-scrim.is-open { opacity: 1; pointer-events: auto; }

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(460px, 100%);
  background-color: var(--color-surface);
  box-shadow: -8px 0 32px rgba(0, 0, 0, 0.16);
  transform: translateX(100%);
  transition: transform 0.24s ease;
  z-index: 21;
  overflow-y: auto;
  padding: 1.75rem;
}

.drawer.is-open { transform: translateX(0); }

.drawer-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.drawer-close:hover { color: var(--color-text); }

.drawer-title {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: normal;
  line-height: 1.2;
  margin-bottom: 0.3rem;
  padding-right: 2.5rem;
}

.drawer-sub {
  font-size: 0.84rem;
  color: var(--color-text-muted);
  margin-bottom: 1.4rem;
}

.drawer-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.drawer-stat {
  background-color: var(--color-bg);
  border-radius: var(--radius-sm);
  padding: 0.65rem 0.75rem;
}

.drawer-stat .stat-label { margin-bottom: 0.25rem; }
.drawer-stat .stat-value { font-size: 1.25rem; }

.drawer-section-title {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--color-text-muted);
  margin: 1.5rem 0 0.6rem;
}

.play-log {
  list-style: none;
  font-size: 0.82rem;
  border-top: 1px solid var(--color-border);
}

.play-log li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--color-border);
  font-variant-numeric: tabular-nums;
}

.play-log .play-date { color: var(--color-text-muted); }

/* ── drawer: BoardGameGeek block ───────────────────────────── */

.drawer-bgg { margin-bottom: 1.6rem; }

.drawer-cover {
  margin-bottom: 1.2rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background-color: var(--color-bg);
}

.drawer-cover img {
  display: block;
  width: 100%;
  height: auto;
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.6rem;
  margin-top: 0.9rem;
}

.tag-row-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--color-text-muted);
}

.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.tag {
  font-size: 0.74rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

/* ── drawer: owned expansions ──────────────────────────────────
   A row per box, with its cover, so the drawer answers "what else
   is in this game's stack" at a glance. Deliberately carries no
   play count: expansion plays are logged against the base game. */
.expansion-list {
  list-style: none;
  font-size: 0.82rem;
  border-top: 1px solid var(--color-border);
}

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

.expansion-thumb {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background-color: var(--color-bg);
}

.expansion-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

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

.expansion-name {
  font-weight: 600;
  color: var(--color-text);
  line-height: 1.35;
}

.expansion-meta {
  font-size: 0.76rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  margin-top: 0.1rem;
}

.expansion-link {
  flex: 0 0 auto;
  font-size: 0.74rem;
  color: var(--color-text-muted);
  text-decoration: none;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
}

.expansion-link:hover {
  color: var(--color-text);
  border-color: var(--color-text-muted);
}

.drawer-desc {
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  margin-top: 1rem;
}

/* ═══════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════ */

@media (max-width: 768px) {
  .bg-header { align-items: flex-start; flex-direction: column; }
  .browse-controls { position: static; }
  .game-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .drawer { padding: 1.25rem; }
  .stat-hero .stat-value { font-size: 2.35rem; }
}

@media (prefers-reduced-motion: reduce) {
  .game-card, .drawer, .drawer-scrim, .viz-tip { transition: none; }
  .game-card:hover { transform: none; }
}

/* ── BoardGameGeek attribution ─────────────────────────────
   Required by BGG's XML API terms: public-facing apps show the
   "Powered by BGG" logo linked back to the site, sized so the
   wordmark stays legible. 171px is half the asset's native
   342px, which keeps the text crisp without dominating. */
.bgg-attribution {
  margin: 2.5rem 0 0.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-border);
}

.bgg-attribution a {
  display: inline-block;
  line-height: 0;
  opacity: 0.85;
  transition: opacity 0.15s ease;
}

.bgg-attribution a:hover,
.bgg-attribution a:focus-visible { opacity: 1; }

.bgg-attribution img { width: 171px; height: auto; max-width: 60vw; }
