/* CSS Custom Properties for Character Theming */
:root {
    /* Default colors (will be overridden by character theme) */
    --char-primary: #3b82f6;
    --char-secondary: #1e3a8a;
    --char-accent: #93c5fd;
    --char-gradient: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);

    /* Trend delta indicators (recent-play up/down arrows) */
    --trend-up: #16a34a;
    --trend-down: #dc2626;

    /* The landing page is character-agnostic, so it cannot use --char-primary:
       AnalyzerView writes that onto :root as an inline style and never clears
       it, so returning from a character would recolor the landing page to
       whichever one was last viewed. This blue is fixed. */
    --landing-accent: #3b82f6;

    /* Horizontal padding of the landing/upload white column. Shared so the
       character band can break out to exactly the column's edges (and no
       further, into the dark side gutters) by cancelling this value. */
    --landing-column-pad: 24px;
}

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

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
        'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    background: white;
    min-height: 100vh;
    margin: 0;
    padding: 0;
}

/* Scatter plot container */
.scatter-plot-container {
    width: 100%;
    height: 100%;
    position: relative;
}

.scatter-plot-container canvas {
    max-width: 100%;
    max-height: 100%;
}

/* Visualization view */
.visualization-view {
    max-width: 1400px;
    margin: 0 auto;
}

.visualization-description {
    line-height: 1.6;
}

.visualization-description strong {
    color: var(--char-primary);
}

.container {
    width: 100%;
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: white;
    overflow: hidden;
}

.scrollable-content {
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

.header {
    background: var(--char-gradient);
    color: white;
    padding: 15px 30px;
    text-align: center;
    flex-shrink: 0;
    /* Positioning context for the home link, which is pulled out of flow so it
       cannot shift the optically centered title. */
    position: relative;
}

/* The home link and the right-side action cluster are pulled out of flow so they
   flank the optically centered title without shifting it: home on the left, the
   glossary + upload buttons on the right. */
.header-home-link,
.header-actions {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.header-home-link,
.header-glossary-link,
.header-upload-link {
    padding: 6px 12px;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.12);
    color: white;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s;
}

.header-home-link {
    left: 30px;
}

/* Right cluster: "?" glossary button then "Upload runs", laid out as a row so
   the glossary sits just left of upload. */
.header-actions {
    right: 30px;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Compact circular "?" — narrower than the text buttons around it. */
.header-glossary-link {
    padding: 6px 0;
    width: 30px;
    text-align: center;
    border-radius: 50%;
    font-size: 15px;
    line-height: 1;
}

.header-home-link:hover,
.header-glossary-link:hover,
.header-upload-link:hover {
    background: rgba(255, 255, 255, 0.25);
}

.header h1 {
    font-size: 24px;
    margin-bottom: 4px;
}

.header .subtitle {
    font-size: 14px;
    opacity: 0.9;
}

.filters {
    background: #1f2937;
    padding: 12px 30px;
    border-bottom: 2px solid #374151;
    display: flex;
    gap: 20px;
    align-items: center;
    flex-wrap: wrap;
    flex-shrink: 0;
    position: sticky;
    top: 0;
    z-index: 100;
}

.filter-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.filter-label {
    font-size: 12px;
    font-weight: 600;
    color: #9ca3af;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.filter-select {
    padding: 8px 12px;
    border: 2px solid #374151;
    border-radius: 6px;
    background: #111827;
    color: white;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
    min-width: 180px;
}

/* The User option labels change as run counts load asynchronously, and a bare
   <select> resizes itself to its widest current option — so the bar would blip
   short-to-wide on every character switch. Pin a fixed width sized (by
   measurement) to hold a typical 'name (X runs, Y total)' label; the rare very
   long persona name ellipsizes in the closed control and is still fully legible
   once the menu is open. */
.filter-select--user {
    width: 320px;
    text-overflow: ellipsis;
}

.filter-select:hover {
    border-color: var(--char-primary);
}

.filter-select:focus {
    outline: none;
    border-color: var(--char-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--char-primary) 10%, transparent);
}

/* Patch filter — a dropdown trigger (styled like .filter-select) that opens a
   checkbox-tree popover. The .filter-group anchors the absolutely-positioned
   panel; it sits inline in the .filters flex row like the other filter groups. */
.patch-filter {
    position: relative;
}

.patch-filter-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    min-width: 180px;
}

