:root {
  --bg: #14160f;
  --panel: #1d2117;
  --border: #2d3324;
  --text: #e7e8e0;
  --muted: #9ba097;
  --accent: #d2a14b;     /* savanna gold */
  --accent2: #6f9a4a;    /* grass green */
  --warn: #d2814b;
  --bad: #c25744;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 14px; }
a { color: var(--accent); text-decoration: none; }
code { font-family: ui-monospace, SFMono-Regular, monospace; color: var(--muted); }

header { padding: 12px 20px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--bg); z-index: 10; }
header h1 { margin: 0 0 8px 0; font-size: 18px; font-weight: 600; }
header .muted { font-weight: 400; color: var(--muted); }

.controls { display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin-bottom: 8px; }
.controls select, .controls button { background: var(--panel); color: var(--text);
  border: 1px solid var(--border); padding: 4px 10px; border-radius: 4px;
  font-size: 13px; cursor: pointer; }
.controls button:hover { border-color: var(--accent); }
.status { font-size: 12px; }
.status.err { color: var(--bad); }

/* Tab strip wraps to a second row when the viewport can't fit them all on
   one line — most often hits on mobile portrait. Horizontal scroll-snap is
   the alt option but a wrap reads better on small screens since each row
   stays visible. */
nav { display: flex; flex-wrap: wrap; gap: 4px; row-gap: 2px; }
nav .tab { padding: 6px 12px; border-radius: 4px 4px 0 0; color: var(--muted);
  border-bottom: 2px solid transparent; white-space: nowrap; }
nav .tab.active { color: var(--text); border-bottom-color: var(--accent); }
nav .tab:hover { color: var(--text); }
@media (max-width: 600px) {
  nav .tab { padding: 5px 8px; font-size: 13px; }
}

main { padding: 20px; max-width: 1400px; margin: 0 auto; }
.panel { display: none; }
.panel.active { display: block; }

/* Sub-nav lives inside a consolidated panel (World, Combat, Reference). Pill
   row, less prominent than top nav. Active sub gets accent underline. */
.subnav { display: flex; flex-wrap: wrap; gap: 4px; row-gap: 2px;
  margin: 0 0 14px; padding: 4px 0 8px; border-bottom: 1px solid var(--border); }
.subnav .subtab { padding: 4px 10px; border-radius: 4px; color: var(--muted);
  font-size: 13px; text-decoration: none; border: 1px solid transparent;
  white-space: nowrap; }
.subnav .subtab:hover { color: var(--text); background: rgba(255,255,255,0.04); }
.subnav .subtab.active { color: var(--text); background: var(--panel);
  border-color: var(--border); border-bottom-color: var(--accent); }
.subpanel { display: none; }
.subpanel.active { display: block; }

/* Network tab stacks two domains (perf log + netstat) without sub-nav.
   Use page-section spacing between blocks for visual breathing room. */
.net-block { margin-bottom: 24px; }
.net-block:last-child { margin-bottom: 0; }

/* Overview alert strip — vertical stack of clickable banner alerts. Tier
   colours pulled from existing semantic palette (crit=red, warn=amber,
   info=blue). Whole row is a deep-link to the relevant detail tab. */
