/* ============================================================
   CSS Custom Properties — Design Token System
   ============================================================
   Tier 1: Primitive palette (raw colors by name)
   Tier 2: Semantic tokens (named by purpose)
   Tier 3: Component tokens (specific UI roles)
   ============================================================ */

:root {
    /* --- Tier 1: Primitive Palette --- */
    --gray-950: #1B1D21;
    --gray-900: #1F2228;
    --gray-850: #262A30;
    --gray-800: #2E333A;
    --gray-750: #353B42;
    --gray-700: #3F4650;
    --gray-650: #3F4E5A;
    --gray-600: #4D5966;
    --gray-500: #5A6672;
    --gray-400: #8A9199;
    --gray-300: #A8B0B8;
    --gray-200: #C8CDD3;
    --gray-100: #D5DAE0;
    --gray-50: #ECEEF1;
    --white: #ffffff;
    --black: #000000;

    --teal-500: #8AA4BF;
    --teal-900: #1B2B3A;
    --blue-600: #4A6F8A;
    --blue-500: #5B7E98;
    --blue-400: #B0C8DB;
    --red-500: #B35A5E;
    --red-400: #C59A96;
    --orange-400: #B89A82;
    --green-500: #6A9B7E;
    --green-400: #7DB892;
    --yellow-500: #D4B870;
    --amber-500: #C49A5C;
    --amber-400: #DFAE6E;
    --amber-700: #8A6A2F;
    --purple-500: #8A7EB5;

    /* --- Tier 2: Semantic Tokens --- */

    /* Backgrounds */
    --color-bg-primary: var(--gray-900);
    --color-bg-surface: var(--gray-850);
    --color-bg-elevated: var(--gray-800);
    --color-bg-input: var(--gray-700);
    --color-bg-inset: var(--gray-900);
    --color-bg-overlay: rgba(0, 0, 0, 0.5);

    /* Text */
    --color-text-primary: var(--gray-100);
    --color-text-secondary: var(--gray-200);
    --color-text-tertiary: var(--gray-300);
    --color-text-muted: var(--gray-400);
    --color-text-faint: var(--gray-500);
    --color-text-heading: var(--blue-400);
    --color-text-on-accent: #1B2B3A;
    --color-text-on-surface: var(--gray-100);

    /* Borders */
    --color-border: var(--gray-650);
    --color-border-subtle: var(--gray-750);
    --color-border-strong: var(--gray-500);

    /* Interactive */
    --color-accent: var(--teal-500);
    --color-info: var(--blue-500);
    --color-success: var(--green-500);
    --color-success-light: var(--green-400);
    --color-danger: var(--red-500);
    --color-danger-soft: var(--red-400);
    --color-warning: var(--amber-500);
    /* Amber for text that has to be READ, not just noticed. --color-warning is
       tuned for borders, dots and glows; at body size on a page background it
       is too low-contrast to read comfortably, in the light theme especially. */
    --color-warning-text: var(--amber-400);
    --color-save: var(--orange-400);
    --color-purple: var(--purple-500);
    --color-gold: var(--yellow-500);
    --color-link: var(--teal-500);
    --color-link-hover: var(--white);

    /* Study set viz (Define Study modal coverage/sample mosaic) */
    --study-viz-included: var(--green-500);
    --study-viz-included-pulse: var(--green-400);
    --study-viz-included-text: var(--gray-50);
    --study-viz-eligible-bg: var(--gray-750);
    --study-viz-outframe-bg: var(--gray-900);
    --study-viz-border: var(--color-border);
    --study-viz-divider: var(--color-border-subtle);
    --study-viz-label-text: var(--gray-50);
    --study-viz-label-halo: var(--gray-900);

    /* Shadows */
    --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.2);
    --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.3);
    --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.4);

    /* Typography — Font Families */
    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;

    /* Typography — Size Scale */
    --text-hero: 2.25rem;
    --text-h2:   1.5rem;
    --text-h3:   1.2rem;
    --text-body: 1rem;
    --text-sm:   0.875rem;
    --text-xs:   0.75rem;
    --text-xxs:  0.625rem;

    /* Typography — Weight Scale */
    --weight-normal:   400;
    --weight-medium:   500;
    --weight-semibold: 600;
    --weight-bold:     700;

    /* Typography — Line Height */
    --leading-tight:   1.2;
    --leading-normal:  1.5;
    --leading-relaxed: 1.65;

    /* Typography — Letter Spacing */
    --tracking-tight:  -0.025em;
    --tracking-normal: 0;
    --tracking-wide:   0.025em;

    /* --- Tier 3: Component Tokens --- */

    /* Buttons */
    --btn-primary-bg: var(--color-accent);
    --btn-primary-text: var(--color-text-on-accent);
    --btn-danger-bg: var(--color-danger-soft);
    --btn-danger-text: var(--color-text-on-accent);
    --btn-save-bg: var(--color-save);
    --btn-save-text: var(--color-text-on-accent);
    --btn-state-bg: var(--color-success);
    --btn-state-text: var(--color-text-on-accent);
    --btn-discreet-bg: var(--gray-650);
    --btn-discreet-text: var(--color-text-primary);
    --btn-discreet-border: var(--gray-500);
    --btn-discreet-hover: var(--gray-600);
    --btn-discreet-active: var(--gray-800);

    /* Tab navigation */
    --tab-bg: inherit;
    --tab-text: var(--color-text-primary);
    --tab-hover-bg: var(--gray-650);
    --tab-active-bg: var(--color-bg-primary);
    --tab-active-text: var(--white);
    --tab-active-border: var(--color-accent);
    --tab-border: var(--color-border);

    /* Inputs */
    --input-bg: var(--color-bg-input);
    --input-border: var(--color-border);
    --input-text: var(--color-text-primary);

    /* Panels */
    --panel-bg: var(--color-bg-surface);
    --panel-border: var(--color-border);

    /* Tables */
    --table-header-bg: var(--color-bg-elevated);
    --table-row-hover: #282D34;
    --table-row-selected: var(--teal-900);
    --table-border: var(--color-border);

    /* Chips / Filter pills */
    --chip-bg: var(--gray-650);
    --chip-border: var(--gray-500);
    --chip-text: var(--white);
    --chip-selected-bg: var(--color-info);
    --chip-selected-border: #7A9AB5;
    --chip-selected-text: var(--white);

    /* Status indicators — one convention everywhere: green = running,
       blue = standing by (idle/queued/stopping), amber = last run didn't
       work, red = critical fault (couldn't start / aborted).
       Deliberately vivid signal colors, outside the muted UI palette:
       an indicator lamp should read at a glance. */
    --status-idle: #0033FF;
    --status-running: #00C853;
    --status-stopped: #0033FF;
    --status-warn: #FFB300;
    --status-error: #FF1E1E;

    /* Modal */
    --modal-bg: var(--color-bg-surface);
    --modal-border: var(--color-accent);
    --modal-text: var(--color-text-primary);
    --modal-overlay: var(--color-bg-overlay);

    /* Progress */
    --progress-bg: var(--color-bg-input);
    --progress-fill: rgba(106, 155, 126, 0.45);
    --progress-text: rgba(255, 255, 255, 0.74);

    /* Slider (noUiSlider) */
    --slider-track: var(--gray-750);
    --slider-connect: var(--color-accent);
    --slider-handle: var(--white);

    /* Control bar (shared across tabs) */
    --control-bar-bg: var(--color-bg-surface);
    --control-bar-border: var(--color-border);
    --control-bar-text: var(--gray-50);
    --control-bar-label: var(--gray-50);
    --control-bar-info: var(--gray-400);

    /* Select dropdowns (within control bars) */
    --select-bg: var(--color-bg-input);
    --select-border: var(--gray-500);
    --select-text: var(--white);

    /* Sections (collapsible) */
    --section-bg: var(--color-bg-surface);
    --section-border: var(--color-border);

    /* Cards */
    --card-bg: var(--color-bg-surface);
    --card-border: var(--color-border);

    /* Tooltip */
    --tooltip-bg: var(--gray-750);
    --tooltip-text: var(--white);
    --tooltip-border: var(--gray-500);

    /* Plotly / chart overrides */
    --chart-bg: var(--color-bg-primary);
    --chart-text: var(--color-text-primary);
    --chart-grid: var(--gray-650);
    --chart-zeroline: var(--gray-400);
    --chart-grid-line: rgba(255, 255, 255, 0.06);
    --chart-overlay-line: rgba(255, 255, 255, 0.12);
    --chart-annotation-text: rgba(255, 255, 255, 0.4);
    --chart-regression-line: rgba(255, 255, 255, 0.5);
    --chart-heatmap-mid: var(--gray-900);
    --chart-badge-bg: rgba(0, 0, 0, 0.7);

    /* Accent variants for data-specific uses */
    --color-slice1: #6A9B7E;
    --color-slice2: #5B7E98;
    --color-significance: var(--yellow-500);

    /* Trend badge colors */
    --trend-rising-bg: rgba(138, 164, 191, 0.15);
    --trend-rising-border: rgba(138, 164, 191, 0.5);
    --trend-falling-bg: rgba(197, 154, 150, 0.15);
    --trend-falling-border: rgba(197, 154, 150, 0.5);
    --trend-spikes-bg: rgba(184, 154, 130, 0.15);
    --trend-spikes-border: rgba(184, 154, 130, 0.5);
    --trend-breaks-bg: rgba(91, 126, 152, 0.15);
    --trend-breaks-border: rgba(91, 126, 152, 0.5);
    --trend-volatile-bg: rgba(138, 126, 181, 0.15);
    --trend-volatile-border: rgba(138, 126, 181, 0.5);
    --trend-stable-bg: rgba(120, 120, 120, 0.2);
}


/* ============================================================
   Light Theme
   ============================================================ */

[data-theme="light"] {
    --color-bg-primary: #F4F6F7;
    --color-bg-surface: #ffffff;
    --color-bg-elevated: #E9ECEF;
    --color-bg-input: #ffffff;
    --color-bg-inset: #EEF1F3;
    --color-bg-overlay: rgba(0, 0, 0, 0.3);

    --color-text-primary: #1e1e1e;
    --color-text-secondary: #2B3137;
    --color-text-tertiary: #4D5966;
    --color-text-muted: #6B7580;
    --color-text-faint: #8A9199;
    --color-text-heading: #4A6F8A;
    --color-text-on-accent: #ffffff;
    --color-text-on-surface: #1e1e1e;

    --color-border: #CED3D9;
    --color-border-subtle: #DDE1E6;
    --color-border-strong: #ACB3BB;

    --color-accent: #5B7E98;
    --color-info: #4A6F8A;
    --color-success: #4E8A64;
    --color-success-light: #6A9B7E;
    --color-danger: #A3535A;
    --color-danger-soft: #B35A5E;
    --color-warning: #A88A4E;
    --color-warning-text: var(--amber-700);
    --color-save: #9A7E66;
    --color-link: #5B7E98;
    --color-link-hover: #3D5A72;

    /* Study set viz overrides (light) */
    --study-viz-eligible-bg: var(--gray-50);
    --study-viz-outframe-bg: var(--gray-100);
    --study-viz-label-text: var(--gray-900);
    --study-viz-label-halo: var(--white);

    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.12);
    --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.15);

    --btn-primary-bg: var(--color-accent);
    --btn-primary-text: var(--white);
    --btn-danger-bg: var(--color-danger);
    --btn-danger-text: var(--white);
    --btn-save-bg: var(--color-save);
    --btn-save-text: var(--white);
    --btn-state-bg: var(--color-success);
    --btn-state-text: var(--white);
    --btn-discreet-bg: #E9ECEF;
    --btn-discreet-text: #2B3137;
    --btn-discreet-border: #BFC5CC;
    --btn-discreet-hover: #DDE1E6;
    --btn-discreet-active: #CED3D9;

    --tab-hover-bg: #DDE1E6;
    --tab-active-bg: #ffffff;
    --tab-active-text: #1e1e1e;
    --tab-active-border: var(--color-accent);

    --input-bg: var(--color-bg-input);
    --input-border: var(--color-border);
    --input-text: var(--color-text-primary);

    --panel-bg: var(--color-bg-surface);
    --panel-border: var(--color-border);

    --table-header-bg: #E9ECEF;
    --table-row-hover: #F0F2F4;
    --table-row-selected: #D5E0EA;
    --table-border: var(--color-border);

    --chip-bg: #E9ECEF;
    --chip-border: #BFC5CC;
    --chip-text: #2B3137;
    --chip-selected-bg: var(--color-info);
    --chip-selected-border: #4A6F8A;
    --chip-selected-text: var(--white);

    /* Same vivid signal colors as dark mode — indicator lamps, not palette. */
    --status-idle: #0033FF;
    --status-running: #00B24A;
    --status-stopped: #0033FF;
    --status-warn: #F5A300;
    --status-error: #EE1111;

    --modal-bg: var(--color-bg-surface);
    --modal-border: var(--color-accent);
    --modal-text: var(--color-text-primary);

    --progress-bg: #DDE1E6;
    --progress-fill: rgba(78, 138, 100, 0.35);
    --progress-text: rgba(0, 0, 0, 0.7);

    --slider-track: #CED3D9;
    --slider-connect: var(--color-accent);
    --slider-handle: var(--white);

    --control-bar-bg: var(--color-bg-surface);
    --control-bar-border: var(--color-border);
    --control-bar-text: #2B3137;
    --control-bar-label: #2B3137;
    --control-bar-info: #6B7580;

    --select-bg: var(--color-bg-input);
    --select-border: var(--color-border);
    --select-text: #1e1e1e;

    --card-bg: var(--color-bg-surface);
    --card-border: var(--color-border);

    --tooltip-bg: #2B3137;
    --tooltip-text: var(--white);
    --tooltip-border: #4D5966;

    --chart-bg: var(--color-bg-surface);
    --chart-text: #2B3137;
    --chart-grid: #DDE1E6;
    --chart-zeroline: #ACB3BB;
    --chart-grid-line: rgba(0, 0, 0, 0.08);
    --chart-overlay-line: rgba(0, 0, 0, 0.10);
    --chart-annotation-text: rgba(0, 0, 0, 0.45);
    --chart-regression-line: rgba(0, 0, 0, 0.4);
    --chart-heatmap-mid: var(--color-bg-primary);
    --chart-badge-bg: rgba(0, 0, 0, 0.65);

    --color-slice1: #4E8A64;
    --color-slice2: #4A6F8A;

    /* Trend badge colors */
    --trend-rising-bg: rgba(138, 164, 191, 0.2);
    --trend-rising-border: rgba(138, 164, 191, 0.5);
    --trend-falling-bg: rgba(197, 154, 150, 0.2);
    --trend-falling-border: rgba(197, 154, 150, 0.5);
    --trend-spikes-bg: rgba(184, 154, 130, 0.2);
    --trend-spikes-border: rgba(184, 154, 130, 0.5);
    --trend-breaks-bg: rgba(91, 126, 152, 0.2);
    --trend-breaks-border: rgba(91, 126, 152, 0.5);
    --trend-volatile-bg: rgba(138, 126, 181, 0.2);
    --trend-volatile-border: rgba(138, 126, 181, 0.5);
    --trend-stable-bg: rgba(120, 120, 120, 0.25);
}

@media (prefers-color-scheme: light) {
    :root:not([data-theme]) {
        /* Uncomment block below to auto-detect OS preference: */
        /* (Intentionally left empty — opt-in only via data-theme attribute) */
    }
}


/* ============================================================
   Global Reset & Base
   ============================================================ */

* {
    box-sizing: border-box;
}

body {
    font-family: var(--font-sans);
    line-height: var(--leading-normal);
    background: var(--color-bg-primary);
    color: var(--color-text-primary);
    margin: 0;
    /* Top padding is half the sides: the app header below already carries
       its own vertical padding, so the brand sits close to the window edge. */
    padding: 10px 20px 20px;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Focus visible for accessibility */
:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

a {
    color: var(--color-link);
}

a:hover {
    color: var(--color-link-hover);
}


/* ============================================================
   Typography Utilities
   ============================================================ */

/* Size utilities */
.text-hero   { font-size: var(--text-hero); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); }
.text-h2     { font-size: var(--text-h2);   line-height: var(--leading-tight); }
.text-h3     { font-size: var(--text-h3);   line-height: var(--leading-tight); }
.text-body   { font-size: var(--text-body); line-height: var(--leading-normal); }
.text-sm     { font-size: var(--text-sm);   line-height: var(--leading-normal); }
.text-xs     { font-size: var(--text-xs);   line-height: var(--leading-normal); }
.text-xxs    { font-size: var(--text-xxs);  line-height: var(--leading-normal); }

/* Weight utilities */
.font-normal   { font-weight: var(--weight-normal); }
.font-medium   { font-weight: var(--weight-medium); }
.font-semibold { font-weight: var(--weight-semibold); }
.font-bold     { font-weight: var(--weight-bold); }

/* Family utilities */
.font-sans { font-family: var(--font-sans); }
.font-mono { font-family: var(--font-mono); }

/* Style utilities */
.italic    { font-style: italic; }
.uppercase { text-transform: uppercase; letter-spacing: var(--tracking-wide); }

/* Prose (long-form reading) */
.prose { line-height: var(--leading-relaxed); }

/* Data display: tabular numbers for aligned columns */
.tabular-nums { font-variant-numeric: tabular-nums; }


/* ============================================================
   Layout: Tabs
   ============================================================ */

.tab-nav {
    display: flex;
    flex: 0 0 auto;
    background: var(--color-bg-surface);
    margin-bottom: 0;
    border-bottom: 1px solid var(--color-border);
}

.tab-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

.tab-pane {
    display: none;
    flex: 1;
    flex-direction: column;
    min-height: 0;
}

.tab-pane.active {
    display: flex;
}

#data_management,
#admin,
#my_stuff {
    flex-direction: row;
    overflow: hidden;
}

/* Data Management Sidebar Sub-Page Navigation */
.dm-sidebar {
    width: 200px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--color-border-subtle);
    background-color: var(--color-bg-surface);
    padding-top: 8px;
    overflow-y: auto;
}

.dm-sidebar-item {
    padding: 10px 16px;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-text-secondary);
    cursor: pointer;
    border-left: 3px solid transparent;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
    user-select: none;
}

.dm-sidebar-item:hover {
    background: var(--tab-hover-bg);
    color: var(--color-text-primary);
}

.dm-sidebar-item.active {
    background: var(--tab-active-bg);
    color: var(--tab-active-text);
    border-left-color: var(--tab-active-border);
    font-weight: var(--weight-bold);
}

/* "A process this page owns is running" — one per sidebar item, shown by
   main.js updateDmSidebarSpinners(). Same spinner as the header task badge. */
.dm-sidebar-spinner {
    margin-left: 6px;
    vertical-align: middle;
}

/* Sidebar rows that are links out of the app rather than sub-pages
   (My stuff -> Information). Anchors, so middle-click and "copy link" work;
   the arrow marks that they leave the Hub for a new tab. */
.dm-sidebar-item--link {
    display: block;
    text-decoration: none;
}

.dm-sidebar-ext {
    /* The markup puts an &nbsp; before this, so the arrow can never wrap onto
       a line of its own; this is only the extra optical gap. */
    margin-left: 1px;
    font-size: var(--text-xs);
    opacity: 0.65;
}

/* Non-clickable section header between sidebar item groups */
.dm-sidebar-group {
    padding: 11px 16px 3px 16px;
    margin-top: 2px;
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-secondary);
    border-top: 1px solid var(--color-border-subtle);
    user-select: none;
}

.dm-sidebar-group:first-child {
    padding-top: 10px;
    margin-top: 0;
    border-top: none;
}

.dm-content {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    min-width: 0;
}

.dm-page {
    display: none;
}

.dm-page.active {
    display: block;
}

.tab-button {
    background: var(--tab-bg);
    border: none;
    outline: none;
    cursor: pointer;
    padding: 8px 14px;
    color: var(--tab-text);
    transition: 0.3s;
    /* Compact menu: small caps-like uppercase at a small size, so the
       row reads as navigation rather than body copy. */
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    /* One line each: a label that wrapped made the row two lines tall and
       pushed the help button off-screen before the phone layout kicked in. */
    white-space: nowrap;
    border-right: 1px solid var(--color-border);
}

.tab-button:hover {
    background: var(--tab-hover-bg);
}

.tab-button.active {
    background: var(--tab-active-bg);
    border-bottom: 2px solid var(--tab-active-border);
    color: var(--tab-active-text);
    font-weight: var(--weight-bold);
}

.tab-button:disabled,
.tab-button.is-disabled {
    cursor: not-allowed;
    opacity: 0.4;
    background: var(--tab-bg);
}

.tab-button:disabled:hover,
.tab-button.is-disabled:hover {
    background: var(--tab-bg);
}


/* ============================================================
   Layout: Container & Panels
   ============================================================ */

.container {
    display: flex;
    gap: 20px;
    flex: 1;
    min-height: 0;
}

.panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    padding: 10px;
    border-radius: 5px;
    min-height: 200px;
}

.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
    flex: 0 0 auto;
}

.controls {
    display: flex;
    gap: 10px;
    align-items: center;
}


/* ============================================================
   Typography
   ============================================================ */

h2 {
    margin: 0;
    font-size: var(--text-h3);
    color: var(--color-text-heading);
}


/* ============================================================
   Form Elements
   ============================================================ */

textarea {
    flex: 1;
    background: var(--color-bg-inset);
    border: 1px solid var(--color-border);
    color: var(--color-text-primary);
    font-family: var(--font-mono);
    padding: 10px;
    font-size: var(--text-sm);
    resize: none;
    min-height: 150px;
    display: block;
    width: 100%;
}

input[type="text"],
input[type="password"],
input[type="number"] {
    background: var(--input-bg);
    border: 1px solid var(--input-border);
    color: var(--input-text);
    padding: 5px;
    border-radius: 3px;
}

/* Hide native number-input spinners. The up/down arrows waste width and we
   use number inputs for batch sizes / counts where freely typing is the norm. */