.patch-filter-caret {
    color: #9ca3af;
    font-size: 12px;
}

.patch-filter-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 200;
    width: 300px;
    max-height: 60vh;
    display: flex;
    flex-direction: column;
    background: #111827;
    border: 2px solid #374151;
    border-radius: 8px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
    overflow: hidden;
}

.patch-filter-actions {
    display: flex;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid #374151;
    flex-shrink: 0;
}

.patch-filter-action {
    flex: 1;
    padding: 6px 10px;
    background: #1f2937;
    color: #d1d5db;
    border: 1px solid #374151;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s;
}

.patch-filter-action:hover {
    background: #374151;
    border-color: var(--char-primary);
    color: white;
}

.patch-filter-tree {
    overflow-y: auto;
    padding: 6px 0;
}

/* One selectable row (leaf, group header, or child). The whole row is a <label>
   so clicking anywhere toggles its checkbox. */
.patch-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 14px;
    cursor: pointer;
    transition: background 0.12s;
}

.patch-row:hover {
    background: #1f2937;
}

.patch-row--group {
    font-weight: 600;
}

.patch-row--child {
    padding-left: 34px;
}

.patch-checkbox {
    width: 16px;
    height: 16px;
    accent-color: var(--char-primary);
    cursor: pointer;
    flex-shrink: 0;
}

.patch-version {
    color: white;
    font-size: 14px;
    white-space: nowrap;
}

.patch-tag {
    margin-left: auto;
    color: #9ca3af;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    white-space: nowrap;
}

.metadata {
    background: #f3f4f6;
    padding: 10px 30px;
    border-bottom: 1px solid #e5e7eb;
    display: flex;
    gap: 30px;
    flex-wrap: wrap;
}

.metadata-item {
    font-size: 14px;
}

.metadata-label {
    font-weight: 600;
    color: #6b7280;
}

.metadata-value {
    color: #1f2937;
    font-weight: 500;
}

.content {
    display: grid;
    /* Match the mobile sidebar pane width (~390px, one phone viewport) so the
       filter palette lays out identically on both — enough room for square
       cost boxes. */
    grid-template-columns: 390px 1fr;
    gap: 0;
    flex: 1;
    overflow: hidden;
}

.sidebar {
    border-right: 1px solid #e5e7eb;
    background: #f9fafb;
    overflow-y: auto;
    height: 100%;
}

.search-box {
    padding: 20px;
    background: #f9fafb;
    border-bottom: 1px solid #e5e7eb;
}

.search-input {
    width: 100%;
    padding: 10px 15px;
    border: 2px solid #d1d5db;
    border-radius: 8px;
    font-size: 14px;
    transition: border-color 0.2s;
}

.search-input:focus {
    outline: none;
    border-color: #3b82f6;
}

.card-list {
    padding: 10px;
}

.card-item {
    padding: 12px 15px;
    margin-bottom: 6px;
    cursor: pointer;
    border-radius: 8px;
    transition: all 0.2s;
    border: 2px solid transparent;
}

.card-item:hover {
    background: #e5e7eb;
}

.card-item.selected {
    background: color-mix(in srgb, var(--char-accent) 30%, white);
    border-color: var(--char-primary);
}

.card-name {
    font-weight: 600;
    color: #1f2937;
    margin-bottom: 4px;
    font-size: 14px;
}

.card-stats {
    font-size: 12px;
    color: #6b7280;
    display: flex;
    gap: 15px;
}

.stat-badge {
    background: white;
    padding: 2px 8px;
    border-radius: 4px;
    font-weight: 500;
}

.main-panel {
    padding: 30px;
    overflow-y: auto;
    height: 100%;
}

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 400px;
    color: #9ca3af;
}

