:root {
    /* Team palette (used by the JS layer too — see TEAM_PALETTE in app.js).
     * These are only the pre-load defaults: displayResults() re-points
     * --team-a..--team-d at the per-match, name-assigned colours
     * (assignTeamColors), so don't use them for anything that isn't
     * literally "the colour of team at index N". */
    --team-a: #ff5050;
    --team-b: #50a0ff;
    --team-c: #4ecdc4;
    --team-d: #ffc107;

    /* Surface palette — body background and panel backgrounds. */
    --bg-deep: #1a1a2e;       /* page + panel background */
    --bg-panel: #2a3f5f;      /* divider, slot tile, header bar */
    --fg-default: #e0e0e0;    /* default body text colour */

    /* Accent palette — the cyan/teal used for headers, focus, key UI chrome. */
    --accent-cyan: #00d9ff;       /* primary accent */
    --accent-cyan-dim: #00b8d9;   /* gradient + hover counterpart */

    /* Armor palette — used by both .armor-XX classes and JS canvas drawing. */
    --armor-ra: #ff5050;
    --armor-ya: #ffc800;          /* = rgb(255, 200, 0) — was duplicated as both forms */
    --armor-ga: #00b400;

}

/* Utility class for elements toggled via classList. Existing show/hide
 * logic in app.js sets el.style.display directly, so this class is only
 * picked up by call sites that use classList.add/remove('hidden'). */
.hidden {
    display: none;
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html, body {
    height: 100%;
}

body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
    background: var(--bg-deep);
    color: var(--fg-default);
    line-height: 1.6;
    animation: fadeIn 0.4s ease-out;
    /* Pin the topbar + sidebar by making the body itself non-scrolling
     * and giving the .main pane its own scroll. */
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

/* Top bar — slim wordmark + version + GitHub link. Body is the
 * scroll-anchor (overflow: hidden, flex column), so this just needs to
 * sit at the top and not shrink. */
.topbar {
    display: flex;
    align-items: center;
    flex: 0 0 44px;
    padding: 0 20px;
    background: #16213e;
    border-bottom: 1px solid rgba(0, 217, 255, 0.12);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    gap: 14px;
    z-index: 100;
}

.topbar-brand {
    font-family: 'Rajdhani', sans-serif;
    color: var(--accent-cyan);
    font-size: 1.1rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-decoration: none;
    text-shadow: 0 0 12px rgba(0, 217, 255, 0.25);
    transition: color 0.15s ease;
}

.topbar-brand:hover {
    color: var(--accent-cyan-dim);
}

.topbar-spacer { flex: 1; }

.topbar-demo-info {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-left: 18px;
    font-family: 'Rajdhani', sans-serif;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    color: #c9d6e8;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

body.no-demo .topbar-demo-info { display: none; }

.topbar-demo-info .topbar-team-a { color: var(--team-a); }
.topbar-demo-info .topbar-team-b { color: var(--team-b); }
.topbar-demo-info .topbar-vs { color: #7a8ba8; font-weight: 500; }
.topbar-demo-info .topbar-score { color: #e8eef7; font-variant-numeric: tabular-nums; }
.topbar-demo-info .topbar-map { color: var(--accent-cyan-dim, #7ad7e8); }
.topbar-demo-info .topbar-date { color: #7a8ba8; font-weight: 500; }
.topbar-demo-info .topbar-sep {
    color: #4a5a6e;
    font-weight: 400;
    margin: 0 2px;
}

.topbar-version {
    color: #4a5a6e;
    font-family: monospace;
    font-size: 0.75rem;
    text-decoration: none;
    transition: color 0.15s ease;
}

.topbar-version:hover {
    color: var(--accent-cyan);
}

/* Inner <a> rendered by setVersionTag in app.js for the commit hash */
.topbar-version a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
}

/* Text link to the portal (API docs, keys, MCP setup, contact). */
.topbar-link {
    color: #7a8ba8;
    font-size: 0.8rem;
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.15s ease;
}

.topbar-link:hover {
    color: var(--accent-cyan);
}

.topbar-github {
    color: #7a8ba8;
    display: inline-flex;
    align-items: center;
    transition: color 0.15s ease;
}

.topbar-github:hover {
    color: var(--accent-cyan);
}

/* Layout — sidebar + main pane fill the rest of the viewport. The
 * sidebar stays fixed; .main scrolls. */
.layout {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
}

.sidebar {
    width: 220px;
    flex-shrink: 0;
    background: #16213e;
    border-right: 1px solid rgba(0, 217, 255, 0.08);
    padding: 12px 8px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    overflow-y: auto;
}

.sidebar-btn {
    display: flex;
    align-items: center;
    width: 100%;
    background: transparent;
    border: none;
    color: #7a8ba8;
    font-family: 'Rajdhani', sans-serif;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    padding: 10px 14px;
    cursor: pointer;
    border-radius: 6px;
    text-align: left;
    transition: all 0.15s ease;
    position: relative;
}

.sidebar-btn:hover {
    color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.06);
}

.sidebar-btn.active {
    color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.08);
}

.sidebar-btn.active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 3px;
    background: var(--accent-cyan);
    border-radius: 2px;
}

/* Two-row main pane: unified-timeline sits in the fixed top row,
 * .main-scroll fills the rest and scrolls. The timeline is no longer
 * position:sticky — that triggered a Firefox compositor bug where the
 * canvases inside the now-active tab were composited through a stale
 * snapshot for the first frame. */
.main {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

#unified-timeline {
    flex: 0 0 auto;
    background: var(--bg-deep);
    /* Match the horizontal padding of .main-scroll so the timeline
     * lines up with the tab content below. */
    margin: 20px 28px 0 28px;
}

.main-scroll {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 20px 28px;
}

/* Empty state for non-Search tabs when no demo loaded.
 * Hides every direct child of an inactive-because-no-demo tab and
 * surfaces only the .empty-state placeholder. The Search tab is
 * exempt because its load + filter form must always be available. */
.empty-state {
    display: none;
    padding: 80px 40px;
    text-align: center;
    color: #7a8ba8;
    font-style: italic;
    font-size: 1rem;
}

body.no-demo .tab-content > .empty-state {
    display: block;
}

body.no-demo .tab-content:not(#tab-search) > :not(.empty-state) {
    display: none;
}

body.no-demo #unified-timeline {
    display: none !important;
}

@media (max-width: 800px) {
    .layout { flex-direction: column; }
    .sidebar {
        width: 100%;
        flex-direction: row;
        overflow-x: auto;
        padding: 6px 8px;
        border-right: none;
        border-bottom: 1px solid rgba(0, 217, 255, 0.08);
    }
    .sidebar-btn {
        flex-shrink: 0;
        padding: 6px 12px;
    }
    .sidebar-btn.active::before {
        left: 6px;
        right: 6px;
        top: auto;
        bottom: 0;
        width: auto;
        height: 2px;
        border-radius: 1px;
    }
    .main { padding: 16px; }
}

.card {
    background: #16213e;
    border-radius: 12px;
    padding: 28px;
    margin-bottom: 24px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(0, 217, 255, 0.08);
    transition: box-shadow 0.3s ease;
}

.card:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

.card h2 {
    font-family: 'Rajdhani', sans-serif;
    color: var(--accent-cyan);
    font-size: 1.4rem;
    letter-spacing: 0.03em;
    margin-bottom: 20px;
    border-bottom: 1px solid var(--bg-panel);
    padding-bottom: 10px;
}

.load-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-radius: 8px;
    transition: background 0.2s ease;
}

.load-row.dragover {
    background: rgba(0, 217, 255, 0.06);
}

.load-file-btn,
.load-row button {
    background: #0f3460;
    color: var(--accent-cyan);
    border: 1px solid rgba(0, 217, 255, 0.4);
    padding: 10px 20px;
    border-radius: 6px;
    cursor: pointer;
    font-weight: 600;
    font-size: 0.9rem;
    transition: all 0.2s ease;
    white-space: nowrap;
}

.load-file-btn:hover,
.load-row button:hover {
    background: var(--accent-cyan);
    color: var(--bg-deep);
    box-shadow: 0 0 12px rgba(0, 217, 255, 0.3);
}

.load-row button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.load-separator {
    color: var(--bg-panel);
    font-size: 1.2rem;
}

.load-row input[type="text"] {
    background: var(--bg-deep);
    border: 1px solid var(--bg-panel);
    border-radius: 6px;
    padding: 10px 15px;
    color: var(--fg-default);
    font-size: 0.95rem;
    width: 320px;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.load-row input[type="text"]:focus {
    outline: none;
    border-color: var(--accent-cyan);
    box-shadow: 0 0 0 3px rgba(0, 217, 255, 0.1);
}

.load-row input[type="text"]::placeholder {
    color: #666;
}

.status {
    margin-top: 15px;
    padding: 10px;
    border-radius: 5px;
    text-align: center;
}

.status.loading {
    background: var(--bg-panel);
    color: var(--accent-cyan);
    position: relative;
    overflow: hidden;
}

.status.loading::after {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(0, 217, 255, 0.1), transparent);
    animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
    to { left: 100%; }
}

.status.success {
    background: rgba(0, 200, 83, 0.2);
    color: #00c853;
}

.status.error {
    background: rgba(255, 82, 82, 0.2);
    color: #ff5252;
}

/* Demo Search Panel — load row + filter form, both inside the Search tab */
.search-load-panel,
.search-filter-panel {
    padding: 16px 20px;
}

.search-load-panel {
    margin-bottom: 16px;
}

.search-form {
    margin-bottom: 12px;
}

.search-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: flex-end;
}