input[type="number"] {
    -moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

select,
.dropdown {
    background: var(--select-bg);
    border: 1px solid var(--select-border);
    color: var(--select-text);
    padding: 5px;
    border-radius: 3px;
}

.control-input {
    background: var(--input-bg);
    border: 1px solid var(--input-border);
    color: var(--input-text);
    padding: 5px;
    border-radius: 3px;
}


/* ============================================================
   Buttons: the base button variants
   ============================================================ */

button {
    cursor: pointer;
    padding: 5px 15px;
    border: none;
    border-radius: 3px;
    font-weight: var(--weight-bold);
}

.btn-primary {
    background: var(--btn-primary-bg);
    color: var(--btn-primary-text);
}

.btn-primary:disabled {
    opacity: 0.35;
    cursor: default;
}

.btn-stop {
    background: var(--btn-danger-bg);
    color: var(--btn-danger-text);
}

.btn-running {
    background: var(--color-warning);
    color: var(--color-bg-primary);
    border: none;
    border-radius: 6px;
    cursor: default;
    pointer-events: none;
    padding: 6px 12px;
    font-weight: var(--weight-semibold);
    animation: btn-running-pulse 1.5s ease-in-out infinite;
}

@keyframes btn-running-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

.btn-save,
.btn-secondary {
    background: var(--btn-discreet-bg);
    color: var(--btn-discreet-text);
    border: 1px solid var(--btn-discreet-border);
}

.btn-danger {
    background: var(--btn-danger-bg);
    color: var(--btn-danger-text);
}


/* ============================================================
   Collection and studies tables, ingest card, upload modal, tag chips,
   account badges
   ============================================================ */

.collection-table {
    min-width: 100%;
    width: max-content;
    border-collapse: collapse;
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.collection-table .edit-activity-item {
    border-bottom: 1px solid var(--chart-grid);
    cursor: pointer;
}

.collection-table .edit-activity-item:hover {
    background: var(--color-bg-input);
}

.collection-table .collection-hidden {
    opacity: 0.5;
    color: var(--color-text-muted);
}

/* A study row mid-save or mid-refresh. The data cells dim to show the row is
   not clickable; the Action cell keeps full opacity so its status message
   stays readable — dimming the whole row is what used to wash it out. */
.study-row-busy > td {
    opacity: 0.45;
}

.study-row-busy > td:last-child {
    opacity: 1;
}

/* Live status text in the studies table's Action column. Inherits the table's
   font size so it sits on the same line as the rest of the row. */
.study-row-status {
    color: var(--color-warning-text);
    white-space: nowrap;
}

.ingest-card {
    background: var(--color-bg-elevated);
    color: var(--color-text-primary);
    padding: 15px;
    border-radius: 4px;
    border: 1px solid var(--color-border-strong);
    flex: 1;
    min-width: 300px;
    margin-bottom: 10px;
}

.upload-modal-overlay {
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: var(--modal-overlay);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
}

.upload-modal {
    background: var(--modal-bg);
    border: 1px solid var(--modal-border);
    border-radius: 8px;
    padding: 24px;
    width: min(520px, 90vw);
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: var(--shadow-md);
    color: var(--modal-text);
}

.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--color-bg-input);
    color: var(--color-text-primary);
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    padding: 2px 8px;
    font-size: var(--text-xs);
}

/* Account-kind pill shown next to usernames in Admin → Active Users
   (participant / placeholder / no login). Muted on purpose. */
.account-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border: 1px solid var(--color-border-strong);
    border-radius: 10px;
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
    font-size: var(--text-xxs);
    line-height: 1.5;
    vertical-align: middle;
    white-space: nowrap;
}

.tag-chip .remove-tag {
    cursor: pointer;
    color: var(--color-text-tertiary);
    font-weight: var(--weight-bold);
    line-height: 1;
}

.tag-chip .remove-tag:hover {
    color: var(--color-danger);
}

.tag-suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border-strong);
    border-radius: 4px;
    max-height: 150px;
    overflow-y: auto;
    z-index: 10;
    box-shadow: var(--shadow-md);
}

.tag-suggestion-item {
    padding: 6px 10px;
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.tag-suggestion-item:hover {
    background: var(--color-bg-hover);
}


/* ============================================================
   Attention states: pending-action and armed pulses, sidebar stale dot
   ============================================================ */


/* Action is needed (e.g. new enrichment data awaiting Consolidate & Refresh):
   a strong amber pulse so the state is unmissable at a glance. */
@keyframes stale-pulse-glow {
    0%, 100% { box-shadow: 0 0 4px var(--color-warning); }
    50% { box-shadow: 0 0 14px var(--color-warning); }
}

.btn-has-pending {
    border: 2px solid var(--color-warning) !important;
    animation: stale-pulse-glow 1.6s ease-in-out infinite;
}

/* Amber pulsing dot on a sidebar item that needs attention (e.g. Refresh
   Caches when new enrichment data awaits consolidation). */
@keyframes stale-dot-pulse {
    0%, 100% { opacity: 0.45; }
    50% { opacity: 1; }
}

.sidebar-stale-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--color-warning);
    margin-left: 6px;
    vertical-align: middle;
    animation: stale-dot-pulse 1.6s ease-in-out infinite;
}

/* Armed state for the Consolidate & Refresh button — gentle blue pulse
   so it's clearly "waiting to fire" but not alarming. */
@keyframes armed-pulse-glow {
    0%, 100% {
        box-shadow: 0 0 4px var(--color-primary, #4a9eff);
        transform: scale(1);
    }
    50% {
        box-shadow: 0 0 14px var(--color-primary, #4a9eff);
        transform: scale(1.02);
    }
}

.btn-armed-pulse {
    background: var(--color-primary, #4a9eff) !important;
    color: var(--btn-primary-text, white) !important;
    border: 1px solid var(--color-primary, #4a9eff) !important;
    animation: armed-pulse-glow 1.6s ease-in-out infinite;
}

.btn-armed-pulse:hover {
    opacity: 0.92;
}


/* ============================================================
   Refresh pipeline (Dataset Assembly): worker cards, Gantt timeline,
   start dialog
   ============================================================ */


/* Dataset Assembly worker cards: one full-width column, in pipeline order, so
   the dispatch order does not read as a grid and the progress bars share one
   width. */
.refresh-card-stack {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.refresh-card {
    background: var(--section-bg);
    padding: 15px;
    border-radius: 4px;
    border: 1px solid var(--color-border);
    width: 100%;
}

/* The "Rebuild Downstream Datasets" box wrapping those cards — same chrome as
   the Consolidate card above it on the Dataset Assembly page. */
.refresh-group {
    background: var(--section-bg);
    padding: 15px;
    border-radius: 4px;
    border: 1px solid var(--color-border);
    margin-bottom: 20px;
}

/* Inside the group the cards would sit on the box's own background, so lift
   them a shade to keep each one readable as a separate card. */
.refresh-group .refresh-card {
    background: var(--color-bg-elevated);
}

/* Refresh-pipeline timeline (Dataset Assembly). One grid row per step —
   label | time track | duration — all rows sharing one wall-clock axis, so a
   bar's position and width are its start and its length in time, never a
   percentage of progress. Colours follow the worker status lights: green =
   ran/running, blue dashed = queued and waiting, red = failed, faded and
   bar-less = not part of this run. Every step is drawn every run, so the
   chart's shape never changes. */
.pipeline-gantt {
    font-variant-numeric: tabular-nums;
}

.gantt-row,
.gantt-axis {
    display: grid;
    grid-template-columns: 4.2rem 1.4rem 11rem minmax(0, 1fr) 6rem;
    align-items: center;
    gap: 10px;
}

.gantt-row { padding: 3px 0; }

/* The card's View Log, repeated on each row so a run can be read and opened in
   one place. Admin-only; everyone else gets the empty cell, which keeps the
   four columns aligned. */
.gantt-log {
    display: block;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
}

/* Transport control on each row: play when the worker is idle, stop while it
   runs. Deliberately small and quiet — the row is a record first and a control
   second — but a real button, with the square/triangle everyone reads without
   a legend. */
.gantt-run {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 3px;
    background: transparent;
    color: var(--color-text-tertiary);
    cursor: pointer;
}
.gantt-run svg { width: 11px; height: 11px; fill: currentColor; }
.gantt-run:hover:not(:disabled) { background: var(--color-border); }
/* Go and stop, coloured as the transport controls they are. A disabled play
   drops to grey — the colour is what says "you can start this", so it must not
   survive into a state where you cannot. */
.gantt-run--play { color: var(--status-running); }
.gantt-run--stop { color: var(--status-error); }
.gantt-run:disabled {
    color: var(--color-text-tertiary);
    opacity: 0.5;
    cursor: not-allowed;
}
span.gantt-run { cursor: default; }

/* Armed: a run that WILL happen as soon as the scraper and annotator finish.
   The play icon and every step the arm will set off pulse together, so the
   block shows the queued run instead of looking idle until it fires. */
.gantt-run--armed:not(:disabled) { animation: gantt-pulse 1.4s ease-in-out infinite; }
/* Two classes deep, to beat the state rules further down the file that are
   also two classes deep (.gantt-row--idle .gantt-label): an armed row is
   usually an idle one, and equal specificity would hand the colour to
   whichever rule happens to come later. */
.gantt-row.gantt-row--armed .gantt-label {
    color: var(--color-warning);
    animation: gantt-pulse 1.4s ease-in-out infinite;
}

.gantt-label {
    color: var(--color-text-primary);
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A name that carries its worker's description. The tooltip is an absolutely
   positioned ::after, so this cell must not clip it — hence overflow:visible,
   which the short noun labels do not need anyway. The pointer stays an arrow:
   the dotted underline already says "there is more here", and swapping to a
   question mark on every row reads as an error state. */
.gantt-label.gantt-label--help {
    overflow: visible;
    cursor: default;
}

.gantt-track {
    position: relative;
    height: 18px;
    /* A message is free to run past the end of its bar, but not out of the
       track: overflowing the whole cell put it on top of the duration column
       and both became unreadable. */
    overflow: hidden;
    background: linear-gradient(var(--color-border), var(--color-border)) center / 100% 1px no-repeat;
}

.gantt-bar {
    position: absolute;
    top: 2px;
    height: 14px;
    min-width: 3px;
    border-radius: 2px;
    background: var(--status-running);
}
.gantt-bar--failed { background: var(--status-error); }
.gantt-bar--running { animation: gantt-pulse 1.4s ease-in-out infinite; }
.gantt-bar--queued {
    background: transparent;
    border: 1px dashed var(--status-idle);
    box-sizing: border-box;
}

/* The step's own progress line, written from the bar's left edge — the same
   text its card shows, minus the percentage. It is allowed to run past the end
   of the bar rather than be truncated, so a long message stays readable. */
.gantt-msg {
    position: absolute;
    top: 0;
    line-height: 18px;
    white-space: nowrap;
    color: var(--color-text-secondary);
}

/* --progress-text is the token the worker cards use for exactly this job: text
   that has to stay legible both on the green fill and on the empty track, in
   either theme (light grey on dark, dark grey on light). Reusing it keeps the
   two blocks reading as one thing. */
.gantt-msg--live {
    padding-left: 5px;
    padding-right: 4px;
    color: var(--progress-text);
}

.gantt-dur {
    color: var(--color-text-tertiary);
    white-space: nowrap;
}
.gantt-row--failed .gantt-dur { color: var(--status-error); }
.gantt-row--running .gantt-dur { color: var(--color-text-secondary); }
.gantt-row--queued .gantt-dur,
.gantt-row--pending .gantt-dur { font-style: italic; }

/* Two bar-less, greyed states that must not be confused. "skipped" is an
   anomaly — the step WAS planned and never ran — so its word carries the
   warning colour. The three quiet states recede further and stay uncoloured:
   "pruned" (planned, then skipped because nothing upstream changed — the
   ordinary outcome of a quiet run, and the right column says why), "upstream"
   (runs before this run's starting point) and "not_planned" (nothing in this
   run feeds it). All keep their row so the chart's shape is the same every run
   and the reader learns what was not needed as well as what ran. */
/* No run has ever been recorded. Every worker is still listed, in dependency
   order, so the block always shows what the pipeline IS — and still reaches
   each log — instead of collapsing to a single empty line. */
.gantt-row--idle .gantt-label { color: var(--color-text-secondary); }

.gantt-row--skipped { opacity: 0.5; }
.gantt-row--skipped .gantt-dur { color: var(--color-warning); font-style: italic; }
.gantt-row--not_planned,
.gantt-row--pruned,
.gantt-row--upstream { opacity: 0.45; }
.gantt-row--not_planned .gantt-label,
.gantt-row--pruned .gantt-label,
.gantt-row--upstream .gantt-label { color: var(--color-text-tertiary); }
.gantt-row--not_planned .gantt-dur,
.gantt-row--pruned .gantt-dur,
.gantt-row--upstream .gantt-dur { font-style: italic; }

/* Why a step was skipped, written across the empty track. The reason is the
   point of a pruned row — a bare "not needed" would leave the reader guessing
   whether the run was clever or broken. */
.gantt-msg--reason { color: var(--color-text-tertiary); font-style: italic; }

/* The step the run started from. Named rather than merely first, because a run
   can start anywhere in the chain and the rows above it are inert. */
.gantt-row--origin .gantt-label { font-weight: var(--weight-bold); opacity: 1; }

/* Guidelines across every track: the fork (dashed) where the step that fanned
   the run out finished, and "now" while live. */
.gantt-fork,
.gantt-now {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
}
.gantt-fork { border-left: 1px dashed var(--color-text-tertiary); opacity: 0.7; }
.gantt-now { border-left: 1px solid var(--color-warning); opacity: 0.8; }

.gantt-axis { margin-top: 4px; }

.gantt-axis-track {
    position: relative;
    height: 18px;
    border-top: 1px solid var(--color-border);
}

.gantt-tick {
    position: absolute;
    top: 3px;
    transform: translateX(-50%);
    white-space: nowrap;
    color: var(--color-text-tertiary);
}
.gantt-tick--origin { transform: none; }
/* The clock time the run finished, pinned to the axis's right edge. */
.gantt-tick--end { transform: translateX(-100%); }
.gantt-tick::before {
    content: "";
    position: absolute;
    top: -4px;
    left: 50%;
    width: 1px;
    height: 3px;
    background: var(--color-border);
}
.gantt-tick--origin::before { left: 0; }
.gantt-tick--end::before { left: 100%; }

/* Legend swatches under the chart. */
.gantt-key {
    display: inline-block;
    width: 14px;
    height: 8px;
    border-radius: 2px;
    vertical-align: middle;
    background: var(--status-running);
}
.gantt-key--running { animation: gantt-pulse 1.4s ease-in-out infinite; }
.gantt-key--queued { background: transparent; border: 1px dashed var(--status-idle); box-sizing: border-box; }
.gantt-key--failed { background: var(--status-error); }
.gantt-key--unplanned { background: var(--color-text-tertiary); opacity: 0.35; }

/* The start dialog: options, then the run this click actually commits to. */
.refresh-opt {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 2px 8px;
    align-items: baseline;
    margin-bottom: 10px;
    cursor: pointer;
}
.refresh-opt--blocked { cursor: not-allowed; opacity: 0.6; }
.refresh-opt--blocked .refresh-opt-help { color: var(--color-warning); }

.refresh-opt-help {
    grid-column: 2;
    color: var(--color-text-tertiary);
    line-height: var(--leading-normal);
}

.refresh-start-plan {
    border: 1px solid var(--color-border);
    border-radius: 4px;
    overflow: hidden;
}
.refresh-plan-row {
    display: grid;
    grid-template-columns: 1.6rem 1fr auto;
    gap: 8px;
    align-items: center;
    padding: 5px 10px;
    border-top: 1px solid var(--color-border);
}
.refresh-plan-row:first-child { border-top: none; }
.refresh-plan-row--origin { background: var(--section-bg); }
/* The leaves are dispatched together and run concurrently, so they are not
   numbered steps in a queue and their times do not add up. */
.refresh-plan-row--parallel .refresh-plan-name { padding-left: 10px; }
.refresh-plan-row--parallel .refresh-plan-name::before {
    content: "\21B3";
    margin-right: 6px;
    color: var(--color-text-tertiary);
}
.refresh-plan-row--origin .refresh-plan-name { font-weight: var(--weight-bold); }
.refresh-plan-num { color: var(--color-text-tertiary); }
.refresh-plan-time { color: var(--color-text-secondary); white-space: nowrap; }

@keyframes gantt-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
}

@media (max-width: 700px) {
    .gantt-row,
    .gantt-axis { grid-template-columns: 3.4rem 1.3rem 7rem minmax(0, 1fr) 4.5rem; }
}


/* ============================================================
   Scraper cards: cookie-status pills, alert and setup banners
   ============================================================ */


/* Per-platform cookie-status pill on the enrichment scraper cards. A colored dot
   + short label; the full message/age/expiry rides in the .meta-tooltip. Colors
   come from semantic tokens so both light and dark themes are covered. */
.cookie-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 1px 8px;
    border-radius: 10px;
    border: 1px solid var(--color-border);
    color: var(--color-text-tertiary);
    font-size: var(--text-xxs);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
    cursor: help;
}

/* The pill is also a .meta-tooltip, whose dotted underline is declared later in
   this file and would otherwise win — this compound selector overrides it so the
   pill text stays clean. */
.cookie-pill.meta-tooltip { text-decoration: none; }

.cookie-pill::before {
    content: "";
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--color-text-tertiary);
}

.cookie-pill--ok { color: var(--color-success-light); border-color: var(--color-success); }
.cookie-pill--ok::before { background: var(--color-success); }

.cookie-pill--warn { color: var(--color-warning); border-color: var(--color-warning); }
.cookie-pill--warn::before { background: var(--color-warning); }

.cookie-pill--bad { color: var(--color-danger-soft); border-color: var(--color-danger); }
.cookie-pill--bad::before { background: var(--color-danger); }

.cookie-pill--unknown { color: var(--color-text-tertiary); border-color: var(--color-border); }
.cookie-pill--unknown::before { background: var(--color-text-tertiary); }

/* Persistent scraper alert banner on an enrichment scraper card (raised by the
   worker when a platform's scraper looks systematically broken, e.g. a
   permanent-failure storm). Outline style: a danger border + danger text on the
   card's own background keeps it readable in both themes without a dedicated
   danger-surface token. */
.scraper-alert {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 12px 0;
    padding: 8px 12px;
    border: 1px solid var(--color-danger);
    border-left: 4px solid var(--color-danger);
    border-radius: 4px;
}

.scraper-alert .scraper-alert-text {
    color: var(--color-danger-soft);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}

/* Same shape as .scraper-alert, in the warning colour: an unconfigured
   optional service is a setup state to act on, not a failure to alarm at. */
.config-notice {
    display: block;
    margin: 0 0 12px 0;
    padding: 8px 12px;
    border: 1px solid var(--color-warning);
    border-left: 4px solid var(--color-warning);
    border-radius: 4px;
}

.config-notice .config-notice-text {
    color: var(--color-warning);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}


/* ============================================================
   Buttons (continued): action buttons, segmented toggles, discreet /
   compact / row-state buttons, row badges
   ============================================================ */

.action-btn {
    background: var(--btn-primary-bg);
    color: var(--btn-primary-text);
    padding: 6px 12px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: var(--weight-medium);
}

.action-btn:hover {
    opacity: 0.9;
}

.action-btn:disabled {
    opacity: 0.35;
    cursor: default;
    pointer-events: none;
}

/* Variant classes must win over the `.action-btn` primary background above,
   which appears later in source order than the .btn-* block. Without these
   explicit overrides, `class="action-btn btn-danger"` renders primary-blue. */
.action-btn.btn-danger {
    background: var(--btn-danger-bg);
    color: var(--btn-danger-text);
}
.action-btn.btn-secondary,
.action-btn.btn-save {
    background: var(--btn-discreet-bg);
    color: var(--btn-discreet-text);
    border: 1px solid var(--btn-discreet-border);
}

/* Segmented toggle control (e.g. Scatter/Heatmap, Distribution/Compare) */
.toggle-group {
    display: inline-flex;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--color-border-strong);
}

.toggle-group .toggle-option {
    padding: 5px 14px;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    cursor: pointer;
    border: none;
    background: var(--color-bg-input);
    color: var(--color-text-tertiary);
    transition: background 0.15s, color 0.15s;
}

.toggle-group .toggle-option:not(:last-child) {
    border-right: 1px solid var(--color-border-strong);
}

.toggle-group .toggle-option.active {
    background: var(--btn-primary-bg);
    color: var(--btn-primary-text);
    font-weight: var(--weight-semibold);
}

.btn-discreet {
    background: var(--btn-discreet-bg);
    color: var(--btn-discreet-text);
    padding: 5px 10px;
    font-size: var(--text-sm);
    border: 1px solid var(--btn-discreet-border);
    transition: background 0.2s;
}

/* Compact row-action button (table "Actions" cells, result-card actions).
   Declared AFTER .btn-discreet so it wins on source order — .btn-discreet sets
   its own font-size, which would otherwise beat a `.text-xs` utility class of
   equal specificity and make View/Edit render larger than their neighbours.
   The transparent border keeps every variant the same box height: .btn-discreet
   and .btn-save carry a 1px border, .btn-primary and .btn-danger do not. */
.btn-compact {
    padding: 4px 10px;
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    font-weight: var(--weight-semibold);
    border: 1px solid transparent;
}

.btn-discreet.btn-compact,
.btn-save.btn-compact,
.btn-secondary.btn-compact {
    border-color: var(--btn-discreet-border);
}

.btn-discreet:hover {
    background: var(--btn-discreet-hover);
}

.btn-discreet:active {
    background: var(--btn-discreet-active);
}

.btn-discreet:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Sticky depressed state for toggle-style row buttons (e.g. the Versions
   page's View buttons while their snapshot is shown below the table). */
.btn-discreet.btn-pressed,
.btn-discreet.btn-pressed:hover {
    background: var(--btn-discreet-active);
    box-shadow: inset var(--shadow-sm);
}

/* Non-interactive "this row holds the state" button (Versions page: the row
   that is Preferred / Active; Contracts page: the active contract). Filled
   green so the current selection stands out clearly from the surrounding
   action buttons; the label states the fact ("✓ Preferred" / "✓ Active"). */
.btn-state {
    pointer-events: none;
    cursor: default;
    background: var(--btn-state-bg);
    color: var(--btn-state-text);
    font-weight: var(--weight-bold);
    border: 1px solid transparent;
}

/* Row-action buttons whose label swaps with state ("Prefer" → "Preferred",
   "Activate" → "Active"): fixed width so the swap doesn't reflow the row.
   Sized to the longest label (the armed "Prefer — sure?"). */
.btn-row-fixed {
    width: 110px;
    padding-left: 0;
    padding-right: 0;
    text-align: center;
}

/* Small pill marking what a table row IS (e.g. the active or built-in
   contract) — informational, never interactive. */
.row-badge {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 9px;
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    white-space: nowrap;
}

.row-badge-active {
    border-color: var(--color-success);
    color: var(--color-success);
}


/* ============================================================
   Status Indicators
   ============================================================ */

.status-indicator {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
    background: var(--status-idle);
    margin-right: 5px;
}

/* Green + pulse: the worker is actively doing something. */
.status-running {
    background: var(--status-running);
    box-shadow: 0 0 5px var(--status-running);
    animation: btn-running-pulse 1.5s ease-in-out infinite;
}

/* Blue: standing by — idle after a clean run (or never run). */
.status-stopped,
.status-completed {
    background: var(--status-stopped);
}

/* Blue + pulse: waiting — on the worker to wind down, or on capacity. */
.status-stopping {
    background: var(--status-idle);
    box-shadow: 0 0 5px var(--status-idle);
    animation: btn-running-pulse 1.5s ease-in-out infinite;
}

/* A forked pipeline leaf that was dispatched but not yet picked up by a worker.
   Distinct from running so the card reads "queued", not "in progress". */
.status-queued {
    background: var(--status-idle);
    box-shadow: 0 0 5px var(--status-idle);
    animation: btn-running-pulse 1.5s ease-in-out infinite;
}

/* Amber: idle, but the last run didn't work. */
.status-warn {
    background: var(--status-warn);
}

/* Red: critical — the task failed to start (e.g. dropped by a 429). */
.status-failed,
.status-error {
    background: var(--status-error);
    box-shadow: 0 0 5px var(--status-error);
}


/* ============================================================
   Progress Bar
   ============================================================ */

.progress-container {
    background: var(--progress-bg);
    height: 20px;
    border-radius: 3px;
    margin: 10px 0;
    overflow: hidden;
    position: relative;
    flex: 0 0 auto;
}

.progress-bar {
    background: var(--progress-fill);
    height: 100%;
    width: 0%;
    transition: width 0.5s ease;
}

.progress-text {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    text-align: center;
    color: var(--progress-text);
    font-size: var(--text-xs);
    line-height: 20px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Streaming log feed for the Async Annotator card (replaces its progress bar). */
.batch-log-feed {
    margin: 10px 0;
    max-height: 96px;
    overflow-y: auto;
    background: var(--color-bg-primary);
    border: 1px solid var(--color-border-subtle);
    border-radius: 4px;
    padding: 8px 10px;
    font-family: var(--font-mono);
    line-height: var(--leading-normal);
    color: var(--color-text-secondary);
    white-space: pre-wrap;
    word-break: break-word;
}


/* ============================================================
   noUiSlider Overrides
   ============================================================ */

.noUi-target {
    background: var(--slider-track);
    border: none;
    box-shadow: none;
    height: 4px;
    margin: 15px 0 10px 0;
}

.noUi-connect {
    background: var(--slider-connect);
}

.noUi-horizontal .noUi-handle {
    height: 8px;
    width: 8px;
    top: -2px;
    right: -4px;
    border-radius: 50%;
    background: var(--slider-handle);
    border: none;
    box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
    cursor: grab;
    outline: none;
}

.noUi-handle:before,
.noUi-handle:after {
    display: none !important;
}

.noUi-tooltip {
    display: none;
}

.noUi-active {
    cursor: grabbing;
    background: var(--slider-handle);
    transform: scale(1.2);
}


/* ============================================================
   Video Analysis Slider Markers (extra_data indicators)
   ============================================================ */

#viewer-slider-wrap {
    position: relative;
}

#viewer-slider-markers {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 0;
    transform: translateY(-50%);
}

.slider-extra-bin {
    position: absolute;
    bottom: 50%;
    background: var(--color-warning);
    border-radius: 1px 1px 0 0;
    pointer-events: auto;
    cursor: default;
}

/* Scrub chip: the activity timestamp of the video under the slider thumb.
   `left` is set from JS to follow the thumb; the translate centres it there. */
#viewer-slider-ts {
    position: absolute;
    top: 100%;
    transform: translateX(-50%);
    margin-top: 2px;
    padding: 2px 6px;
    white-space: nowrap;
    pointer-events: none;
    z-index: 3;
    border-radius: 4px;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    border: 1px solid var(--tooltip-border);
    box-shadow: var(--shadow-sm);
}


/* ============================================================
   Modal
   ============================================================ */

.modal {
    display: none;
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: var(--modal-overlay);
    backdrop-filter: blur(2px);
}

.modal-content {
    background-color: var(--modal-bg);
    margin: 15% auto;
    padding: 20px;
    border: 1px solid var(--modal-border);
    width: 50%;
    color: var(--modal-text);
    border-radius: 8px;
    box-shadow: var(--shadow-md);
    position: relative;
    font-size: var(--text-h3);
    line-height: var(--leading-relaxed);
}

.close-button,
.close {
    color: var(--color-text-tertiary);
    float: right;
    font-size: var(--text-h2);
    font-weight: var(--weight-bold);
    cursor: pointer;
    position: absolute;
    top: 5px;
    right: 15px;
}

/* The modal close control is a real <button> (templates/_macros.html modal()):
   drop the browser's button chrome so it looks like the old &times; glyph. */
button.close-button,
button.close {
    background: none;
    border: 0;
    padding: 0;
    line-height: inherit;
    font-family: inherit;
}

.close-button:hover,
.close-button:focus,
.close:hover,
.close:focus {
    /* Not --white: on the light theme that made the X vanish on hover. */
    color: var(--color-text-primary);
    text-decoration: none;
    cursor: pointer;
}


/* ============================================================
   Donation pre-upload review (My Collections upload modal)
   ============================================================ */

/* Review mode widens the upload modal (its inline max-width is overridden). */
.modal-content.myc-review-mode {
    max-width: 780px !important;
    width: 92vw;
    margin: 6vh auto;
}

.rev-intro {
    color: var(--color-text-muted);
    margin-bottom: 12px;
}

.rev-card {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    margin-bottom: 10px;
    overflow: hidden;
}

.rev-card-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    flex-wrap: wrap;
}