.alert-strip { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.alert { display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  border-radius: 6px; border: 1px solid var(--border); text-decoration: none;
  color: var(--text); font-size: 13px; line-height: 1.3; }
.alert-icon { font-family: monospace; font-weight: 700; flex-shrink: 0; }
.alert-text { flex: 1; }
.alert-crit { background: #3a1d18; border-color: #c25744; }
.alert-crit .alert-icon { color: #c25744; }
.alert-warn { background: #3a2f18; border-color: #d2a14b; }
.alert-warn .alert-icon { color: #d2a14b; }
.alert-info { background: #1a2a3a; border-color: #4b6eaa; }
.alert-info .alert-icon { color: #4b6eaa; }
.alert:hover { border-color: var(--accent); }

/* Nav-card grid — exec-view shortcuts to detail tabs. Each card carries a
   single headline metric + a sub line; whole card is the link target.
   Replaces the chart-pop-now / chart-cause widgets that floated on the
   Overview tab w/o context. */
.nav-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px; margin-top: 4px; }
.nav-card { display: block; background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; padding: 14px; text-decoration: none; color: var(--text);
  transition: border-color 80ms ease, transform 80ms ease; }
.nav-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.nav-card-title { font-weight: 600; font-size: 14px; display: flex;
  justify-content: space-between; align-items: baseline; }
.nav-card-arrow { color: var(--muted); font-weight: 400; }
.nav-card-metric { font-size: 16px; margin-top: 6px; }
.nav-card-sub { font-size: 11px; margin-top: 4px; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px; margin-bottom: 16px; }
.cards .stat { background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; padding: 12px; }
.cards .stat .label { color: var(--muted); font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.5px; }
.cards .stat .val { font-size: 22px; font-weight: 600; margin-top: 4px; }
.cards .stat .sub { font-size: 11px; color: var(--muted); margin-top: 2px; }

/* KPI card variant — used by the players page so each headline number ships
   with a delta-vs-prior-half badge and an inline sparkline. Consistent
   anatomy (label / value+delta / sub / sparkline) per Tabular Editor's
   KPI-card best-practices write-up. */
.cards .kpi { background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; padding: 12px; display: flex; flex-direction: column;
  gap: 4px; min-height: 96px; }
.cards .kpi .kpi-label { color: var(--muted); font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.5px; }
.cards .kpi .kpi-row { display: flex; align-items: baseline; gap: 8px; }
.cards .kpi .kpi-val { font-size: 24px; font-weight: 600;
  font-variant-numeric: tabular-nums; line-height: 1.1; }
.cards .kpi .kpi-delta { font-size: 11px; font-variant-numeric: tabular-nums;
  padding: 1px 5px; border-radius: 3px; }
.cards .kpi .kpi-delta.up   { color: var(--accent2); background: rgba(111,154,74,0.14); }
.cards .kpi .kpi-delta.down { color: var(--bad);     background: rgba(194,87,68,0.14); }
.cards .kpi .kpi-delta.flat { color: var(--muted);   background: rgba(255,255,255,0.04); }
.cards .kpi .kpi-sub { color: var(--muted); font-size: 11px; }
.cards .kpi .kpi-spark { margin-top: auto; height: 28px; width: 100%;
  display: block; }

/* Page section — named, divider-bound grouping of widgets. Every section
   gets a small heading so widgets stop floating on the page background as
   equals (Datadog effective-dashboards rule). Subhead style is intentionally
   subtle so the data still leads. */
.page-section { margin-bottom: 24px; }
.page-section > h2 { font-size: 12px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 1.2px; margin: 0 0 10px;
  padding-bottom: 6px; border-bottom: 1px solid var(--border); }
.page-section > h2 .section-hint { color: var(--border); font-weight: 400;
  letter-spacing: 0.5px; text-transform: none; margin-left: 8px;
  font-size: 11px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
@media (max-width: 900px) { .grid2 { grid-template-columns: 1fr; } }

.card { background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
  padding: 16px; margin-bottom: 16px; }
.card h3 { margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.5px; }
.card canvas { max-height: 280px; }
.card.hidden { display: none; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { padding: 6px 10px; text-align: left; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 500; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.5px; }
tbody tr { cursor: pointer; }
tbody tr:hover { background: rgba(210, 161, 75, 0.06); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

#kill-matrix table th:first-child, #kill-matrix table td:first-child { font-weight: 600; }
#kill-matrix .zero { color: var(--border); }

ul#eco-warnings { margin: 0; padding-left: 18px; color: var(--warn); }
ul#eco-warnings li { margin-bottom: 4px; }
ul#eco-warnings:empty::before { content: "No warnings — ecosystem looks balanced.";
  color: var(--accent2); }

.muted { color: var(--muted); }
.bad { color: var(--bad); }
.warn { color: var(--warn); }
.good { color: var(--accent2); }


/* Species + perks reference tabs */
.species-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle; }
.species-icon { width: 22px; height: 22px; vertical-align: middle;
  margin-right: 6px; image-rendering: pixelated; border-radius: 3px; }
th .species-icon { width: 18px; height: 18px; }
.perk-icon { width: 40px; height: 40px; display: block; image-rendering: pixelated;
  border: 1px solid var(--border); border-radius: 4px; background: #0d0f0c; }
.filter-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin: 8px 0 12px 0; }
.filter-row label { color: var(--muted); font-size: 13px; }
.filter-row select { background: var(--panel); color: var(--text);
  border: 1px solid var(--border); padding: 4px 10px; border-radius: 4px;
  font-size: 13px; cursor: pointer; margin-left: 4px; }
table.sortable thead th { cursor: pointer; user-select: none; }
table.sortable thead th:hover { color: var(--accent); }
.tag { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px;
  border: 1px solid var(--border); margin-right: 4px; }
.tag.flag { background: var(--panel); color: var(--muted); }
.tag.rarity-0 { color: #d8d4c4; border-color: #424536; }    /* Common */
.tag.rarity-1 { color: #6f9a4a; border-color: #355224; }    /* Rare */
.tag.rarity-2 { color: #d2a14b; border-color: #5a4524; background: #2a2114; } /* Legendary */

/* Perks tab — per-species skill tree */
.perk-species-pill { display: inline-flex; align-items: center; gap: 4px; }
.perk-species-pill .species-icon { width: 18px; height: 18px; margin-right: 2px; }
.perk-species-block { margin: 0 0 22px 0; }
.perk-species-title { display: flex; align-items: center; gap: 8px;
  font-size: 16px; margin: 0 0 10px 0; padding-left: 8px; }
.perk-species-title .species-icon { width: 26px; height: 26px; }
.perk-species-title .muted { font-size: 12px; font-weight: 400; }
.perk-branch { margin: 0 0 10px 0; }
.perk-branch-label { color: var(--muted); font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; margin: 0 0 6px 2px; }
.perk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 8px; }
.perk-tile { display: flex; gap: 10px; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--panel); }
.perk-tile.perk-retired { opacity: 0.5; }
.perk-tile .perk-icon { flex: 0 0 40px; }
.perk-tile-body { min-width: 0; }
.perk-tile-head { display: flex; align-items: center; flex-wrap: wrap; gap: 4px;
  margin-bottom: 3px; }
.perk-effects { font-size: 12px; line-height: 1.35; }
.perk-prereq { font-size: 11px; margin-top: 2px; }
.perk-flavor { font-size: 11px; font-style: italic; margin-top: 3px; }

/* Stats tab — growth-time simulator */
.stats-constants { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.stats-pill { display: inline-flex; align-items: center; padding: 4px 10px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--panel); color: var(--muted); font-size: 12px; }
.stats-pill b { color: var(--text); margin-right: 6px; font-weight: 600; }
.stats-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.stats-table th { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border);
  color: var(--muted); font-weight: 600; }
.stats-table td { padding: 8px 10px; border-bottom: 1px solid var(--border);
  vertical-align: top; }
.stats-table tr:hover td { background: rgba(255,255,255,0.02); }
.stats-species-cell { white-space: nowrap; }
.stats-cell .stats-time { font-weight: 600; color: var(--text); }
.stats-cell .stats-meta { font-size: 11px; color: var(--muted); margin-top: 2px; }
.diet-pill { display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: 11px; border: 1px solid var(--border); }
.diet-pill.diet-carnivore { color: #d27a4b; border-color: #5a3624; background: #2a1814; }
.diet-pill.diet-herbivore { color: #8fb84a; border-color: #3f5224; background: #1f2614; }
.diet-pill.diet-omnivore  { color: #d2c44b; border-color: #524824; background: #2a2614; }
.stats-strategy-list { margin: 0; padding-left: 18px; color: var(--muted); }
.stats-strategy-list li { margin-bottom: 6px; }
.stats-strategy-list strong { color: var(--text); }

/* Map tab. Canvas is sized via JS; wrapper provides aspect + relative anchor
   for the absolutely-positioned tooltip. */
.map-wrap { position: relative; width: 100%; aspect-ratio: 1 / 1;
  background: #1a1d14; border: 1px solid var(--border); border-radius: 6px;
  overflow: hidden; }
/* Override the generic `.card canvas { max-height: 280px }` chart cap; the
   map canvas must fill its square wrap to keep the world's 1:1 aspect. */
.map-wrap canvas { display: block; width: 100%; height: 100%; max-height: none; }
.map-tooltip { position: absolute; pointer-events: none; background: var(--bg);
  border: 1px solid var(--border); border-radius: 4px; padding: 4px 8px;
  font-size: 12px; color: var(--text); z-index: 5; white-space: nowrap; }
.map-tooltip.hidden { display: none; }
.map-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 8px;
  font-size: 12px; color: var(--muted); }
.map-legend .swatch { display: inline-block; width: 10px; height: 10px;
  border-radius: 50%; margin-right: 4px; vertical-align: middle; }
.map-legend .swatch-diamond { border-radius: 0; transform: rotate(45deg); }

/* Map filter strip — collapsible details/summary above the map. Each row is
   a flex line so checkboxes + sliders wrap cleanly on narrow viewports. */
/* Map toolbar — layers + recenter + rewind. Distinct from FilterBar pills
   below it because these toggle DRAW state (layer visibility, time
   rewind), not the entity-set filter. */
.map-toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  padding: 8px 12px; margin: 0 0 10px; background: var(--bg);
  border: 1px solid var(--border); border-radius: 6px; font-size: 13px; }
.map-toolbar label { display: inline-flex; align-items: center; gap: 4px; }
.map-toolbar-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
.map-toolbar-sep { color: var(--border); }
.map-toolbar input[type="range"] { width: 160px; }
.map-toolbar button { background: var(--panel); color: var(--muted);
  border: 1px solid var(--border); border-radius: 4px; padding: 3px 10px;
  font-size: 12px; cursor: pointer; }
.map-toolbar button:hover { color: var(--text); border-color: var(--accent); }

/* Inline filter strip used inside subpanels where the filter is the primary
   control (no need to hide behind a <details>). Reuses .map-filter-row's
   widget styling so radios/labels stay consistent across the dashboard. */
.inline-filter { display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 12px; margin: 0 0 12px; font-size: 13px; }
.inline-filter label { display: inline-flex; align-items: center; gap: 4px; }
.inline-filter-label { color: var(--muted); font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.5px; }

.map-filters { background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 8px 12px; margin-bottom: 12px; }
.map-filters summary { cursor: pointer; color: var(--muted);
  text-transform: uppercase; font-size: 12px; letter-spacing: 0.5px; }
.map-filters summary:hover { color: var(--text); }
.map-filter-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  margin-top: 8px; font-size: 12px; color: var(--muted); }
.map-filter-row label { display: inline-flex; align-items: center; gap: 4px; }
.map-filter-row input[type="range"] { width: 100px; }
.map-filter-row select, .map-filter-row button { background: var(--panel);
  color: var(--text); border: 1px solid var(--border); border-radius: 4px;
  padding: 2px 8px; cursor: pointer; }
.map-filter-row button:hover { border-color: var(--accent); }
.species-pill { display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border: 1px solid var(--border); border-radius: 999px;
  cursor: pointer; user-select: none; }
.species-pill .swatch { width: 8px; height: 8px; border-radius: 50%; }
.species-pill.off { opacity: 0.35; text-decoration: line-through; }
.species-cols-toggle { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.species-col-pill { display: inline-block; padding: 2px 10px; border-radius: 999px;
  border: 1px solid var(--border); cursor: pointer; user-select: none;
  font-size: 12px; opacity: 0.45; }
.species-col-pill.on { opacity: 1; border-color: var(--accent); }

/* Map layout: canvas + click-to-inspect sidebar. Grid collapses to a single
   column on narrow viewports so the sidebar stacks under the map. */
.map-layout { display: grid; grid-template-columns: minmax(0, 2fr) minmax(220px, 1fr);
  gap: 16px; align-items: start; }
@media (max-width: 900px) { .map-layout { grid-template-columns: 1fr; } }
.map-sidebar { background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 12px; min-height: 280px; }
.map-sidebar h4 { margin: 0 0 6px 0; font-size: 14px; }
.map-sidebar .stat-row { display: flex; justify-content: space-between;
  font-size: 12px; padding: 3px 0; border-bottom: 1px dashed var(--border); }
.map-sidebar .stat-row:last-child { border-bottom: none; }
.map-sidebar .stat-row .k { color: var(--muted); }
.map-sidebar .stat-row .v { font-variant-numeric: tabular-nums; }
.map-sidebar .bar { height: 6px; background: var(--border); border-radius: 3px;
  overflow: hidden; margin-top: 2px; margin-bottom: 8px; }
.map-sidebar .bar > div { height: 100%; }
.map-sidebar .badges { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0; }

/* ── deploy multi-select picker ─────────────────────────────────────────── */
.deploy-picker { position: relative; }
.deploy-toggle { background: var(--panel); color: var(--text);
  border: 1px solid var(--border); padding: 4px 12px; border-radius: 4px;
  font-size: 13px; cursor: pointer; min-width: 140px; text-align: left; }
.deploy-toggle:hover { border-color: var(--accent); }
.deploy-toggle #deploy-summary { color: var(--accent); font-weight: 600; }

.deploy-panel { position: absolute; top: calc(100% + 4px); left: 0;
  background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
  min-width: 360px; max-width: 540px; max-height: 50vh; overflow: auto;
  padding: 8px; z-index: 20; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); }
.deploy-panel.hidden { display: none; }

.deploy-panel-actions { display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center; padding-bottom: 8px;
  border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.deploy-panel-actions button { background: var(--bg); color: var(--text);
  border: 1px solid var(--border); padding: 3px 8px; border-radius: 3px;
  font-size: 12px; cursor: pointer; }
.deploy-panel-actions button:hover { border-color: var(--accent); }
.deploy-range-row { display: flex; align-items: center; gap: 4px;
  margin-left: auto; font-size: 11px; color: var(--muted); }
.deploy-range-row input[type="datetime-local"] {
  background: var(--bg); color: var(--text); border: 1px solid var(--border);
  border-radius: 3px; padding: 2px 4px; font-size: 11px;
}

.deploy-list { list-style: none; margin: 0; padding: 0; }
.deploy-list li { padding: 3px 4px; border-radius: 3px; }
.deploy-list li:hover { background: var(--bg); }
.deploy-list label { display: flex; align-items: center; gap: 8px;
  cursor: pointer; font-size: 12px; }
.deploy-list .deploy-id { font-family: ui-monospace, SFMono-Regular, monospace;
  color: var(--text); }
.deploy-list .deploy-stamp { font-size: 11px; margin-left: auto; }
.deploy-list .deploy-live-tag { background: var(--accent2); color: var(--bg);
  padding: 0 5px; border-radius: 3px; font-size: 10px; font-weight: 600; }
.deploy-list .deploy-sealed-tag { background: var(--border); color: var(--muted);
  padding: 0 5px; border-radius: 3px; font-size: 10px; }

/* Map / Groups: banner shown when forced-single narrowing is active. */
.deploy-forced-banner { background: var(--panel); border: 1px solid var(--warn);
  border-left: 3px solid var(--warn); border-radius: 4px;
  padding: 6px 10px; font-size: 12px; color: var(--muted);
  margin-bottom: 8px; }
.deploy-forced-banner .actor { color: var(--warn); font-weight: 600; }

/* ── Players tab — ping sparkline + filter row ─────────────────────────── */
.ping-spark { display: inline-block; vertical-align: middle;
  background: rgba(255,255,255,0.02); border-radius: 2px; }
.ping-spark-label { font-size: 11px; margin-left: 8px; vertical-align: middle; }

/* Toolbar above the players table — column toggle on the left, density on
   the right. Inline so it doesn't push the table down a full row. */
.table-toolbar { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 8px; flex-wrap: wrap; }
.density-toggle { display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; }
.density-toggle .density-label { text-transform: uppercase; letter-spacing: 0.5px;
  font-size: 10px; margin-right: 4px; }
.density-toggle button { background: var(--panel); color: var(--muted);
  border: 1px solid var(--border); border-radius: 3px; padding: 2px 8px;
  font-size: 11px; cursor: pointer; }
.density-toggle button:hover { color: var(--text); }
.density-toggle button.active { color: var(--text); border-color: var(--accent);
  background: rgba(210,161,75,0.08); }

/* Sticky thead so the column row stays anchored when the players list
   scrolls past the viewport. The wrapper (.players-table-scroll) is the
   scroll container so `top: 0` snaps to its top edge. Background fills
   over the rows passing under the header. */
table.sticky-head thead th { position: sticky; top: 0; z-index: 2;
  background: var(--panel); box-shadow: inset 0 -1px 0 var(--border); }

/* Density toggle drives row padding. Compact = denser by ~33%, font drops a
   step so eight rows always fit before scroll. The class can sit on the
   table itself (Players) or a card ancestor (Reference tables) — descendant
   selector covers both. */
.density-compact td,
.density-compact th { padding: 3px 8px; font-size: 12px; }
/* Keep the toolbar from collapsing when only one side has content. */
.table-toolbar .toolbar-left { display: inline-flex; align-items: center; gap: 8px; }

/* In-cell sparklines for the merged ping + activity cells. Two-line layout:
   sparkline on top, numeric headline below in tabular-nums for at-a-glance
   comparison while the spark gives the trend context. Datacamp/Microsoft
   recommend ≤15 points per cell-spark; we cap at 24 / 30 in JS. */
.cell-spark-wrap { display: flex; flex-direction: column; gap: 1px;
  align-items: flex-end; min-width: 90px; }
.cell-spark-wrap svg { display: block; }
.cell-spark-wrap .spark-foot { font-size: 11px;
  font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; }
.cell-spark-wrap .spark-foot .spark-sub { color: var(--muted); margin-left: 4px; }
.cell-spark-empty { color: var(--muted); font-size: 11px; font-style: italic; }

/* Aggregate-ping card uses a fixed-height wrap so Chart.js's
   maintainAspectRatio:false can size to it without growing the card. */
.agg-ping-wrap { position: relative; height: 240px; }

/* Split-pane: table left, run-detail right. Each pane scrolls independently
   so the runs accordion never pushes the table out of view, and the table
   never forces the runs panel below the fold. Stacks on narrow viewports
   (1100 px breakpoint). */
.players-split { display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 16px; align-items: start; margin-bottom: 16px; }
.players-split > .card { margin-bottom: 0;
  max-height: calc(100vh - 80px); overflow: hidden;
  display: flex; flex-direction: column; }
.players-table-pane > .players-table-scroll { overflow: auto; flex: 1 1 auto; }
/* Specificity 0,3,0 beats `.players-split > .card { overflow: hidden }` (0,2,0)
   so the runs accordion actually scrolls when it overflows the pane. */
.players-split > .card.player-detail-pane { padding: 12px 16px; overflow-y: auto; }
.player-detail-empty { padding: 32px 12px; text-align: center; font-size: 13px; }
.player-detail-header { display: flex; align-items: center;
  justify-content: space-between; gap: 8px; margin-bottom: 8px;
  position: sticky; top: -12px; background: var(--panel);
  padding: 8px 0 6px; border-bottom: 1px solid var(--border); z-index: 1; }
.player-detail-header h3 { margin: 0; }
#player-detail-close { background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 4px;
  width: 28px; height: 28px; cursor: pointer; font-size: 18px;
  line-height: 1; padding: 0; }
#player-detail-close:hover { color: var(--text); border-color: var(--accent); }

#player-detail-summary { display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 8px 12px; margin-bottom: 12px; padding: 10px;
  background: rgba(255,255,255,0.02); border-radius: 4px; }
#player-detail-summary .kv .label { color: var(--muted); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.5px; }
#player-detail-summary .kv .val { font-size: 14px; font-weight: 600; margin-top: 2px; }

/* Highlight the selected player row so the table stays anchored to what's
   shown in the detail pane. */
#players-table tbody tr.selected { background: rgba(210, 161, 75, 0.16); }
#players-table tbody tr.selected:hover { background: rgba(210, 161, 75, 0.22); }

/* Runs accordion. Each run is a card; clicking the head toggles a detail
   panel below it (full ping chart + end-of-run stats). One open at a time. */
.run-card { border: 1px solid var(--border); border-radius: 4px;
  margin-bottom: 8px; overflow: hidden; background: var(--bg); }
.run-card-head { display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto auto;
  gap: 8px; align-items: center; padding: 8px 10px;
  cursor: pointer; user-select: none; }
.run-card-head:hover { background: rgba(210, 161, 75, 0.06); }
.run-card.expanded .run-card-head { background: rgba(210, 161, 75, 0.1);
  border-bottom: 1px solid var(--border); }
.run-card-head .run-num { color: var(--muted); font-variant-numeric: tabular-nums;
  font-size: 12px; }
.run-card-head .run-meta { display: flex; flex-wrap: wrap; gap: 6px 10px;
  font-size: 12px; align-items: center; min-width: 0; }
.run-card-head .run-meta b { font-weight: 600; }
.run-card-head .run-cause { color: var(--muted); font-size: 11px;
  text-transform: lowercase; }
.run-card-head .run-survival { font-variant-numeric: tabular-nums;
  font-size: 12px; }
.run-card-head .run-spark-row { grid-column: 1 / -1;
  display: flex; align-items: center; gap: 8px; padding-top: 4px;
  border-top: 1px dashed rgba(255,255,255,0.04); margin-top: 4px; }

.run-detail { padding: 12px; background: rgba(255,255,255,0.02);
  display: none; }
.run-card.expanded .run-detail { display: block; }
.run-detail-grid { display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 8px 12px; margin-bottom: 12px; }
.run-detail-grid .kv .label { color: var(--muted); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.5px; }
.run-detail-grid .kv .val { font-size: 13px; font-weight: 500; margin-top: 2px; }
.run-detail-chart-wrap { position: relative; height: 200px; }
.run-detail-empty { color: var(--muted); text-align: center; padding: 60px 0; }

@media (max-width: 1100px) {
  .players-split { grid-template-columns: 1fr; }
  .players-split > .card { max-height: none; overflow: visible; }
  .players-table-pane > .players-table-scroll { overflow: visible; }
  .player-detail-pane { overflow: visible; }
}

#players-filters .map-filter-row label { font-size: 12px; }
#players-filters input[type="number"] { width: 70px; }
#players-filters input[type="search"] { width: 180px; }
#players-filters select[multiple] { min-width: 110px; }
#players-filter-summary { font-weight: 400; font-size: 12px; }

/* ── Netstat tab — peer-buf heatmap grid ───────────────────────────────── */
.heat-grid { border-collapse: collapse; font-size: 11px; width: auto; }
.heat-grid th, .heat-grid td { padding: 4px 6px; border: 1px solid var(--border);
  text-align: center; min-width: 60px; }
.heat-grid th { color: var(--muted); font-weight: 400; font-size: 10px;
  white-space: nowrap; }
.heat-grid .heat-label { text-align: left; white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, monospace; }
.heat-grid .heat-cell { font-size: 10px; color: var(--text); }

/* ── Filter bar (Players tab) ───────────────────────────────────────────
   Pattern: inline pill bar (PatternFly / Helios / Linear). Each active
   filter is one pill that opens a popover for editing; unused dimensions
   live behind "+ Add filter" so the bar stays compact. The bar IS the
   active-filter surface — no separate strip. */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; padding: 8px 10px; margin-bottom: 12px;
}
.filter-bar-search { position: relative; }
.filter-bar-search-icon {
  position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
  color: var(--muted); font-size: 13px; pointer-events: none;
}
.filter-bar-input {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 4px 8px 4px 24px; font-size: 12px; width: 200px;
}
.filter-bar-input:focus { outline: none; border-color: var(--accent); }
.filter-bar-pills { display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center; flex: 1 1 auto; min-width: 0; }
.filter-bar-summary { font-size: 11px; margin-left: auto;
  white-space: nowrap; }

.filter-pill {
  display: inline-flex; align-items: center; gap: 4px;
  background: rgba(210, 161, 75, 0.12); color: var(--text);
  border: 1px solid var(--accent); border-radius: 12px;
  padding: 2px 4px 2px 10px; font-size: 12px; cursor: pointer;
  user-select: none; max-width: 360px;
}
.filter-pill:hover { background: rgba(210, 161, 75, 0.2); }
.filter-pill.filter-pill-exclude {
  border-color: var(--bad); background: rgba(194, 87, 68, 0.12);
}
.filter-pill.filter-pill-exclude:hover { background: rgba(194, 87, 68, 0.2); }
.filter-pill .pill-key { color: var(--muted); font-size: 11px; margin-right: 2px; }
.filter-pill .pill-op { color: var(--accent); font-weight: 600; margin: 0 4px; }
.filter-pill.filter-pill-exclude .pill-op { color: var(--bad); }
.filter-pill .pill-val { font-weight: 500; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.filter-pill .pill-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; opacity: 0.6;
  font-size: 14px; line-height: 1; margin-left: 2px;
}
.filter-pill .pill-x:hover { opacity: 1; background: rgba(255,255,255,0.08); }

.filter-add-btn, .filter-reset-btn {
  background: transparent; color: var(--muted);
  border: 1px dashed var(--border); border-radius: 4px;
  padding: 3px 10px; font-size: 12px; cursor: pointer;
}
.filter-add-btn:hover { color: var(--accent); border-color: var(--accent); }
.filter-reset-btn { border-style: solid; }
.filter-reset-btn:hover { color: var(--bad); border-color: var(--bad); }
.filter-reset-btn.hidden { display: none; }

/* Popover anchored to the clicked pill / add button. Position computed in
   JS (top/left) so it tracks the anchor across tabs + scrolls. */
.filter-popover {
  position: absolute; z-index: 30;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; box-shadow: 0 6px 24px rgba(0,0,0,0.4);
  padding: 8px; min-width: 240px; max-width: 320px;
}
.filter-popover.hidden { display: none; }
.filter-popover-title {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); font-weight: 600; padding: 0 4px 6px;
}
.filter-popover-tabs {
  display: flex; gap: 2px; margin: 0 0 8px;
  border-bottom: 1px solid var(--border);
}
.filter-popover-tab {
  background: transparent; color: var(--muted); border: none;
  border-bottom: 2px solid transparent; padding: 4px 12px;
  font-size: 12px; cursor: pointer; font-weight: 500;
}
.filter-popover-tab:hover { color: var(--text); }
.filter-popover-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.filter-popover-tab.active.tab-exclude {
  color: var(--bad); border-bottom-color: var(--bad);
}

.filter-popover-search {
  width: 100%; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 4px 8px; font-size: 12px; margin-bottom: 6px;
  box-sizing: border-box;
}
.filter-popover-list {
  max-height: 240px; overflow-y: auto; display: flex;
  flex-direction: column; gap: 1px;
}
.filter-popover-list .opt {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 6px; font-size: 12px; cursor: pointer;
  border-radius: 3px; user-select: none;
}
.filter-popover-list .opt:hover { background: rgba(255,255,255,0.04); }
.filter-popover-list .opt.checked { background: rgba(210, 161, 75, 0.12); }
.filter-popover-list .opt.checked.opt-exclude { background: rgba(194, 87, 68, 0.12); }
.filter-popover-list .opt input { margin: 0; pointer-events: none; }
.filter-popover-list .opt-empty { color: var(--muted); font-size: 11px;
  padding: 8px; text-align: center; }
.filter-popover-list .opt-other-mode {
  color: var(--muted); font-style: italic; font-size: 10px;
  margin-left: auto;
}

.filter-popover-range { display: flex; align-items: center; gap: 6px;
  padding: 4px 0; }
.filter-popover-range input {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 4px 8px; font-size: 12px; width: 90px;
}
.filter-popover-range input:focus { outline: none; border-color: var(--accent); }
.filter-popover-range .range-sep { color: var(--muted); }
.filter-popover-foot { display: flex; justify-content: space-between;
  align-items: center; margin-top: 8px; padding-top: 6px;
  border-top: 1px solid var(--border); }
.filter-popover-clear {
  background: transparent; color: var(--muted); border: none;
  font-size: 11px; cursor: pointer; padding: 2px 4px;
  text-decoration: underline; text-underline-offset: 2px;
}
.filter-popover-clear:hover { color: var(--bad); }
.filter-popover-done {
  background: var(--accent); color: var(--bg); border: none;
  border-radius: 4px; padding: 4px 12px; font-size: 12px;
  cursor: pointer; font-weight: 600;
}

/* Add-filter menu (popover variant: a flat list of available dimensions). */
.filter-add-menu .opt { padding: 6px 8px; }
.filter-add-menu .opt:hover { background: rgba(210, 161, 75, 0.1);
  color: var(--accent); }

/* Mobile: shrink the search input + let pills wrap freely. */
@media (max-width: 700px) {
  .filter-bar-input { width: 100%; }
  .filter-bar-search { flex: 1 1 100%; }
  .filter-bar-summary { margin-left: 0; flex: 1 1 100%; }
  .filter-popover { min-width: calc(100vw - 40px); max-width: calc(100vw - 40px); }
}

/* ── Deploy picker — drag-lasso + range-hover + search box ─────────────── */
.deploy-search {
  background: var(--bg); color: var(--text); border: 1px solid var(--border);
  border-radius: 4px; padding: 3px 8px; font-size: 12px; width: 100%;
  margin-bottom: 6px; box-sizing: border-box;
}
.deploy-list { user-select: none; }
.deploy-list li { -webkit-user-select: none; user-select: none; }
.deploy-list li.deploy-list-hover-range {
  background: rgba(210, 161, 75, 0.18);
  outline: 1px solid var(--accent);
}
.deploy-list .deploy-list-sep {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); padding: 6px 4px 2px;
  border-top: 1px dashed var(--border); margin-top: 4px;
  pointer-events: none; cursor: default;
}
.deploy-list .deploy-list-sep:hover { background: transparent; }

/* ── Alphas tab — inline radio mode toggle in the K/D card header ──────── */
.alpha-kd-toggle {
  display: inline-flex; gap: 12px; margin-left: 12px; font-size: 11px;
  font-weight: 400; color: var(--muted);
}
.alpha-kd-toggle label { display: flex; align-items: center; gap: 4px;
  cursor: pointer; }
.alpha-kd-toggle input[type=radio] { margin: 0; }

/* ── Window picker (datetime range + quick-set buttons) ───────────────── */
.window-picker { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.window-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  font-weight: 600; }
.window-picker input[type="datetime-local"] {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 3px 6px; font-size: 12px;
  /* Native datetime widget: keep its own padding sane. */
  min-width: 160px;
}
.window-picker input[type="datetime-local"]:focus {
  outline: none; border-color: var(--accent);
}
.window-sep { color: var(--muted); font-size: 14px; }
.window-quick { display: inline-flex; gap: 2px; margin-left: 4px;
  padding-left: 8px; border-left: 1px solid var(--border); }
.window-quick button {
  background: var(--panel); color: var(--muted);
  border: 1px solid var(--border); border-radius: 3px;
  padding: 2px 8px; font-size: 11px; cursor: pointer;
}
.window-quick button:hover { color: var(--text); border-color: var(--accent); }
.window-quick button.active {
  background: var(--accent); color: var(--bg); border-color: var(--accent);
  font-weight: 600;
}

/* Deploy list rows: keep stamp + duration on one line, label cleanly. */
.deploy-list .deploy-stamp {
  font-family: ui-monospace, SFMono-Regular, monospace;
  color: var(--text); font-size: 11px;
}
.deploy-list .deploy-dur { font-size: 11px; }

/* Players-tab live status indicator: filled dot in accent2 = ping seen
   within last 60s, hollow muted dot = offline / archived. */
.status-dot { font-size: 14px; line-height: 1; }
.status-dot.status-online { color: var(--accent2); text-shadow: 0 0 6px rgba(111,154,74,0.6); }
.status-dot.status-offline { color: var(--muted); }

/* Live-now card: one row per online player with name + session/run timers.
   Compact monospaced layout so timers don't shift mid-tick. */
.live-now-list { display: flex; flex-direction: column; gap: 4px; }
.live-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px;
  padding: 6px 8px; border-radius: 4px; background: rgba(255,255,255,0.02);
}
.live-row:hover { background: rgba(255,255,255,0.04); }
.live-row .live-name { margin-right: 6px; }
.live-row .live-meta { font-size: 12px; font-variant-numeric: tabular-nums; }
.live-status-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent2); box-shadow: 0 0 6px rgba(111,154,74,0.6);
}