.search-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 140px;
    min-width: 120px;
}

.search-field-date {
    flex: 0 1 160px;
}

.search-field > span {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #7a8ba8;
}

.search-field input,
.search-field select {
    background: var(--bg-deep);
    border: 1px solid var(--bg-panel);
    border-radius: 6px;
    padding: 8px 12px;
    color: var(--fg-default);
    font-size: 0.95rem;
    font-family: inherit;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.search-field input:focus,
.search-field select:focus {
    outline: none;
    border-color: var(--accent-cyan);
    box-shadow: 0 0 0 3px rgba(0, 217, 255, 0.1);
}

.search-field input::placeholder {
    color: #4a5a6e;
    font-style: italic;
}

.search-submit {
    background: #0f3460;
    color: var(--accent-cyan);
    border: 1px solid rgba(0, 217, 255, 0.4);
    padding: 9px 22px;
    border-radius: 6px;
    cursor: pointer;
    font-weight: 600;
    font-size: 0.9rem;
    font-family: inherit;
    transition: all 0.2s ease;
}

.search-submit:hover {
    background: var(--accent-cyan);
    color: var(--bg-deep);
    box-shadow: 0 0 12px rgba(0, 217, 255, 0.3);
}

.search-submit:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.search-results {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
}

.search-result {
    display: grid;
    grid-template-columns: 56px 110px 1fr auto auto;
    gap: 16px;
    align-items: center;
    background: var(--bg-deep);
    border: 1px solid rgba(42, 63, 95, 0.5);
    border-radius: 6px;
    padding: 10px 14px;
    color: var(--fg-default);
    font-size: 0.92rem;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: all 0.15s ease;
}

.search-result:hover {
    border-color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.06);
    box-shadow: 0 0 12px rgba(0, 217, 255, 0.15);
}

.search-result-mode {
    color: var(--accent-cyan);
    font-family: 'Rajdhani', sans-serif;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.search-result-map {
    color: #c8d4e3;
    font-weight: 500;
}

.search-result-contest {
    color: var(--fg-default);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.search-result-tag {
    color: #7a8ba8;
    font-size: 0.82rem;
    font-style: italic;
    min-width: 0;
    text-align: right;
}

.search-result-date {
    color: #7a8ba8;
    font-family: monospace;
    font-size: 0.85rem;
    white-space: nowrap;
}

.search-empty {
    padding: 20px;
    text-align: center;
    color: #7a8ba8;
    font-style: italic;
}

.search-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 10px 0 4px;
}

.search-pager-count {
    color: #7a8ba8;
    font-size: 0.85rem;
}

/* Tab Navigation */
.tab-nav {
    display: flex;
    gap: 4px;
    margin-bottom: 24px;
    border-bottom: 2px solid var(--bg-panel);
    padding-bottom: 0;
}

.tab-btn {
    background: transparent;
    border: none;
    color: #7a8ba8;
    padding: 12px 22px;
    font-family: 'Rajdhani', sans-serif;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    cursor: pointer;
    border-radius: 6px 6px 0 0;
    transition: all 0.2s ease;
    position: relative;
}

.tab-btn:hover {
    color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.06);
}

.tab-btn.active {
    color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.08);
    box-shadow: inset 0 -2px 0 var(--accent-cyan);
    margin-bottom: -2px;
}

.tab-content {
    display: none;
}

.tab-content.active {
    display: block;
}
/* (Removed `animation: tabFadeIn 0.3s ease-out`. The opacity +
 * translateY transition forced Firefox to composite the .tab-content
 * subtree through its own layer, which used a snapshot of the canvas
 * for the 300 ms animation window — anything renderMap / updateDetailView
 * wrote to the canvas backing store during that window stayed invisible
 * until the user navigated away and back, triggering another repaint.) */

.panel {
    background: var(--bg-deep);
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 24px;
    border: 1px solid rgba(42, 63, 95, 0.5);
}

.panel h3 {
    font-family: 'Rajdhani', sans-serif;
    color: var(--accent-cyan);
    margin-bottom: 15px;
    font-size: 1.15rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.panel h4 {
    font-family: 'Rajdhani', sans-serif;
    color: #a0c4d8;
    margin: 12px 0 6px 0;
    font-size: 0.95rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 20px;
}

.summary-item {
    text-align: center;
}

.summary-item label {
    display: block;
    color: #6b7d99;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 6px;
}

.summary-item span {
    font-size: 1.6rem;
    font-weight: 600;
    color: var(--fg-default);
}

/* --- Duel mode: hide redundant "Per Team" aggregates ---
 *
 * When the Go-side normalizer detects a 1v1 it rewrites every team
 * reference to the player's own name, so the "Per Team" tables would
 * just restate the "Per Player" tables with different headers. These
 * selectors hide:
 *   - the standalone Teams summary panel (#teams-panel)
 *   - the "Per Team" h4 headings above each stats block
 *   - the "Per Team" aggregate tables themselves
 *   - the "Per Player" heading (redundant once there's no sibling)
 */
body.duel-mode .team-aggregate-panel,
body.duel-mode .team-aggregate-heading,
body.duel-mode .team-aggregate-table,
body.duel-mode .per-player-heading {
    display: none !important;
}

/* --- No team rows: same treatment for FFA ---
 *
 * playerStats emits no `teams` section when the demo has no teams to
 * aggregate (FFA is not teamplay), so the Summary "Per Team" tables would
 * render as three headers over an empty tbody. Scoped to #tab-summary
 * because those tables are the ones fed from playerStats.teams; the
 * Pickups tab derives its own team rows from the item phases. */
body.no-team-rows #tab-summary .team-aggregate-heading,
body.no-team-rows #tab-summary .team-aggregate-table,
body.no-team-rows #tab-summary .per-player-heading {
    display: none !important;
}

/* Explanatory note under a panel heading — used where a number deviates
 * from the server's own end-of-match table on purpose (the possession
 * columns of Item Pickups) and the viewer deserves to know why without a
 * hover. */
.panel-note {
    margin: 0 0 0.75rem;
    max-width: 68ch;
    color: #8a9bb8;
    font-size: 0.8rem;
    line-height: 1.45;
}

/* Evidence-grade banner under a panel heading: what the numbers below were
 * measured from, when that is not the wire. One line, always visible (unlike
 * .panel-info, which the reader has to open) — a reconstructed damage figure
 * read as a measured one is the mistake this exists to prevent. `.warn` is
 * the incomplete-evidence variant, not a severity: it fires where
 * damage.coverage.ratio < 1, i.e. where the figures below are floors. */
.panel-provenance {
    margin: 0 0 0.75rem;
    max-width: 78ch;
    padding: 8px 12px;
    border-radius: 5px;
    border-left: 3px solid #7a8ba8;
    background: rgba(122, 139, 168, 0.1);
    color: #a9b8cf;
    font-size: 0.8rem;
    line-height: 1.45;
}

.panel-provenance.warn {
    border-left-color: #ffb347;
    background: rgba(255, 165, 0, 0.1);
    color: #e0c08a;
}

/* The "≠" an Acc cell wears when its hit convention is not the one the
 * server's own counter uses for that weapon (see ktxHitsConvention). Marked
 * in the TEXT, not by colour: the comparison it guards against is made from a
 * screenshot, which carries the glyph and not the tooltip. Its footnote sits
 * under the table and appears only while some cell wears the mark. */
.acc-off-scale {
    color: #ffb347;
    font-size: 0.75em;
    margin-left: 1px;
}

.acc-scale-note {
    margin: 0.5rem 0 0;
}

/* A figure the reconstruction recovered rather than the wire measured — the
 * v73/v74 aim recon tier's hits, and any accuracy built on them. Legible
 * (these are real numbers, unlike .stat-muted's half-measurements) but
 * visibly marked, so a recon hit count can never be mistaken for the
 * server's own. Hover carries the definition and the coverage caveat. */
.stat-recon {
    border-bottom: 1px dotted #7a8ba8;
    cursor: help;
}

/* result.noMatch (schema v74): the recording holds no analyzable match.
 * Deliberately NOT the red of .status.error — a parse failure and "this
 * demo has no match in it" are different findings, and the marker exists
 * precisely so a consumer can tell them apart. */
.no-match-panel {
    border-left: 3px solid #ffb347;
}

.no-match-panel h3 {
    color: #ffb347;
}

.no-match-detail {
    margin: 0 0 1rem;
    max-width: 78ch;
    color: #a9b8cf;
    font-size: 0.9rem;
    line-height: 1.5;
}

/* The plain reading of the finding above it — what the marker means for the
 * person holding the file, and the one action that honestly exists. Sits a
 * shade brighter than .no-match-detail (the pipeline's own sentence), since
 * this is the line a reader acts on; same size, same measure, no second
 * accent colour — it is not a warning, it is the answer. */
.no-match-reading {
    margin: 0 0 1rem;
    max-width: 78ch;
    color: #c8d4e6;
    font-size: 0.9rem;
    line-height: 1.5;
}

/* The evidence cells reuse .summary-item, whose span is sized for a map name
 * or a duration. These carry sentences ("1 min left", "not sent", a date
 * marker), so they take a readable size instead. */
.no-match-panel .summary-item span {
    font-size: 0.95rem;
    font-weight: 500;
}

.no-match-errors {
    margin: 1rem 0 0;
    max-width: 78ch;
    color: #ff8a8a;
    font-size: 0.8rem;
    line-height: 1.45;
}