.rev-disclose {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: inherit;
    font: inherit;
    display: flex;
    align-items: center;
    gap: 6px;
}

.rev-chevron { color: var(--color-text-tertiary); }

.rev-title { font-weight: 600; }

.rev-count {
    color: var(--color-text-muted);
    margin-left: auto;
}

.rev-actions { display: flex; gap: 8px; }

.rev-chip {
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: none;
    color: inherit;
    font-size: var(--text-sm, 0.85em);
    padding: 2px 10px;
    cursor: pointer;
}

.rev-chip:disabled { opacity: 0.45; cursor: default; }

.rev-chip:not(:disabled):hover { border-color: var(--color-text-tertiary); }

.rev-body { padding: 0 12px 10px 12px; }

.rev-search {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 8px;
    padding: 4px 8px;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: inherit;
    color: inherit;
}

.rev-toolbar { margin-bottom: 6px; }

.rev-table-wrap {
    max-height: 300px;
    overflow-y: auto;
    overflow-x: auto;
    border: 1px solid var(--color-border);
    border-radius: 6px;
}

.rev-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm, 0.85em);
}

.rev-table th {
    position: sticky;
    top: 0;
    background: var(--modal-bg);
    text-align: left;
    padding: 6px 8px;
    border-bottom: 1px solid var(--color-border);
    z-index: 1;
}

.rev-table td {
    padding: 4px 8px;
    border-bottom: 1px solid var(--color-border);
    max-width: 320px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: top;
}

.rev-check-col { width: 28px; }

.rev-empty { color: var(--color-text-muted); padding: 10px; }

.rev-card-muted { background: rgba(128, 128, 128, 0.06); }

.rev-muted-body {
    color: var(--color-text-muted);
    padding: 0 12px 10px 12px;
}

.rev-table a {
    color: var(--color-link);
    text-decoration: underline;
}

.rev-table a:hover { color: var(--color-link-hover); }

.rev-more-note td {
    color: var(--color-text-muted);
    font-style: italic;
    white-space: normal;
}

.rev-toggle { display: flex; align-items: center; gap: 6px; cursor: pointer; }

.rev-consent {
    border-top: 1px solid var(--color-border);
    margin-top: 12px;
    padding-top: 10px;
    margin-bottom: 10px;
}

.rev-consent-label {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
}

.rev-consent-label input[type="checkbox"] { margin-top: 3px; }

.rev-consent-toggle {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--color-link);
    text-decoration: underline;
    cursor: pointer;
}

.rev-consent-toggle:hover { color: var(--color-link-hover); }

.rev-consent-text {
    max-height: 280px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 4px 14px;
    margin-bottom: 10px;
    color: var(--color-text-muted);
    line-height: var(--leading-relaxed);
}

.rev-consent-text h4 {
    margin: 10px 0 6px 0;
    color: var(--color-text);
}

.rev-consent-text h5 {
    margin: 12px 0 2px 0;
    font-size: inherit;
    color: var(--color-text);
}

.rev-consent-text p { margin: 4px 0 8px 0; }

.rev-consent-text a { color: var(--color-link); }


/* ============================================================
   Donation upload cards + "how to get my data" guide modal
   ============================================================ */

.myc-howto-link {
    display: inline-block;
    margin-top: 4px;
    color: var(--color-link);
    text-decoration: underline;
}

.myc-howto-link:hover { color: var(--color-link-hover); }

.modal-content.myc-howto {
    max-width: 640px;
    width: 90vw;
    margin: 8vh auto;
    font-size: var(--text-base, 1rem);
}

.myc-howto-body h3 { margin: 0 0 10px 0; }

.myc-howto-body p { margin: 0 0 12px 0; line-height: var(--leading-relaxed); }

.myc-howto-body ul { margin: 0 0 14px 0; padding-left: 22px; }

.myc-howto-body li { margin-bottom: 6px; line-height: var(--leading-relaxed); }

.myc-howto-body a { color: var(--color-link); }

.myc-steps {
    margin: 0 0 14px 0;
    padding-left: 24px;
}

.myc-steps li { margin-bottom: 8px; }

.myc-howto-note {
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
    padding-top: 10px;
    margin-bottom: 0;
}

.myc-howto-actions {
    display: flex;
    justify-content: flex-end;
    border-top: 1px solid var(--color-border);
    padding-top: 12px;
    margin-top: 16px;
}


/* ============================================================
   My Collections table (collections + share rows in one)
   ============================================================ */

.myc-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--color-border);
    border-radius: 10px;
    background: var(--color-bg-elevated);
}

.myc-table {
    width: 100%;
    min-width: 760px;
    border-collapse: collapse;
    font-size: var(--text-sm, 0.9em);
}

.myc-table th {
    text-align: left;
    padding: 9px 10px;
    color: var(--color-text-muted);
    font-weight: 600;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.myc-table td {
    padding: 9px 10px;
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
    white-space: nowrap;
}

.myc-table tbody tr:last-child td { border-bottom: none; }

.myc-table .myc-num { text-align: right; font-variant-numeric: tabular-nums; }

.myc-cell-platform { font-weight: 600; }
.myc-cell-id { color: var(--color-text-muted); }
.myc-cell-muted { color: var(--color-text-faint); }
.myc-cell-center { text-align: center; }
.myc-cell-actions { text-align: right; }

tr.myc-row { cursor: pointer; }
tr.myc-row:hover { background: rgba(128, 128, 128, 0.07); }
tr.myc-row-active td { background: rgba(128, 128, 128, 0.12); box-shadow: inset 3px 0 0 var(--color-accent); }
tr.myc-row-active td ~ td { box-shadow: none; }

/* Withdrawn rows dim, but the Restore control stays fully legible. */
tr.myc-row-withdrawn td { opacity: 0.55; }
tr.myc-row-withdrawn td.myc-cell-actions { opacity: 1; }

.myc-badge {
    display: inline-block;
    border-radius: 10px;
    padding: 1px 9px;
    font-size: 0.85em;
    border: 1px solid transparent;
    white-space: nowrap;
}
.myc-badge--ok { color: var(--color-success, #4cbf8f); border-color: var(--color-success, #4cbf8f); }
.myc-badge--wait { color: var(--color-accent); border-color: var(--color-accent); }
.myc-badge--gone { color: var(--color-danger, #c0392b); border-color: var(--color-danger, #c0392b); }

.myc-actbtn {
    background: none;
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    border-radius: 4px;
    padding: 2px 10px;
    cursor: pointer;
    font-size: 0.85em;
}
.myc-actbtn--danger { border-color: var(--color-danger, #c0392b); color: var(--color-danger, #c0392b); }
.myc-actbtn--accent { border-color: var(--color-accent); color: var(--color-accent); }

/* Share rows: the inviting upload entry points. The whole row is clickable. */
tr.myc-share-row { cursor: pointer; }
tr.myc-share-row td {
    background: linear-gradient(90deg, rgba(0, 168, 150, 0.14), rgba(0, 168, 150, 0.03) 55%, transparent);
    padding-top: 12px;
    padding-bottom: 12px;
}
tr.myc-share-row:hover td {
    background: linear-gradient(90deg, rgba(0, 168, 150, 0.24), rgba(0, 168, 150, 0.07) 55%, transparent);
}

.myc-share-flex {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.myc-share-label { font-weight: 600; color: var(--color-accent); }

/* Persona container while a subset fetch is in flight. */
#myc-personality.myc-persona-loading { opacity: 0.5; transition: opacity 0.15s ease; }

.myc-share-plus {
    display: inline-flex;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 1.5px solid var(--color-accent);
    align-items: center;
    justify-content: center;
    margin-right: 8px;
    font-weight: 700;
}


/* ============================================================
   Help Icon
   ============================================================ */

.help-icon {
    font-size: var(--text-h3);
    color: var(--color-accent);
    cursor: pointer;
    background: transparent;
    border: 1px solid var(--color-accent);
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    margin-left: 10px;
}

.help-icon:hover {
    background: var(--color-accent);
    color: var(--color-text-on-accent);
}


/* Global help button — sits on the right side of the main tab navigation.
   Same rounded box as the presentation-mode toggle beside it. */
.global-help-btn {
    margin-left: 0;
    margin-right: 18px;
    align-self: center;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    padding: 0;
    border-radius: 6px;
    border: 1px solid var(--color-border);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.global-help-btn:hover {
    background: var(--tab-hover-bg);
    color: var(--color-text-primary);
}

/* ============================================================
   Presentation mode — hide the header + tab menu (wide screens)
   ============================================================
   body.is-focus-mode lifts #app-chrome (header + tab nav) out of the flow
   and parks it above the viewport; the 6px #focus-peek strip stays at the
   top, and hovering the strip — or the chrome itself once it has slid in —
   brings the chrome back as an overlay. Pure CSS after the class flip. */

.app-chrome {
    /* No box of its own in normal mode: body's flex column still sees the
       header and nav as direct children. */
    display: contents;
}

.focus-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: center;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    /* Right-aligned with the help button, not trailing the last menu item. */
    margin-left: auto;
    margin-right: 10px;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: transparent;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.focus-toggle-btn:hover {
    color: var(--color-text-primary);
    background: var(--tab-hover-bg);
}

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

.focus-toggle-btn .focus-icon-show { display: none; }
body.is-focus-mode .focus-toggle-btn .focus-icon-show { display: block; }
body.is-focus-mode .focus-toggle-btn .focus-icon-hide { display: none; }

.focus-peek {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 6px;
    /* Below the chrome (940) so the chrome covers it once slid in. */
    z-index: 930;
    background: var(--color-accent);
    opacity: 0.35;
    cursor: n-resize;
    transition: opacity 0.15s ease;
}

.focus-peek:hover { opacity: 0.8; }

body.is-focus-mode .focus-peek { display: block; }

body.is-focus-mode .app-chrome {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    /* Below modals/overlays (1000+), above tab content. */
    z-index: 940;
    background: var(--color-bg-primary);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    transform: translateY(-100%);
    transition: transform 0.2s ease;
}

body.is-focus-mode .focus-peek:hover ~ .app-chrome,
body.is-focus-mode .app-chrome:hover,
body.is-focus-mode .app-chrome:focus-within,
body.is-focus-mode.is-peeking .app-chrome {
    transform: translateY(0);
}

/* Room for the strip so the content's first line is never under it. */
body.is-focus-mode { padding-top: 12px; }


/* ============================================================
   Control Bar (shared across tabs)
   ============================================================ */

/* One compact bar for every analysis tab (Semantic Space, Explore,
   Correlations, Video Analysis, Sessions, Timelines): a single row of small
   type, plain-weight labels and narrow inputs. The min-height keeps a bar that
   holds only a status line (Semantic Space, Sessions) the same height as one
   holding selects and toggles (Correlations). */
.control-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    min-height: 32px;
    padding: 5px 10px;
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    background-color: var(--control-bar-bg);
    margin-bottom: 10px;
    border-bottom: 1px solid var(--control-bar-border);
}

.control-bar h3 {
    margin: 0;
    color: var(--control-bar-text);
    font-size: var(--text-body);
}

.control-bar label {
    font-weight: var(--weight-normal);
    color: var(--control-bar-label);
}

.control-bar .dropdown {
    width: auto;
    padding: 2px 4px;
    font-size: var(--text-xs);
    background: var(--select-bg);
    border: 1px solid var(--select-border);
    color: var(--select-text);
}

.control-bar .toggle-group .toggle-option {
    padding: 3px 10px;
    font-size: var(--text-xs);
}

/* Checkbox + short caption, sized to sit flush with the selects. Also used
   by the Semantic Space side rail, hence not scoped to the bar. */
.bar-checkbox {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    font-weight: var(--weight-normal);
    color: var(--color-text-muted);
    cursor: pointer;
    user-select: none;
}

.bar-checkbox input[type="checkbox"] {
    margin: 0;
    cursor: pointer;
}


/* ============================================================
   Cards
   ============================================================ */

.panel-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 8px;
    padding: 15px;
    box-shadow: var(--shadow-sm);
}


/* ============================================================
   Tables
   ============================================================ */


.detail-row > td {
    overflow: hidden;
}


/* ============================================================
   Chips / Filter Pills
   ============================================================ */

.chip {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: var(--text-sm);
    cursor: pointer;
    background: var(--chip-bg);
    border: 1px solid var(--chip-border);
    color: var(--chip-text);
    transition: all 0.15s;
}

.chip.selected,
.chip.active {
    background: var(--chip-selected-bg);
    border-color: var(--chip-selected-border);
    color: var(--chip-selected-text);
}


/* ============================================================
   Tooltip
   ============================================================ */

.meta-tooltip {
    position: relative;
    cursor: help;
    text-decoration: underline dotted var(--color-text-muted);
}

button.meta-tooltip {
    cursor: pointer;
    text-decoration: none;
}

button.meta-tooltip:disabled {
    cursor: not-allowed;
}

.meta-tooltip::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: 100%;
    left: 0;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    padding: 6px 8px;
    border-radius: 4px;
    font-size: var(--text-xs);
    font-weight: var(--weight-normal);
    line-height: var(--leading-normal);
    white-space: pre-wrap;
    max-width: 280px;
    width: max-content;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
    z-index: 1000;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
    border: 1px solid var(--tooltip-border);
}

.meta-tooltip:hover {
    z-index: 1000;
}

.meta-tooltip:hover::after {
    opacity: 1;
    bottom: 110%;
}

/* Modifier: render the tooltip below the trigger instead of above. Use on
   buttons that sit near the top of an overflow-hidden container, where an
   upward tooltip would be clipped. */
.meta-tooltip.tooltip-below::after {
    bottom: auto;
    top: 100%;
}

.meta-tooltip.tooltip-below:hover::after {
    bottom: auto;
    top: 110%;
}

/* Modifier: anchor the tooltip to the trigger's right edge (extends leftward).
   Pairs with .tooltip-below for elements at the top-right of a container. */
.meta-tooltip.tooltip-right-anchored::after {
    left: auto;
    right: 0;
}

/* Modifier: a wider box for tooltips carrying a paragraph rather than a label.
   The default 280px turns an explanation into a tall narrow column. */
.meta-tooltip.tooltip-wide::after {
    max-width: 420px;
}

/* The (i) affordance next to a card title. Reveals what a worker does on hover
   or focus; keeps the title line uncluttered. */
.card-info {
    margin-left: 5px;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-normal);
}

.card-info.meta-tooltip {
    text-decoration: none;
    cursor: help;
}

.card-info:hover,
.card-info:focus-visible {
    color: var(--color-text-secondary);
}

/* floating_tooltip.js re-homes every tooltip onto #fyp-tooltip so it can escape
   clipping ancestors (the scrolling details panel, the overflow-hidden tab
   panes). It stamps this class on <html> once loaded; the ::after rendering
   above stays as the no-JS fallback and is suppressed here so only one tooltip
   is ever drawn. Keep the two visually identical. */
html.fyp-floating-tooltips .meta-tooltip::after {
    display: none;
}

#fyp-tooltip {
    position: fixed;
    top: -9999px;
    left: -9999px;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    padding: 6px 8px;
    border-radius: 4px;
    font-size: var(--text-xs);
    font-weight: var(--weight-normal);
    line-height: var(--leading-normal);
    white-space: pre-wrap;
    max-width: 280px;
    width: max-content;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
    z-index: 4000;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
    border: 1px solid var(--tooltip-border);
}

#fyp-tooltip.is-visible {
    opacity: 1;
}

/* Mirrors .meta-tooltip.tooltip-wide — floating_tooltip.js copies the modifier
   onto the shared element, so the two renderings stay identical. */
#fyp-tooltip.tooltip-wide {
    max-width: 420px;
}


/* ============================================================
   Custom ribbon tooltip (Timelines)
   Plotly's built-in tooltip can't render multi-line content inside
   a 50px-tall ribbon, so we draw our own positioned to the cursor.
   ============================================================ */

/* Hide Plotly's built-in hover label inside ribbons — we render our own. */
[id^="ribbon-"] .hoverlayer {
    display: none !important;
}

.ribbon-tooltip {
    position: fixed;
    display: none;
    z-index: 1000;
    pointer-events: none;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    border: 1px solid var(--tooltip-border);
    border-radius: 4px;
    padding: 6px 10px;
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
    max-width: 260px;
}

.ribbon-tooltip-title {
    font-weight: var(--weight-semibold);
    margin-bottom: 3px;
}

.ribbon-tooltip-line {
    color: var(--tooltip-text);
}

.ribbon-tooltip-badge {
    color: var(--tooltip-text);
    margin-top: 2px;
}


/* ============================================================
   Timelines — shared time-window (zoom) control in the control bar
   ============================================================ */

.timelines-zoom-control {
    display: flex;
    align-items: center;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.timelines-zoom-hint {
    color: var(--color-text-faint);
}

.timelines-zoom-window {
    display: flex;
    align-items: center;
    gap: 6px;
}

.timelines-zoom-window .font-medium {
    color: var(--color-text-primary);
}


/* ============================================================
   Plotly modebar — themed colors, fade in only on chart hover
   ============================================================ */

.timeline-chart-wrapper .modebar {
    opacity: 0;
    transition: opacity 0.15s ease;
}

.timeline-chart-wrapper:hover .modebar {
    opacity: 1;
}

.timeline-chart-wrapper .modebar-btn .icon path {
    fill: var(--color-text-faint) !important;
}

.timeline-chart-wrapper .modebar-btn:hover .icon path,
.timeline-chart-wrapper .modebar-btn.active .icon path {
    fill: var(--color-text-secondary) !important;
}


/* ============================================================
   Chart type badge (Timelines)
   ============================================================ */

.chart-type-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--text-xxs);
    font-weight: var(--weight-medium);
    font-family: var(--font-sans);
    letter-spacing: 0.02em;
    background: var(--chip-bg);
    border: 1px solid var(--chip-border);
    color: var(--color-text-secondary);
    text-decoration: none;
    cursor: help;
    user-select: none;
    white-space: nowrap;
}

.chart-type-badge.meta-tooltip {
    text-decoration: none;
}


/* ============================================================
   Collection detail layout and stats table
   ============================================================ */

.pe-edit-activity-section {
    padding: 10px 20px;
}

.stats-table {
    width: 100%;
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
}

.stats-table td {
    padding: 4px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-primary);
}