/* Players-tab heatmap. Wrap is the relative anchor for the floating
   tooltip; svg is sized via JS attributes (preserveAspectRatio fits the
   chosen scope into the container width). */
.heatmap-controls { display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; margin-bottom: 8px; font-size: 12px; }
.heatmap-controls label { display: inline-flex; gap: 4px; align-items: center;
  color: var(--muted); cursor: pointer; }
.heatmap-controls label:has(input:checked) { color: var(--text); }
.heatmap-wrap { position: relative; width: 100%; height: 420px;
  background: #0d0f0c; border: 1px solid var(--border); border-radius: 6px;
  overflow: hidden; }
.heatmap-wrap svg { display: block; width: 100%; height: 100%; }
.heatmap-region { stroke: rgba(255,255,255,0.12); stroke-width: 0.5;
  vector-effect: non-scaling-stroke; transition: fill 0.2s; }
.heatmap-region.has-players { cursor: pointer; }
.heatmap-region.has-players:hover { stroke: var(--accent); stroke-width: 1.5; }
.heatmap-graticule { fill: none; stroke: rgba(255,255,255,0.04);
  stroke-width: 0.5; vector-effect: non-scaling-stroke; }
.heatmap-sphere { fill: rgba(60,90,40,0.06); stroke: rgba(255,255,255,0.08);
  stroke-width: 0.5; vector-effect: non-scaling-stroke; }