/* The Summary tables below the marker are all fed from `streams`, which is
 * absent exactly when `noMatch` is present — they would render as headers
 * over empty bodies. The panels that DO have something to say on such a demo
 * (Match Info, Match Settings, Server Info) stay. */
body.no-match #players-panel,
body.no-match #weapons-summary-panel,
body.no-match #items-panel,
body.no-match #weapons-panel {
    display: none;
}

/* The one disclosure idiom for methodology prose under a panel heading.
 * Every panel that used to open with a paragraph of explanation now
 * carries it here instead, collapsed, so the numbers start at the top of
 * the panel. Visually derived from .per-player-detail (same subtle
 * summary line); the body keeps whichever prose class it already had
 * (.panel-note / .aim-desc / .locheatmap-desc), which is why the last
 * child's own bottom margin is dropped in favour of this one. */
.panel-info {
    margin: 0 0 0.75rem;
}

.panel-info > summary {
    cursor: pointer;
    font-size: 0.8rem;
    color: #aaa;
    user-select: none;
}

.panel-info > summary:hover {
    color: #ddd;
}

.panel-info[open] > summary {
    margin-bottom: 8px;
}

.panel-info > :last-child {
    margin-bottom: 0;
}

/* A cell showing a measurement we could only partly make — e.g. an
 * accuracy block reconstructed from the fire stream on a demo with no
 * damage stream to link the fires against, which yields attacks but no
 * hits. Rendered muted so it never reads as a real accuracy figure. */
.stat-muted {
    color: #6b7d99;
    font-style: italic;
}

/* The match wall clock (streams.global, schema v72) when its anchor is not
 * graded `exact`. `unverified` is muted; `contradicted` means a hard check
 * failed and takes the warning colour. Both keep the "~" the JS puts in the
 * text — a colour alone does not survive a screenshot or a colour-blind
 * reader, and this is a date somebody may write down. */
.date-unverified {
    color: #8a9bb8;
    cursor: help;
}

.date-contradicted {
    color: #ffb347;
    cursor: help;
}

/* --- Match Settings: modifier pills (Dmgfrags / Midair / etc.) --- */
.modifier-label {
    display: block;
    color: #6b7d99;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 8px;
    text-align: center;
}

.modifier-list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: center;
}

.modifier-tag {
    display: inline-block;
    padding: 4px 10px;
    background: var(--bg-panel);
    color: var(--accent-cyan);
    border-radius: 12px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    border: 1px solid rgba(0, 217, 255, 0.25);
}

/* --- Server Info: collapsible cvar table --- */
#server-info-panel details > summary {
    cursor: pointer;
    list-style: none;
    user-select: none;
}
#server-info-panel details > summary::-webkit-details-marker {
    display: none;
}
#server-info-panel details > summary::before {
    content: '▶ ';
    color: var(--accent-cyan);
    font-size: 0.85rem;
    margin-right: 6px;
}
#server-info-panel details[open] > summary::before {
    content: '▼ ';
}

.serverinfo-table {
    margin-top: 12px;
    width: auto;
}
.serverinfo-table td:first-child code {
    color: var(--accent-cyan-dim);
    font-size: 0.85rem;
}
.serverinfo-table td:nth-child(2) {
    color: var(--fg-default);
    font-size: 0.85rem;
    word-break: break-all;
}

/* --- Scoreboard: bot badge next to player name --- */
.bot-badge {
    display: inline-block;
    padding: 1px 6px;
    margin-left: 6px;
    background: rgba(255, 165, 0, 0.18);
    color: #ffb347;
    border-radius: 8px;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    border: 1px solid rgba(255, 165, 0, 0.4);
    vertical-align: middle;
}

.teams-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 15px;
}

.team-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 20px;
    background: #16213e;
    border-radius: 8px;
    border-left: 3px solid var(--bg-panel);
    transition: border-color 0.2s;
}

.team-item:nth-child(1) {
    border-left-color: var(--team-a);
}

.team-item:nth-child(2) {
    border-left-color: var(--team-b);
}

.team-item:nth-child(3) {
    border-left-color: var(--team-c);
}

.team-item:nth-child(4) {
    border-left-color: var(--team-d);
}

.team-name {
    font-weight: 600;
    font-size: 1.05rem;
}

.team-frags {
    font-family: 'Rajdhani', sans-serif;
    color: var(--accent-cyan);
    font-weight: 700;
    font-size: 1.2rem;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-lnum;
}

table th,
table td {
    padding: 10px 12px;
    text-align: left;
    border-bottom: 1px solid rgba(42, 63, 95, 0.6);
}

table th {
    background: #16213e;
    color: #7a8ba8;
    font-weight: 600;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    position: sticky;
    top: 0;
    z-index: 5;
}

table tbody tr:nth-child(even) {
    background: rgba(255, 255, 255, 0.015);
}

table tbody tr:hover {
    background: rgba(0, 217, 255, 0.07);
}

/* Right-align numeric columns (all columns except first) */
.stats-table td:not(:first-child),
.stats-table th:not(:first-child) {
    text-align: right;
}

/* In a two-row header the Player <th> spans both rows via rowspan, so the
   second row's :first-child is a data column and must right-align like the
   cells under it. */
.stats-table thead tr + tr th:first-child {
    text-align: right;
}

.chart {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.weapon-bar {
    display: grid;
    grid-template-columns: 120px 1fr 60px;
    align-items: center;
    gap: 10px;
}

.weapon-name {
    color: #888;
    font-size: 0.9rem;
}

.bar-container {
    background: var(--bg-panel);
    border-radius: 4px;
    height: 20px;
    overflow: hidden;
}

.bar {
    height: 100%;
    background: linear-gradient(90deg, var(--accent-cyan), var(--accent-cyan-dim));
    border-radius: 4px;
    transition: width 0.5s ease-out;
    box-shadow: 0 0 6px rgba(0, 217, 255, 0.2);
}

.weapon-count {
    text-align: right;
    font-weight: bold;
    color: var(--accent-cyan);
}

/* Aim Stats tab (experimental) */
.aim-controls {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

/* The player picker lives inside the Crosshair placement panel (it only
   drives the per-player plots); give the in-panel strip its own spacing. */
#aim-heatmap-panel .aim-controls {
    margin-bottom: 10px;
}

.aim-controls-label {
    color: #888;
    font-size: 0.85rem;
}

.aim-chips {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    outline: none;
}

.aim-chips:focus-within,
.aim-chips:focus {
    box-shadow: 0 0 0 2px var(--accent-cyan-dim);
    border-radius: 6px;
}

.aim-chip {
    padding: 3px 10px;
    background: var(--bg-panel);
    color: var(--fg-default);
    border: 1px solid transparent;
    border-bottom: 2px solid #666;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
}

.aim-chip:hover {
    border-color: var(--accent-cyan-dim);
}

.aim-chip.active {
    background: var(--accent-cyan-dim);
    color: #06121a;
    font-weight: 600;
}

.aim-mode {
    color: #888;
    font-size: 0.85rem;
}

.aim-desc {
    color: #888;
    font-size: 0.85rem;
    margin-bottom: 10px;
    max-width: 72ch;
}

/* One stats table per weapon, players on the rows (team-coloured stripe). */
.aim-weapon-block {
    margin-bottom: 18px;
    /* The widest of these (SG/SSG, 14 columns) overruns the pane at 1280 px
       and its right-hand columns were simply unreachable; scroll the table
       inside its own block instead, as Item Pickups does. */
    overflow-x: auto;
}

.aim-weapon-block h4 {
    margin: 0 0 6px;
    color: var(--accent-cyan);
}

/* LG and SG sit side by side where the pane is wide enough (each column is
   ~570px of canvas + hists) and wrap to a stack on narrow panes. */
.aim-heatmap-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 32px;
    align-items: flex-start;
}

.aim-heatmap-block {
    margin-top: 14px;
    min-width: 0;
}

/* Smoothed crosshair-density image; intrinsic size set by drawAimDensity.
   The background doubles as the image's zero-density colour (read back by
   aimSurfaceRGB) — bg-deep so empty regions match the table heatmaps. */
.aim-density {
    display: block;
    background: var(--bg-deep);
    border-radius: 4px;
    max-width: 100%;
    height: auto; /* keep aspect when max-width shrinks the bitmap */
}

.aim-heatmap-block h4 {
    margin: 0 0 6px;
    color: var(--accent-cyan);
}

.aim-weapon-table th {
    font-size: 0.8rem;
}

.aim-sel-note {
    color: #888;
    font-size: 0.8rem;
    font-weight: 400;
}

/* In-flow "no data" note (unlike .map-no-data, which is absolutely centred
   over the map canvas and would float mid-panel without scrolling). */
.aim-nodata {
    color: #888;
    font-size: 0.9rem;
    padding: 6px 2px;
}

/* Histograms under each crosshair image, stacked vertically: one column per
   bin, the shaded band marks |n| ≤ 1 (on the hull), the rule marks dead
   center. */
.aim-hist-row {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 10px;
}

.aim-hist {
    width: 100%;
    max-width: 580px;
}

.aim-hist-title {
    color: #888;
    font-size: 0.8rem;
    margin-bottom: 4px;
}

.aim-hist-plot {
    position: relative;
    display: flex;
    align-items: flex-end;
    gap: 1px;
    height: 84px;
    background: var(--bg-panel);
    border-radius: 4px;
    padding-top: 2px;
}