.stats-table td:last-child {
    text-align: right;
    font-weight: var(--weight-bold);
}


.hidden {
    display: none;
}


/* ============================================================
   Hover effects: Timelines chips and toggles, Video Analysis side panel
   ============================================================ */

/* Timelines — hover effects for interactive elements */
.filter-chip {
    transition: opacity 0.15s, transform 0.15s;
}

.filter-chip:hover {
    opacity: 1 !important;
    transform: scale(1.08);
}

#timelines-sidebar-toggle,
#explore-sidebar-toggle,
#explore-sidebar-toggle-2,
#va-left-toggle,
#va-right-toggle {
    transition: background 0.15s;
    border-radius: 4px;
}

#timelines-sidebar-toggle:hover,
#explore-sidebar-toggle:hover,
#explore-sidebar-toggle-2:hover,
#va-left-toggle:hover,
#va-right-toggle:hover {
    background: var(--color-bg-elevated) !important;
}

#timelines-sidebar-toggle:hover svg,
#explore-sidebar-toggle:hover svg,
#explore-sidebar-toggle-2:hover svg,
#va-left-toggle:hover svg,
#va-right-toggle:hover svg {
    stroke: var(--color-text-primary);
}

#timelines-charts-container button[id^="findings-toggle-"] {
    transition: background 0.15s, border-color 0.15s;
}

#timelines-charts-container button[id^="findings-toggle-"]:hover {
    background: var(--color-bg-hover) !important;
    border-color: var(--color-text-tertiary) !important;
}


/* ============================================================
   Video Analysis Tab Styles
   ============================================================ */

/* When nav-controls wraps to its own row, expand to full width so buttons centre */
#video_analysis .nav-controls {
    flex-grow: 1;
}

#viewer-metadata {
    display: block;
}

#viewer-metadata tbody {
    display: block;
}

#viewer-metadata tr.detail-section-header {
    display: block;
    cursor: pointer;
    background: var(--color-border);
}

#viewer-metadata tr.detail-section-header td {
    display: block;
    padding: 8px 10px;
    font-weight: var(--weight-bold);
    color: var(--color-text-primary);
    border-bottom: none;
}

#viewer-metadata tr.detail-row {
    display: flex;
    flex-wrap: wrap;
    padding: 6px 10px;
    border-bottom: 1px solid var(--color-border-subtle);
}

#viewer-metadata tr.detail-row:hover {
    background: var(--table-row-hover);
}

#viewer-metadata tr.detail-row td.detail-key {
    flex: 0 0 100%;
    padding: 0 0 2px 0;
    font-weight: var(--weight-bold);
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
    border-bottom: none;
    word-break: break-word;
}

#viewer-metadata tr.detail-row td.detail-val {
    flex: 0 0 100%;
    padding: 0;
    color: var(--color-text-secondary);
    border-bottom: none;
    word-break: break-word;
}

#viewer-metadata tr.detail-display-id {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 10px;
    background: var(--color-border-subtle);
}

#viewer-metadata tr.detail-display-id td {
    padding: 0;
    border-bottom: none;
}

/* Drill-down notice bar (Video Analysis): message on the left, dismiss on the
   right. Hidden with display:none, so the flex layout only applies when shown. */
#viewer-drilldown-notice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 8px 15px;
    background: var(--color-bg-elevated);
    border-bottom: 1px solid var(--color-border-subtle);
    color: var(--color-text-muted);
}

#viewer-drilldown-notice .viewer-notice-text {
    flex: 1 1 auto;
}

#viewer-drilldown-notice .viewer-notice-close {
    flex: 0 0 auto;
    background: none;
    border: none;
    padding: 0 4px;
    line-height: 1;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: var(--text-h3);
}

#viewer-drilldown-notice .viewer-notice-close:hover {
    color: var(--color-text-primary);
}


/* ============================================================
   Data Management: Define Study form rows
   ============================================================ */

.form-group-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}

.form-group-row label {
    flex: 1;
    text-align: left;
    margin-right: 15px;
    color: var(--color-text-secondary);
}

.form-group-row .control-input {
    flex: 1;
    max-width: 50%;
    text-align: right;
}


/* ============================================================
   Auth Pages (login / signup shared styles)
   ============================================================ */

.auth-form-container {
    padding: 30px 20px;
    border-bottom: 1px solid var(--color-border-subtle);
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

.auth-form label {
    display: block;
    margin-bottom: 5px;
    color: var(--color-text-tertiary);
}

.auth-form input[type="text"],
.auth-form input[type="password"] {
    width: 100%;
    padding: 10px;
    border-radius: 4px;
    border: 1px solid var(--color-border);
    background: var(--color-bg-input);
    color: var(--input-text);
}

.auth-submit-btn {
    background: var(--color-info);
    color: var(--white);
    border: none;
    padding: 12px;
    border-radius: 4px;
    cursor: pointer;
    font-weight: var(--weight-bold);
    margin-top: 10px;
}

.auth-submit-btn:hover {
    opacity: 0.9;
}

.auth-flash-info {
    background: rgba(91, 126, 152, 0.15);
    border: 1px solid var(--color-info);
    color: var(--color-info);
    padding: 10px;
    border-radius: 4px;
    margin-bottom: 20px;
    text-align: center;
}

.auth-flash-error {
    background: rgba(179, 90, 94, 0.15);
    border: 1px solid var(--color-danger);
    color: var(--color-danger);
    padding: 10px;
    border-radius: 4px;
    margin-bottom: 20px;
    text-align: center;
}


/* ============================================================
   Home Page Styles
   ============================================================ */


.widget-box {
    padding: 20px;
    border-bottom: 1px solid var(--color-border-subtle);
}

.widget-box h3 {
    margin-top: 0;
    color: var(--color-text-primary);
    font-size: var(--text-body);
    margin-bottom: 15px;
}

.slack-updates {
    background: var(--color-bg-inset);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 10px;
    max-height: 400px;
    overflow-y: auto;
}

.slack-message {
    padding: 10px;
    border-bottom: 1px solid var(--color-border-subtle);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}

.slack-message strong {
    color: var(--color-accent);
}

.slack-message span {
    color: var(--color-text-secondary);
}

.slack-message .timestamp {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin-top: 4px;
}

.note-callout {
    background: var(--color-bg-surface);
    border-left: 4px solid var(--color-save);
    padding: 15px 20px;
    border-radius: 0 4px 4px 0;
    margin-bottom: 15px;
}

.note-callout p {
    margin: 0;
    line-height: var(--leading-relaxed);
    color: var(--color-text-secondary);
}

.hero-title {
    color: var(--color-accent);
    margin-bottom: 20px;
    font-size: var(--text-hero);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    text-align: center;
}

.hero-subtitle {
    font-size: var(--text-body);
    line-height: var(--leading-relaxed);
    color: var(--color-text-primary);
    margin-bottom: 40px;
    text-align: center;
}


/* ============================================================
   Auth Header (index.html)
   ============================================================ */

.auth-header {
    display: flex;
    align-items: center;
    /* Tight vertical padding: the study dropdown (31px) is the tallest
       thing in here, so the header runs ~49px and the tab menu sits close
       under the brand. (The public mini-site's .public-header is taller;
       the two no longer match on purpose.) */
    padding: 9px 24px;
    background: var(--color-bg-primary);
}

#global-study-select {
    margin-left: 16px;
    max-width: 220px;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.auth-header-user {
    margin-left: auto;
    color: var(--color-text-secondary);
}

/* Theme toggle: an icon-only button beside the username. The icon shows
   the theme you would switch TO — sun while dark, moon while light. */
.theme-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin-left: 12px;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: transparent;
    color: var(--color-text-secondary);
    cursor: pointer;
    flex-shrink: 0;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.theme-toggle-btn:hover {
    color: var(--color-text-primary);
    background: var(--color-bg-elevated);
}

.theme-toggle-btn:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

.theme-toggle-btn .theme-icon-moon { display: none; }
[data-theme="light"] .theme-toggle-btn .theme-icon-moon { display: block; }
[data-theme="light"] .theme-toggle-btn .theme-icon-sun { display: none; }

.global-tasks-badge {
    margin-left: auto;
    margin-right: 12px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 12px;
    background: var(--color-bg-tertiary);
    color: var(--status-running);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    cursor: default;
    user-select: none;
}

.global-tasks-spinner {
    width: 12px;
    height: 12px;
    border: 2px solid var(--color-border);
    border-top-color: var(--status-running);
    border-radius: 50%;
    animation: global-spin 0.8s linear infinite;
}

@keyframes global-spin {
    to { transform: rotate(360deg); }
}

.global-tasks-count {
    font-variant-numeric: tabular-nums;
}

.app-brand {
    font-family: 'DM Sans', var(--font-sans);
    /* Matches .public-wordmark — see .auth-header. */
    font-size: var(--text-h3);
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--color-text-primary);
    text-decoration: none;
    cursor: pointer;
    transition: opacity 0.15s ease;
}

.app-brand:hover {
    opacity: 0.8;
}


/* ============================================================
   Utility Classes
   ============================================================ */

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

.text-secondary {
    color: var(--color-text-secondary);
}

.text-accent {
    color: var(--color-accent);
}

.text-danger {
    color: var(--color-danger);
}

.text-success {
    color: var(--color-success);
}

.text-info {
    color: var(--color-info);
}

.text-warning {
    color: var(--color-warning);
}

.bg-surface {
    background: var(--color-bg-surface);
}

.bg-elevated {
    background: var(--color-bg-elevated);
}

.bg-inset {
    background: var(--color-bg-inset);
}

.border-default {
    border: 1px solid var(--color-border);
}

.border-subtle {
    border-bottom: 1px solid var(--color-border-subtle);
}


/* ============================================================
   Loading Animation (Pacman)
   ============================================================ */

.fun-loader-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 30px;
}

.fun-loader {
    display: inline-block;
    position: relative;
    width: 80px;
    height: 80px;
}

.fun-loader div {
    position: absolute;
    border-radius: 50%;
    animation-timing-function: cubic-bezier(0, 1, 1, 0);
}

.fun-loader div:nth-child(1) {
    width: 80px;
    height: 80px;
    top: 0;
    left: 0;
    background: var(--color-gold);
    clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
    transform-origin: 50% 50%;
    animation: pacman-top 0.5s infinite alternate ease-in-out;
    z-index: 1;
}

.fun-loader div:nth-child(2) {
    width: 80px;
    height: 80px;
    top: 0;
    left: 0;
    background: var(--color-gold);
    clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0 100%);
    transform-origin: 50% 50%;
    animation: pacman-bottom 0.5s infinite alternate ease-in-out;
    z-index: 1;
}

.fun-loader div:nth-child(3),
.fun-loader div:nth-child(4),
.fun-loader div:nth-child(5) {
    width: 15px;
    height: 15px;
    background: var(--color-text-primary);
    top: 32.5px;
    left: 90px;
    animation: pacman-food 1.5s infinite linear;
    z-index: 0;
    opacity: 0;
}

.fun-loader div:nth-child(3) {
    animation-delay: 0s;
}
.fun-loader div:nth-child(4) {
    animation-delay: 0.5s;
}
.fun-loader div:nth-child(5) {
    animation-delay: 1.0s;
}

@keyframes pacman-top {
    0% { transform: rotate(-35deg); }
    100% { transform: rotate(0deg); }
}

@keyframes pacman-bottom {
    0% { transform: rotate(35deg); }
    100% { transform: rotate(0deg); }
}

@keyframes pacman-food {
    0% {
        transform: translateX(0);
        opacity: 1;
    }
    100% {
        transform: translateX(-60px);
        opacity: 0;
    }
}

.loading-text {
    margin-top: 15px;
    color: var(--color-gold);
    font-family: var(--font-mono);
    font-weight: var(--weight-bold);
    font-size: var(--text-h3);
    animation: pulse-text 1s infinite alternate;
}

@keyframes pulse-text {
    from { opacity: 0.7; }
    to { opacity: 1; }
}

/* Explanatory notice shown in place of the video player when an item has no
   downloaded media (e.g. YouTube long-form past the download duration cap). */
.viewer-video-notice {
    max-width: 360px;
    margin: 0 auto;
    padding: 24px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--text-body);
    line-height: var(--leading-relaxed);
}


/* ============================================================
   Accessibility: Reduced Motion
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}


/* ============================================================
   Filter panels, Explore / Correlations layout, drill-down popup,
   stop / log modals, scrollbars
   ============================================================ */

/* ── Filter panels (Explore / Video Analysis) ──
   Two collapsible levels: a section (schema section, e.g. "Content") holding
   groups (one per variable). Compact by design: small regular-weight type,
   tight padding. A section header turns accent when any of its variables
   filters; a group header turns blue and shows a badge with the number of
   ticked values (or the bound range for sliders). */
.filter-section {
    margin-bottom: 6px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    overflow: hidden;
}

.filter-section-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    cursor: pointer;
    user-select: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium, 500);
    line-height: 1.4;
    color: var(--color-text-primary);
    background: var(--color-border);
    transition: background 0.2s;
}

.filter-section-header.has-active-filter {
    background: var(--color-accent);
    color: var(--btn-primary-text) !important;
}

/* The ▸/▾ glyphs draw at about half their em box, so they are enlarged with a
   transform rather than font-size: the header row keeps its height. */
.filter-section-arrow,
.filter-group-arrow {
    display: inline-block;
    width: 18px;
    flex: 0 0 18px;
    font-size: 1.15em;
    line-height: 1;
    opacity: 0.9;
    text-align: center;
    transform: scale(1.6);
    transform-origin: center;
}

.filter-section-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filter-section-count {
    font-size: var(--text-xs);
    opacity: 0.9;
}

.filter-section-body {
    padding: 2px 6px;
    background: var(--color-bg-surface);
}

/* Group = one variable */
.filter-group {
    border-bottom: 1px solid var(--color-border-subtle);
}
.filter-group:last-child {
    border-bottom: none;
}

.filter-group-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 2px;
    cursor: pointer;
    user-select: none;
    font-size: var(--text-xs);
    font-weight: var(--weight-normal, 400);
    line-height: 1.4;
    color: var(--color-text-primary);
    border-radius: 3px;
    transition: color 0.15s, background 0.15s;
}
.filter-group-header:hover {
    background: var(--color-bg-elevated);
}
.filter-group-header:focus-visible {
    outline: 1px solid var(--color-accent);
    outline-offset: -1px;
}

.filter-group-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filter-group-badge {
    flex: 0 0 auto;
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.filter-group.has-active-filter > .filter-group-header {
    color: var(--color-info);
}
.filter-group.has-active-filter > .filter-group-header .filter-group-name {
    font-weight: var(--weight-medium, 500);
}

.filter-group-header-extra {
    margin-left: 2px;
}
/* The sort toggle only earns its place while the list is open. */
.filter-group:not(.is-open) > .filter-group-header .filter-group-header-extra {
    visibility: hidden;
}

.filter-group-body {
    padding: 2px 2px 8px 18px;
}

/* Sliders */
.filter-slider {
    margin: 10px 6px 4px;
}
.filter-slider-labels {
    display: flex;
    justify-content: space-between;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Checkbox lists */
.filter-checkbox-list {
    max-height: 150px;
    overflow-y: auto;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 3px;
    padding: 2px 4px;
}
.filter-checkbox-item {
    display: flex;
    align-items: center;
    gap: 5px;
    line-height: 1.35;
    padding: 1px 0;
}
.filter-checkbox-item input[type="checkbox"] {
    margin: 0;
    flex: 0 0 auto;
}
.filter-checkbox-item span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.filter-list-notice {
    color: var(--color-text-faint);
    padding: 4px 2px 1px;
    font-style: italic;
}

.filter-sort-toggle {
    background: none;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    color: var(--color-text-muted);
    cursor: pointer;
    font-size: var(--text-xs);
    font-family: var(--font-mono);
    line-height: 1;
    padding: 2px 5px;
    transition: color 0.15s, border-color 0.15s;
    flex-shrink: 0;
}
.filter-sort-toggle:hover {
    color: var(--color-text-primary);
    border-color: var(--color-text-muted);
}

/* ── Per-variable value search (capped filter dropdowns) ── */
.filter-value-search-wrap {
    margin: 4px 0;
}
.filter-value-search {
    width: 100%;
    box-sizing: border-box;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    color: var(--color-text-primary);
    font-family: var(--font-sans);
    padding: 3px 6px;
}
.filter-value-search::placeholder {
    color: var(--color-text-faint);
}
.filter-value-search-status {
    color: var(--color-text-faint);
    padding: 2px 2px 0;
}
.filter-checkbox-item--pinned {
    border-left: 2px solid var(--color-accent);
    padding-left: 3px;
}
.filter-value-search {
    font-size: var(--text-xs);
}
/* Hides items whose inline display:flex would defeat the hidden attribute. */
.fvs-hidden {
    display: none !important;
}


/* ── Explore chart drill-down affordance ── */
#explorer-v2-stats .js-plotly-plot .bars .point path,
#explorer-v2-stats .js-plotly-plot .trace.bars path {
    cursor: pointer !important;
}

#explorer-v2-stats .drill-hint {
    opacity: 0;
    transition: opacity 0.2s;
}

#explorer-v2-stats .stats-card:hover .drill-hint {
    opacity: 1;
}


/* ── Correlations tab layout ── */
.corr-status {
    color: var(--control-bar-info);
}

/* The bar itself is the shared .control-bar; only the Correlations-specific
   widths and groupings live here. */
.corr-scatter-controls {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.corr-control-label {
    color: var(--control-bar-label);
    margin-right: 0;
}

/* Fixed widths, scoped to the bar so they outrank `.control-bar .dropdown`:
   a <select> otherwise auto-sizes to its longest option, which pushed the bar
   onto a second row. Full variable names stay readable in the open dropdown
   and in the axis titles. */
.control-bar .corr-select {
    width: 150px;
}

.control-bar .corr-select-narrow {
    width: 105px;
}

/* Sized to sit flush with the selects on the same row. .btn-discreet sets its
   own font-size, so the size has to be restated here rather than left to the
   .text-xxs utility class. */
.control-bar .corr-control-disabled {
    opacity: 0.45;
}

.corr-control-disabled input {
    cursor: not-allowed;
}

.corr-bar-button {
    padding: 2px 8px;
    font-size: var(--text-xxs);
    line-height: 1.4;
}

/* Status / errors only - pushed right so it never shifts the knobs, which all
   read left-to-right. */
.corr-bar-status {
    margin-left: auto;
}

/* Observation count sits in the corner of the plot/table it describes. */
.corr-plot-wrap {
    position: relative;
    flex: 1 1 auto;
    min-height: 460px;
    display: flex;
    flex-direction: column;
}

.corr-obs-badge {
    position: absolute;
    top: 6px;
    left: 12px;
    z-index: 2;
    max-width: min(75%, 760px);
    color: var(--color-text-tertiary);
    line-height: var(--leading-normal);
    pointer-events: none;
}

/* Count first, then the view's note, flowing as one line that wraps. */
.corr-obs-badge #pca-point-count:not(:empty) {
    margin-right: 10px;
}

.corr-obs-badge .corr-caption-warning {
    color: var(--color-warning);
}

.help-text-extra {
    margin-top: 14px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border-subtle);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-relaxed);
}

.help-text-extra p {
    margin: 0 0 8px;
}

.corr-help-heading {
    margin: 12px 0 4px;
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

/* The app shell locks body scrolling, so a fixed `height: calc(100vh - 180px)`
   clipped the x-axis title and the caption outright on shorter viewports
   (Chrome with a bookmarks bar). The tab now scrolls internally instead: the
   plot keeps a workable minimum size and anything past the fold is reachable. */
.corr-main {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    gap: 20px;
}

.corr-viz-panel {
    flex: 1;
    min-width: 0;
    min-height: calc(100vh - 200px);
    display: flex;
    flex-direction: column;
    padding: 0;
}

.corr-plot {
    flex: 1 1 auto;
    min-height: 0;
}

.corr-banner {
    padding: 6px 12px;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    background: var(--color-bg-elevated);
    border-bottom: 1px solid var(--color-border-subtle);
}

.corr-banner-warn {
    color: var(--color-text-primary);
    border-left: 3px solid var(--color-warning);
}

/* Sortable Group-differences headers. */
.corr-stats-table th.corr-sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
    color: var(--color-text-muted);
}