/* Geo card: choropleth on the left + ranked top-N list on the right. List
   replaces the redundant country/region bar charts so the same data only
   reads in one place. Stacks on narrow viewports so the map keeps full
   width on phones. */
.geo-card .geo-layout { display: grid;
  grid-template-columns: minmax(0, 1fr) 220px; gap: 16px;
  align-items: stretch; }
.geo-card .heatmap-map-col { display: flex; flex-direction: column; gap: 8px;
  min-width: 0; }
.geo-card .geo-map-wrap { height: 420px; }
/* Active map-click filter — outline the selected country / state so the
   choropleth shows what the pill bar is filtering on. */
.heatmap-region.selected { stroke: var(--accent2); stroke-width: 2; }
.heatmap-region.selected:hover { stroke: var(--accent2); stroke-width: 2.5; }

/* Stepped legend — one swatch per quantile bin + range label. Reads
   left-to-right low-to-high so users scan the colour ramp the same way
   the choropleth encodes it. Wraps on narrow viewports. */
.heatmap-legend { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 11px; color: var(--muted); padding: 0 2px; }
.heatmap-legend:empty { display: none; }
.heatmap-legend .hl-label { text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); margin-right: 4px; }
.heatmap-legend .hl-stop { display: inline-flex; align-items: center; gap: 4px; }
.heatmap-legend .hl-swatch { width: 14px; height: 12px; border-radius: 2px;
  border: 1px solid rgba(255,255,255,0.08); display: inline-block; }