.empty-state-icon {
    font-size: 64px;
    margin-bottom: 20px;
}

.card-detail-header {
    margin-bottom: 30px;
}

.card-title {
    font-size: 28px;
    font-weight: 700;
    color: #1f2937;
    margin-bottom: 15px;
}

.summary-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 15px;
    margin-bottom: 30px;
}

.stat-card {
    background: #f3f4f6;
    padding: 15px;
    border-radius: 8px;
    border-left: 4px solid var(--char-primary);
}

.stat-label {
    font-size: 12px;
    color: #6b7280;
    font-weight: 600;
    text-transform: uppercase;
    margin-bottom: 5px;
}

.stat-value {
    font-size: 24px;
    font-weight: 700;
    color: #1f2937;
}

.chart-section {
    margin-top: 30px;
}

.chart-title {
    font-size: 18px;
    font-weight: 600;
    color: #1f2937;
    margin-bottom: 15px;
}

.chart-container {
    background: #f9fafb;
    padding: 20px;
    border-radius: 8px;
    border: 1px solid #e5e7eb;
}

.data-table {
    margin-top: 30px;
}

.table-wrapper {
    overflow-x: auto;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

th {
    background: #f3f4f6;
    padding: 12px;
    text-align: left;
    font-weight: 600;
    color: #374151;
    border-bottom: 2px solid #e5e7eb;
}

td {
    padding: 10px 12px;
    border-bottom: 1px solid #e5e7eb;
    color: #1f2937;
}

tr:hover {
    background: #f9fafb;
}

.progress-bar {
    height: 8px;
    background: #e5e7eb;
    border-radius: 4px;
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    background: var(--char-gradient);
    transition: width 0.3s;
}

.loading {
    text-align: center;
    padding: 40px;
    color: #6b7280;
}

.error {
    text-align: center;
    padding: 40px;
    color: #dc2626;
}

/* Tab Navigation */
.tab-navigation {
    display: flex;
    gap: 4px;
    margin-bottom: 24px;
    border-bottom: 2px solid #e5e7eb;
}

.tab-button {
    padding: 12px 24px;
    background: none;
    border: none;
    border-bottom: 3px solid transparent;
    font-size: 15px;
    font-weight: 600;
    color: #6b7280;
    cursor: pointer;
    transition: all 0.2s;
    margin-bottom: -2px;
}

.tab-button:hover {
    color: var(--char-primary);
    background: color-mix(in srgb, var(--char-accent) 10%, white);
}

.tab-button.active {
    color: var(--char-primary);
    border-bottom-color: var(--char-primary);
}

.tab-content {
    animation: fadeIn 0.3s;
}

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

@keyframes slideInRight {
    from {
        transform: translateX(100%);
    }
    to {
        transform: translateX(0);
    }
}

/* Info icon - prevent uppercase transformation */
.info-icon-wrapper {
    text-transform: none !important;
}

.info-tooltip {
    text-transform: none !important;
}

/* ============================================================
   Landing page
   Character-agnostic: renders under the :root default theme.
   ============================================================ */

/* Dark full-gutter frame shared by the landing and upload pages. The outer
   element fills the viewport width and paints the dark gutter color; a centered
   white column of capped width sits on top, so the dark shows only in the
   left/right margins. min-height keeps the white column reaching the viewport
   floor even when content is short, so the dark never becomes a band below it. */
.landing-page,
.uploader-guide {
    background: #111827;
    min-height: 100vh;
}

/* One width for both pages so their white columns line up inside the frame. */
.landing-column,
.uploader-column {
    max-width: 900px;
    margin: 0 auto;
    background: white;
    min-height: 100vh;
}

.landing-column {
    padding: 64px var(--landing-column-pad) 80px;
}

.landing-hero {
    text-align: center;
    margin-bottom: 48px;
}

.landing-title {
    font-size: 40px;
    font-weight: 800;
    color: #111827;
    letter-spacing: -0.02em;
    margin-bottom: 16px;
}

.landing-tagline {
    font-size: 17px;
    line-height: 1.65;
    color: #4b5563;
    max-width: 620px;
    margin: 0 auto;
}

.landing-totals {
    display: flex;
    justify-content: center;
    gap: 64px;
    padding: 28px 0;
    margin-bottom: 56px;
    border-top: 1px solid #e5e7eb;
    border-bottom: 1px solid #e5e7eb;
}

.landing-total {
    text-align: center;
}

.landing-total-value {
    font-size: 32px;
    font-weight: 700;
    color: var(--landing-accent);
    font-variant-numeric: tabular-nums;
}

.landing-total-label {
    font-size: 13px;
    color: #6b7280;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-top: 4px;
}

/* Light band behind the character row. It fills the width of the white column
   but stops at its edges: negative margins cancel only the column's own side
   padding (--landing-column-pad), so the band never reaches into the dark side
   gutters. Kept light so it reads as a panel on white, distinct from the frame. */
.landing-section--band {
    margin-left: calc(-1 * var(--landing-column-pad));
    margin-right: calc(-1 * var(--landing-column-pad));
    padding: 40px var(--landing-column-pad);
    background: #f3f4f6;
}

.landing-section-heading {
    font-size: 14px;
    font-weight: 700;
    color: #6b7280;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    text-align: center;
    margin-bottom: 24px;
}

/* Equal-width columns that still wrap on narrow screens. The 150px floor sets
   the wrap threshold; grid-auto-columns keeps every resolved column identical
   so a longer name ('Necrobinder') can't widen its own tile. */
.landing-character-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    grid-auto-columns: 1fr;
    gap: 16px;
}