.corr-stats-table th.corr-sortable:hover {
    color: var(--color-text-primary);
}

.corr-stats-table th.corr-sorted {
    color: var(--color-text-primary);
}

/* "What does this table show?" toggle per group-diff table */
.corr-table-explainer-toggle a {
    color: var(--color-link);
    text-decoration: none;
    white-space: nowrap;
}

.corr-table-explainer-toggle a:hover {
    text-decoration: underline;
}

.corr-table-explainer-toggle {
    margin: 0 0 6px;
    font-size: var(--text-xs);
}

/* Collapsed-by-default explanatory copy per group-diff table. */
.corr-explainer-body {
    margin-top: 8px;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.corr-explainer-body p {
    margin: 0 0 8px;
}

.corr-explainer-body p:last-child {
    margin-bottom: 0;
}

.corr-table-wrap .corr-table-explainer {
    margin: 0 0 10px;
    padding: 8px 12px;
    background: var(--color-bg-elevated);
    border-left: 3px solid var(--color-border-strong);
}

.corr-table-wrap {
    overflow: auto;
    padding: 12px 16px;
    height: 100%;
}

.corr-table-wrap h3 {
    margin: 16px 0 8px;
}

/* Group-differences panels: each of the two question-panels is a bordered
   section so their separation is unmistakable. */
.corr-table-wrap .corr-panel {
    margin: 0 0 24px;
    padding: 2px 16px 14px;
    border: 1px solid var(--color-border-subtle);
    border-radius: 6px;
}

.corr-table-wrap .corr-panel-title {
    margin: 14px 0 4px;
}

.corr-table-wrap .corr-panel-intro {
    margin: 0 0 8px;
    color: var(--color-text-secondary);
}

/* Combined "Key findings" report at the top of the Group-differences page */
.corr-table-wrap .corr-report {
    margin: 4px 0 20px;
    padding: 10px 14px;
    background: var(--color-bg-elevated);
    border-left: 3px solid var(--color-accent);
    color: var(--color-text-primary);
}

.corr-report .corr-report-title {
    margin-bottom: 4px;
}

.corr-report ul {
    margin: 0;
    padding-left: 18px;
}

.corr-report li {
    margin: 3px 0;
    line-height: var(--leading-normal);
}

/* On-demand AI interpretation inside the Key findings box */
.corr-report .corr-interp-actions {
    margin: 8px 0 0;
}

.corr-report .corr-interp {
    margin: 8px 0 0;
    padding-top: 8px;
    border-top: 1px solid var(--color-border-subtle);
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.corr-report .corr-interp-loading,
.corr-report .corr-interp-disclaimer {
    margin: 6px 0 0;
    color: var(--color-text-tertiary);
}

.corr-report .corr-interp-error {
    margin: 6px 0 0;
    color: var(--color-warning);
}

/* Dagger marking a comparison nested in collections (hover for the caveat) */
.corr-nested-badge {
    color: var(--color-warning);
    cursor: help;
    font-weight: var(--weight-semibold);
}

.corr-table-wrap .collection-table th {
    text-align: left;
    padding: 4px 10px;
    border-bottom: 1px solid var(--color-border-strong);
    color: var(--color-text-secondary);
}

.corr-table-wrap .collection-table td {
    padding: 3px 10px;
}


/* ── Correlations scatter drill-down affordance ── */
#pca-plot .js-plotly-plot .scatterlayer .trace .points path,
#pca-plot .scatterlayer .trace .points path {
    cursor: pointer !important;
}

/* ── Drill-down confirmation popup ── */
.drilldown-overlay {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--modal-overlay);
    backdrop-filter: blur(3px);
    opacity: 0;
    transition: opacity 0.2s ease;
}

.drilldown-overlay.visible {
    opacity: 1;
}

.drilldown-card {
    background: var(--modal-bg);
    border: 1px solid var(--modal-border);
    border-radius: 12px;
    padding: 24px 28px;
    max-width: 420px;
    width: 90%;
    box-shadow: var(--shadow-md);
    color: var(--modal-text);
    transform: translateY(8px) scale(0.97);
    transition: transform 0.2s ease;
}

.drilldown-overlay.visible .drilldown-card {
    transform: translateY(0) scale(1);
}

.drilldown-header {
    display: flex;
    align-items: center;
    gap: 10px;
}

.drilldown-icon {
    font-size: var(--text-h2);
    line-height: 1;
}

.drilldown-filter-preview {
    display: flex;
    align-items: center;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 8px 14px;
}

.drilldown-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

.drilldown-actions .btn {
    padding: 8px 20px;
    border-radius: 6px;
    cursor: pointer;
    font-size: var(--text-body);
    font-weight: var(--weight-medium);
    font-family: var(--font-sans);
    border: none;
    transition: opacity 0.15s;
}

.drilldown-actions .btn:hover {
    opacity: 0.85;
}


/* ── Stop confirmation overlay ── */
.stop-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--modal-overlay);
    backdrop-filter: blur(3px);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.stop-confirm-overlay.visible {
    pointer-events: auto;
    opacity: 1;
}

.stop-confirm-card {
    background: var(--modal-bg);
    border: 1px solid var(--modal-border);
    border-radius: 12px;
    padding: 24px 28px;
    max-width: 380px;
    width: 90%;
    box-shadow: var(--shadow-md);
    color: var(--modal-text);
    transform: translateY(8px) scale(0.97);
    transition: transform 0.2s ease;
}

.stop-confirm-overlay.visible .stop-confirm-card {
    transform: translateY(0) scale(1);
}


/* ── Log modal ── */
.log-modal-card {
    background: var(--modal-bg);
    border: 1px solid var(--modal-border);
    border-radius: 12px;
    padding: 24px 28px;
    max-width: 900px;
    width: 90%;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-md);
    color: var(--modal-text);
    transform: translateY(8px) scale(0.97);
    transition: transform 0.2s ease;
}

.log-modal-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}

.log-modal-toolbar #log-modal-run {
    flex: 1 1 260px;
    min-width: 0;
}

.log-modal-toolbar #log-modal-filter {
    flex: 0 1 180px;
    min-width: 0;
}

.stop-confirm-overlay.visible .log-modal-card {
    transform: translateY(0) scale(1);
}

.log-modal-body {
    flex: 1;
    overflow-y: auto;
    background: var(--color-bg-primary);
    border: 1px solid var(--color-border-subtle);
    border-radius: 4px;
    padding: 10px;
    white-space: pre-wrap;
    word-break: break-word;
    line-height: 1.4;
    min-height: 300px;
}


/* ── View log link ── */
.view-log-link {
    text-decoration: none;
    color: var(--color-text-tertiary);
    opacity: 0.7;
    transition: opacity 0.15s;
    cursor: pointer;
}

.view-log-link:hover {
    opacity: 1;
    text-decoration: underline;
}

/* ── Scrollbars ── */

/* Cross-browser thin, discreet scrollbars (matches macOS overlay feel on Windows/Linux) */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--color-border-strong) transparent;
}

*::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--color-border);
    border-radius: 8px;
    border: 2px solid transparent;
    background-clip: padding-box;
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--color-border-strong);
    border: 2px solid transparent;
    background-clip: padding-box;
}

*::-webkit-scrollbar-corner {
    background: transparent;
}

/* Scrollable panels whose content must be discoverable: force the scrollbar
   gutter and thumb to stay visible instead of the overlay/hidden default
   (e.g. the human-test variable list — hidden rows were being missed). */
.always-scroll {
    overflow-y: scroll;
    scrollbar-width: thin;
    scrollbar-color: var(--color-border) var(--color-bg-elevated);
}

.always-scroll::-webkit-scrollbar-track {
    background: var(--color-bg-elevated);
    border-radius: 8px;
}

.always-scroll::-webkit-scrollbar-thumb {
    background-color: var(--color-border-strong);
}


/* ============================================================
   Small-screen responsive layer
   (single breakpoint at 1100px — the narrowest width at which every main
   menu item plus the presentation-mode and help buttons fit on one line)
   ============================================================ */

/* Base utilities (apply at all widths) */
.mobile-only { display: none; }

.nav-toggle {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--tab-text);
    font-size: 22px;
    line-height: 1;
    padding: 10px 16px;
    align-items: center;
    justify-content: center;
}

.nav-toggle:hover {
    background: var(--tab-hover-bg);
}

.tab-nav-drawer {
    display: contents;
}

.current-tab-label {
    display: none;
    align-items: center;
    font-family: var(--font-sans);
    font-size: var(--text-body);
    font-weight: var(--weight-semibold);
    color: var(--color-text-primary);
    padding: 0 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.responsive-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 90;
}

.responsive-backdrop.is-visible {
    display: block;
}

.mobile-drawer-trigger {
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    color: var(--color-text-primary);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    padding: 6px 12px;
    border-radius: 4px;
    cursor: pointer;
}

.mobile-drawer-trigger:hover {
    background: var(--tab-hover-bg);
}

/* Two-level mobile nav: sub-page list under a top-level tab.
   Hidden at all widths by default; revealed inside the mobile breakpoint. */
.tab-subnav {
    display: none;
}

@media (max-width: 1100px) {
    /* ----- Utility visibility ----- */
    .mobile-only { display: inline-flex; }

    /* ----- Header: wrap so study dropdown gets its own row ----- */
    .auth-header {
        flex-wrap: wrap;
        padding: 5px 14px;
        gap: 8px;
        row-gap: 4px;
    }

    .app-brand {
        font-size: 1.15rem;
        flex: 0 0 auto;
    }

    #global-study-select {
        order: 99;
        flex: 1 1 100%;
        margin-left: 0;
        max-width: none;
    }

    .auth-header-user {
        margin-left: auto;
        font-size: var(--text-xs);
    }

    .global-tasks-badge {
        margin-left: 0;
    }

    /* ----- Presentation mode is a wide-screen feature ----- */
    .focus-toggle-btn,
    body.is-focus-mode .focus-peek {
        display: none;
    }

    body.is-focus-mode .app-chrome {
        display: contents;
        position: static;
        transform: none;
        box-shadow: none;
    }

    body.is-focus-mode { padding-top: 10px; }

    /* ----- Tab nav → hamburger drawer ----- */
    .tab-nav {
        position: relative;
        padding: 0;
        align-items: center;
    }

    .nav-toggle {
        display: inline-flex;
    }

    .current-tab-label {
        display: inline-flex;
        flex: 1 1 auto;
    }

    /* Hide the desktop sidebar/details toggles inside tab control bars */
    .desktop-only-toggle {
        display: none !important;
    }

    .tab-nav-drawer {
        display: flex;
        flex-direction: column;
        position: fixed;
        top: 0;
        left: 0;
        width: min(78vw, 300px);
        max-width: 320px;
        height: 100%;
        background: var(--color-bg-surface);
        border-right: 1px solid var(--color-border);
        z-index: 100;
        padding-top: 16px;
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        box-shadow: var(--shadow-lg);
    }

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

    .tab-nav-drawer .tab-button {
        border-right: none;
        border-bottom: 1px solid var(--color-border-subtle);
        text-align: left;
        width: 100%;
        padding: 14px 18px;
        font-size: var(--text-sm);
    }

    /* ----- Side panels → fixed slide-in drawers ----- */
    [data-mobile-drawer] {
        position: fixed !important;
        top: 0;
        bottom: 0;
        width: min(85vw, 340px) !important;
        max-width: 340px !important;
        flex: 0 0 auto !important;
        z-index: 100;
        background: var(--color-bg-surface);
        transition: transform 0.2s ease;
        box-shadow: var(--shadow-lg);
        overflow-y: auto;
        display: flex !important;
        flex-direction: column !important;
        height: 100% !important;
    }

    [data-mobile-drawer="left"] {
        left: 0;
        right: auto;
        transform: translateX(-100%);
    }

    [data-mobile-drawer="right"] {
        right: 0;
        left: auto;
        transform: translateX(100%);
    }

    [data-mobile-drawer].is-open {
        transform: translateX(0);
    }

    /* Make container rows reflow so panel removal collapses to full-width content */
    #explore .container,
    #correlations .corr-main,
    #video_analysis .container {
        flex-direction: column !important;
        height: auto !important;
        min-height: calc(100vh - 200px);
    }

    /* Slice 2 mobile drawer visibility is controlled by JS (setExplorerV2SliceMode) */

    /* Data Management / Admin / My stuff: sidebar becomes a left drawer */
    #data_management,
    #admin,
    #my_stuff {
        flex-direction: column !important;
    }

    /* The desktop sub-page sidebar is replaced by the two-level main drawer
       on small screens. Keep the markup (for desktop) but hide it. */
    .dm-sidebar {
        display: none !important;
    }

    .dm-content {
        padding: 14px;
    }

    /* ----- Two-level main nav: chevron + nested sub-pages ----- */
    .tab-nav-drawer .tab-button.has-subpages {
        position: relative;
        padding-right: 40px;
    }

    .tab-nav-drawer .tab-button.has-subpages::after {
        content: '\203A';
        position: absolute;
        right: 16px;
        top: 50%;
        font-size: 1.4em;
        line-height: 1;
        color: var(--color-text-secondary);
        transform: translateY(-50%) rotate(0deg);
        transform-origin: center center;
        transition: transform 0.15s ease, color 0.15s ease;
        pointer-events: none;
    }

    .tab-nav-drawer .tab-button.has-subpages.is-expanded::after {
        transform: translateY(-50%) rotate(90deg);
        color: var(--color-text-primary);
    }

    .tab-subnav {
        display: flex;
        flex-direction: column;
        list-style: none;
        margin: 0;
        padding: 0;
        background: var(--color-bg-primary);
        border-bottom: 1px solid var(--color-border-subtle);
    }

    .tab-subnav[hidden] {
        display: none;
    }

    .tab-subnav-item {
        padding: 12px 18px 12px 36px;
        font-size: var(--text-sm);
        font-weight: var(--weight-medium);
        color: var(--color-text-secondary);
        cursor: pointer;
        border-left: 3px solid transparent;
        transition: background 0.15s, color 0.15s, border-color 0.15s;
        user-select: none;
    }

    .tab-subnav-item:hover {
        background: var(--tab-hover-bg);
        color: var(--color-text-primary);
    }

    .tab-subnav-item.active {
        background: var(--tab-active-bg);
        color: var(--tab-active-text);
        border-left-color: var(--tab-active-border);
        font-weight: var(--weight-bold);
    }

    /* Reference links mirrored from a .dm-sidebar-item--link. Styled as a
       sub-nav row, but the anchor does the navigating. */
    .tab-subnav-linkitem > a {
        display: block;
        padding: 12px 18px 12px 36px;
        font-size: var(--text-sm);
        font-weight: var(--weight-medium);
        color: var(--color-text-secondary);
        text-decoration: none;
        border-left: 3px solid transparent;
        transition: background 0.15s, color 0.15s;
    }

    .tab-subnav-linkitem > a:hover {
        background: var(--tab-hover-bg);
        color: var(--color-text-primary);
    }

    /* Non-clickable section header inside the two-level drawer (mirrors
       the desktop .dm-sidebar-group). */
    .tab-subnav-group {
        padding: 10px 18px 3px 28px;
        font-size: var(--text-xs);
        font-weight: var(--weight-bold);
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--color-text-secondary);
        border-top: 1px solid var(--color-border-subtle);
        user-select: none;
        pointer-events: none;
    }

    .tab-subnav-group:first-child {
        border-top: none;
        padding-top: 8px;
    }

    /* ----- Modals: shrink to fit viewport ----- */
    .modal-content,
    .stop-confirm-card {
        max-width: min(92vw, 480px) !important;
        width: auto !important;
        margin: 16px auto;
        max-height: 90vh;
        overflow-y: auto;
    }

    #tagging-modal > div {
        width: min(92vw, 480px) !important;
        max-height: 90vh;
        overflow-y: auto;
    }

    /* ----- Tables: scroll horizontally rather than break layout ----- */
    .dm-content table,
    #home table,
    #my_studies table,
    #admin table {
        display: block;
        overflow-x: auto;
        max-width: 100%;
    }

    /* ----- Tab content: prevent horizontal layout-break ----- */
    .tab-pane {
        overflow-x: hidden;
    }

    /* ----- Login page ----- */

    .auth-form-container {
        padding: 24px 18px;
    }

    /* ----- Home tab + auth content: smaller hero on small screens ----- */
    .hero-title {
        font-size: var(--text-h2);
        margin-bottom: 14px;
    }

    .hero-subtitle {
        font-size: var(--text-body);
        margin-bottom: 24px;
    }

    #home > div {
        padding: 18px 14px !important;
    }

    #home img {
        max-width: 100% !important;
        height: auto;
    }

    /* Header brand can be tightened further on very narrow phones */
}

@media (max-width: 420px) {
    .app-brand {
        font-size: 1rem;
    }

    .auth-header {
        padding: 4px 10px;
    }

    /* Signup page: relax fixed paddings on very narrow phones */
    body > .container > div[style*="max-width: 400px"] {
        padding: 24px 18px !important;
    }
}

/* ===== Define Study modal: date-range control + chart edge handles ===== */
.study-date-input {
    width: 140px;
    padding: 2px 4px;
    font-variant-numeric: tabular-nums;
}

.study-date-step {
    padding: 1px 6px;
    line-height: var(--leading-tight);
    font-size: var(--text-xs);
    cursor: pointer;
}

.study-date-reset {
    padding: 2px 8px;
    cursor: pointer;
}

.study-date-step:disabled,
.study-date-reset:disabled {
    opacity: 0.45;
    cursor: default;
}

/* Draggable window edges over the daily-activities chart. The element is a wide
   invisible hit area; only the centered rule is painted. */
.study-range-handle {
    position: absolute;
    width: 13px;
    margin-left: -6px;
    cursor: ew-resize;
    z-index: 30;
    touch-action: none;
}

.study-range-handle__rule {
    position: absolute;
    left: 6px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--color-accent);
    opacity: 0.75;
}

.study-range-handle__grip {
    position: absolute;
    left: 2px;
    top: -1px;
    width: 9px;
    height: 9px;
    border-radius: 2px;
    background: var(--color-accent);
}

.study-range-handle:hover .study-range-handle__rule,
.study-daily-chart-wrap.dragging-range .study-range-handle__rule {
    opacity: 1;
    width: 2px;
}

/* Suppress text selection and hover cursors across the page while dragging an edge. */
body.study-range-dragging {
    cursor: ew-resize;
    user-select: none;
}

/* ===== Define Study modal: video coverage / sample mosaic ===== */
.study-set-viz-empty {
    color: var(--color-text-tertiary);
    padding: 12px;
    border: 1px dashed var(--color-border);
    border-radius: 4px;
    background: var(--color-bg-surface);
}

.study-viz__main {
    display: flex;
    align-items: stretch;
    gap: 0;
}

.study-viz__help.meta-tooltip {
    text-decoration: none;
    cursor: help;
    color: var(--color-text-muted);
    margin-left: 5px;
}

.study-viz__help.meta-tooltip:hover {
    color: var(--color-text-secondary);
}

/* Vertical "what is this?" sitting right against the mosaic. */
.study-viz__help--side.meta-tooltip {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    margin-left: 0;
    padding: 0 0 0 1px;
    display: flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    text-decoration: underline dotted var(--color-text-muted);
}

/* Keep the tooltip copy horizontal even though the trigger reads vertically. */
.study-viz__help--side::after {
    writing-mode: horizontal-tb;
    text-orientation: mixed;
}

.study-viz__box {
    display: flex;
    flex: 1 1 auto;
    /* Kept short enough that the mosaic plus its headline and legend stay
       under the sampling block beside it (116px), so the metrics row is
       sized by the controls rather than by the picture. Floor is ~62px: the
       vertical "what is this?" label runs the full height of the box and
       clips below that. */
    height: 66px;
    border: 1px solid var(--study-viz-border);
    border-radius: 4px;
    background: var(--study-viz-eligible-bg);
}

.study-viz__col {
    position: relative;
    height: 100%;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--study-viz-eligible-bg);
    border-right: 3px solid var(--color-border-strong);
    box-sizing: border-box;
    text-decoration: none;
    cursor: help;
}

.study-viz__col:last-child {
    border-right: none;
}

.study-viz__col:first-child {
    border-top-left-radius: 4px;
    border-bottom-left-radius: 4px;
}

.study-viz__col:last-child {
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
}

.study-viz__col--outframe {
    background: var(--study-viz-outframe-bg);
}

.study-viz__fill {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--study-viz-included);
    animation: study-viz-pulse 2s ease-in-out infinite;
}

.study-viz__col:first-child .study-viz__fill {
    border-bottom-left-radius: 4px;
}

.study-viz__col:last-child .study-viz__fill {
    border-bottom-right-radius: 4px;
}

@keyframes study-viz-pulse {
    0%, 100% { background: var(--study-viz-included); }
    50% { background: var(--study-viz-included-pulse); }
}

@media (prefers-reduced-motion: reduce) {
    .study-viz__fill { animation: none; }
}

/* Column label sits centred inside the field; the count is in the tooltip. */
.study-viz__collabel-in {
    position: relative;
    z-index: 1;
    padding: 0 2px;
    color: var(--study-viz-label-text);
    white-space: nowrap;
    text-align: center;
    pointer-events: none;
    text-shadow: 0 1px 2px var(--study-viz-label-halo), 0 0 2px var(--study-viz-label-halo);
}

/* Too narrow for horizontal text — rotate the label like a Plotly axis tick. */
.study-viz__col--vlabel .study-viz__collabel-in {
    writing-mode: vertical-rl;
    text-orientation: mixed;
}

.study-viz__col--tinylabel .study-viz__collabel-in {
    transform: scale(0.78);
    transform-origin: center;
}

.study-viz__headline {
    margin-top: 8px;
    color: var(--color-text-primary);
    font-variant-numeric: tabular-nums;
}