.heatmap-legend .hl-range { font-variant-numeric: tabular-nums; color: var(--text); }
.geo-card .geo-side { display: flex; flex-direction: column;
  background: rgba(255,255,255,0.02); border: 1px solid var(--border);
  border-radius: 6px; padding: 10px 12px; min-width: 0; }
.geo-card .geo-side-head { color: var(--muted); font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.5px;
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
  margin-bottom: 6px; }
.geo-card .geo-top-list { list-style: none; margin: 0; padding: 0;
  font-size: 13px; overflow-y: auto; flex: 1 1 auto; }
.geo-card .geo-top-list li { display: grid;
  grid-template-columns: 22px 1fr auto;
  gap: 6px; padding: 4px 0; align-items: center;
  border-bottom: 1px dashed rgba(255,255,255,0.04); }
.geo-card .geo-top-list li:last-child { border-bottom: none; }
.geo-card .geo-top-list li .rank { color: var(--muted);
  font-variant-numeric: tabular-nums; font-size: 11px; text-align: right; }
.geo-card .geo-top-list li .name { white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.geo-card .geo-top-list li .count { font-variant-numeric: tabular-nums;
  font-weight: 600; color: var(--accent2); padding-left: 8px; }
.geo-card .geo-top-list li.empty { color: var(--muted); font-style: italic;
  grid-template-columns: 1fr; padding: 12px 0; text-align: center;
  border-bottom: none; }
@media (max-width: 900px) {
  .geo-card .geo-layout { grid-template-columns: 1fr; }
  .geo-card .geo-map-wrap { height: 360px; }
  .geo-card .geo-side { max-height: 240px; }
}

/* Population-over-time gets extra vertical room — six species + a player
   line packed into 280px (the default canvas cap) reads cramped. Override
   the .card canvas max-height so this one chart can breathe. */
.pop-series-card .pop-series-wrap { height: 360px; position: relative; }
.pop-series-card .pop-series-wrap canvas {
  width: 100% !important; height: 100% !important; max-height: none !important;
}

/* /items page — color swatch matching the in-game cube tint per kind. */
.berry-swatch {
  display: inline-block; width: 22px; height: 22px; border-radius: 4px;
  border: 1px solid rgba(0,0,0,0.45);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18);
  vertical-align: middle;
}