/* Flat: a solid field of the character's color, edged with a dark hairline but
   no shadow or bevel — nothing pretends to be a physical object. Hover swells
   the tile from its center, which is the whole of the interaction. */
.landing-character-tile {
    min-width: 0;
    /* A dark hairline reads on every character color, giving the flat tiles a
       defined edge against the light band without a shadow or bevel. */
    border: 2px solid rgba(0, 0, 0, 0.35);
    border-radius: 12px;
    padding: 36px 16px;
    cursor: pointer;
    color: white;
    transition: transform 0.15s ease;
}

.landing-character-tile:hover {
    transform: scale(1.05);
}

/* Settle back toward rest on press. Without a shadow beneath it there is no
   base to sink onto, so this is a slight release rather than a downward push. */
.landing-character-tile:active {
    transform: scale(1.02);
}

.landing-character-name {
    font-size: 18px;
    font-weight: 700;
    letter-spacing: 0.01em;
}

.landing-footer {
    margin-top: 64px;
    text-align: center;
}

.landing-footer-text {
    font-size: 15px;
    color: #4b5563;
    margin-bottom: 16px;
}

.landing-upload-cta {
    padding: 12px 28px;
    border: 2px solid var(--landing-accent);
    border-radius: 8px;
    background: white;
    color: var(--landing-accent);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s;
}

.landing-upload-cta:hover {
    background: var(--landing-accent);
    color: white;
}

/* ============================================================
   Uploader instructions page
   ============================================================ */

.uploader-column {
    padding: 40px var(--landing-column-pad) 80px;
}

.uploader-back-link {
    border: none;
    background: none;
    padding: 0;
    color: #6b7280;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    margin-bottom: 32px;
}

.uploader-back-link:hover {
    color: var(--char-primary);
}

.uploader-title {
    font-size: 34px;
    font-weight: 800;
    color: #111827;
    letter-spacing: -0.02em;
    margin-bottom: 16px;
}

.uploader-intro {
    font-size: 16px;
    line-height: 1.7;
    color: #4b5563;
}

/* Fast path for returning users, sitting above the numbered steps. Tinted with
   the fixed landing accent (never --char-primary, which drifts once a character
   has been viewed) and given a left rule so it reads as a callout, not a step. */
.uploader-quickrun {
    margin-top: 32px;
    padding: 20px 24px;
    border-left: 4px solid var(--landing-accent);
    border-radius: 8px;
    background: color-mix(in srgb, var(--landing-accent) 8%, white);
}