.aim-hist-band {
    position: absolute;
    top: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.05);
    pointer-events: none;
}

.aim-hist-zero {
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    background: rgba(255, 255, 255, 0.22);
    pointer-events: none;
}

.aim-hist-col {
    flex: 1;
    height: 100%;
    display: flex;
    align-items: flex-end;
}

.aim-hist-col:hover .aim-hist-fill {
    background: var(--accent-cyan);
}

.aim-hist-fill {
    width: 100%;
    background: var(--accent-cyan-dim);
    border-radius: 1px 1px 0 0;
}

/* Ramp histogram: y-axis gutter for its dynamic hit-% scale; the x axis
   below gets a matching inset so its labels stay under the plot. */
.aim-hist-ywrap {
    display: flex;
    gap: 6px;
}

.aim-hist-ywrap .aim-hist-plot {
    flex: 1;
}

.aim-hist-yaxis {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 34px;
    color: #666;
    font-size: 0.72rem;
    text-align: right;
    padding: 2px 0;
}

.aim-hist-axis-inset {
    margin-left: 40px; /* y-axis gutter width + gap */
}

.aim-hist-axis {
    display: flex;
    color: #666;
    font-size: 0.72rem;
    margin-top: 2px;
}

/* Equal thirds so the middle "0" sits at the true center regardless of how
   wide the unit suffix on the right label is. */
.aim-hist-axis span {
    flex: 1;
    text-align: center;
}

.aim-hist-axis span:first-child { text-align: left; }
.aim-hist-axis span:last-child { text-align: right; }

.aim-na {
    color: #555;
    text-align: center;
}

/* Timeline Styles */
.timeline-container {
    max-height: 600px;
    overflow-y: auto;
}

.timeline-header {
    display: grid;
    grid-template-columns: 1fr 80px 1fr;
    gap: 10px;
    padding: 10px;
    background: #16213e;
    border-radius: 8px 8px 0 0;
    position: sticky;
    top: 0;
    z-index: 10;
}

.team-header {
    font-weight: bold;
    color: var(--accent-cyan);
    text-align: center;
}

.timeline-center {
    text-align: center;
    color: #888;
    font-size: 0.9rem;
}

.timeline-body {
    display: flex;
    flex-direction: column;
}

.timeline-row {
    display: grid;
    grid-template-columns: 1fr 80px 1fr;
    gap: 10px;
    padding: 4px 10px;
    border-bottom: 1px solid var(--bg-panel);
}

.timeline-row:hover {
    background: rgba(0, 217, 255, 0.05);
}

.timeline-left {
    text-align: right;
    font-size: 0.85rem;
}

.timeline-time {
    text-align: center;
    color: #888;
    font-size: 0.8rem;
    font-family: monospace;
}

.timeline-right {
    text-align: left;
    font-size: 0.85rem;
}

.timeline-event {
    padding: 2px 6px;
    border-radius: 3px;
    display: inline-block;
}

.timeline-event.frag {
    color: #ff6b6b;
}

.timeline-event.chat {
    color: #4ecdc4;
}

.timeline-event.teamsay {
    color: #45b7d1;
}

/* Timeline Analysis Tab */
.timeline-nav-panel {
    margin-bottom: 12px;
    padding: 18px 15px 4px 15px;
}

.team-status-panel {
    margin-bottom: 12px;
    padding: 8px 15px;
}

.team-status-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
}

.team-status-side h4 {
    margin: 0 0 6px 0;
    font-size: 0.9rem;
    color: var(--accent-cyan);
    border-bottom: 1px solid var(--bg-panel);
    padding-bottom: 4px;
}

.team-status-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
    font-family: monospace;
}

.team-status-table th {
    text-align: left;
    color: #888;
    font-weight: normal;
    padding: 2px 6px;
    border-bottom: 1px solid #1a2744;
}

.team-status-table td {
    padding: 2px 6px;
    color: #ccc;
}

.team-status-table tr:hover {
    background: rgba(0, 217, 255, 0.04);
}

.team-status-table .totals-row td {
    border-top: 1px solid var(--bg-panel);
    color: var(--accent-cyan);
    font-weight: bold;
    padding-top: 4px;
}

.armor-ra { color: var(--armor-ra); }
.armor-ya { color: var(--armor-ya); }
.armor-ga { color: var(--armor-ga); }

.timeline-nav-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.timeline-nav-time {
    font-family: monospace;
    font-size: 0.9rem;
    color: var(--accent-cyan);
    min-width: 50px;
    text-align: center;
    line-height: 24px;
}

.timeline-nav-bar-wrapper {
    flex: 1;
    position: relative;
    user-select: none;
    padding-bottom: 7px;
}

.timeline-nav-bar {
    position: relative;
    height: 24px;
    background: #1a2744;
    border-radius: 4px;
    cursor: crosshair;
}

.timeline-nav-track {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 4px;
}

.timeline-nav-caret {
    position: absolute;
    top: 24px;
    font-size: 14px;
    line-height: 1;
    color: var(--accent-cyan);
    cursor: grab;
    z-index: 10;
    transform: translateX(-50%);
    padding: 0 6px;
    text-shadow: 0 0 4px rgba(0, 217, 255, 0.5);
}

.timeline-nav-caret:active {
    cursor: grabbing;
}

.timeline-selection-overlay {
    position: absolute;
    top: 0;
    bottom: 0;
    background: rgba(0, 217, 255, 0.15);
    border-left: 2px solid rgba(0, 217, 255, 0.6);
    border-right: 2px solid rgba(0, 217, 255, 0.6);
    pointer-events: none;
    z-index: 3;
    display: none;
    border-radius: 2px;
}

.timeline-nav-cursor {
    position: absolute;
    top: -2px;
    bottom: -2px;
    width: 3px;
    background: var(--accent-cyan);
    border-radius: 2px;
    pointer-events: none;
    z-index: 5;
    box-shadow: 0 0 6px rgba(0, 217, 255, 0.5);
}

.timeline-nav-segment {
    font-family: monospace;
    font-size: 0.9rem;
    color: #888;
    white-space: nowrap;
    min-width: 90px;
    margin-top: 2px;
}

.timeline-nav-axis {
    position: relative;
    height: 16px;
    font-size: 0.9rem;
    color: #666;
    font-family: monospace;
}

.timeline-nav-axis span {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    white-space: nowrap;
}

.timeline-nav-axis span:first-child {
    transform: none;
}

.timeline-nav-axis span:last-child {
    transform: translateX(-100%);
}

.current-time-indicator {
    position: absolute;
    top: 0;
    bottom: 25px;
    width: 2px;
    background: var(--accent-cyan);
    /* Grabbable: dragging the line scrubs the clock. A wider transparent
       ::before gives a comfortable hit target while the visible line stays 2px. */
    pointer-events: auto;
    cursor: ew-resize;
    z-index: 5;
    opacity: 0.8;
}

.current-time-indicator::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: -5px;
    right: -5px;
}

/* Per-player minis reserve no in-canvas axis (they share one axis row at the
   bottom of the grid), so the line spans the full row height instead of the
   team graphs' 25px axis strip. */
.per-player-canvas-wrap .current-time-indicator {
    bottom: 0;
}

/* While dragging a playhead line, force the resize cursor everywhere so it
   doesn't flicker as the pointer leaves the 2px line. */
body.graph-scrubbing,
body.graph-scrubbing * {
    cursor: ew-resize !important;
}

/* Shared time axis row at the bottom of a per-player grid. The empty
   .per-player-label spacer aligns the tick track with the graph column. */
.per-player-axis-cell {
    margin-top: 2px;
}

.per-player-axis-track {
    position: relative;
    flex: 1;
    min-width: 0;
    height: 14px;
}

.per-player-axis-track span {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    font-size: 0.7rem;
    color: #888;
    font-family: monospace;
    white-space: nowrap;
}

.timeline-legend {
    display: flex;
    gap: 20px;
    justify-content: center;
    flex-wrap: wrap;
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 0.9rem;
    color: #888;
}

.legend-color {
    width: 12px;
    height: 12px;
    border-radius: 2px;
}

.legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

.legend-dot.rl { background: rgba(255, 107, 107, 0.9); }
.legend-dot.lg { background: rgba(0, 217, 255, 0.9); }

/* Granular weapon/powerup colors */
.legend-color.weapons { background: rgba(0, 217, 255, 0.9); }
.legend-color.powerups { background: rgba(255, 193, 7, 0.9); }
.legend-color.rl { background: rgba(255, 107, 107, 0.9); }
.legend-color.lg { background: rgba(0, 217, 255, 0.9); }
.legend-color.rllg { background: rgba(156, 39, 176, 0.9); }
.legend-color.quad { background: rgba(0, 150, 255, 0.9); }
.legend-color.pent { background: rgba(255, 0, 0, 0.9); }
.legend-color.ring { background: rgba(255, 235, 59, 0.9); }
.legend-color.health { background: rgba(0, 200, 83, 0.9); }
.legend-color.armor { background: rgba(255, 193, 7, 0.9); }
.legend-color.ra { background: rgba(255, 50, 50, 0.9); }
.legend-color.ya { background: rgba(255, 200, 0, 0.9); }
.legend-color.ga { background: rgba(0, 180, 0, 0.9); }

/* Y-Axis styling */
.detail-graph-wrapper,
.health-graph-wrapper {
    display: flex;
    gap: 5px;
}

.timeline-y-axis {
    width: 30px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-end;
    padding: 10px 5px 20px 0;
    font-size: 0.9rem;
    color: #888;
    font-family: monospace;
}