.study-viz__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 6px;
    color: var(--color-text-secondary);
}

.study-viz__seeded-note {
    margin-top: 6px;
    color: var(--color-text-tertiary);
    font-style: italic;
}

.study-viz__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.study-viz__swatch {
    width: 12px;
    height: 12px;
    border-radius: 2px;
    display: inline-block;
    border: 1px solid var(--study-viz-divider);
    box-sizing: border-box;
}

.study-viz__swatch--included { background: var(--study-viz-included); }
.study-viz__swatch--eligible { background: var(--study-viz-eligible-bg); }
.study-viz__swatch--outframe { background: var(--study-viz-outframe-bg); }


/* ============================================================
   Public Mini-Site (landing / about / guide / faq)
   Semantic tokens only — light theme works with no overrides.
   ============================================================ */

/* The global body rule locks the SPA shell to the viewport
   (height:100vh; overflow:hidden; padding:20px). Public pages are ordinary
   scrolling documents, so undo that when a public page is present. */
body:has(.public-page) {
    height: auto;
    overflow: visible;
    padding: 0;
    display: block;
}

.public-page {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--color-bg-primary);
    color: var(--color-text-primary);
}

/* ----- Header ----- */

.public-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 14px 28px;
    background: var(--color-bg-surface);
    border-bottom: 1px solid var(--color-border-subtle);
}

.public-wordmark {
    font-family: var(--font-sans);
    font-size: var(--text-h3);
    font-weight: var(--weight-bold);
    color: var(--color-accent);
    text-decoration: none;
    white-space: nowrap;
}

.public-nav {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

.public-nav-link {
    color: var(--color-text-secondary);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    padding: 6px 12px;
    border-radius: 6px;
}

.public-nav-link:hover {
    color: var(--color-text-primary);
    background: var(--color-bg-elevated);
}

.public-nav-link.active {
    color: var(--color-accent);
}

.public-nav-actions {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-left: 14px;
}

/* Anchor-friendly sizing for btn-primary/.btn-discreet used as links. */
.public-cta {
    display: inline-block;
    padding: 9px 18px;
    border-radius: 6px;
    border: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    text-decoration: none;
    cursor: pointer;
    text-align: center;
}

.public-nav-toggle {
    display: none;
    background: none;
    border: none;
    color: var(--color-text-primary);
    font-size: var(--text-h3);
    cursor: pointer;
    margin-left: auto;
    padding: 2px 8px;
}

/* ----- Page body ----- */

.public-main {
    flex: 1;
    width: 100%;
    max-width: 1060px;
    margin: 0 auto;
    padding: 30px 28px 70px;
}

.landing-hero {
    text-align: center;
    padding: 40px 0 20px;
}

.landing-hero-sub {
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
}

.landing-hero-ctas {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 6px;
}

/* ----- Video-mosaic hero (about page) -----
   A grid of opening frames from real corpus videos. The point is the mass and
   the variety, so the cells stay small and the gradient frame ties it to the
   public palette. Regenerate with scripts/make_video_grid_hero.py. */

.hero-mosaic {
    margin: 34px 0 0;
}

.hero-mosaic__frame {
    padding: 3px;
    border-radius: 16px;
    background: var(--fyr-grad);
    line-height: 0;
}

.hero-mosaic img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 13px;
}

/* On a phone the full grid shrinks to visual noise; crop it instead so the
   cells that do show stay readable. */
@media (max-width: 640px) {
    .hero-mosaic img {
        height: 230px;
        object-fit: cover;
    }
}

.landing-section {
    margin-top: 60px;
}

/* Anchor targets (audience cards jump here): keep the heading clear of the
   sticky public header when the browser scrolls to the fragment. */
.landing-section[id] {
    scroll-margin-top: 80px;
}

.landing-section-title {
    color: var(--color-text-heading);
    margin-bottom: 14px;
}

.landing-section-intro {
    color: var(--color-text-secondary);
    max-width: 720px;
    margin-bottom: 26px;
}

/* ----- Feature cards ----- */

.feature-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 18px;
}

.feature-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    padding: 22px;
}

.feature-card h3 {
    margin: 0 0 8px;
    color: var(--color-text-primary);
}

.feature-card p {
    margin: 0;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.feature-card-icon {
    font-size: var(--text-h2);
    color: var(--color-accent);
    margin-bottom: 10px;
    line-height: 1;
}

/* Icon images inside feature-card-icon (about page cards). */
.feature-card-img {
    height: 36px;
    width: auto;
    display: block;
}

/* ----- Public mini-site accent palette -----
   The app is colorful (category ribbons, semantic map); these accents bring a
   taste of that to the public pages without touching the app theme tokens. */

.public-page {
    --fyr-pink: #ec4899;
    --fyr-orange: #f97316;
    --fyr-teal: #0d9488;
    --fyr-violet: #8b5cf6;
    --fyr-grad: linear-gradient(100deg, var(--fyr-pink), var(--fyr-orange) 35%, var(--fyr-teal) 70%, var(--fyr-violet));
}

/* Scroll-reveal: hidden state only applies once JS has signalled it will run
   (html.js-reveal), so the pages work fully without JavaScript. */
.js-reveal .reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}

.js-reveal .reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .js-reveal .reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* ----- Logo strip ----- */

.logo-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 14px;
    margin-top: 26px;
}

/* Every partner mark is dark ink on transparent (or on white), so the tile
   keeps a light plate in BOTH themes — on the dark theme the logos would
   otherwise disappear into the surface. */
.logo-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 104px;
    padding: 14px 18px;
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    background: #fff;
}

/* Matching heights is what makes a mixed logo strip look deliberate. */
.logo-tile img {
    max-width: 100%;
    max-height: 62px;
    width: auto;
    height: auto;
    object-fit: contain;
}

/* ...except for a stacked lockup, which reads smaller than a wide one at the
   same height. Give it the extra room the tile already has. */
.logo-tile img.logo-tile__img--stacked {
    max-height: 76px;
}

/* On narrow tiles the wide marks are limited by width rather than height, so
   the bonus would make the stacked one the biggest instead of the smallest. */
@media (max-width: 560px) {
    .logo-tile img.logo-tile__img--stacked {
        max-height: 58px;
    }
}

/* ----- Story rows (participate page: short chunk + image, alternating) ----- */

.story-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    align-items: center;
    margin-bottom: 56px;
}

.story-row--flip .story-row__text {
    order: 2;
}

.story-row--flip .story-row__media {
    order: 1;
}

.story-row__text h2 {
    margin: 0 0 10px;
    color: var(--color-text-primary);
}

.story-row__text .prose {
    color: var(--color-text-secondary);
}

.story-row__eyebrow {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 4px 12px;
    border-radius: 999px;
    margin-bottom: 12px;
    color: #fff;
}

.story-row__media {
    position: relative;
}

.story-row__media img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 14px;
}

/* Transparency label overlaid on AI-generated imagery. */
.ai-image-tag {
    position: absolute;
    top: 10px;
    right: 10px;
    font-size: var(--text-xs);
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    pointer-events: none;
}

.story-row__eyebrow--pink { background: var(--fyr-pink); }
.story-row__eyebrow--orange { background: var(--fyr-orange); }
.story-row__eyebrow--teal { background: var(--fyr-teal); }
.story-row__eyebrow--violet { background: var(--fyr-violet); }

@media (max-width: 760px) {
    .story-row {
        grid-template-columns: 1fr;
        gap: 18px;
        margin-bottom: 44px;
    }

    .story-row--flip .story-row__text {
        order: 1;
    }

    .story-row--flip .story-row__media {
        order: 2;
    }
}

/* ----- Audience cards (landing "What would you like to do?") ----- */

.audience-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 18px;
}

.audience-card {
    display: block;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    padding: 22px;
    color: inherit;
    text-decoration: none;
    transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.audience-card:hover {
    border-color: var(--color-accent);
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
    text-decoration: none;
}

.audience-card h3 {
    margin: 0 0 8px;
    color: var(--color-text-primary);
}

.audience-card p {
    margin: 0;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.audience-card__eyebrow {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-accent);
    margin-bottom: 10px;
}

.audience-card__cta {
    margin-top: 14px;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--color-accent);
}

/* Per-card accent: colored top bar, matching eyebrow/CTA, tinted hover. */
.audience-card--pink,
.audience-card--teal,
.audience-card--violet {
    border-top: 4px solid var(--color-accent);
}

.audience-card--pink { border-top-color: var(--fyr-pink); }
.audience-card--teal { border-top-color: var(--fyr-teal); }
.audience-card--violet { border-top-color: var(--fyr-violet); }

.audience-card--pink .audience-card__eyebrow,
.audience-card--pink .audience-card__cta { color: var(--fyr-pink); }
.audience-card--teal .audience-card__eyebrow,
.audience-card--teal .audience-card__cta { color: var(--fyr-teal); }
.audience-card--violet .audience-card__eyebrow,
.audience-card--violet .audience-card__cta { color: var(--fyr-violet); }

.audience-card--pink:hover { border-color: var(--fyr-pink); }
.audience-card--teal:hover { border-color: var(--fyr-teal); }
.audience-card--violet:hover { border-color: var(--fyr-violet); }

/* Colored feature-card variants (about page: research questions + streams). */
.feature-card--pink { border-top: 4px solid var(--fyr-pink); }
.feature-card--orange { border-top: 4px solid var(--fyr-orange); }
.feature-card--teal { border-top: 4px solid var(--fyr-teal); }
.feature-card--violet { border-top: 4px solid var(--fyr-violet); }

/* ----- Feature tour rows (text + screenshot) ----- */

.feature-row {
    display: grid;
    grid-template-columns: minmax(240px, 2fr) 3fr;
    gap: 30px;
    align-items: center;
    margin-top: 42px;
}

.feature-row:nth-child(odd) .feature-row-text {
    order: 2;
}

.feature-row-text h3 {
    margin: 0 0 8px;
    color: var(--color-text-primary);
}

.feature-row-text .prose {
    color: var(--color-text-secondary);
    margin: 0;
}

.screenshot-frame {
    margin: 0;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    padding: 8px;
    box-shadow: var(--shadow-md);
}

.screenshot-frame img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 6px;
}

.screenshot-frame figcaption {
    padding: 8px 6px 2px;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    text-align: center;
}

/* When a screenshot asset is missing (not yet captured), hide the broken
   image and show a tasteful placeholder instead of a broken-image icon. */
.screenshot-frame--pending img {
    display: none;
}

.screenshot-frame--pending::before {
    content: "Screenshot coming soon";
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 16 / 10;
    border-radius: 6px;
    background: var(--color-bg-inset);
    border: 1px dashed var(--color-border);
    color: var(--color-text-faint);
    font-size: var(--text-sm);
}

/* ----- CTA band ----- */

.landing-cta-band {
    text-align: center;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 12px;
    padding: 40px 28px;
}

.participate-cta-note {
    color: var(--color-text-muted);
    margin-top: 12px;
}

.landing-cta-band h2 {
    margin-top: 0;
    color: var(--color-text-heading);
}

.landing-cta-band .prose {
    color: var(--color-text-secondary);
    max-width: 620px;
    margin: 0 auto 22px;
}

/* ----- Step list (guide) ----- */

.step-list {
    counter-reset: step;
    list-style: none;
    padding: 0;
    margin: 0;
    max-width: 760px;
}

.step-list li {
    counter-increment: step;
    position: relative;
    padding: 0 0 22px 52px;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.step-list li strong {
    color: var(--color-text-primary);
}

.step-list li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    color: var(--color-accent);
    font-weight: var(--weight-bold);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ----- Guide tab list + tips ----- */

.guide-tab-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 16px;
}

.guide-tab-item {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    padding: 18px 20px;
}

.guide-tab-item h3 {
    margin: 0 0 6px;
    color: var(--color-text-primary);
}

.guide-tab-item p {
    margin: 0;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

.guide-tips {
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
    padding-left: 22px;
    margin: 0 0 22px;
}

.guide-tips li {
    margin-bottom: 8px;
}

.guide-tips strong {
    color: var(--color-text-primary);
}

/* ----- Team cards (about) ----- */

.team-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 14px;
}

.team-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    padding: 16px 18px;
}

.team-card-photo {
    width: 84px;
    height: 84px;
    object-fit: cover;
    border-radius: 50%;
    margin-bottom: 12px;
    border: 1px solid var(--color-border-subtle);
}

.team-card-bio {
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
    margin: 8px 0 0;
}

.team-card-name {
    font-weight: var(--weight-semibold);
    color: var(--color-text-primary);
}

.team-card-role {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin-top: 3px;
}

.team-card-inst {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin-top: 6px;
}

/* ----- FAQ accordion (native details/summary) ----- */

.faq-accordion {
    max-width: 760px;
    margin-left: auto;
    margin-right: auto;
}

.faq-accordion details {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    margin-bottom: 12px;
    overflow: hidden;
}

.faq-accordion summary {
    cursor: pointer;
    padding: 16px 20px;
    font-weight: var(--weight-semibold);
    color: var(--color-text-primary);
    list-style: none;
    position: relative;
    padding-right: 44px;
}

.faq-accordion summary::-webkit-details-marker {
    display: none;
}

.faq-accordion summary::after {
    content: "+";
    position: absolute;
    right: 20px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-accent);
    font-size: var(--text-h3);
    line-height: 1;
}

.faq-accordion details[open] summary::after {
    content: "−";
}

.faq-accordion details p {
    margin: 0;
    padding: 0 20px 18px;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

/* ----- Auth pages (login / signup) on the public layout ----- */

.auth-page-card {
    max-width: 420px;
    margin: 40px auto 0;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    box-shadow: var(--shadow-md);
    overflow: hidden;
}

.auth-page-card .auth-form-container {
    border-bottom: none;
    padding: 30px 28px;
}

.auth-card-title {
    margin: 0 0 20px;
    color: var(--color-text-primary);
    text-align: center;
}

.auth-card-footer {
    text-align: center;
    margin-top: 15px;
}

/* ----- Landing: hero image + login card above the main title ----- */

.landing-login-hero {
    padding-top: 24px;
    max-width: 860px;
    margin: 0 auto;
}

.landing-login-hero__media {
    margin: 0;
}

.landing-login-hero__media img {
    display: block;
    width: 100%;
    height: auto;
}

@media (max-width: 1100px) {
    .landing-login-hero {
        padding-top: 10px;
    }
}

/* ----- Footer ----- */

.public-footer {
    border-top: 1px solid var(--color-border-subtle);
    background: var(--color-bg-surface);
    /* Roomier bottom than the sides: the "how to cite" box is the last thing
       in here now that the copyright line is gone, and it needs the breathing
       room that line used to provide. */
    padding: 36px 28px 34px;
    margin-top: 40px;
}

/* Align the footer's own blocks with .public-main's 1060px column — the legal
   line was already constrained, so unconstrained paragraphs sat wider than it. */
.public-footer > p {
    max-width: 1060px;
    margin-left: auto;
    margin-right: auto;
}

/* ----- "How to cite" (footer disclosure, /thehub section, FAQ entry) -----
   One partial (public/_how_to_cite.html) renders in all three places, so the
   styles have to survive both a bare page section and the .faq-accordion
   cascade. */

.cite-disclosure {
    max-width: 1060px;
    margin: 18px auto 0;
    border: 1px solid var(--color-border-subtle);
    border-radius: 10px;
    background: var(--color-bg-surface);
    overflow: hidden;
}

.cite-disclosure summary {
    cursor: pointer;
    padding: 12px 18px;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--color-text-primary);
    list-style: none;
    position: relative;
    padding-right: 40px;
}

.cite-disclosure summary::-webkit-details-marker {
    display: none;
}

.cite-disclosure summary::after {
    content: "+";
    position: absolute;
    right: 18px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-accent);
    line-height: 1;
}

.cite-disclosure[open] summary::after {
    content: "\2212";
}

.cite-disclosure__body {
    padding: 0 18px 16px;
}

.cite-blocks {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.cite-item {
    border: 1px solid var(--color-border-subtle);
    border-radius: 8px;
    background: var(--color-bg-inset);
    overflow: hidden;
}

.cite-item__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-bg-elevated);
}

.cite-item__label {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide, 0.08em);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.cite-copy {
    flex: none;
    padding: 4px 12px;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: var(--color-bg-surface);
    color: var(--color-text-secondary);
    font-size: var(--text-xs);
    font-family: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.cite-copy:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.cite-copy--done {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

/* .cite-text is a div, not a p, so the .faq-accordion "details p" padding
   rule cannot reach it. */
.cite-text {
    margin: 0;
    padding: 12px;
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-relaxed);
}

.cite-code {
    margin: 0;
    padding: 12px;
    overflow-x: auto;
    color: var(--color-text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.6;
    white-space: pre;
}

.cite-note {
    margin: 0;
    color: var(--color-text-muted);
    line-height: var(--leading-relaxed);
}

/* Inside a FAQ answer the shared "details p" padding would double up. */
.faq-accordion .cite-blocks p.cite-note {
    padding: 0;
}

/* ----- Public mini-site: responsive ----- */

@media (max-width: 860px) {
    .public-nav-toggle {
        display: block;
    }

    .public-nav {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        background: var(--color-bg-surface);
        border-bottom: 1px solid var(--color-border-subtle);
        padding: 10px 16px 16px;
        gap: 4px;
    }

    .public-nav.open {
        display: flex;
    }

    .public-nav-actions {
        margin-left: 0;
        margin-top: 8px;
        justify-content: flex-start;
        flex-wrap: wrap;
    }

    .feature-row,
    .feature-row:nth-child(odd) {
        grid-template-columns: 1fr;
        gap: 14px;
    }

    .feature-row:nth-child(odd) .feature-row-text {
        order: 0;
    }

    .landing-hero {
        padding-top: 20px;
    }

    .landing-section {
        margin-top: 44px;
    }

    .public-main {
        padding: 20px 18px 50px;
    }
}


/* ============================================================
   Public Mini-Site — researcher narrative components
   (kicker, pull-quote, comparison, can/can't)
   Semantic tokens only.
   ============================================================ */

/* "What is data donation?" link under the hero subtitle */
.hero-donation-link {
    text-align: center;
    margin: 4px 0 0;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}

/* Two-column comparison (donated feeds vs platform libraries) */
.compare-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    margin-top: 24px;
}

.compare-col {
    border-radius: 12px;
    padding: 24px;
    border: 1px solid var(--color-border-subtle);
    background: var(--color-bg-surface);
}

.compare-col--accent {
    border-color: var(--color-accent);
    background: var(--color-bg-elevated);
}

.compare-col h3 {
    margin: 0 0 4px;
    color: var(--color-text-primary);
}

.compare-col .compare-sub {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin: 0 0 16px;
}

.compare-col ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.compare-col li {
    position: relative;
    padding: 6px 0 6px 22px;
    color: var(--color-text-secondary);
    line-height: var(--leading-normal);
}

.compare-col li::before {
    content: "—";
    position: absolute;
    left: 0;
    color: var(--color-text-faint);
}

.compare-col--accent li::before {
    content: "→";
    color: var(--color-accent);
}

/* Can / can't tell us cards */
.canuse-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    margin-top: 24px;
}

.canuse-card {
    border-radius: 12px;
    padding: 24px;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
}

.canuse-head {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: var(--weight-semibold);
    margin-bottom: 14px;
    color: var(--color-text-primary);
}

.canuse-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    line-height: 1;
}

.canuse-badge--yes {
    background: color-mix(in srgb, var(--color-success) 20%, transparent);
    color: var(--color-success);
}

.canuse-badge--no {
    background: color-mix(in srgb, var(--color-danger) 20%, transparent);
    color: var(--color-danger);
}

.canuse-card ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.canuse-card li {
    position: relative;
    padding: 6px 0 6px 24px;
    color: var(--color-text-secondary);
    line-height: var(--leading-normal);
}

.canuse-card--yes li::before {
    content: "✓";
    position: absolute;
    left: 0;
    color: var(--color-success);
    font-weight: var(--weight-bold);
}

.canuse-card--no li::before {
    content: "✕";
    position: absolute;
    left: 0;
    color: var(--color-danger);
    font-weight: var(--weight-bold);
}

@media (max-width: 1100px) {
    .compare-grid,
    .canuse-grid {
        grid-template-columns: 1fr;
    }
}


/* ============================================================
   "See it in action" — the five pipeline steps (Ingest…Share)
   as a tabbed stepper. A row of numbered step-pills is both the
   pipeline summary and the tab navigation; only the selected
   stage's panel is shown below. CSS-only tabs: hidden radio
   inputs drive pill state and panel visibility, so the component
   needs no JS on either the public landing page or the app shell.
   ============================================================ */

.see-tabs {
    margin-top: 22px;
}

/* Visually hidden but focusable, so keyboard users can arrow
   between steps via the radio group. */
.see-tabs__input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.see-tabs__pills {
    display: grid;
    /* --see-step-count is set inline by _pipeline_tour.html: the logged-in
       guide renders only the stages the account can reach, so the column
       count is not always five. */
    grid-template-columns: repeat(var(--see-step-count, 5), 1fr);
    gap: 14px;
}

/* A single visible stage has nothing to step between — drop the pill row and
   show the panel on its own. */
.see-tabs--single .see-tabs__pills {
    display: none;
}

.see-tab-pill {
    position: relative;
    display: block;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 12px;
    padding: 22px 16px;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.see-tab-pill:hover {
    border-color: var(--color-border);
}

.see-tab-pill__num {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--color-accent);
    color: var(--btn-primary-text, #fff);
    font-weight: var(--weight-bold);
    margin-bottom: 12px;
}

.see-tab-pill__title {
    display: block;
    margin-bottom: 6px;
    color: var(--color-text-primary);
    font-size: var(--text-body);
}

.see-tab-pill__desc {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    line-height: var(--leading-normal);
}

/* Connector arrows between pills (desktop only) */
.see-tab-pill:not(:last-child)::after {
    content: "→";
    position: absolute;
    right: -12px;
    top: 34px;
    transform: translateX(50%);
    color: var(--color-text-faint);
    z-index: 1;
}

/* Selected pill */
#see-tab-ingest:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-ingest"],
#see-tab-enrich:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-enrich"],
#see-tab-annotate:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-annotate"],
#see-tab-analyse:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-analyse"],
#see-tab-share:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-share"] {
    border-color: var(--color-accent);
    background: var(--color-bg-elevated, var(--color-bg-surface));
}