.uploader-quickrun-heading {
    font-size: 15px;
    font-weight: 700;
    color: #1f2937;
    margin-bottom: 14px;
}

.uploader-quickrun-note {
    font-size: 13px;
    color: #6b7280;
    margin-top: 4px;
}

.uploader-steps {
    margin: 48px 0;
}

.uploader-step {
    display: flex;
    gap: 20px;
    margin-bottom: 36px;
}

.uploader-step-number {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--char-primary);
    color: white;
    font-size: 15px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}

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

.uploader-step-heading {
    font-size: 18px;
    font-weight: 700;
    color: #1f2937;
    margin-bottom: 10px;
}

.uploader-text {
    font-size: 15px;
    line-height: 1.7;
    color: #4b5563;
    margin-bottom: 12px;
}

.uploader-code-block {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    background: #1f2937;
    border-radius: 8px;
    padding: 14px 16px;
    margin-bottom: 12px;
    cursor: pointer;
    overflow-x: auto;
}

.uploader-code-block code {
    font-family: 'SF Mono', Monaco, 'Courier New', monospace;
    font-size: 14px;
    color: #f9fafb;
    white-space: nowrap;
}

.uploader-copy-hint {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 600;
    color: #9ca3af;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.uploader-code-block:hover .uploader-copy-hint {
    color: #f9fafb;
}

.uploader-access-code {
    display: inline-block;
    font-family: 'SF Mono', Monaco, 'Courier New', monospace;
    font-size: 16px;
    font-weight: 600;
    color: var(--char-primary);
    background: color-mix(in srgb, var(--char-accent) 22%, white);
    border: 1px dashed var(--char-primary);
    border-radius: 6px;
    padding: 8px 16px;
}

.uploader-section {
    margin-top: 48px;
    padding-top: 32px;
    border-top: 1px solid #e5e7eb;
}

.uploader-section-heading {
    font-size: 20px;
    font-weight: 700;
    color: #1f2937;
    margin-bottom: 16px;
}

.uploader-list {
    padding-left: 20px;
    margin-bottom: 16px;
}

.uploader-list li {
    font-size: 15px;
    line-height: 1.8;
    color: #4b5563;
}

.uploader-path-row {
    margin-bottom: 16px;
}

.uploader-path-os {
    font-size: 13px;
    font-weight: 700;
    color: #6b7280;
    margin-bottom: 6px;
}

.uploader-path {
    display: block;
    font-family: 'SF Mono', Monaco, 'Courier New', monospace;
    font-size: 13px;
    color: #374151;
    background: #f3f4f6;
    border-radius: 6px;
    padding: 10px 12px;
    overflow-x: auto;
    white-space: nowrap;
}

.uploader-trouble-row {
    padding: 14px 0;
    border-bottom: 1px solid #f3f4f6;
}

.uploader-trouble-row:last-child {
    border-bottom: none;
}

.uploader-trouble-problem {
    font-family: 'SF Mono', Monaco, 'Courier New', monospace;
    font-size: 14px;
    font-weight: 600;
    color: #b91c1c;
    margin-bottom: 4px;
}

.uploader-trouble-fix {
    font-size: 14px;
    line-height: 1.6;
    color: #4b5563;
}

/* ============================================================
   Mobile responsive layout (analyzer)
   The two toggle/back controls exist in the DOM on every viewport
   but are desktop-hidden here; the media block below reveals and
   restyles the analyzer for narrow screens. Desktop is untouched.
   ============================================================ */
.filters-toggle { display: none; }
.mobile-back-button { display: none; }

/* On desktop these two wrappers must not exist as layout boxes, or they would
   break the .filters flex row (their children would stack instead of flowing in
   a row). display:contents dissolves each wrapper so its children participate
   directly in .filters. The mobile media query re-materializes them as real flex
   containers (a column for the dropdowns, a row for the view controls). */
.filters-expandable,
.filters-controls { display: contents; }