.y-label {
    line-height: 1;
}

/* Full-width panels */
.timeline-detail-graph-panel-full,
.timeline-health-panel-full {
    margin-bottom: 20px;
}

.detail-graph-outer,
.health-graph-outer {
    flex: 1;
    display: flex;
    flex-direction: column;
    position: relative;
}

.timeline-canvas {
    width: 100%;
    height: 200px;
    display: block;
    border-radius: 4px;
}

.detail-graph-axis,
.health-graph-axis {
    display: flex;
    justify-content: space-between;
    padding: 5px 10px 0 10px;
    font-size: 0.9rem;
    color: #888;
    font-family: monospace;
}

/* Chat tab layout */
.chat-panel-outer {
    padding: 12px 16px;
    margin-bottom: 20px;
}

.chat-toolbar {
    display: flex;
    gap: 16px;
    align-items: center;
    margin-bottom: 8px;
    font-size: 0.9rem;
    color: #ccc;
}

.chat-toolbar label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}

.chat-header-row {
    display: flex;
    gap: 0;
    margin-bottom: 4px;
}

.chat-header-row .chat-time-axis-col {
    width: 54px;
    flex-shrink: 0;
}

.chat-header-row .chat-time-axis-col h3 {
    visibility: hidden;
}

.chat-column-header {
    flex: 1;
    min-width: 0;
}

.chat-scroll-viewport {
    position: relative;
    height: 700px;
    overflow-y: auto;
    overflow-x: hidden;
}

.chat-scroll-inner {
    display: flex;
    gap: 0;
    position: relative;
}

.chat-time-axis-col {
    width: 54px;
    flex-shrink: 0;
}

.chat-time-axis {
    position: relative;
}

.chat-tick {
    position: absolute;
    right: 4px;
    display: flex;
    align-items: center;
    transform: translateY(-50%);
    font-size: 0.67rem;
    font-family: monospace;
    color: #666;
    white-space: nowrap;
    pointer-events: none;
}

.chat-tick::after {
    content: '';
    display: inline-block;
    width: 8px;
    height: 1px;
    background: #3a3a5a;
    margin-left: 3px;
    flex-shrink: 0;
}

.chat-tick-current {
    color: var(--accent-cyan);
    font-weight: 600;
}

.chat-tick-current::after {
    background: var(--accent-cyan);
    width: 10px;
}

.chat-columns-wrapper {
    display: flex;
    flex: 1;
    gap: 4px;
}

.chat-timeline-scroll {
    position: relative;
    flex: 1;
    min-width: 0;
    background: #0d1929;
    border-radius: 4px;
}

.chat-time-marker {
    position: absolute;
    left: 0;
    right: 0;
    display: flex;
    align-items: flex-start;
    gap: 6px;
    padding: 1px 6px;
    font-size: 0.82rem;
    white-space: nowrap;
    overflow: hidden;
}

.chat-time-marker-msg {
    word-break: break-word;
    white-space: normal;
}

.chat-time-marker-msg.frag { color: #ff6b6b; }
.chat-time-marker-msg.powerup { color: #ffc107; }
.chat-time-marker-msg.chat { color: #4ecdc4; }
.chat-time-marker-msg.teamsay { color: #45b7d1; }

.chat-displaced {
    padding-left: 16px;
}

.chat-displaced-dots {
    color: #555;
    font-size: 0.7rem;
    margin-right: 2px;
}

.chat-current-time-line {
    position: absolute;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--accent-cyan);
    z-index: 10;
    pointer-events: none;
    opacity: 0.6;
    top: 0;
}

/* Diverging bar chart - Team A up, Team B down */
.diverging-bar {
    flex: 1;
    display: flex;
    flex-direction: column;
    height: 100%;
    min-width: 2px;
    position: relative;
}

.diverging-bar-top {
    position: absolute;
    bottom: 50%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column-reverse;
    align-items: stretch;
}

.diverging-bar-bottom {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

.bar-segment {
    transition: height 0.1s;
    width: 100%;
}

/* Legacy colors */
.bar-segment.weapons { background: rgba(0, 217, 255, 0.9); }
.bar-segment.powerups { background: rgba(255, 193, 7, 0.9); }

/* Granular weapon colors */
.bar-segment.rl { background: rgba(255, 107, 107, 0.9); }
.bar-segment.lg { background: rgba(0, 217, 255, 0.9); }
.bar-segment.rllg { background: rgba(156, 39, 176, 0.9); }

/* Granular powerup colors */
.bar-segment.quad { background: rgba(0, 150, 255, 0.9); }
.bar-segment.pent { background: rgba(255, 0, 0, 0.9); }
.bar-segment.ring { background: rgba(255, 235, 59, 0.9); }

/* Health/Armor segments */
.bar-segment.health { background: rgba(0, 200, 83, 0.9); }
.bar-segment.armor { background: rgba(255, 193, 7, 0.9); }
.bar-segment.ra { background: rgba(255, 50, 50, 0.9); }
.bar-segment.ya { background: rgba(255, 200, 0, 0.9); }
.bar-segment.ga { background: rgba(0, 180, 0, 0.6); }
.bar-segment.frags { background: rgba(255, 100, 100, 0.9); }

/* Gridlines for diverging charts */
.detail-graph-container::before,
.health-armor-graph-diverging::before,
.score-timeline-graph::before {
    content: '';
    position: absolute;
    left: 10px;
    right: 10px;
    top: 25%;
    height: 1px;
    background: rgba(255, 255, 255, 0.04);
    pointer-events: none;
    box-shadow: 0 0 0 0 transparent,
                0 calc(50% - 25%) 0 0 rgba(255, 255, 255, 0.04);
}

/* Detail Row Layout */
.timeline-detail-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin-bottom: 20px;
}

@media (max-width: 900px) {
    .timeline-detail-row {
        grid-template-columns: 1fr;
    }
}

/* Messages Panel */

/* Detail Graph Panel */
.timeline-detail-graph-panel {
    min-height: 250px;
}

.detail-graph-container {
    height: 200px;
    background: #16213e;
    border-radius: 4px;
    padding: 10px;
    display: flex;
    align-items: center;
    gap: 1px;
    position: relative;
}

.detail-graph-container::after {
    content: '';
    position: absolute;
    left: 10px;
    right: 10px;
    top: 50%;
    height: 2px;
    background: rgba(0, 0, 0, 0.8);
    pointer-events: none;
}

/* Powerup Lines Container */
.powerup-lines-container {
    position: relative;
    height: 24px;
    background: rgba(0, 0, 0, 0.2);
    border-radius: 2px;
    margin: 0 10px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
}

.powerup-line {
    position: absolute;
    height: 6px;
    border-radius: 2px;
    opacity: 0.9;
}

.powerup-line.quad { background: rgba(0, 150, 255, 0.9); top: 2px; }
.powerup-line.pent { background: rgba(255, 0, 0, 0.9); top: 9px; }
.powerup-line.ring { background: rgba(255, 235, 59, 0.9); top: 16px; }

/* Region Control Timeline */
.region-timeline-wrapper {
    display: flex;
    gap: 0;
}

.region-timeline-labels {
    display: flex;
    flex-direction: column;
    min-width: 80px;
}

.region-timeline-label {
    font-size: 0.75rem;
    color: #aaa;
    text-align: right;
    padding-right: 6px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.region-timeline-outer {
    flex: 1;
    position: relative;
}

#region-time-indicator {
    bottom: 0;
}

/* Per-player drill-downs under the Team Health/Armor + Weapons panels */
.per-player-detail {
    margin-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 6px;
}

.per-player-detail > summary {
    cursor: pointer;
    font-size: 0.8rem;
    color: #aaa;
    user-select: none;
}

.per-player-detail > summary:hover {
    color: #ddd;
}

.per-player-detail[open] > summary {
    margin-bottom: 8px;
}

/* Health/armor minis: full-width rows stacked vertically, matching the
   per-player weapons layout (label column + full-width canvas). */
.per-player-grid {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.per-player-cell {
    display: flex;
    align-items: stretch;
    gap: 0;
    min-width: 0;
}

.per-player-label {
    min-width: 80px;
    max-width: 80px;
    font-size: 0.75rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: right;
    padding-right: 6px;
    align-self: center;
}

.per-player-canvas-wrap {
    position: relative;
    flex: 1;
    min-width: 0;
}

/* Frags/deaths drill-down: a two-line label (name + frags/deaths · eff%)
   needs a touch more width and wrapping than the single-line minis above. */
#frags-per-player .per-player-label {
    min-width: 104px;
    max-width: 104px;
    white-space: normal;
    line-height: 1.15;
}

#frags-per-player .fd-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

#frags-per-player .fd-stat {
    font-size: 0.68rem;
    color: #999;
}

/* Loc Heatmap */
.locheatmap-desc {
    font-size: 0.75rem;
    color: #888;
    margin-bottom: 8px;
    max-width: 60ch;
}

/* Standalone metric selector governing the loc graph + heatmap. */
.locmetric-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.locmetric-label {
    font-weight: 600;
}

.locmetric-hint {
    font-size: 0.75rem;
    color: #888;
}

/* Heatmap matrices (loc heatmap + region control) — sortable .stats-table. */
.heatmap-table-wrap {
    overflow-x: auto;
}

.heatmap-table {
    font-size: 0.78rem;
}

.heatmap-table th.heatmap-col,
.heatmap-table td.heatmap-cell {
    text-align: center;
    white-space: nowrap;
}

.heatmap-table th.heatmap-col {
    font-weight: normal;
}

.heatmap-table th.heatmap-col-team {
    font-weight: bold;
}

.heatmap-table td.heatmap-rowname {
    white-space: nowrap;
}

.heatmap-table .heatmap-col-sep {
    border-left: 2px solid rgba(255, 255, 255, 0.35);
}

/* Sequential viridis legend — gradient stops mirror HEAT_STOPS in app.js. */
.heatmap-legend {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.7rem;
    color: #888;
    margin-bottom: 8px;
}

.heatmap-legend-bar {
    width: 120px;
    height: 10px;
    border-radius: 2px;
    background: linear-gradient(to right,
        rgb(68, 1, 84) 0%,
        rgb(59, 82, 139) 25%,
        rgb(33, 145, 140) 50%,
        rgb(94, 201, 98) 75%,
        rgb(253, 231, 37) 100%);
}

/* Diverging Health/Armor Graph */
.health-armor-graph-diverging {
    height: 200px;
    background: #16213e;
    border-radius: 4px;
    padding: 10px;
    display: flex;
    align-items: center;
    gap: 1px;
    position: relative;
}

.health-armor-graph-diverging::after {
    content: '';
    position: absolute;
    left: 10px;
    right: 10px;
    top: 50%;
    height: 2px;
    background: rgba(0, 0, 0, 0.8);
    pointer-events: none;
}

/* Score Timeline Graph */
.score-timeline-graph {
    height: 200px;
    background: #16213e;
    border-radius: 4px;
    padding: 10px;
    display: flex;
    align-items: center;
    gap: 0;
    position: relative;
}

.score-timeline-graph::after {
    content: '';
    position: absolute;
    left: 10px;
    right: 10px;
    top: 50%;
    height: 2px;
    background: rgba(0, 0, 0, 0.8);
    pointer-events: none;
}

.score-bar {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    position: relative;
}

.score-bar-fill {
    position: absolute;
    left: 0;
    right: 0;
    /* background-color set inline by updateScoreTimeline from TEAM_COLORS */
}

.score-bar-fill.positive {
    bottom: 50%; /* grows up from the zero line */
}

.score-bar-fill.negative {
    top: 50%;    /* grows down from the zero line */
}

#time-range-label {
    font-size: 0.85rem;
    color: #888;
    font-weight: normal;
}

/* Accuracy Table */
#accuracy-table td.accuracy-high {
    color: #00c853;
}