/* Forward keyboard focus from the hidden radio to its pill */
#see-tab-ingest:focus-visible ~ .see-tabs__pills .see-tab-pill[for="see-tab-ingest"],
#see-tab-enrich:focus-visible ~ .see-tabs__pills .see-tab-pill[for="see-tab-enrich"],
#see-tab-annotate:focus-visible ~ .see-tabs__pills .see-tab-pill[for="see-tab-annotate"],
#see-tab-analyse:focus-visible ~ .see-tabs__pills .see-tab-pill[for="see-tab-analyse"],
#see-tab-share:focus-visible ~ .see-tabs__pills .see-tab-pill[for="see-tab-share"] {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* Panels: only the selected stage's content is shown */
.see-panel {
    display: none;
    margin-top: 26px;
}

#see-tab-ingest:checked ~ .see-panels #see-panel-ingest,
#see-tab-enrich:checked ~ .see-panels #see-panel-enrich,
#see-tab-annotate:checked ~ .see-panels #see-panel-annotate,
#see-tab-analyse:checked ~ .see-panels #see-panel-analyse,
#see-tab-share:checked ~ .see-panels #see-panel-share {
    display: block;
}

.see-panel__lead {
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
    margin: 0 0 8px;
    max-width: 760px;
}

/* Reuse .feature-row inside a panel, but keep the image always on the
   right (cancel the alternating order used by the flat tour) and tighten
   the vertical rhythm. */
.see-panel .feature-row {
    margin-top: 20px;
    align-items: start;
}

.see-panel .feature-row .feature-row-text {
    order: 0;
}

.see-panel .feature-row .feature-row-text .prose {
    margin: 0;
}

/* Small screens: stacked pills break the tab metaphor, so the stepper
   degrades into a linear walkthrough — every panel is shown, interleaved
   directly after its own pill, and the reader steps forward by scrolling.
   The wrappers are flattened with display:contents so pills and panels
   become siblings that can interleave via flex order. */
@media (max-width: 1100px) {
    .see-tabs {
        display: flex;
        flex-direction: column;
        gap: 26px;
    }

    .see-tabs__pills,
    .see-panels {
        display: contents;
    }

    .see-tab-pill {
        cursor: default;
        pointer-events: none;
    }

    .see-tab-pill:not(:last-child)::after {
        content: none;
    }

    .see-tab-pill[for="see-tab-ingest"]   { order: 1; }
    #see-panel-ingest                     { order: 2; }
    .see-tab-pill[for="see-tab-enrich"]   { order: 3; }
    #see-panel-enrich                     { order: 4; }
    .see-tab-pill[for="see-tab-annotate"] { order: 5; }
    #see-panel-annotate                   { order: 6; }
    .see-tab-pill[for="see-tab-analyse"]  { order: 7; }
    #see-panel-analyse                    { order: 8; }
    .see-tab-pill[for="see-tab-share"]    { order: 9; }
    #see-panel-share                      { order: 10; }

    .see-panel {
        display: block;
        margin-top: 0;
    }

    /* No selection on mobile — neutralize the selected/focus pill state */
    #see-tab-ingest:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-ingest"],
    #see-tab-enrich:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-enrich"],
    #see-tab-annotate:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-annotate"],
    #see-tab-analyse:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-analyse"],
    #see-tab-share:checked ~ .see-tabs__pills .see-tab-pill[for="see-tab-share"] {
        border-color: var(--color-border-subtle);
        background: var(--color-bg-surface);
        outline: none;
    }
}


/* ============================================================
   Sessions tab — session list, strip, episode inspector
   ============================================================ */

/* Thin horizontal coverage bar inside a list row */
.sess-cov-bar {
    width: 64px;
    height: 6px;
    border-radius: 3px;
    background: var(--color-border-subtle);
    overflow: hidden;
    display: inline-block;
    vertical-align: middle;
}

.sess-cov-fill {
    display: block;
    height: 100%;
    background: var(--color-accent);
}

/* Episode selector chips */
.sess-chip {
    padding: 5px 10px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg-surface);
    cursor: pointer;
    display: inline-flex;
    flex-direction: column;
    gap: 2px;
}

.sess-chip:hover { background: var(--color-bg-elevated); }

.sess-chip.active {
    border-color: var(--color-accent);
    background: var(--color-bg-elevated);
}

.sess-badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border-subtle);
    color: var(--color-text-secondary);
}

/* Admin "all sessions" view: a row the selected study does not contain. */
.sess-outside td { color: var(--color-text-muted); }
.sess-outside-badge {
    margin-left: 6px;
    color: var(--color-text-tertiary);
    font-weight: normal;
    vertical-align: middle;
}
.sess-scope-toggle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--color-text-secondary);
    cursor: pointer;
    white-space: nowrap;
}

/* A run whose videos are all by one creator — the donor sat on one account
   rather than being served a topic assembled across creators. */
.sess-solo-creator {
    color: var(--color-warning);
}

/* Off-theme videos a binge tolerated — never hidden, so a long binge cannot
   present itself as an unbroken run. */
.sess-skipped {
    color: var(--color-text-muted);
}

/* The within-binge trend line, on its own row under the binge's metrics. */
.sess-trend {
    display: block;
    margin-top: 3px;
    color: var(--color-text-muted);
}

/* Full play-sequence table rows */
.sess-play-row { border-bottom: 1px solid var(--color-border-subtle); }
.sess-play-row td { padding: 3px 8px; vertical-align: top; }
.sess-play-row.in-episode { background: var(--color-bg-elevated); }


/* Session list as a real table: sticky always-visible header, sortable
   columns, row selection mirroring the old .sess-row states. */
.sess-table {
    width: 100%;
    border-collapse: collapse;
}

.sess-table th {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--table-header-bg);
    color: var(--color-text-secondary);
    text-align: left;
    padding: 6px 10px;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
    user-select: none;
}

.sess-table th.sortable { cursor: pointer; }
.sess-table th.sortable:hover { color: var(--color-text-primary); }
.sess-table th.sorted { color: var(--color-text-primary); }

.sess-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--color-border-subtle);
    vertical-align: middle;
}

.sess-table tbody tr { cursor: pointer; }
.sess-table tbody tr:hover { background: var(--color-bg-elevated); }

.sess-table tbody tr.active {
    background: var(--color-bg-elevated);
    box-shadow: inset 3px 0 0 var(--color-accent);
}

/* Collapsible range-filter panel above the session table */
.sess-filters {
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-bg-elevated);
}

.sess-filters-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 5px 14px 5px 8px;
}

/* flex:1 + text-align:left — the whole header strip left of "Clear" is the
   toggle's hit area, so the label and the empty space beside it collapse the
   panel too, not just the caret glyph. */
.sess-filters-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    min-width: 0;
    padding: 4px 6px;
    background: none;
    border: none;
    text-align: left;
    color: var(--color-text-secondary);
    font-family: var(--font-sans);
    cursor: pointer;
}

.sess-filters-toggle:hover { color: var(--color-text-primary); }

.sess-filters-toggle:hover .sess-filters-caret { color: var(--color-text-primary); }

/* Sized above the button's own text-xs: at the label's size the glyph reads
   as punctuation rather than as the control it is. */
.sess-filters-caret {
    display: inline-block;
    width: 14px;
    font-size: var(--text-sm);
    line-height: 1;
    color: var(--color-text-tertiary);
}

.sess-filters-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px 18px;
    padding: 2px 14px 10px;
}

.sess-filter-row { min-width: 0; }

.sess-filter-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 1px;
}

.sess-filter-label { color: var(--color-text-tertiary); }

.sess-filter-val {
    color: var(--color-text-muted);
    white-space: nowrap;
}

.sess-filter-val.is-active {
    color: var(--color-accent);
    font-weight: var(--weight-medium);
}

/* Dual-handle slider: two stacked native range inputs over one shared track.
   The inputs are transparent (track hidden) and click-through except for
   their thumbs; the visible track + selected-range fill are separate divs. */
.sess-dual-range {
    position: relative;
    height: 18px;
}

.sess-dual-track,
.sess-dual-fill {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    height: 4px;
    border-radius: 2px;
    pointer-events: none;
}

.sess-dual-track {
    left: 0;
    right: 0;
    background: var(--color-border);
}

.sess-dual-fill { background: var(--color-accent); }

.sess-dual-range input[type="range"] {
    position: absolute;
    inset: 0;
    width: 100%;
    margin: 0;
    background: none;
    -webkit-appearance: none;
    appearance: none;
    pointer-events: none;
}

.sess-dual-range input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    pointer-events: auto;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--color-text-secondary);
    border: 2px solid var(--color-bg-elevated);
    box-shadow: 0 0 0 1px var(--color-border);
    cursor: ew-resize;
}

.sess-dual-range input[type="range"]::-moz-range-thumb {
    pointer-events: auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-text-secondary);
    border: 2px solid var(--color-bg-elevated);
    box-shadow: 0 0 0 1px var(--color-border);
    cursor: ew-resize;
}

.sess-dual-range input[type="range"]::-webkit-slider-thumb:hover,
.sess-dual-range input[type="range"]:focus::-webkit-slider-thumb {
    background: var(--color-accent);
}

.sess-dual-range input[type="range"]::-moz-range-thumb:hover,
.sess-dual-range input[type="range"]:focus::-moz-range-thumb {
    background: var(--color-accent);
}

.sess-dual-range input[type="range"]::-moz-range-track { background: none; }

/* Free-text session search: lives in the filter panel header area, outside
   the rebuildable slider grid. */
.sess-search-wrap { padding: 0 14px 8px; }

.sess-search {
    width: 100%;
    padding: 5px 9px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg-primary);
    color: var(--color-text-primary);
    font-family: var(--font-sans);
}

.sess-search::placeholder { color: var(--color-text-muted); }

.sess-search:focus {
    outline: none;
    border-color: var(--color-accent);
}

.sess-search:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* The "session max of variable" filter row's picker + binges-only knob */
.sess-varmax-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 2px 0 4px;
}

.sess-varmax-select {
    flex: 1;
    min-width: 0;
    padding: 3px 6px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg-primary);
    color: var(--color-text-primary);
    font-family: var(--font-sans);
}

.sess-varmax-scope {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--color-text-tertiary);
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
}

.sess-varmax-scope input { accent-color: var(--color-accent); cursor: pointer; }

.sess-varmax-scope:has(input:disabled) {
    opacity: 0.5;
    cursor: not-allowed;
}

.sess-varmax-scope:has(input:checked) { color: var(--color-accent); }

/* Binge card + its collapsible min/max panel stack in one column */
.sess-seq-wrap {
    display: flex;
    flex-direction: column;
}

.sess-seq-wrap .sess-chip { width: 100%; }

/* The "▸ (more info)" toggle inside a card / the detail header */
.sess-more-toggle {
    color: var(--color-text-muted);
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
}

.sess-more-toggle:hover { color: var(--color-text-secondary); }

.sess-more-caret { display: inline-block; }

/* Collapsed-by-default per-variable min/max list (label + range columns) */
.sess-minmax {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 1px 10px;
    padding: 6px 10px 8px;
    border: 1px solid var(--color-border-subtle);
    border-top: none;
    border-radius: 0 0 4px 4px;
    background: var(--color-bg-elevated);
    color: var(--color-text-tertiary);
}

.sess-minmax-label { color: var(--color-text-muted); }

/* Clickable variable names inside the min/max panels — they toggle the
   session line plot above the strip. */
.sess-varplot-link { cursor: pointer; }

.sess-varplot-link:hover {
    color: var(--color-text-secondary);
    text-decoration: underline;
}

.sess-varplot-link.is-plotted { color: var(--color-accent); }

/* The per-variable line plot above the session strip */
.sess-varplot {
    padding: 4px 0 2px;
    border-bottom: 1px dashed var(--color-border-subtle);
    margin-bottom: 4px;
}

.sess-varplot-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-bottom: 2px;
}

.sess-varplot-close { padding: 0 6px; line-height: 1.2; }

.sess-minmax-title {
    color: var(--color-text-secondary);
    font-weight: var(--weight-medium);
    padding-bottom: 2px;
}

/* Pagination footer under the session table */
.sess-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 14px;
    border-top: 1px solid var(--color-border-subtle);
}


/* Low-entropy sequence chips: dashed border mirrors the dashed strip bands. */
.sess-chip--seq { border-style: dashed; }
.sess-chip--seq.active {
    border-color: var(--color-text-secondary);
    border-style: dashed;
}


/* Sequence selector entries (stacked column of binge / low-entropy cards)
   + the shared single-video player pane. */
.sess-seq-entry {
    width: 100%;
    text-align: left;
    align-items: flex-start;
}

.sess-player-pane {
    border: 1px solid var(--color-border-subtle);
    border-radius: 6px;
    background: var(--color-bg-surface);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sess-player-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
}

.sess-player-nav {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.sess-player-media {
    height: 420px;
    background: var(--color-bg-elevated);
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.sess-player-media video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: var(--color-bg-elevated);
}

.sess-player-meta {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.sess-meta-row {
    display: flex;
    gap: 10px;
    align-items: baseline;
}

.sess-meta-label {
    flex: 0 0 90px;
    color: var(--color-text-tertiary);
}


/* Context steps in the sequence player: unmistakably NOT part of the sequence. */
.sess-player-pane.is-context {
    border-style: dashed;
    border-color: var(--color-warning);
}

.sess-context-banner {
    padding: 6px 10px;
    border: 1px dashed var(--color-warning);
    border-radius: 4px;
    color: var(--color-warning);
    background: var(--color-bg-elevated);
}

/* Off-theme steps: plays inside the sequence's span that the detector
   skipped — shown while stepping, but flagged as not part of it. Same idea
   as the context treatment, deliberately distinct (dotted, info colour). */
.sess-player-pane.is-offtheme {
    border-style: dotted;
    border-color: var(--color-info);
}

.sess-offtheme-banner {
    padding: 6px 10px;
    border: 1px dotted var(--color-info);
    border-radius: 4px;
    color: var(--color-info);
    background: var(--color-bg-elevated);
}


/* ============================================================
   Semantic Space
   ============================================================ */

/* ── Disclosure link (collection-trajectory controls) ── */
.ss-disclosure {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: transparent;
    border: none;
    padding: 2px 0;
    color: var(--color-accent);
    font-family: var(--font-sans);
    cursor: pointer;
}

.ss-disclosure:hover {
    text-decoration: underline;
}

.ss-disclosure-caret {
    display: inline-block;
    transition: transform 0.15s ease;
}

.ss-disclosure[aria-expanded="true"] .ss-disclosure-caret {
    transform: rotate(90deg);
}

/* The disclosed row's layout lives here rather than inline: an inline
   `display` wins over the `hidden` attribute, so the row would stay visible
   while collapsed. */
.ss-traj-controls {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 10px;
}

.ss-traj-controls[hidden] {
    display: none;
}

/* WebGL-unavailable notice above the map (the dots are a WebGL trace). */
.ss-webgl-notice {
    padding: 8px 14px;
    margin-bottom: 10px;
    border-radius: 4px;
    color: var(--color-text-primary);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-warning);
    line-height: var(--leading-normal);
}

.ss-webgl-notice[hidden] {
    display: none;
}

/* ── Panel layout: map column + side rail ── */
.ss-panel {
    flex: 1;
    min-height: 0;
    flex-direction: row;
    padding: 0;
    overflow: hidden;
}

.ss-map-col {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.ss-side {
    flex: 0 0 280px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
    border-left: 1px solid var(--color-border-subtle);
    background: var(--color-bg-elevated);
    color: var(--control-bar-info);
}

.ss-side-section {
    padding: 12px 14px;
    border-top: 1px solid var(--color-border-subtle);
}

/* Colour legend at the top of the rail: swatches wrap across the column,
   the gradient bar spans it, the click hint takes its own line. */
.ss-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 12px;
    padding: 12px 14px;
}

.ss-legend-bar {
    flex: 1 1 100px;
    height: 10px;
    border-radius: 2px;
}

.ss-legend-hint {
    flex-basis: 100%;
    margin-top: 4px;
    color: var(--color-text-faint);
}

/* Focused-niche readings: one item per line, long lists wrap inside their
   own line. Empty (zero height) until a niche is focused. */
.ss-niche-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    border-top: 1px solid var(--color-border-subtle);
}

.ss-niche-info:empty {
    display: none;
}

.ss-side-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

.ss-side-field .dropdown {
    width: 100%;
    font-size: var(--text-xs);
}

.ss-side-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ss-scrub {
    flex: 1;
    min-width: 0;
}

/* Bar controls: label + input pairs, sized like the Correlations bar. */
.control-bar .ss-bar-field {
    display: flex;
    align-items: center;
    gap: 6px;
}

.control-bar .ss-info {
    color: var(--control-bar-label);
    font-weight: var(--weight-medium);
    cursor: help;
    text-decoration: underline dotted;
    text-underline-offset: 2px;
}

.control-bar .ss-dot-size {
    width: 90px;
    margin: 0;
}

.control-bar .ss-niche-trigger {
    min-width: 180px;
    max-width: 260px;
    padding: 2px 8px;
    font-size: var(--text-xs);
}

@media (max-width: 1100px) {
    .ss-panel {
        flex-direction: column;
    }

    .ss-side {
        flex: 0 0 auto;
        max-height: 40%;
        border-left: none;
        border-top: 1px solid var(--color-border-subtle);
    }
}


/* ── Focus-niche picker (search + sort combobox) ── */
.ss-niche-picker {
    position: relative;
    display: inline-block;
}

.ss-niche-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-width: 230px;
    max-width: 330px;
    padding: 5px 8px;
    background: var(--select-bg);
    border: 1px solid var(--select-border);
    border-radius: 3px;
    color: var(--select-text);
    font-family: var(--font-sans);
    text-align: left;
    cursor: pointer;
}

.ss-niche-trigger:hover {
    border-color: var(--color-accent);
}

#ss-niche-trigger-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ss-niche-caret {
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
}

/* Clearing the focus is a one-click affordance rather than "reopen the list
   and pick the placeholder" — hidden entirely while nothing is focused. */
.ss-niche-clear {
    width: 22px;
    height: 22px;
    line-height: 1;
    padding: 0;
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 3px;
    color: var(--color-text-tertiary);
    font-size: var(--text-body);
    cursor: pointer;
}

.ss-niche-clear:hover {
    color: var(--color-text-primary);
    border-color: var(--color-accent);
}

.ss-niche-panel {
    position: absolute;
    z-index: 900;
    top: calc(100% + 4px);
    left: 0;
    width: 360px;
    max-width: 80vw;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    box-shadow: var(--shadow-md);
    overflow: hidden;
}

.ss-niche-panel[hidden] {
    display: none;
}

.ss-niche-panel-head {
    display: flex;
    gap: 6px;
    align-items: center;
    padding: 8px;
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-bg-elevated);
}

.ss-niche-search {
    flex: 1 1 auto;
    min-width: 0;
    padding: 5px 8px;
    background: var(--input-bg);
    border: 1px solid var(--input-border);
    border-radius: 3px;
    color: var(--input-text);
    font-family: var(--font-sans);
}

.ss-niche-search::placeholder {
    color: var(--color-text-muted);
}

/* Tall enough that the list reads as a list rather than a top-N preview. A
   fixed 320px showed ~10 of several hundred niches, and macOS overlay
   scrollbars paint nothing until you already scroll — so the remainder was
   invisible AND unadvertised. The viewport-relative height plus the count line
   below fix both halves of that. */
.ss-niche-list {
    max-height: min(52vh, 480px);
    overflow-y: auto;
}

/* Says how many niches are in the list and, when it overflows, that there are
   more below — the affordance the hidden scrollbar does not provide. */
.ss-niche-count {
    padding: 5px 10px;
    border-top: 1px solid var(--color-border-subtle);
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
}

.ss-niche-option {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 5px 10px;
    cursor: pointer;
    color: var(--color-text-primary);
}

.ss-niche-option:hover,
.ss-niche-option.is-active {
    background: var(--table-row-hover);
}

.ss-niche-option.is-selected {
    color: var(--color-accent);
}

.ss-niche-option-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ss-niche-option-meta {
    flex: 0 0 auto;
    color: var(--color-text-tertiary);
    white-space: nowrap;
}

.ss-niche-empty {
    padding: 10px;
    color: var(--color-text-muted);
}

/* The map's click dialog offers three actions where the shared drill-down card
   was built for two, so it needs the extra width to keep them on one row. */
.ss-point-dialog-card {
    max-width: 580px;
}


/* Each measured neighbour in the detail bar is a control, not just a name:
   clicking one offers the same two moves as clicking a dot on the map. */
.ss-niche-link {
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    color: var(--color-accent);
    cursor: pointer;
}

.ss-niche-link:hover,
.ss-niche-link:focus-visible {
    text-decoration: underline;
}


/* ── "Show me where they are" pulse control, beside Closest niches ── */
.ss-flash-btn {
    margin-left: 4px;
    padding: 0 5px;
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 3px;
    color: var(--color-text-tertiary);
    line-height: 1.5;
    cursor: pointer;
}

.ss-flash-btn:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

/* ============================================================
   Participation wizard (/participate/start)
   ============================================================ */

.wizard-hero { padding-bottom: 8px; }

.wizard-eyebrow {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: 6px;
}

.wizard-body { max-width: 820px; margin: 0 auto; }

.wizard-stages {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    margin: 0 0 30px;
}

.wizard-stage-pill {
    position: relative;
    overflow: hidden;
    text-align: left;
    border: 1px solid var(--color-border);
    border-radius: 10px;
    background: var(--color-bg-surface);
    padding: 12px 14px 11px;
    cursor: pointer;
    font-family: var(--font-sans);
}

.wizard-stage-pill::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--color-border);
}

.wizard-stage-pill:hover { border-color: var(--color-accent); }
.wizard-stage-pill.active { border-color: var(--color-accent); }
.wizard-stage-pill.active::before { background: var(--color-accent); }