/* /items page — pixel-art icon. 16x16 source scaled to 40px with NEAREST. */
.berry-icon {
  display: block; width: 40px; height: 40px;
  image-rendering: pixelated; image-rendering: crisp-edges;
  border: 1px solid var(--border); border-radius: 4px; background: #0d0f0c;
}

/* /items page — permanent legendary stack callout. Goes under the Hooks
 * cell so the existing layout stays intact. Subtle gold fill matches the
 * legendary tier accent. */
.berry-perm { margin-top: 4px; font-size: 12px; line-height: 1.4; }
.tag.perm   { color: #d2a14b; border-color: #5a4524; background: #2a2114; }

/* /reference/poi page — biased-item chips. Inline-flex row of icon + name +
 * tier tag, wraps freely so wide POI groups (grove, ridge) don't blow out
 * the table cell. Icon shrinks to 24px so chips stay one-line tall. */
.poi-item-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.poi-item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 6px 2px 4px; border: 1px solid var(--border);
  border-radius: 14px; background: var(--card-bg, #181a16);
  font-size: 12px;
}
.poi-item .berry-icon { width: 24px; height: 24px; border-radius: 3px; }
.poi-item-name { font-weight: 500; }

/* Player-detail rank progression — small bar + caption shown in the Next
 * KV cell. Width-driven inner span; the muted text underneath gives the
 * literal "X/Y of Z+ runs" so the bar isn't the only signal. */
.rank-progress { display: flex; flex-direction: column; gap: 3px; min-width: 140px; }
.rank-progress-bar {
  height: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  overflow: hidden;
}
.rank-progress-bar > span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--accent2) 0%, var(--accent) 100%);
  transition: width 200ms ease-out;
}
.rank-progress-text { font-size: 11px; }