#accuracy-table td.accuracy-medium {
    color: #ffc107;
}

#accuracy-table td.accuracy-low {
    color: #ff5252;
}

.acc-detail {
    color: #888;
    font-size: 0.8rem;
    margin-left: 4px;
}

/* Weapon cells in stats table */
.weapon-kills {
    color: #ff6b6b;
    margin-left: 4px;
}

.weapon-dmg {
    color: #888;
    font-size: 0.85rem;
    margin-left: 4px;
}

/* Weapon stats split table */
.weapon-stats-split {
    font-size: 0.82rem;
}

.weapon-stats-split th[colspan="4"] {
    text-align: center;
    border-bottom: none;
}

/* Separator between weapon groups - data rows (col 1 is name, groups are
   4 wide since the eK column joined: separators land on 6, 10, 14, 18, 22) */
.weapon-stats-split tbody td:nth-child(4n+6) {
    border-left: 1px solid #333;
}

/* Separator between weapon groups - sub-header row (no name col due to rowspan) */
.weapon-stats-split thead tr:last-child th:nth-child(4n+5) {
    border-left: 1px solid #333;
}

/* Separator between weapon groups - top header row */
.weapon-stats-split thead tr:first-child th[colspan="4"] ~ th[colspan="4"] {
    border-left: 1px solid #333;
}

/* Item Pickups & Drops is 22 columns wide once possession is merged in —
   ~1500 px, more than the panel gets at a 1280 px viewport — so each of its
   two tables scrolls inside its own box rather than spilling out of the
   panel. Same idiom as .heatmap-table-wrap / .pickups-table-wrap. */
.items-table-wrap {
    overflow-x: auto;
}

/* Grouped columns in two-row headers (Item Pickups, Aim Stats). The older
   .weapon-stats-split rules above encode uniform 3-wide groups in
   nth-child strides; these groups are 1, 2 and 4 columns wide, so the
   separator is carried by an explicit .group-start on the first cell of
   each group instead of column arithmetic. .col-group marks the spanning
   header itself; it is qualified with .stats-table to beat the
   `th:not(:first-child)` right-align rule above on specificity. */
.stats-table th.col-group {
    text-align: center;
    border-bottom: none;
}

.stats-table th.group-start,
.stats-table td.group-start {
    border-left: 1px solid #333;
}

/* Stats tables */
.stats-table {
    font-size: 0.9rem;
}

.stats-table th {
    white-space: nowrap;
}

.stats-table td {
    white-space: nowrap;
}

/* Sortable table headers. The sort indicator is rendered as inline text
   rather than an absolutely-positioned pseudo-element so it contributes
   to the header's natural width. Without the asymmetric padding trick the
   previous version used, header and body cells share the same padding and
   their columns stay aligned in auto-layout tables. */
th.sortable {
    cursor: pointer;
    user-select: none;
}

th.sortable:hover {
    color: var(--accent-cyan);
}

th.sortable::after {
    content: ' ⇅';
    opacity: 0.3;
    font-size: 0.7em;
    margin-left: 2px;
}

th.sortable.sort-asc::after {
    content: ' ▲';
    opacity: 0.8;
    color: var(--accent-cyan);
}

th.sortable.sort-desc::after {
    content: ' ▼';
    opacity: 0.8;
    color: var(--accent-cyan);
}

/* Hub viewer [w] link */
.hub-watch-link {
    color: #7a8ba8;
    text-decoration: none;
    font-size: 0.75rem;
    font-weight: 600;
    opacity: 0.6;
    transition: opacity 0.15s;
    cursor: pointer;
}

.hub-watch-link:hover {
    opacity: 1;
    color: var(--accent-cyan);
}

/* Timeline playback controls */
.timeline-playback-controls {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-right: 8px;
}

.timeline-playback-controls button {
    background: none;
    border: 1px solid rgba(0, 217, 255, 0.3);
    color: #7a8ba8;
    cursor: pointer;
    font-size: 0.9rem;
    padding: 0 9px;
    border-radius: 3px;
    height: 24px;
    line-height: 22px;
    transition: all 0.15s;
}

.timeline-playback-controls button:hover {
    color: var(--accent-cyan);
    border-color: var(--accent-cyan);
}

.timeline-playback-controls button.active {
    color: var(--accent-cyan);
    border-color: var(--accent-cyan);
    background: rgba(0, 217, 255, 0.15);
}

/* Map legend table */
.map-legend-health,
.map-legend-armor,
.map-legend-wpn {
    text-align: right;
}

.map-player-trail-cb {
    width: 14px;
    height: 14px;
    cursor: pointer;
    margin: 0;
    padding: 0;
}

.map-trail-cell {
    padding: 2px 2px !important;
    width: 18px;
    text-align: center;
}

/* Accuracy Graphs */
.accuracy-graphs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
}

.player-graph {
    background: #16213e;
    border-radius: 8px;
    padding: 15px;
}

.player-graph h4 {
    color: var(--accent-cyan);
    margin-bottom: 10px;
    font-size: 0.95rem;
}

.graph-container {
    height: 150px;
    position: relative;
    background: var(--bg-deep);
    border-radius: 4px;
    padding: 10px;
}

.graph-bars {
    display: flex;
    align-items: flex-end;
    height: 100%;
    gap: 2px;
}

.graph-bar {
    flex: 1;
    background: linear-gradient(to top, var(--accent-cyan), var(--accent-cyan-dim));
    border-radius: 2px 2px 0 0;
    min-width: 8px;
    position: relative;
}

.graph-bar:hover::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    background: #16213e;
    color: #eee;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.75rem;
    white-space: nowrap;
    z-index: 10;
}

.graph-labels {
    display: flex;
    justify-content: space-between;
    margin-top: 5px;
    font-size: 0.75rem;
    color: #888;
}

/* Scrollbar styling */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: var(--bg-deep);
}

::-webkit-scrollbar-thumb {
    background: var(--bg-panel);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: #3a4f6f;
}

/* Quake high-bit "gold/brown" characters (128-255)
   Based on ezQuake font_gradient_alternate colors:
   Top: rgb(175, 120, 52) -> Bottom: rgb(75, 52, 22)
   Using the top/brighter color for better visibility */
.quake-gold {
    color: #af7834;
}

/* Key Moments Tab */
.empty-message {
    color: #888;
    text-align: center;
    padding: 40px;
    font-style: italic;
}

.time-cell {
    font-family: monospace;
    color: #888;
}

.time-link {
    cursor: pointer;
    color: var(--accent-cyan);
}

.time-link:hover {
    text-decoration: underline;
}

.powerup-cell {
    font-weight: bold;
}