.wizard-stage-n {
    display: block;
    font-size: 11px;
    font-weight: var(--weight-semibold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.wizard-stage-pill.active .wizard-stage-n { color: var(--color-accent); }

.wizard-stage-t {
    display: block;
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    margin-top: 2px;
    color: var(--color-text-primary);
}

.wizard-pane { display: none; }
.wizard-pane.show { display: block; }

.wizard-sub { color: var(--color-text-muted); max-width: 62ch; margin-bottom: 22px; }

.wizard-platform-tabs { display: flex; gap: 8px; }

.wizard-platform-tab {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border: 1px solid var(--color-border);
    border-bottom: none;
    border-radius: 8px 8px 0 0;
    background: var(--color-bg-primary);
    padding: 9px 18px;
    font-family: var(--font-sans);
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    cursor: pointer;
}

.wizard-platform-tab.active {
    background: var(--color-bg-surface);
    color: var(--color-text-primary);
    position: relative;
}

.wizard-platform-tab.active::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -1px;
    height: 1px;
    background: var(--color-bg-surface);
}

.wizard-pdot { width: 8px; height: 8px; border-radius: 50%; }
.wizard-pdot--tiktok { background: #ec4899; }
.wizard-pdot--instagram { background: #8b5cf6; }
.wizard-pdot--youtube { background: #dc2626; }

.wizard-howto {
    border: 1px solid var(--color-border);
    border-radius: 0 10px 10px 10px;
    background: var(--color-bg-surface);
    padding: 20px 24px;
}

.wizard-howto .myc-howto-body h3 { margin-top: 0; }

.wizard-actions {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 26px;
}

.wizard-hint { font-size: var(--text-sm); color: var(--color-text-muted); }

.wizard-cta-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin: 4px 0 10px; }
.wizard-or { font-size: var(--text-sm); color: var(--color-text-muted); }

.wizard-step-row {
    display: flex;
    gap: 26px;
    align-items: flex-start;
    margin-bottom: 8px;
}

.wizard-step-row .wizard-sub { flex: 1 1 320px; margin-bottom: 12px; }

.wizard-hero-mini {
    margin: 4px 0 0;
    flex: 0 1 340px;
}

.wizard-hero-mini img {
    width: 100%;
    height: auto;
    display: block;
}

.wizard-h3 {
    font-size: var(--text-body);
    font-weight: var(--weight-semibold);
    margin: 26px 0 10px;
}

.wizard-timeline { display: grid; }

.wizard-tl { display: grid; grid-template-columns: 22px 1fr; gap: 12px; }
.wizard-tl-rail { display: flex; flex-direction: column; align-items: center; }
.wizard-tl-dot {
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--color-accent);
    flex: none;
    margin-top: 5px;
}
.wizard-tl-bar { width: 2px; flex: 1; background: var(--color-border); }
.wizard-tl-body { padding-bottom: 16px; }
.wizard-tl-body span {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    max-width: 58ch;
}

@media (max-width: 640px) {
    .wizard-stages { grid-template-columns: 1fr; }
    .wizard-step-row { flex-direction: column; }
    .wizard-hero-mini { flex-basis: auto; }
}

/* ============================================================
   Guided tour (hub_tour.js)
   ============================================================ */

.hub-tour-backdrop {
    position: fixed;
    inset: 0;
    background: var(--color-bg-overlay);
    z-index: 1200;
}

.hub-tour-ring {
    position: fixed;
    z-index: 1201;
    border: 2px solid var(--color-accent);
    border-radius: 8px;
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-accent) 25%, transparent);
    pointer-events: none;
    display: none;
}

.hub-tour-card {
    position: fixed;
    z-index: 1202;
    width: 360px;
    max-width: calc(100vw - 28px);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
    padding: 16px 18px 14px;
}

.hub-tour-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 2px;
}

.hub-tour-step-n {
    font-size: 11px;
    font-weight: var(--weight-semibold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent);
}

.hub-tour-nav-btns { display: inline-flex; gap: 4px; }

.hub-tour-nav-btn {
    width: 26px;
    height: 26px;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: var(--color-bg-surface);
    color: var(--color-text-primary);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.hub-tour-nav-btn:hover:not(:disabled) {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.hub-tour-nav-btn:disabled { opacity: 0.4; cursor: default; }
.hub-tour-close { color: var(--color-text-muted); }

.hub-tour-card h4 {
    margin: 3px 0 5px;
    font-size: var(--text-h3);
}

.hub-tour-card > p {
    margin: 0 0 12px;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.hub-tour-sketch {
    margin: 2px 0 6px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 8px 10px;
    background: var(--color-bg-surface);
}

.hub-tour-sketch svg { display: block; width: 100%; height: auto; }

/* --- Automatic enrichment: coverage bar + annotation target ---------------
   (Edit Collection modal). The bar is display-only: four zones that sum to
   the collection's unique videos, with the target as a marker. The slider
   under it is the log-scaled input; the number field stays authoritative. */
.dm-enrich-bar {
    position: relative;
    display: flex;
    height: 18px;
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-strong);
    border-radius: 3px;
    margin-top: 8px;
}
.dm-enrich-bar-seg { height: 100%; }
.dm-enrich-bar-seg.annotated { background: var(--color-success); }
.dm-enrich-bar-seg.scraped { background: var(--color-info); }
.dm-enrich-bar-spacer { flex: 1; }
.dm-enrich-bar-seg.failed {
    background: repeating-linear-gradient(45deg,
        var(--color-bg-input), var(--color-bg-input) 4px,
        var(--color-border-strong) 4px, var(--color-border-strong) 6px);
}
/* The target: a bare line, no label — the number lives in the field below.
   Red for contrast against every zone (green, blue, empty, hatched grey). */
.dm-enrich-bar-marker {
    position: absolute;
    top: -5px;
    bottom: -5px;
    width: 3px;
    background: var(--color-danger);
    transform: translateX(-1.5px);
    pointer-events: none;
}

/* Where coverage stood when this run was armed. Quieter than the target
   marker on purpose: it is history, not a goal, and the two sit on the same
   bar. */
.dm-enrich-bar-start {
    position: absolute;
    top: -3px;
    bottom: -3px;
    width: 2px;
    background: var(--color-text-tertiary);
    transform: translateX(-1px);
    pointer-events: none;
}

.dm-enrich-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 3px 14px;
    margin-top: 6px;
    color: var(--color-text-tertiary);
}
.dm-enrich-legend .sw {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-right: 5px;
    border-radius: 2px;
    vertical-align: -1px;
}
.dm-enrich-legend .sw.annotated { background: var(--color-success); }
.dm-enrich-legend .sw.scraped { background: var(--color-info); }
.dm-enrich-legend .sw.rest {
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-strong);
}
.dm-enrich-legend .sw.failed {
    background: repeating-linear-gradient(45deg,
        var(--color-bg-input), var(--color-bg-input) 3px,
        var(--color-border-strong) 3px, var(--color-border-strong) 5px);
}

/* The line above the chart: the collection's size, then how much of it is
   already analysable, then the (i). */
.dm-enrich-headline {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin-bottom: 8px;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}
.dm-enrich-headline .dm-enrich-ready { color: var(--color-text-muted); }
.dm-enrich-headline .dm-enrich-ready::before {
    content: "\00b7";
    margin-right: 8px;
}
.dm-enrich-headline .dm-enrich-ready:empty::before { content: none; }

/* --- Run progress -------------------------------------------------------
   Where this run started, where it is now, where it stops. The chart and the
   coverage bar describe the COLLECTION; this describes the run, which is the
   only thing that answers "how far has it come". */
.dm-enrich-run {
    margin-top: 12px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: 3px;
    background: var(--color-bg-surface);
}
.dm-enrich-run-head {
    display: flex;
    align-items: baseline;
    gap: 6px;
    color: var(--color-text-secondary);
}
.dm-enrich-run-pct {
    margin-left: auto;
    font-weight: var(--weight-bold);
    color: var(--color-text-primary);
}
.dm-enrich-run-meter {
    height: 6px;
    margin: 6px 0 5px 0;
    border-radius: 3px;
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-strong);
    overflow: hidden;
}
.dm-enrich-run-fill {
    height: 100%;
    width: 0;
    background: var(--color-success);
    transition: width 0.3s ease;
}
.dm-enrich-run-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 14px;
    color: var(--color-text-tertiary);
}
.dm-enrich-run-legend b {
    font-weight: var(--weight-bold);
    color: var(--color-text-secondary);
}
.dm-enrich-run-cursors {
    margin-top: 3px;
    color: var(--color-text-tertiary);
}
.dm-enrich-run-cursors:empty { display: none; }

/* --- Plan settings ------------------------------------------------------
   Every knob visible, on one grid, in one type scale: a field label is
   .text-sm secondary (the same label style the rest of the modal uses), a
   sub-field label inside a field is .text-xs muted, and values are primary.
   The target owns a full row - it has a slider, a readout and a warning
   under it - and the remaining four sit under it two by two. */
.dm-enrich-settings {
    /* Three sliders, stacked: each runs the full width of the panel so the
       log scales get all the travel there is, with its value on the label
       line rather than beside the track. */
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
    align-items: start;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
}
.dm-enrich-slider-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 6px;
}
.dm-enrich-slider-head .dm-enrich-label { margin-bottom: 0; }
/* The slider's value, on the label line in the label's own type. */
.dm-enrich-inline-value {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}
.dm-enrich-inline-value:empty { display: none; }
.dm-enrich-slider {
    display: block;
    width: 100%;
    margin: 0;
}
.dm-enrich-label {
    display: block;
    margin-bottom: 6px;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}
.dm-enrich-settings .control-input { padding: 6px; }
.dm-enrich-range-ends {
    display: flex;
    justify-content: space-between;
    margin-top: 2px;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}
/* --- Earliest date: a handle on the chart, and a row under it ---------- */
.dm-enrich-chart-wrap { position: relative; }
.dm-enrich-earliest-handle {
    position: absolute;
    width: 13px;
    margin-left: -6px;
    cursor: ew-resize;
    z-index: 30;
    touch-action: none;
}
.dm-enrich-earliest-handle__rule {
    position: absolute;
    left: 6px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--color-accent);
    opacity: 0.75;
}
.dm-enrich-earliest-handle__grip {
    position: absolute;
    left: 2px;
    top: -1px;
    width: 9px;
    height: 9px;
    border-radius: 2px;
    background: var(--color-accent);
}
.dm-enrich-earliest-handle:hover .dm-enrich-earliest-handle__rule,
.dm-enrich-chart-wrap.dragging-range .dm-enrich-earliest-handle__rule {
    opacity: 1;
    width: 2px;
}
.dm-enrich-earliest-row {
    display: flex;
    align-items: center;
    gap: 4px;
    margin: 0 0 8px 0;
    color: var(--color-text-secondary);
}
.dm-enrich-earliest-row > .meta-tooltip { margin-right: 4px; }
.dm-enrich-earliest-value {
    min-width: 84px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.dm-enrich-earliest-step { padding: 0 6px; line-height: 1.4; }
#dm-enrich-earliest-reset { margin-left: 6px; }
.dm-enrich-hint {
    margin-top: 5px;
    color: var(--color-text-muted);
}
.dm-enrich-warn {
    margin-top: 4px;
    color: var(--color-warning-text);
}
.dm-enrich-status-row {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
}

/* ------------------------------------------------------------
   Edit Collection modal (Data Management -> Edit Collections)
   ------------------------------------------------------------ */

/* The title bar is the modal's only fixed point. .modal-content is itself the
   scroll container, and the shared .close is absolutely positioned inside it,
   so on a modal this tall the [x] used to scroll away entirely. Sticky keeps
   the bar - title, Save, [x] - at the top of the scrollport instead.
   The container gives up its top padding to the bar, which carries its own:
   a sticky box is constrained by its MARGIN box, so a negative top margin
   here would just be handed back as a 20px strip of padding above the bar,
   for the content to scroll up through. The negative side margins are safe,
   and make the bar full-bleed. */
#editCollectionModal .modal-content {
    padding-top: 0;
}

.edit-collection-header {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin: 0 -20px 20px -20px;
    padding: 16px 20px;
    background: var(--modal-bg);
    border-bottom: 1px solid var(--color-border-strong);
}

.edit-collection-header-actions {
    display: flex;
    align-items: center;
    gap: 16px;
}

/* In the bar the [x] is just another flex item, not a corner overlay. */
.edit-collection-header .close {
    position: static;
    float: none;
    line-height: 1;
}

/* The modal has no Save button for a single collection: the fields write
   themselves. This is the whole feedback for that, so it has to be legible
   without being loud - grey while writing, green when the write lands, red
   when it does not. */
.dm-save-state { color: var(--color-text-tertiary); }
.dm-save-state.ok { color: var(--color-success); font-weight: var(--weight-bold); }
.dm-save-state.err { color: var(--color-danger); font-weight: var(--weight-bold); }

/* Display ID is the one field that cannot save on every keystroke, so it
   says so: a Save button while the box differs from what is stored, a green
   tick for a moment after the write. */
.dm-inline-save {
    display: flex;
    align-items: center;
    gap: 8px;
}
.dm-save-tick {
    color: var(--color-success);
    font-weight: var(--weight-bold);
    font-size: var(--text-body);
    line-height: 1;
}

/* A display ID two collections answer to. Writes are unique-checked, so this
   only marks data written before that check existed - loud enough to find in
   a long table, quiet enough not to read as an error. */
.dm-dup-display-id {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 10px;
    background: transparent;
    color: var(--color-warning);
    border: 1px solid var(--color-warning);
    font-weight: var(--weight-normal);
    white-space: nowrap;
    vertical-align: middle;
}

/* Display ID / User account beside Tags / Hide Collection. Two explicit
   tracks rather than auto-fit: auto-fit leaves a collapsed third track and
   its gutter unused at this width, so the pair would not fill the modal. */
/* The Edit Collection modal's two blocks (Collection settings, Automatic
   enrichment) share one panel: heading + explanation above, this below. */
.dm-block-panel {
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg-elevated);
    padding: 10px 12px;
}

.edit-collection-fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px 28px;
    align-items: start;
}

.edit-collection-col {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

/* The house breakpoint (1100px): below it the modal is too narrow for two columns. */
@media (max-width: 1100px) {
    .edit-collection-fields { grid-template-columns: 1fr; }
}

/* The disclosure link used by History, Collection persona and the annotation
   queue's Advanced block. Named for what it does, not for the one panel it
   was born in — the enrichment plan's own settings are no longer hidden. */
.dm-disclosure-toggle {
    background: none;
    border: none;
    padding: 0;
    color: var(--color-link);
    cursor: pointer;
    font-size: var(--text-sm);
}
.dm-disclosure-toggle .caret {
    display: inline-block;
    margin-right: 4px;
    transition: transform 0.15s ease;
}
.dm-disclosure-toggle.open .caret { transform: rotate(90deg); }

/* A running plan's Arm button (then reading "Pause") pulses gently, so an
   armed collection is unmissable in the modal. */
@keyframes dm-enrich-armed-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(106, 155, 126, 0.7); }
    50% { box-shadow: 0 0 0 6px rgba(106, 155, 126, 0); }
}
.dm-enrich-armed-pulse {
    animation: dm-enrich-armed-pulse 1.8s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .dm-enrich-armed-pulse { animation: none; outline: 2px solid var(--color-success); }
}

/* The queue dialog shown before a cycle starts: one row per non-empty queue,
   with whose work it is and the empty-first tick box. */
.dm-enrich-queue-row {
    padding: 8px 10px;
    margin-bottom: 8px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg-elevated);
}
.dm-enrich-queue-row .who { color: var(--color-text-tertiary); margin-top: 2px; }
.dm-enrich-queue-row label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

/* Enrichment history (the Dataset Assembly card and the Edit Collections
   panel's History disclosure): a compact table, one row per event, coloured
   by the event's family. */
.dm-history-box {
    max-height: 360px;
    overflow: auto;
    border: 1px solid var(--color-border);
    background: var(--color-bg-elevated);
}
.dm-history { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
.dm-history td {
    padding: 4px 8px;
    vertical-align: top;
    border-top: 1px solid var(--color-border);
}
.dm-history tr:first-child td { border-top: none; }
.dm-history-when { white-space: nowrap; width: 1%; color: var(--color-text-tertiary); }
.dm-history-kind { white-space: nowrap; width: 1%; }
.dm-history-chip {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: var(--text-xxs);
    font-weight: var(--weight-bold);
    background: var(--color-bg-subtle, rgba(128, 128, 128, 0.15));
    color: var(--color-text-secondary);
}
.dm-history--queue .dm-history-chip { background: color-mix(in srgb, var(--color-info) 18%, transparent); color: var(--color-info); }
.dm-history--worker .dm-history-chip { background: color-mix(in srgb, var(--color-accent) 18%, transparent); color: var(--color-accent); }
.dm-history--consolidate .dm-history-chip { background: color-mix(in srgb, var(--color-success) 18%, transparent); color: var(--color-success); }
.dm-history--refresh .dm-history-chip { background: color-mix(in srgb, var(--color-link) 18%, transparent); color: var(--color-link); }
.dm-history--attention .dm-history-chip { background: color-mix(in srgb, var(--color-danger) 18%, transparent); color: var(--color-danger); }
.dm-history-msg { color: var(--color-text-secondary); }
.dm-history-coll { font-weight: var(--weight-bold); margin-right: 6px; color: var(--color-text-primary); }

/* "An armed plan drains this queue" beside the queue counters. */
.dm-plan-note { color: var(--color-warning-text); margin-left: 8px; }


/* ------------------------------------------------------------------------
   Variable customizer (static/js/variable_prefs.js): the Word-style dual list
   behind My Stuff -> Preferences -> Variable customizations and Admin ->
   Variable Visibility's "Arrange default layout". Sits below the app dialog
   (z-index 1100) so its discard/alert prompts appear on top.
   ------------------------------------------------------------------------ */
.vc-overlay {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--modal-overlay);
}
.vc-dialog {
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    width: min(880px, 94vw);
    height: min(720px, 90vh);
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.vc-muted { color: var(--color-text-muted); }
.vc-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px 8px;
}
.vc-close { padding: 2px 8px; }
.vc-tabs {
    display: flex;
    align-self: center;
    gap: 2px;
    padding: 3px;
    border-radius: 8px;
    background: var(--color-bg-inset);
    border: 1px solid var(--color-border-subtle);
    max-width: calc(100% - 32px);
    overflow-x: auto;
}
.vc-tab {
    border: none;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    padding: 5px 14px;
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
}
.vc-tab:hover { color: var(--color-text-primary); }
.vc-tab.active {
    background: var(--color-accent);
    color: var(--color-text-on-accent);
    font-weight: var(--weight-bold);
}
.vc-tab-dot {
    display: none;
    margin-left: 5px;
    font-size: 0.7em;
    vertical-align: middle;
    color: var(--color-save);
}
.vc-tab.active .vc-tab-dot { color: inherit; }
.vc-tab.dirty .vc-tab-dot { display: inline; }
.vc-hint { text-align: center; padding: 6px 16px 0; }
.vc-panes {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 12px;
    padding: 10px 16px 8px;
}
.vc-pane {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 0;
}
.vc-pane-label { color: var(--color-text-secondary); }
.vc-select,
.vc-search {
    width: 100%;
    background: var(--color-bg-input);
    color: var(--color-text-primary);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 5px 8px;
}
.vc-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    background: var(--color-bg-inset);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 0;
    outline: none;
}
.vc-list:focus-visible { border-color: var(--color-accent); }
.vc-list.vc-drop-target { border-color: var(--color-accent); border-style: dashed; }
.vc-section-head {
    font-size: var(--text-xxs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    padding: 8px 10px 2px;
}
.vc-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    font-size: var(--text-sm);
    color: var(--color-text-primary);
    cursor: pointer;
    user-select: none;
}
.vc-row:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.vc-row.selected {
    background: var(--color-accent);
    color: var(--color-text-on-accent);
}
.vc-row.dragging { opacity: 0.4; }
.vc-row-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vc-grip {
    color: var(--color-text-faint);
    font-size: var(--text-xs);
    letter-spacing: -2px;
    cursor: grab;
}
.vc-row.selected .vc-grip { color: inherit; }
.vc-changed { font-size: 0.7em; color: var(--color-accent); }
.vc-row.selected .vc-changed { color: inherit; }
.vc-row.vc-drop-before::before,
.vc-row.vc-drop-after::after {
    content: "";
    position: absolute;
    left: 8px;
    right: 8px;
    height: 2px;
    background: var(--color-accent);
    border-radius: 1px;
}
.vc-row.vc-drop-before::before { top: -1px; }
.vc-row.vc-drop-after::after { bottom: -1px; }
.vc-group-head {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    border: none;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 7px 8px 3px;
    cursor: pointer;
    text-align: left;
}
.vc-chevron { display: inline-block; width: 10px; transition: transform 0.15s; }
.vc-chevron.open { transform: rotate(90deg); }
.vc-count {
    margin-left: auto;
    font-weight: var(--weight-normal);
    color: var(--color-text-faint);
    text-transform: none;
}
.vc-group-body .vc-row { padding-left: 22px; }
.vc-empty { padding: 16px 12px; color: var(--color-text-muted); text-align: center; }
.vc-move {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 8px;
}
.vc-move-btn { width: 44px; height: 34px; font-size: 1.3em; line-height: 1; padding: 0; }
.vc-arrow { display: inline-block; }
.vc-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: var(--color-bg-surface);
}
.vc-toolbar .btn-discreet { padding: 2px 10px; }
.vc-toolbar-sep { width: 1px; align-self: stretch; background: var(--color-border); margin: 0 4px; }
.vc-desc {
    margin: 0 16px;
    padding: 6px 10px;
    min-height: 3.2em;
    max-height: 6em;
    overflow-y: auto;
    border-radius: 6px;
    background: var(--color-bg-surface);
    color: var(--color-text-secondary);
}
.vc-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px 16px 4px;
}
.vc-footer-left,
.vc-footer-right { display: flex; gap: 8px; flex-wrap: wrap; }
.vc-caption { padding: 0 16px 10px; text-align: right; }

@media (max-width: 640px) {
    .vc-dialog { width: 100vw; height: 100vh; border-radius: 0; }
    .vc-panes { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto minmax(0, 1fr); }
    .vc-move { flex-direction: row; justify-content: center; }
    .vc-arrow { transform: rotate(90deg); }
    .vc-tab { padding: 5px 10px; }
    .vc-caption { text-align: left; }
}