/* /reference/levels + /reference/ranks tier tables — same look-and-feel as
 * the existing #species-table / #perks-table cards. Tier-icon column is
 * fixed-width to keep the pixel-art crisp at 32px. Selectors are shared so
 * the levels page (career progression) and the ranks page (percentile
 * placement) render identically. */
#ranks-table table, #levels-table table, #ranks-splits-table table { width: 100%; border-collapse: collapse; }
#ranks-table th, #ranks-table td,
#levels-table th, #levels-table td,
#ranks-splits-table th, #ranks-splits-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
#ranks-table th, #levels-table th, #ranks-splits-table th { font-weight: 600; color: var(--muted); font-size: 12px; text-transform: uppercase; }
#ranks-table td.num, #levels-table td.num, #ranks-splits-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Vector emblems (RhosGFX Vector Ranks, CC0) — smooth art, so NO pixelated
 * rendering (that would alias the 256px source on downscale). */
#ranks-table .rank-icon, #levels-table .rank-icon, #ranks-splits-table .rank-icon {
  width: 36px; height: 36px;
  vertical-align: middle;
}
#ranks-table .tier-name, #levels-table .tier-name, #ranks-splits-table .tier-name { font-weight: 700; }
#ranks-table tr.tier-bronze   .tier-name,
#levels-table tr.tier-bronze   .tier-name,
#ranks-splits-table tr.tier-bronze   .tier-name { color: #a66633; }
#ranks-table tr.tier-silver   .tier-name,
#levels-table tr.tier-silver   .tier-name,
#ranks-splits-table tr.tier-silver   .tier-name { color: #c7c7d1; }
#ranks-table tr.tier-gold     .tier-name,
#levels-table tr.tier-gold     .tier-name,
#ranks-splits-table tr.tier-gold     .tier-name { color: #ffd133; }
#ranks-table tr.tier-platinum .tier-name,
#levels-table tr.tier-platinum .tier-name,
#ranks-splits-table tr.tier-platinum .tier-name { color: #66d9f3; }
#ranks-table tr.tier-emerald  .tier-name,
#levels-table tr.tier-emerald  .tier-name,
#ranks-splits-table tr.tier-emerald  .tier-name { color: #9048d8; }
#ranks-table tr.tier-diamond  .tier-name,
#levels-table tr.tier-diamond  .tier-name,
#ranks-splits-table tr.tier-diamond  .tier-name { color: #3078d8; }