.powerup-cell.quad { color: #0096ff; }
.powerup-cell.pent { color: #ff5050; }
.powerup-cell.ring { color: #ffeb3b; }

.viewer-link {
    color: var(--accent-cyan);
    text-decoration: none;
    padding: 5px 14px;
    border: 1px solid rgba(0, 217, 255, 0.4);
    border-radius: 5px;
    font-size: 0.85rem;
    font-weight: 500;
    transition: all 0.2s ease;
}

.viewer-link:hover {
    background: var(--accent-cyan);
    color: var(--bg-deep);
    box-shadow: 0 0 10px rgba(0, 217, 255, 0.3);
}

.keymoments-grid.active {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 15px;
}

/* The airgibs table is wider (more columns) and the headline highlight,
   so it spans the full Key Moments grid width below the two top panels. */
.keymoments-grid.active .airgibs-panel {
    grid-column: 1 / -1;
}

/* Sortable table headers (used by the airgibs table). */
.sortable-table thead th[data-sort] {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}
.sortable-table thead th[data-sort]:hover {
    color: var(--accent-cyan);
}
.sortable-table thead th.sort-asc::after { content: ' \25B2'; font-size: 0.75em; opacity: 0.85; }
.sortable-table thead th.sort-desc::after { content: ' \25BC'; font-size: 0.75em; opacity: 0.85; }

/* Lethal airgib badge. Deliberately NOT var(--team-a): that var is
 * re-pointed per match by team name, and this badge wants a stable
 * danger-red, which --armor-ra already is. */
.airgib-lethal {
    color: var(--armor-ra);
    font-weight: 700;
    text-transform: uppercase;
    font-size: 0.78rem;
    letter-spacing: 0.04em;
}

/* Powerup-run filter on the Key Moments tab. Same shape as
   .packdrops-filters, but its controls are number inputs. */
.keymoments-filters {
    display: flex;
    gap: 16px;
    align-items: center;
    margin-bottom: 12px;
    flex-wrap: wrap;
    font-size: 0.9rem;
}
.keymoments-filters label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.keymoments-filters input[type="number"] {
    background: var(--bg-deep);
    color: #ccc;
    border: 1px solid #666;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.9rem;
    font-family: inherit;
    width: 64px;
}

.packdrops-filters {
    display: flex;
    gap: 16px;
    align-items: center;
    margin-bottom: 12px;
    flex-wrap: wrap;
    font-size: 0.9rem;
}
.packdrops-filters label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.packdrops-filters select {
    background: var(--bg-deep);
    color: #ccc;
    border: 1px solid #666;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.9rem;
    cursor: pointer;
}
.packdrops-filters select option {
    background: var(--bg-deep);
    color: #ccc;
}
.packdrops-count {
    margin-left: auto;
    color: #888;
    font-variant-numeric: tabular-nums;
}

.pack-status {
    font-weight: bold;
    padding: 2px 8px;
    border-radius: 3px;
    font-size: 0.82rem;
    margin-right: 4px;
}
.pack-status.status-xfer     { color: #7fff7f; background: rgba(0, 180, 0, 0.15); }
.pack-status.status-xfer-had { color: #bfe5bf; background: rgba(0, 180, 0, 0.08); }
.pack-status.status-enemy    { color: #ff8080; background: rgba(255, 50, 50, 0.15); }
.pack-status.status-enemy-had { color: #e5bfbf; background: rgba(255, 50, 50, 0.08); }
.pack-status.status-expired  { color: #888; background: rgba(128, 128, 128, 0.12); }
/* A RECONSTRUCTED drop the pack-entity track shows being taken. Deliberately
   not coloured by team like status-xfer / status-enemy: the wire's
   `hadBefore` is unknowable there, so the row states only that it was taken
   and by whom (the Picker column). */
.pack-status.status-picked   { color: #9fd4ff; background: rgba(80, 160, 255, 0.14); }
/* Neither the observed "nobody took it" that status-expired states nor a
   pickup: the pack entity was never bound, or it left the wire with nobody
   on it before KTX's removal timeout. */
.pack-status.status-unobserved { color: #7a7a9a; background: rgba(122, 122, 154, 0.12); font-style: italic; }

/* ── Pickups tab ── */
.pickups-toolbar {
    display: flex;
    gap: 16px;
    align-items: center;
    margin-bottom: 16px;
    font-size: 0.9rem;
}
.pickups-toolbar label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.pickups-toolbar select {
    background: var(--bg-deep);
    color: #ccc;
    border: 1px solid #666;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 0.9rem;
    cursor: pointer;
}
.pickups-toolbar select option {
    background: var(--bg-deep);
    color: #ccc;
}
.pickups-table-wrap {
    overflow-x: auto;
    margin-bottom: 1.5rem;
}
/* Pickups tables inherit .stats-table look from the summary page; the only
   addition is a sticky-left first column so the player/team label stays in
   view while the per-entity columns scroll horizontally. */
.pickups-table th:first-child,
.pickups-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
}
.pickups-table tbody td:first-child {
    background: var(--bg-deep);
    /* Team color strip — same 3px team-coloured edge that the summary
       scoreboard renders via border-left on <tr>. We put it on the
       sticky-left cell so it stays in view while the per-entity columns
       scroll horizontally. The CSS variable is set per-row in JS. */
    border-left: 3px solid var(--row-team-color, transparent);
}
.pickups-table thead th:first-child {
    /* sit above the sticky-top header siblings and the body sticky-left */
    z-index: 6;
}
.pickups-loc {
    color: #888;
    font-size: 0.78rem;
    font-weight: normal;
}
.pickups-team-tag {
    color: #777;
    font-size: 0.78rem;
    margin-left: 4px;
}
.pickups-verify-header {
    color: #aaa;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-left: 4px;
}
.pickups-table .muted {
    color: #555;
}

.weapon-cell.weapon-rl { color: #ff7040; font-weight: bold; }
.weapon-cell.weapon-lg { color: #70c0ff; font-weight: bold; }

.kills-cell { text-align: right; font-variant-numeric: tabular-nums; }
.kills-redundant { color: #888; }

@media (max-width: 1200px) {
    .keymoments-grid {
        grid-template-columns: 1fr;
    }
}

#keymoments-table th:first-child,
#keymoments-table td:first-child {
    width: 80px;
}

#keymoments-table th:last-child,
#keymoments-table td:last-child {
    width: 100px;
    text-align: center;
}

/* Map View Styles */
.map-panel {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

/* Fullscreen layout: the whole .map-panel becomes the fullscreen element.
   Top bar stays at top, .map-main-content grows to fill remaining height,
   canvas grows to fill the available width (minus sidebar), sidebar keeps
   its max-width. The region-control table below the canvas is hidden in
   fullscreen to free vertical space. */
.map-panel--fullscreen {
    width: 100vw;
    height: 100vh;
    margin: 0;
    padding: 10px;
    background: var(--bg-deep);
    overflow: hidden;
}

.map-panel--fullscreen .map-main-content {
    flex: 1 1 auto;
    min-height: 0;
}

.map-panel--fullscreen .map-canvas-container {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    display: flex;
    align-items: stretch;
}

.map-panel--fullscreen #map-canvas {
    width: 100%;
    height: 100%;
}

/* Sidebar needs to fit the items panel (218 px) + region-status side by
 * side, so its min-width has to be wide enough that region-status gets
 * a usable column. The map shrinks to give it that room; on larger
 * screens the sidebar still tops out at max-width and the rest goes to
 * the canvas. */
.map-panel--fullscreen .map-sidebar {
    max-height: 100%;
    overflow-y: auto;
    min-width: 500px;
    max-width: 780px;
}

.map-panel--fullscreen #region-control-panel {
    display: none !important;
}

/* Relocated unified timeline: pin to the bottom of the fullscreen map panel,
   shrunk to one row of controls so it never eats canvas space. */
.map-panel--fullscreen #unified-timeline {
    margin: 0;
    padding: 6px 10px;
    flex: 0 0 auto;
}

.locgraph-panel {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

.locgraph-controls {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

.locgraph-controls label {
    color: #888;
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    gap: 6px;
}

.locgraph-controls select {
    background: var(--bg-deep);
    border: 1px solid #666;
    color: #ccc;
    padding: 5px 8px;
    border-radius: 4px;
    font-size: 0.85rem;
    cursor: pointer;
}

.locgraph-controls select:hover,
.locgraph-controls select:focus {
    border-color: var(--accent-cyan);
    outline: none;
}

.locgraph-canvas-container {
    position: relative;
    width: 100%;
}

#locgraph-canvas {
    background: #0a0a15;
    border: 1px solid rgba(0, 217, 255, 0.15);
    border-radius: 6px;
    display: block;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.4), inset 0 0 30px rgba(0, 0, 0, 0.3);
    width: 100%;
    height: 700px;
}

.locgraph-btn {
    background: transparent;
    border: 1px solid #666;
    color: #888;
    padding: 5px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    transition: all 0.2s;
}

.locgraph-btn:hover {
    border-color: var(--accent-cyan);
    color: var(--accent-cyan);
}

.locgraph-tooltip,
.canvas-tooltip {
    position: absolute;
    pointer-events: none;
    background: rgba(10, 10, 21, 0.95);
    border: 1px solid rgba(0, 217, 255, 0.3);
    border-radius: 4px;
    padding: 8px 10px;
    color: #e0e0e0;
    font-size: 0.85rem;
    white-space: pre;
    z-index: 100;
    max-width: 280px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}

/* Drop-dot tooltip uses normal whitespace so multi-line content stacks
   instead of forming a single long line. */
.canvas-tooltip {
    white-space: normal;
}

.map-track-controls {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.map-track-sep {
    display: inline-block;
    width: 1px;
    height: 22px;
    background: #3a3a55;
    margin: 0 4px;
}

/* Trails → Players dropdown: uses <details> so it toggles natively, no JS
   needed to show/hide the panel itself. */
.map-trails-dropdown {
    position: relative;
}

.map-trails-dropdown > summary {
    list-style: none;
    cursor: pointer;
    background: transparent;
    border: 1px solid #666;
    color: #888;
    padding: 6px 12px;
    border-radius: 4px;
    font-size: 0.85rem;
    user-select: none;
    transition: all 0.2s;
}

.map-trails-dropdown > summary::-webkit-details-marker { display: none; }

.map-trails-dropdown > summary:hover {
    border-color: var(--accent-cyan);
    color: var(--accent-cyan);
}

.map-trails-dropdown[open] > summary {
    border-color: var(--accent-cyan);
    color: var(--accent-cyan);
}

.map-trails-dropdown > summary::after {
    content: ' ▾';
    font-size: 0.75rem;
    opacity: 0.8;
}

.map-trails-players-panel {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    background: #16213e;
    border: 1px solid rgba(0, 217, 255, 0.3);
    border-radius: 6px;
    padding: 8px 10px;
    min-width: 180px;
    max-height: 360px;
    overflow-y: auto;
    z-index: 30;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.6);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.map-trails-players-panel label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: #d0d0e0;
    cursor: pointer;
    padding: 2px 0;
}

.map-trails-players-panel input[type="checkbox"] {
    width: 14px;
    height: 14px;
    cursor: pointer;
    flex-shrink: 0;
}

.map-trails-players-panel .map-trails-player-name {
    font-weight: 600;
}

.map-trail-label {
    color: #888;
    font-size: 0.9rem;
}

.map-track-controls button {
    background: transparent;
    border: 1px solid #666;
    color: #888;
    padding: 6px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    transition: all 0.2s;
}

.map-track-controls button:hover {
    border-color: var(--accent-cyan);
    color: var(--accent-cyan);
}

/* Every toggle button in the map controls reads its state the same way:
   highlighted (filled cyan) while active, plain otherwise. Covers Learn map,
   View, Vel, 3D, Fullscreen, and the Trails All / None buttons. */
.map-track-controls button.active {
    border-color: var(--accent-cyan);
    color: var(--bg-deep);
    background: var(--accent-cyan);
}

/* LOS computes lazily on first toggle; show a wait cue while the worker runs
   the (potentially multi-second) raycast pass. */
.map-track-controls button.pending {
    opacity: 0.6;
    cursor: progress;
}

/* The Trails "Players" dropdown summary highlights when a custom subset of
   players is selected (neither All nor None). */
.map-trails-dropdown > summary.active {
    border-color: var(--accent-cyan);
    color: var(--bg-deep);
    background: var(--accent-cyan);
}

/* Learn-map sidebar: category filter checklist. */
.map-entities-panel {
    margin-bottom: 12px;
}

.map-entity-filters {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.map-entity-filter {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: #ccc;
    cursor: pointer;
}

.map-entity-filter input[type="checkbox"] {
    width: 14px;
    height: 14px;
    cursor: pointer;
    flex-shrink: 0;
}

.map-entity-swatch {
    width: 12px;
    height: 12px;
    border-radius: 2px;
    flex-shrink: 0;
    display: inline-block;
}

.map-entity-hint {
    margin: 8px 0 0;
    font-size: 0.75rem;
    color: #777;
    line-height: 1.3;
}

/* Below-map entity table (learn mode): standard .stats-table styling,
   expands with the tab (no inner scrollbar). */
.map-entities-table-wrap {
    margin-top: 12px;
}

.map-trail-select {
    background: var(--bg-deep);
    border: 1px solid #666;
    color: #ccc;
    padding: 5px 8px;
    border-radius: 4px;
    font-size: 0.85rem;
    cursor: pointer;
}

.map-trail-select:hover,
.map-trail-select:focus {
    border-color: var(--accent-cyan);
    outline: none;
}

.map-player-trail-cb {
    width: 14px;
    height: 14px;
    cursor: pointer;
}

.map-main-content {
    display: flex;
    gap: 20px;
}

.map-canvas-container {
    position: relative;
    flex-shrink: 0;
}

#map-canvas {
    background: #0a0a15;
    border: 1px solid rgba(0, 217, 255, 0.15);
    border-radius: 6px;
    display: block;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.4), inset 0 0 30px rgba(0, 0, 0, 0.3);
}

.map-no-data {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    color: #666;
    font-size: 0.9rem;
}

.map-no-data p:first-child {
    color: #888;
    margin-bottom: 5px;
}

.map-sidebar {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 15px;
    min-width: 320px;
    max-width: 720px;
}

/* Players panel sits alone (full sidebar width); below it, Items and
 * Region Status share a flex row so their combined width matches the
 * Players panel above. */
.map-sidebar-row {
    display: flex;
    gap: 12px;
    align-items: flex-start;
}

.map-legend {
    background: #16213e;
    border-radius: 8px;
    padding: 15px;
}

/* The Stack column merges health and armor: H rendered white, then armor
 * "120 RA" using the existing .armor-{ra,ya,ga} colour classes. */
.team-status-table .stack-h {
    color: #ffffff;
}

#region-status-panel {
    background: #16213e;
    border-radius: 8px;
    padding: 15px;
    flex: 1 1 auto;
    min-width: 0;
}

#region-status-panel h4 {
    color: var(--accent-cyan);
    margin-bottom: 8px;
    font-size: 0.95rem;
}

/* Items panel — narrow column to the right of the player table. Three
 * columns only: swatch, loc, status. The item name is conveyed by the
 * swatch's colour and inline label (RL/LG/MH/Q/P/...) so we drop the
 * dedicated name column to keep the panel narrow. */
.map-items-panel {
    background: #16213e;
    border-radius: 8px;
    padding: 12px;
    flex: 0 0 auto;
    width: 218px;
    min-width: 195px;
}

.map-items-panel h4 {
    color: var(--accent-cyan);
    margin-bottom: 8px;
    font-size: 0.95rem;
}

.map-items-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
}

.map-items-table td {
    padding: 2px 6px;
    vertical-align: middle;
    color: #d0d0e0;
}

.map-items-table tr.taken {
    opacity: 0.55;
}

.map-items-table .item-swatch {
    width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 2px;
    vertical-align: middle;
    font-size: 9px;
    font-weight: 700;
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    letter-spacing: -0.5px;
    line-height: 1;
}

.map-items-table .item-swatch-cell {
    width: 22px;
    padding-right: 4px;
}

.map-items-table .item-loc {
    color: #c3c3d6;
    font-size: 0.75rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 140px;
}

.map-items-table .item-status {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.map-items-table .item-status.up {
    color: #4ade80;
}

.map-items-table .item-status.respawn {
    color: #fbbf24;
}

.map-items-table .item-status.pending {
    color: #60a5fa; /* MH waiting for rot to finish */
}

.region-status-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 0;
    border-bottom: 1px solid var(--bg-deep);
    font-size: 0.85rem;
}

.region-status-name {
    font-weight: bold;
    min-width: 60px;
    color: #ccc;
}

.region-status-state {
    min-width: 70px;
    font-size: 0.8rem;
}

.region-status-players {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.region-player-icon {
    width: 40px;
    height: 40px;
    cursor: default;
}

#region-control-panel {
    background: #16213e;
    border-radius: 8px;
    padding: 15px;
}

#region-control-panel h4 {
    color: var(--accent-cyan);
    margin-bottom: 12px;
    font-size: 0.95rem;
}

.region-config {
    margin-bottom: 12px;
}

.region-config-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}

.region-name-input {
    width: 110px;
    background: var(--bg-deep);
    border: 1px solid #333;
    color: #ccc;
    padding: 4px 6px;
    border-radius: 4px;
    font-size: 0.85rem;
    font-weight: bold;
    font-family: inherit;
}

.region-locs-input {
    flex: 1;
    background: var(--bg-deep);
    border: 1px solid #333;
    color: #ccc;
    padding: 4px 6px;
    border-radius: 4px;
    font-size: 0.8rem;
    font-family: monospace;
}

.region-name-input:focus,
.region-locs-input:focus {
    border-color: var(--accent-cyan);
    outline: none;
}

.region-remove-btn {
    background: transparent;
    border: 1px solid #444;
    color: #aaa;
    width: 24px;
    height: 24px;
    line-height: 1;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    padding: 0;
}

.region-remove-btn:hover {
    background: #3a1f1f;
    border-color: #a44;
    color: #fff;
}

.region-config-actions {
    display: flex;
    gap: 8px;
    margin-top: 8px;
}

.region-config-actions button {
    background: var(--bg-deep);
    border: 1px solid #333;
    color: #ccc;
    padding: 4px 10px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.8rem;
}

.region-config-actions button:hover {
    border-color: var(--accent-cyan);
    color: #fff;
}

.map-legend h4 {
    color: var(--accent-cyan);
    margin-bottom: 12px;
    font-size: 0.95rem;
}

.map-legend-team {
    margin-bottom: 10px;
}

.map-legend-team-name {
    font-weight: bold;
    margin-bottom: 6px;
    font-size: 0.9rem;
}

.map-legend-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
    font-size: 0.85rem;
    color: #ccc;
}

.map-legend-symbol {
    font-family: monospace;
    font-size: 1.3rem;
    font-weight: bold;
    width: 20px;
    text-align: center;
}

@media (max-width: 900px) {
    .map-main-content {
        flex-direction: column;
    }

    .map-sidebar {
        max-width: none;
        flex-direction: row;
    }

    .map-legend {
        flex: 1;
    }
}