/* Mobile layout: narrow screens OR short-landscape phones (which are wider than
   768px but too short for the desktop layout). This condition MUST stay identical
   to MOBILE_BREAKPOINT in index.html so the CSS and the JS useIsMobile() hook
   switch together. */
@media (max-width: 768px), (max-height: 500px) and (orientation: landscape) {
    /* --- Fixed viewport cage: each pane scrolls independently ---
       The document itself does not scroll on mobile. The header + filter bar
       stay pinned at the top; below them the master-detail track fills the
       remaining height, and each pane owns its own vertical scroll. This is
       what lets the list and detail keep separate scroll positions for free —
       switching panes is a pure horizontal slide with no shared scroll to
       reset (which previously snapped the list to the top mid-animation).
       100dvh (dynamic viewport height) tracks the mobile URL bar so the cage
       fills the real visible area. */
    .container {
        height: 100vh;
        height: 100dvh;
        overflow: hidden;
    }
    .scrollable-content {
        /* Header + filters pin here; the track below scrolls internally, so
           this wrapper itself never scrolls. Clip the off-screen slide pane. */
        overflow: hidden;
        min-height: 0; /* allow the flex child (.content) to shrink and scroll */
    }

    /* --- Compact header --- */
    .header {
        padding: 12px 12px;
    }
    .header h1 {
        font-size: 18px;
    }
    .header .subtitle {
        font-size: 12px;
    }
    /* Shrink and pull the flanking controls in so they clear the smaller title. */
    .header-home-link,
    .header-glossary-link,
    .header-upload-link {
        font-size: 11px;
        padding: 4px 8px;
    }
    .header-home-link {
        left: 10px;
    }
    .header-actions {
        right: 10px;
        gap: 8px;
    }
    /* Keep the compact circular "?" round at the smaller size. */
    .header-glossary-link {
        padding: 4px 0;
        width: 26px;
    }

    /* --- Filter bar becomes a collapsible full-width block --- */
    .filters {
        /* Must scroll away with the page, so drop the desktop stickiness. A
           wrapping row lets the Filters toggle and the View switch share one
           line (order 0 + 1), while the expandable dropdowns wrap to a full
           line below (order 2, flex-basis:100%) — reclaiming vertical space. */
        position: static;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        padding: 10px 12px;
        gap: 10px;
    }
    /* Filters toggle shares the top row with the View switch: it takes the
       remaining width on the left, the View switch sits at the right. */
    .filters-toggle {
        order: 0;
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex: 1 1 auto;
        padding: 10px 14px;
        background: #374151;
        color: white;
        border: 2px solid #4b5563;
        border-radius: 6px;
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
    }
    /* The dropdowns stack vertically on their own full-width line below the
       toggle/View row (order:2 + flex-basis:100% forces the wrap). */
    .filters-expandable {
        order: 2;
        display: flex;
        flex-direction: column;
        gap: 10px;
        flex: 0 0 100%;
    }
    .filters.filters-collapsed .filters-expandable {
        display: none;
    }
    .filter-group {
        width: 100%;
    }
    /* Every select fills the row; override the desktop fixed/min widths. */
    .filter-select,
    .filter-select--user {
        min-width: 0;
        width: 100%;
    }

    /* Standalone view controls: the View switch, sitting to the RIGHT of the
       Filters toggle on the same top row (order:1). Never collapses — it's not
       a data filter — so it stays reachable whether or not the dropdowns are open. */
    .filters-controls {
        order: 1;
        display: flex;
        align-items: center;
        gap: 12px;
        flex: 0 0 auto;
    }
    /* The desktop inline marginLeft:20px would misalign it as the row's first
       item; the row's own gap handles spacing on mobile. */
    .filters-controls .view-toggle-group {
        margin-left: 0 !important;
        width: auto;
    }
    /* Drop the "View" label on mobile — the Table/Chart switch is self-evident,
       and the label sits above the toggle, needlessly raising the row's height. */
    .filters-controls .view-toggle-group .filter-label {
        display: none;
    }

    /* --- Metadata bar: shrink and let it wrap; it scrolls away --- */
    .metadata {
        padding: 8px 12px;
        gap: 8px 16px;
        font-size: 12px;
    }
    .metadata-item {
        font-size: 12px;
    }

    /* --- Master-detail slide-over (Table view only) ---
       Kill the desktop 2-column grid and rebuild .content--table as a 200%-wide
       flex track that fills the height left below the pinned header/filters. Each
       child is 50% of that track == 100% of the viewport; translating the
       track by -50% swaps the visible pane. Each pane scrolls internally, so
       the two keep independent scroll positions. The Chart view (.content--viz)
       has no slide-over and opts out — see its own rule below. */
    .content--table {
        display: flex;
        flex: 1; /* fill the height under the pinned header + filter bar */
        width: 200%;
        grid-template-columns: none;
        overflow: hidden; /* the panes scroll, not the track */
        min-height: 0;
        transform: translateX(0);
        transition: transform 0.3s ease;
    }
    .content--table.mobile-pane-detail {
        transform: translateX(-50%);
    }
    /* Each pane is exactly one viewport wide and owns its own vertical scroll,
       so leaving one and returning preserves its position with no JS. Scoped to
       the table track so the viz sidebar is NOT forced to a 50% pane width. */
    .content--table > .sidebar,
    .content--table > .main-panel {
        width: 50%;
        flex: 0 0 50%;
        height: 100%;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch; /* momentum scroll on iOS */
    }
    .content--table > .sidebar {
        border-right: none;
    }
    .content--table > .main-panel {
        padding: 16px 12px;
    }

    /* --- Chart view: single-column stack (no slide-over) ---
       The viz has no master-detail track, so on mobile it stacks the filter
       sidebar above the scatter and scrolls as one column. The outer wrapper's
       inline overflow:hidden is relaxed so this column owns the scroll. */
    .viz-view-wrapper {
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
        min-height: 0; /* let this flex child scroll instead of growing the cage */
    }
    .content--viz {
        display: flex;
        flex-direction: column;
        width: 100%;
        /* Grow to the natural height of sidebar + chart so the wrapper scrolls
           the whole column (sidebar scrolls away above the chart). */
        flex: 0 0 auto;
    }
    .content--viz > .sidebar {
        width: 100%;
        height: auto;
        overflow: visible;
        border-right: none;
        flex: 0 0 auto; /* don't compress the filters — let them scroll away */
    }
    .content--viz > .main-panel {
        flex: 0 0 auto; /* keep the chart at its full height, don't shrink it */
    }
    /* Give the scatter real vertical room in the scrolling column (its inline
       desktop height is calc(100vh - 200px), which is too tall stacked). */
    .viz-chart-section {
        height: 70dvh !important;
        margin: 8px 12px 16px !important;
        padding: 8px 12px 16px !important;
    }

    /* --- Back button (shown only inside the detail pane on mobile) --- */
    .mobile-back-button {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        margin-bottom: 16px;
        padding: 8px 14px;
        background: #f3f4f6;
        border: 1px solid #e5e7eb;
        border-radius: 8px;
        font-size: 14px;
        font-weight: 600;
        color: #374151;
        cursor: pointer;
    }

    /* --- Scale detail content down for the narrow pane --- */
    .card-title {
        font-size: 22px;
    }
    .summary-stats {
        grid-template-columns: repeat(2, 1fr);
        gap: 10px;
    }
    .stat-value {
        font-size: 20px;
    }
    /* Tabs can overflow their row; let them scroll horizontally instead of wrapping. */
    .tab-navigation {
        overflow-x: auto;
    }
    .tab-button {
        padding: 10px 14px;
        font-size: 14px;
        white-space: nowrap;
    }
    .chart-container {
        padding: 12px;
    }
    .chart-section {
        margin-top: 20px;
    }

    /* --- Card list: larger touch targets --- */
    .card-item {
        padding: 14px 15px;
    }
    .card-stats {
        flex-wrap: wrap;
        gap: 8px;
    }
}