#ranks-mult-table table, #levels-mult-table table { width: auto; border-collapse: collapse; margin-top: 8px; }
#ranks-mult-table th, #ranks-mult-table td,
#levels-mult-table th, #levels-mult-table td {
  padding: 6px 14px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
#ranks-mult-table th, #levels-mult-table th { font-weight: 600; color: var(--muted); font-size: 12px; text-transform: uppercase; }
#ranks-mult-table td.num, #levels-mult-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
#ranks-mult-table tr.harder td.num, #levels-mult-table tr.harder td.num  { color: #e58a8a; }
#ranks-mult-table tr.easier td.num, #levels-mult-table tr.easier td.num  { color: #8ad29a; }

/* ── Chat tab ──────────────────────────────────────────────────────────── */
table.chat-log { font-size: 13px; }
table.chat-log thead th { text-align: left; padding: 6px 10px;
  border-bottom: 1px solid var(--border); color: var(--muted);
  font-weight: 600; font-size: 11px; text-transform: uppercase; }
table.chat-log td { padding: 5px 10px; border-bottom: 1px solid var(--border);
  vertical-align: top; }
table.chat-log td:nth-child(4) { white-space: pre-wrap; word-break: break-word; }
table.chat-log tr:hover td { background: rgba(255,255,255,0.02); }
table.chat-log .sp-0 { color: #d9a55c; }   /* lion   */
table.chat-log .sp-1 { color: #9aa1a8; }   /* ele    */
table.chat-log .sp-2 { color: #c0c47a; }   /* gaz    */
table.chat-log .sp-3 { color: #6fa37b; }   /* croc   */
table.chat-log .sp-4 { color: #b08a5a; }   /* wbeest */
table.chat-log .sp-5 { color: #e0c060; }   /* cheetah*/
table.chat-log .sp-6 { color: #8095a8; }   /* vulture*/
#chat-filters { display: flex; flex-wrap: wrap; align-items: center;
  gap: 12px; margin-bottom: 10px; }
#chat-filters label { display: inline-flex; align-items: center;
  gap: 6px; font-size: 12px; color: var(--muted); }
#chat-filters input, #chat-filters select {
  background: var(--bg-elev); color: var(--text); border: 1px solid var(--border);
  border-radius: 4px; padding: 4px 8px; font-size: 13px; }
#chat-load-more { background: var(--bg-elev); color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
  padding: 6px 16px; cursor: pointer; font-size: 13px; }
#chat-load-more:disabled { opacity: 0.4; cursor: default; }
