/* ============================================================
   YouthShape Data Browser — global stylesheet
   ------------------------------------------------------------
   Theme is driven by CSS custom properties on :root. Components
   that need branded colour should reference these rather than
   hard-coding hex values, so the whole app retunes from a single
   place.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

:root {
    /* Brand */
    --color-primary:        #1e6fdc;
    --color-primary-hover:  #1858b3;
    --color-primary-soft:   #eef4fc;
    --color-accent:         #f59e0b;

    /* Categorical (sex) — kept in sync with core/plotly_theme.py */
    --color-male:           #1e6fdc;
    --color-female:         #5cb88f;

    /* Surfaces */
    --color-bg:             #f6f7f9;
    --color-surface:        #ffffff;
    --color-surface-alt:    #fafbfc;
    --color-border:         #e5e7eb;
    --color-border-strong:  #cbd5e1;

    /* Text */
    --color-text:           #1f2937;
    --color-text-heading:   #0f172a;
    --color-muted:          #6b7280;
    --color-muted-soft:     #94a3b8;

    /* Effects */
    --shadow-sm:            0 1px 2px rgba(15, 23, 42, 0.06);
    --shadow-md:            0 4px 12px rgba(15, 23, 42, 0.08);
    --radius-sm:            6px;
    --radius-md:            10px;
    --radius-lg:            14px;

    /* Rhythm */
    --space-1:              4px;
    --space-2:              8px;
    --space-3:              12px;
    --space-4:              16px;
    --space-5:              24px;
    --space-6:              32px;
}

/* ---------- Base typography ---------- */

html, body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
                 system-ui, Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 14px;
    color: var(--color-text);
    background: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    line-height: 1.5;
}

h1, h2, h3, h4, h5, h6 {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
                 system-ui, sans-serif;
    color: var(--color-text-heading);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.25;
}

h1 { font-size: 1.5rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.1rem; }
h4 { font-size: 0.95rem; }
h5, h6 { font-size: 0.85rem; }

p { font-size: 0.875rem; color: var(--color-text); }

b, strong { font-weight: 600; }

/* The legacy inline style sprinkled through the layout — keep this
   working but in the new palette so old code still renders sensibly. */
b[style*="#5a8ad7"],
p[style*="#5a8ad7"] {
    color: var(--color-primary) !important;
}

/* ---------- Cards & panels ---------- */

.card {
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.card .card-header {
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    font-weight: 600;
    color: var(--color-text-heading);
    border-top-left-radius: var(--radius-md);
    border-top-right-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
}

.card .card-body {
    padding: var(--space-4);
}

/* ---------- Forms & controls ---------- */

.form-control,
.Select-control,
.Select-input > input,
.Select-value,
.dropdown {
    font-family: 'Inter', sans-serif;
    font-size: 0.875rem;
}

.Select-control {
    border-radius: var(--radius-sm) !important;
    border-color: var(--color-border) !important;
    box-shadow: none !important;
}

.Select-control:hover {
    border-color: var(--color-border-strong) !important;
}

.btn {
    font-family: 'Inter', sans-serif;
    font-weight: 500;
    border-radius: var(--radius-sm);
    transition: all 0.15s ease;
}

.btn-primary,
.btn-primary:focus {
    background: var(--color-primary);
    border-color: var(--color-primary);
}

.btn-primary:hover {
    background: var(--color-primary-hover);
    border-color: var(--color-primary-hover);
}

/* ---------- Tabs ---------- */

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

.nav-tabs .nav-link {
    color: var(--color-muted);
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    font-weight: 500;
    padding: var(--space-3) var(--space-4);
    transition: color 0.15s ease, border-color 0.15s ease;
}

.nav-tabs .nav-link:hover {
    color: var(--color-text);
    border-color: transparent;
}

.nav-tabs .nav-link.active {
    color: var(--color-primary);
    background: transparent;
    border-color: var(--color-primary);
    font-weight: 600;
}

/* ---------- Tables ---------- */

.table {
    font-size: 0.85rem;
    background: var(--color-surface);
}

.table thead th {
    font-weight: 600;
    color: var(--color-text-heading);
    background: var(--color-surface-alt);
    border-bottom: 2px solid var(--color-border) !important;
    border-top: none;
    text-align: left;
    padding: var(--space-2) var(--space-3);
}

.table tbody td {
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--color-border);
    color: var(--color-text);
}

.table-striped tbody tr:nth-of-type(odd) {
    background: var(--color-surface-alt);
}

.table-hover tbody tr:hover {
    background: var(--color-primary-soft);
}

/* ---------- Sliders ---------- */

.rc-slider-track {
    background: var(--color-primary) !important;
}

.rc-slider-handle {
    border-color: var(--color-primary) !important;
    box-shadow: 0 0 0 4px rgba(30, 111, 220, 0.15);
}

.rc-slider-handle:hover,
.rc-slider-handle-active:active {
    border-color: var(--color-primary-hover) !important;
}

.rc-slider-dot-active {
    border-color: var(--color-primary) !important;
}

.rc-slider-rail {
    background: var(--color-border);
}

.rc-slider-mark-text,
.rc-slider-mark-text-active {
    color: var(--color-muted);
    font-size: 0.72rem;
    /* Hint to the renderer not to overlap labels; truncate with
       an ellipsis when the available track width is too short. */
    white-space: nowrap;
    overflow: visible;
}

/* On narrow sidebars (typical when md/lg breakpoint sidebar is ~250px)
   skip every other mark label so they stop colliding. Marks of single
   digits stay; we visually hide 4, 6, 8, … via the even-indexed
   pseudo-class. */
.ys-card .rc-slider-mark-text:nth-child(even) {
    visibility: hidden;
}

@media (min-width: 1200px) {
    .ys-card .rc-slider-mark-text:nth-child(even) {
        visibility: visible;
    }
}

/* ---------- Header / page chrome ---------- */

/* The legacy black header bar from layout.py — we soften the edges
   and tighten the height. */
.row[style*="background-color: #292B2B"] {
    border-bottom: 3px solid var(--color-primary);
    box-shadow: 0 1px 2px rgba(15,23,42,0.04);
}

/* ---------- Legacy compatibility ---------- */

.row-height {
    height: 20px;
}

.zoom {
    zoom: 100%;
}

/* Custom dropdown for dimensions with images */
.custom-dropdown .dropdown-menu {
    max-height: 400px !important;
    overflow-y: auto !important;
}

/* ---------- Plot area cards ---------- */

/* dcc.Graph sits flush in its parent card with a tiny inset so the
   plot's white background blends into the card surface */
.dash-graph {
    padding: var(--space-2);
}

/* ---------- Loading indicator ---------- */

._dash-loading,
.dash-spinner {
    color: var(--color-primary) !important;
}

/* ---------- Subtle utilities ---------- */

.text-muted-soft { color: var(--color-muted-soft) !important; }
.text-muted      { color: var(--color-muted) !important; }
.bg-surface-alt  { background: var(--color-surface-alt) !important; }
.shadow-soft     { box-shadow: var(--shadow-sm) !important; }
.rounded-card    { border-radius: var(--radius-md) !important; }


/* ============================================================
   App shell — header + left nav + main content
   ============================================================ */

.app-shell {
    display: flex;
    flex-direction: column;
    /* Fixed-height shell so the main column can own its own
       vertical scroll while the header and left nav stay put. */
    height: 100vh;
    background: var(--color-bg);
    overflow: hidden;
}

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

.app-header {
    display: grid;
    /* DATASET column flexes to absorb free space so the dropdown
       can show its full active-dataset name on wide screens; the
       auth chip and links pack to the right at their natural
       widths. */
    grid-template-columns: minmax(180px, auto) minmax(480px, 1fr) auto auto;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-2) var(--space-4);
    background: var(--color-surface);
    color: var(--color-text);
    border-bottom: 3px solid var(--color-primary);
    box-shadow: var(--shadow-sm);
    /* Sticks to the top of the viewport so the database picker /
       auth chip / nav links stay accessible while the user scrolls
       the long card stacks below. */
    position: sticky;
    top: 0;
    z-index: 100;
}

.app-header__title-block {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    line-height: 1.05;
}

.app-header__logo-link {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    text-decoration: none;
    line-height: 0;
}

.app-header__logo {
    height: 44px;
    width: auto;
    display: block;
    flex-shrink: 0;
}

.app-header__brand-text { display: flex; flex-direction: column; }

.app-header__brand {
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--color-text-heading);
}

.app-header__subbrand {
    font-size: 0.78rem;
    color: var(--color-muted);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.app-header__db {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-width: 480px;
}

.app-header__db .app-header__db-dropdown {
    flex: 1 1 390px;
    min-width: 360px;
}

/* The indicator under the dropdown was repeating the active
   dataset name shown in the dropdown itself — hide it to recover
   header height. */
.app-header__db-indicator { display: none; }

/* Inline "DATASET" label sits to the left of the dropdown — tiny
   uppercase chip so it reads as a field caption, not a heading. */
.app-header__db-label {
    margin: 0;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-muted);
    white-space: nowrap;
}

.app-header__db-dropdown .Select-control {
    background: var(--color-surface-alt) !important;
    border-color: var(--color-border) !important;
    color: var(--color-text) !important;
}

.app-header__db-dropdown .Select-value-label,
.app-header__db-dropdown .Select-placeholder {
    color: var(--color-text) !important;
}

.app-header__db-dropdown .Select-arrow { border-top-color: var(--color-muted) !important; }

.app-header__db-dropdown .Select-menu-outer {
    background: var(--color-surface) !important;
    color: var(--color-text) !important;
}

.app-header__db-indicator {
    font-size: 0.75rem;
    color: var(--color-muted);
    margin-top: 2px;
}

/* ---- Pro-key auth block in header ---- */

.app-header__auth {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-width: 130px;
}

.app-header__auth-input {
    font-size: 0.78rem;
    padding: 4px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    outline: none;
    /* Capped at ~20 monospace characters of input — keeps the
       header tidy without clipping the dropdown for users who
       type slightly longer keys. */
    width: 20ch;
    max-width: 100%;
}

.app-header__auth-input::placeholder { color: var(--color-muted-soft); }
.app-header__auth-input:focus       { border-color: var(--color-primary); }

.app-header__auth-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.app-header__auth-status {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    text-align: center;
    padding: 1px 6px;
    border-radius: 999px;
}

.app-header__auth-clear {
    font-size: 0.65rem;
    color: var(--color-muted);
    background: transparent;
    border: 0;
    padding: 0 2px;
    cursor: pointer;
    text-decoration: underline;
}

.app-header__auth-clear:hover { color: var(--color-primary); }

.app-header__auth-status--locked {
    color: var(--color-muted);
    background: #eef2f7;
}

.app-header__auth-status--ok {
    color: #047857;
    background: #d1fae5;
}

.app-header__auth-status--bad {
    color: #b91c1c;
    background: #fee2e2;
}

.app-header__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
    justify-content: flex-end;
    font-size: 0.85rem;
}

.app-header__links a {
    color: var(--color-muted);
    text-decoration: none;
    transition: color 0.15s ease;
}

.app-header__links a:hover { color: var(--color-primary); }

.app-header__copyright {
    color: var(--color-muted-soft) !important;
    font-size: 0.72rem;
    font-style: italic;
}

/* "✓ {date}" badge sitting just AFTER the License link confirming
   the user has accepted the agreement. Hover shows the full
   date + license version hash in a native title tooltip. */
.app-header__license-status {
    font-size: 0.78rem;
    color: var(--color-muted);
    padding: 2px 6px;
    border-radius: 4px;
    border: 1px solid transparent;
    white-space: nowrap;
}
.app-header__license-status:hover {
    color: var(--color-text);
    border-color: var(--color-border);
    background: var(--color-surface-alt, #f1f5f9);
}
.app-header__license-status:empty {
    display: none;
}

/* ---- Body (sidebar + main) ---- */

.app-body {
    display: flex;
    flex: 1;
    min-height: 0;
    /* Hide overflow at this level so the only scrolling container
       below is .app-main. .app-nav becomes a fixed left strip. */
    overflow: hidden;
}

/* ---- Left navigation ---- */

.app-nav {
    width: 200px;
    flex-shrink: 0;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    padding: var(--space-4) var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

/* Wrap div around each nav button — exists so a string-id
   ``dbc.Tooltip`` can target it. It must fill the column width so
   the button inside reaches the same width as before the wrap was
   added. */
.app-nav__wrap {
    display: block;
    width: 100%;
}

.app-nav__item {
    appearance: none;
    background: transparent;
    text-align: left;
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    color: var(--color-muted);
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease,
                border-color 0.15s ease;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Hover should not look "selected" — only nudge the colour so the
   user knows the button is interactive. The selected/active state
   gets the bordered, tinted treatment below. */
.app-nav__item:hover {
    color: var(--color-text);
}

.app-nav__item--active {
    background: var(--color-primary-soft);
    color: var(--color-primary);
    font-weight: 600;
    border-color: var(--color-primary);
}

.app-nav__icon {
    width: 18px;
    flex: 0 0 18px;
    text-align: center;
    color: var(--color-muted);
    font-size: 1rem;
    transition: color 0.15s ease;
}

.app-nav__item--active .app-nav__icon {
    color: var(--color-primary);
}

/* Nav-button tooltip — Bootstrap's default is a dark pill with
   light text; override to a light card so the markdown body stays
   readable. Applies to the inner ``.tooltip-inner`` (the box) and
   the arrow that points at the button. */
.app-nav__tooltip .tooltip-inner {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    box-shadow: var(--shadow-md);
    max-width: 280px;
    text-align: left;
    padding: 8px 10px;
    font-size: 0.82rem;
    line-height: 1.4;
    opacity: 1;
}
.app-nav__tooltip .tooltip-inner p:last-child {
    margin-bottom: 0;
}
.app-nav__tooltip.tooltip.show {
    opacity: 1;
}
.app-nav__tooltip .tooltip-arrow::before,
.app-nav__tooltip.bs-tooltip-end .tooltip-arrow::before,
.app-nav__tooltip.bs-tooltip-auto[data-popper-placement="right"] .tooltip-arrow::before {
    border-right-color: var(--color-border);
}

.app-nav__label {
    flex: 1 1 auto;
    min-width: 0;
}

/* ---- Main content ---- */

.app-main {
    flex: 1;
    padding: var(--space-4) var(--space-5);
    min-width: 0;
    /* Only the main column scrolls — the header and left nav stay
       pinned in place. */
    overflow-x: hidden;
    overflow-y: auto;
}

.app-page { /* router toggles display: block/none */
    width: 100%;
}

/* Responsive — collapse left nav above main on narrow screens */
@media (max-width: 767.98px) {
    .app-header {
        grid-template-columns: 1fr;
        gap: var(--space-3);
        padding: var(--space-3);
    }
    .app-header__links { justify-content: flex-start; }
    .app-body { flex-direction: column; }
    .app-nav {
        width: 100%;
        flex-direction: row;
        overflow-x: auto;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
        padding: var(--space-2) var(--space-3);
    }
    .app-nav__item { white-space: nowrap; }
    .app-main { padding: var(--space-3); }
}


/* ============================================================
   YouthShape page-level building blocks
   ============================================================ */

.ys-page { width: 100%; }

.ys-row { margin: 0 !important; }

.ys-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.ys-main {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    /* Establish a query container so ``.ys-plots-row`` can stack
       its cards based on its own available width, not viewport
       width. Triggered when the user shrinks the main column on
       wide screens — viewport media queries can't see that. */
    container-type: inline-size;
    container-name: ys-main;
}

/* ---- Cards ---- */

.ys-card {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    /* Deliberately NOT overflow:hidden — that clips the dropdown's
       expanded menu and any other child popout. Card-header rounds its
       own top corners via .card .card-header border-radius rules. */
    overflow: visible;
}

/* Card-body should not clip dropdown menus either. */
.ys-card .card-body { overflow: visible; }

.ys-card .card-header {
    background: var(--color-surface-alt);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.ys-card .card-body { padding: var(--space-4); }

.ys-card__label {
    font-size: 0.8rem;
    color: var(--color-muted);
    font-weight: 500;
    margin-bottom: 4px;
}

.ys-card__hint {
    font-size: 0.75rem;
    color: var(--color-muted-soft);
    margin-bottom: var(--space-2);
}

.ys-card--plot .card-body,
.ys-card__plot-body {
    padding: var(--space-2);
}

/* Resizable plot cards. A JS-injected grab handle (see
   /assets/resize.js) drives the resize via mouse events so we can
   keep ``overflow: visible`` — native CSS resize would force
   ``overflow: hidden`` and clip child overlays like the X picker
   dropdown menu. */
.ys-card--plot {
    position: relative;
    display: flex;
    flex-direction: column;
    /* Default tall enough to give a comfortable plot area after
       reserving the top + bottom picker strips on cards that
       have overlay pickers. Any card can be resized via the
       bottom-right grab handle. */
    height: 696px;
    min-height: 260px;
    min-width: 320px;
}

/* Bottom-right grab handle — small diagonal-line glyph painted via
   gradient. Only meaningful for cards in flex containers (every
   plot card) since dragging sets explicit inline width / height. */
.ys-resize-handle {
    position: absolute;
    bottom: 2px;
    right: 2px;
    width: 14px;
    height: 14px;
    cursor: nwse-resize;
    z-index: 60;
    opacity: 0.45;
    transition: opacity 0.15s ease;
    background-image: linear-gradient(
        135deg,
        transparent 0%, transparent 40%,
        var(--color-muted) 40%, var(--color-muted) 50%,
        transparent 50%, transparent 65%,
        var(--color-muted) 65%, var(--color-muted) 75%,
        transparent 75%
    );
}

.ys-resize-handle:hover { opacity: 1; }

.ys-card--plot > .card-body,
.ys-card--plot .ys-card__plot-body {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: var(--space-2);
}

/* Every direct descendant of card-body — and the descendants those
   wrappers create — needs to grow + zero out its min-height so the
   chain reaches dcc.Graph, which now ships height:100% inline. */
.ys-card--plot > .card-body > *,
.ys-card--plot .ys-card__plot-body > * {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
}

.ys-card--plot .ma-scatter-wrap {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    position: relative;
    /* Reserve top and bottom strips for the overlay X / Y pickers.
       The pickers sit at top:6px / bottom:6px of this wrap; the
       padding here pushes the plot's drawable area inward so the
       pickers occupy unused space instead of sitting over the
       data. Bottom is a little taller to also clear Plotly's
       tick-label band. */
    padding-top: 60px;
    padding-bottom: 100px;
    box-sizing: border-box;
}

/* dcc.Loading renders a double-div nest:
     <div class="dash-spinner-container">           ← outer
       <div class="_dash-loading[-callback]">       ← inner
         <Graph .../>
       </div>
     </div>
   We need both levels to pass the height through so dcc.Graph's
   inline ``height: 100%`` actually resolves to something.
   The ``:not([class*="picker"]):not([class*="controls"])`` filter
   excludes the absolute-positioned overlay divs (Y picker, X
   picker, box-plot bins / switch controls), which would otherwise
   stretch to fill the entire wrap. */
.ys-card--plot .ma-scatter-wrap > div:not([class*="picker"]):not([class*="controls"]):not([class*="overlay"]) {
    flex: 1 1 0;
    min-height: 0;
    height: 100%;
}

.ys-card--plot .ma-scatter-wrap > div:not([class*="picker"]):not([class*="controls"]):not([class*="overlay"]) > div {
    height: 100%;
    min-height: 0;
}

.ys-card--plot .js-plotly-plot {
    flex: 1 1 0;
    min-height: 0;
    width: 100% !important;
    height: 100% !important;
}

/* Lightweight scatter wrapper used by the Summary tab's Trend-by-age
   card. Same height-passing chain as .ma-scatter-wrap but without
   the picker padding — this card only carries an overlay checkbox,
   no absolute-positioned axis pickers. Without these rules the
   dcc.Loading wrappers stay at auto height and corner-drag resize
   on the card stretches the box but not the plot inside it. */
.ys-card--plot .ys-plot-wrap {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    position: relative;
}

.ys-card--plot .ys-plot-wrap > div:not([class*="overlay"]) {
    flex: 1 1 0;
    min-height: 0;
    height: 100%;
}

.ys-card--plot .ys-plot-wrap > div:not([class*="overlay"]) > div {
    height: 100%;
    min-height: 0;
}

/* ---- KPI tiles ---- */

.ys-kpi-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-3);
}

@media (max-width: 575.98px) {
    .ys-kpi-row { grid-template-columns: 1fr; }
}

.kpi-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease;
}

.kpi-card:hover { box-shadow: var(--shadow-md); }

.kpi-card__title {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-muted);
    font-weight: 600;
    margin-bottom: var(--space-2);
}

.kpi-card__value {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--color-text-heading);
    line-height: 1.1;
}

.kpi-card__hint {
    font-size: 0.78rem;
    color: var(--color-muted);
    margin-top: 6px;
}

/* ---- Plot row (two plots side by side, stack on narrow) ----
   Uses flexbox so a user-drag on the left card's resize handle
   shrinks it and the right card flows in to fill the freed width.
   Same class used everywhere — Analysis (scatter + box), Explorer
   (KDE + age trend), Growth Curves (M + F), etc. */

.ys-plots-row {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: var(--space-3);
    /* Without this, the flex default ``align-items: stretch`` makes a
       collapsed card snap back to the row's tallest sibling height,
       defeating the point of collapsing. */
    align-items: flex-start;
}

/* First child starts at 50% of the row but uses ``flex: 0 0
   auto`` so the inline ``width`` set by the user-drag resize
   takes effect. Second child flexes to fill what's left. */
.ys-plots-row > :first-child {
    flex: 0 0 auto;
    width: 50%;
    min-width: 0;
}

.ys-plots-row > :nth-child(2) {
    flex: 1 1 0;
    min-width: 0;
}

@media (max-width: 767.98px) {
    .ys-plots-row { flex-direction: column; }
    .ys-plots-row > :first-child { flex-basis: auto; }
}

/* Stack the plot cards as soon as the parent column (``.ys-main``)
   gets narrower than roughly two 360-px cards plus a gap. This is
   the wider-of-the-two responsive triggers: it kicks in even when
   the viewport is broad (e.g. wide window, narrow main column due
   to a long sidebar tree) — whereas the media query above only
   handles a narrow viewport. */
@container ys-main (max-width: 760px) {
    .ys-plots-row { flex-direction: column; flex-wrap: nowrap; }
    .ys-plots-row > :first-child {
        flex-basis: auto;
        width: 100%;
    }
}

/* ---- Body image ---- */

.ys-bodyimage-wrap {
    display: flex;
    justify-content: center;
    padding: var(--space-2);
    background: #ffffff;
    border-radius: var(--radius-sm);
}

.ys-bodyimage { max-height: 220px; max-width: 100%; }

/* ---- Visual menu (dimension picker) ---- */

.ys-visual-menu .btn {
    width: 100%;
    text-align: left;
    background: var(--color-surface) !important;
    border: 1px solid var(--color-border) !important;
    color: var(--color-text) !important;
    font-weight: 500;
}

.ys-visual-menu .btn:hover {
    background: var(--color-primary-soft) !important;
    border-color: var(--color-border-strong) !important;
}

/* ---- Radios / checkboxes ---- */

.ys-radio-group .form-check { margin-right: var(--space-4); }
.ys-radio-group .form-check-label { font-size: 0.875rem; }

.ys-checklist label { font-size: 0.85rem; }
.ys-checklist--stack { display: flex; flex-direction: column; gap: 4px; }

/* ---- Regression prediction badge ---- */

.ys-prediction {
    margin: 0 !important;
    padding: var(--space-2) var(--space-3) !important;
    background: var(--color-surface-alt) !important;
    border: 1px solid var(--color-border) !important;
    color: var(--color-text-heading) !important;
    font-weight: 600;
    text-align: center;
    border-radius: var(--radius-sm) !important;
}

.ys-model-summary {
    background: var(--color-surface-alt) !important;
    border: 1px solid var(--color-border) !important;
    color: var(--color-text) !important;
    font-family: 'JetBrains Mono', 'Menlo', 'Consolas', monospace;
    font-size: 0.78rem;
    white-space: pre-wrap;
    max-height: 400px;
    overflow-y: auto;
}

/* ---- Accordion (by-age tables) ---- */

.accordion-button {
    background: var(--color-surface) !important;
    color: var(--color-text) !important;
    font-weight: 500 !important;
    box-shadow: none !important;
}

.accordion-button:not(.collapsed) {
    background: var(--color-primary-soft) !important;
    color: var(--color-primary) !important;
}

.accordion-item { border-color: var(--color-border) !important; }


/* ============================================================
   Collapsible card
   ============================================================ */

.ys-collapsible__header {
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    transition: background 0.12s ease;
}

.ys-collapsible__header:hover {
    background: var(--color-primary-soft);
}

.ys-collapsible__title {
    flex: 1;
    /* Inherit the card-header's font sizing (uppercase muted). */
}

.ys-collapsible__chevron {
    font-size: 0.85rem;
    color: var(--color-muted);
    flex-shrink: 0;
    transition: transform 0.15s ease;
}


/* ============================================================
   Dimension picker — compact card + on-demand modal browser
   ============================================================ */

.ys-dim-picker-wrap { display: contents; }

.ys-dim-picker .card-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.ys-dim-picker__browse-link {
    appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    margin-top: 4px;
    color: var(--color-primary);
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    align-self: flex-start;
    text-align: left;
    transition: color 0.15s ease;
}

.ys-dim-picker__browse-link:hover {
    color: var(--color-primary-hover);
    text-decoration: underline;
}

.ys-dim-picker__browse-icon {
    margin-right: 2px;
    font-size: 0.85rem;
}

.ys-dim-picker__browse-count {
    color: var(--color-muted);
    font-weight: 400;
    margin-left: 2px;
}

/* ---- Modal frame ---- */

.ys-dim-picker__modal .modal-content {
    border-radius: var(--radius-lg);
    border: 1px solid var(--color-border);
    box-shadow: var(--shadow-md);
}

.ys-dim-picker__modal .modal-header {
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface-alt);
}

.ys-dim-picker__modal .modal-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-text-heading);
}

.ys-dim-picker__modal .modal-body {
    padding: var(--space-3);
}

.ys-dim-picker__search {
    margin-bottom: var(--space-2);
    font-size: 0.9rem;
}

.ys-dim-picker__tabs .nav-link {
    font-size: 0.82rem;
    padding: 4px 12px;
}

.ys-dim-picker__pane {
    padding: 4px 0;
    max-height: 70vh;
    overflow-y: auto;
}

.ys-dim-picker__empty {
    color: var(--color-muted);
    padding: var(--space-3);
    text-align: center;
    font-style: italic;
}

.ys-dim-picker__category {
    font-size: 0.66rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-muted);
    margin: 10px 0 4px;
    padding-bottom: 2px;
    border-bottom: 1px solid var(--color-border);
}

.ys-dim-picker__category:first-of-type { margin-top: 0; }

/* ---- List view ---- */

.ys-dim-picker__list-items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 2px;
}

.ys-dim-picker__list-item {
    appearance: none;
    border: 1px solid transparent;
    background: transparent;
    color: var(--color-text);
    text-align: left;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    font-size: 0.82rem;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease;
}

.ys-dim-picker__list-item:hover {
    background: var(--color-primary-soft);
    border-color: var(--color-primary);
    color: var(--color-primary);
}

/* ---- Visual grid ---- */

.ys-dim-picker__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 6px;
}

.ys-dim-picker__grid-item {
    appearance: none;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    border-radius: var(--radius-sm);
    padding: 4px 4px 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    cursor: pointer;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.ys-dim-picker__grid-item:hover {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-sm);
}

.ys-dim-picker__thumb-wrap {
    width: 100%;
    aspect-ratio: 1 / 1;
    background: var(--color-surface-alt);
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.ys-dim-picker__thumb-img {
    max-width: 100%;
    max-height: 100%;
}

.ys-dim-picker__thumb-label {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--color-text);
    text-align: center;
    line-height: 1.15;
}


/* ============================================================
   Percentile ↔ value card (compact)
   ============================================================ */

.qs-card .card-body { padding: var(--space-3) var(--space-4); }

.qs-row { margin: 0 !important; }

.qs-col {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

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

.qs-block__units {
    text-transform: none;
    font-weight: 400;
    color: var(--color-muted-soft);
    margin-left: 2px;
}

/* Slider needs vertical room above for the always-visible tooltip. */
.qs-slider { margin: 22px 4px 0; }

.qs-readout-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 6px;
}

.qs-readout-row {
    display: grid;
    grid-template-columns: 56px 1fr auto;
    gap: 8px;
    align-items: baseline;
    padding: 2px 0;
    font-size: 0.85rem;
}

.qs-readout-row__label {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-muted);
}

.qs-readout-row__value {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--color-primary);
    text-align: right;
    line-height: 1.1;
}

.qs-readout-row__units {
    font-size: 0.75rem;
    color: var(--color-muted);
    text-align: left;
}

/* Colour each row's value by sex so M/F are visually distinct. */
.qs-readout-list .qs-readout-row:nth-child(1) .qs-readout-row__value { color: var(--color-male); }
.qs-readout-list .qs-readout-row:nth-child(2) .qs-readout-row__value { color: var(--color-female); }

/* ---- Inline layout: slider and readouts on the same line ---- */
.qs-inline {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: var(--space-3);
}

.qs-inline__slider {
    min-width: 0;            /* allow the slider to shrink below its content size */
}

.qs-inline__slider .qs-slider {
    margin: 22px 0 0;        /* keep room above for the tooltip */
}

.qs-inline .qs-readout-list {
    margin-top: 0;
}

/* On narrow widths the side-by-side breaks down; stack again. */
@media (max-width: 575.98px) {
    .qs-inline { grid-template-columns: 1fr; }
    .qs-inline .qs-readout-list { margin-top: 8px; }
}


/* ============================================================
   Distribution headline table
   ============================================================ */

.ys-distribution-card .card-body { padding: var(--space-3) var(--space-4); }

.ys-distribution-table-wrap {
    overflow-x: auto;        /* horizontal scroll on narrow widths */
}

.ys-distribution-table {
    margin: 0;
    font-size: 0.85rem;
    table-layout: auto;
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
}

.ys-distribution-table thead th {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border) !important;
    padding: 6px 10px;
    text-align: right;
    white-space: nowrap;
}

.ys-distribution-table thead th:first-child {
    text-align: left;
}

.ys-distribution-table tbody td {
    padding: 6px 10px;
    text-align: right;
    border-top: 1px solid var(--color-border);
    font-variant-numeric: tabular-nums;
}

.ys-distribution-table tbody td:first-child {
    text-align: left;
    font-weight: 600;
    color: var(--color-text-heading);
}

/* Units column (right-most) — left-align, muted, italic so it reads as
   metadata rather than a numeric column. */
.ys-distribution-table thead th:last-child,
.ys-distribution-table tbody td:last-child {
    text-align: left;
    color: var(--color-muted);
    font-style: italic;
    padding-left: var(--space-3);
    border-left: 1px solid var(--color-border);
    white-space: nowrap;
}

/* Tint the row label by sex. */
.ys-distribution-table tbody tr:nth-child(1) td:first-child { color: var(--color-male); }
.ys-distribution-table tbody tr:nth-child(2) td:first-child { color: var(--color-female); }
.ys-distribution-table tbody tr:nth-child(3) td:first-child { color: var(--color-muted); }

.ys-distribution-table tbody tr:hover td {
    background: var(--color-primary-soft);
}

/* ---- Multivariate Accommodation tab ---- */

.ma-bounds-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 12px 0 8px 0;
}

.ma-bound-header,
.ma-bound-row {
    display: grid;
    grid-template-columns:
        24px                    /* arrows */
        minmax(0, 1.4fr)        /* dimension name */
        72px                    /* kind selector (Both / ≥ Lower / ≤ Upper) */
        102px                   /* lower input + lock button */
        102px                   /* upper input + lock button */
        minmax(36px, 0.35fr)    /* units */
        minmax(120px, 1.6fr)    /* M / F stacked marginals */
        minmax(120px, 1.6fr)    /* cumulative through-row */
        28px;                   /* delete */
    align-items: center;
    gap: 8px;
}

.ma-bound-header {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    padding: 0 0 4px 0;
    border-bottom: 1px solid var(--color-border);
}

.ma-bound-header__value { text-align: center; }
.ma-bound-header__bar   { padding-left: 4px; }

.ma-bound-row__name {
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.ma-bound-row__name-text {
    font-size: 0.85rem;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ma-bound-row__coverage {
    font-size: 0.66rem;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ma-coverage--good { color: var(--color-muted); }
.ma-coverage--warn { color: #b45309; }
.ma-coverage--bad  { color: #b91c1c; }

.ma-bound-input {
    text-align: right;
    font-variant-numeric: tabular-nums;
    padding: 4px 8px;
}

/* Upper-input cell wraps the input plus the (hidden by default)
   lock button into a flex row so the input flexes and the lock
   sits flush on its right edge. */
.ma-input-with-lock {
    display: flex;
    align-items: center;
    gap: 4px;
    width: 100%;
}

.ma-input-with-lock .ma-bound-input {
    flex: 1;
    min-width: 0;
}

/* The lock is only meaningful while auto-balance is on, so the
   button is hidden until ``ma-bounds-list`` gets the
   ``ma-autobalance-on`` class. */
.ma-row-lock {
    display: none;
    background: transparent;
    border: 0;
    padding: 2px 4px;
    color: var(--color-muted);
    cursor: pointer;
    font-size: 0.78rem;
    line-height: 1;
    flex: 0 0 auto;
}

.ma-row-lock:hover { color: var(--color-primary); }

.ma-row-lock--locked { color: var(--color-primary); }

.ma-autobalance-on .ma-row-lock { display: inline-flex; }

.ma-bound-row__units {
    font-size: 0.75rem;
    color: var(--color-muted);
    text-align: left;
    white-space: nowrap;
}

.ma-cum-bar-slot {
    width: 100%;
}

/* Each bar fills its own column. The cumulative bar is the
   "headline" so it gets full height; the per-sex marginal bars are
   half height since they're supplementary detail. */
.ma-cum-bar {
    position: relative;
    background: #eef2f7;
    border-radius: 4px;
    overflow: hidden;
    border: 1px solid #e5edf6;
}

.ma-cum-bar--cum { height: 28px; }
.ma-cum-bar--m,
.ma-cum-bar--f   { height: 14px; }

/* M and F stack in one cell with a hairline gap between them. */
.ma-bound-mf-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 100%;
}

.ma-cum-bar__fill {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    transition: width 0.18s ease;
}

.ma-cum-bar__fill--cum {
    background: linear-gradient(90deg, #1e6fdc, #4d8fea);
}
.ma-cum-bar__fill--m { background: var(--color-male); }
.ma-cum-bar__fill--f { background: var(--color-female); }

.ma-cum-bar__label {
    position: absolute;
    top: 0;
    right: 6px;
    color: #0f172a;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 0 2px rgba(255, 255, 255, 0.85);
}

.ma-cum-bar--cum .ma-cum-bar__label {
    line-height: 28px;
    font-size: 0.8rem;
    font-weight: 700;
}

.ma-cum-bar--m .ma-cum-bar__label,
.ma-cum-bar--f .ma-cum-bar__label {
    line-height: 14px;
    font-size: 0.7rem;
    font-weight: 700;
    /* Heavier shadow + outline keeps the small label legible even
       when the fill covers it. */
    text-shadow:
        0 0 2px rgba(255,255,255,0.95),
        0 0 4px rgba(255,255,255,0.7);
}

.ma-cum-bar__none {
    font-size: 0.75rem;
    color: var(--color-muted);
    text-align: center;
    line-height: 22px;
}

/* Summary headline card */
.ma-summary-card {
    background: linear-gradient(135deg, var(--color-primary-soft), #ffffff);
}

.ma-summary__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: var(--space-2) 0;
}

.ma-summary__numbers {
    display: flex;
    align-items: baseline;
    gap: 2px;
}

.ma-summary__pct {
    font-size: 3.2rem;
    font-weight: 800;
    line-height: 1;
    color: var(--color-primary);
    font-variant-numeric: tabular-nums;
}

.ma-summary__pct-suffix {
    font-size: 1.6rem;
    font-weight: 600;
    color: var(--color-primary);
}

.ma-summary__desc {
    font-size: 0.85rem;
    color: var(--color-muted);
    text-align: center;
}

.ma-summary__split {
    display: flex;
    gap: 8px;
    font-size: 0.85rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.ma-summary__split-sep {
    color: var(--color-muted);
    font-weight: 400;
}

/* Auto-balance gap chip — shown when current joint pass rate
   disagrees with the target. Headline % also recolors. */
.ma-summary__pct--off-target { color: #b45309 !important; }

.ma-summary__gap {
    margin-top: 6px;
    padding: 4px 10px;
    border-radius: 6px;
    font-size: 0.78rem;
    line-height: 1.35;
    max-width: 100%;
    text-align: center;
}

.ma-summary__gap--low {
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fcd34d;
}

.ma-summary__gap--high {
    background: #dbeafe;
    color: #1e40af;
    border: 1px solid #93c5fd;
}

/* Scatter X/Y picker row */
/* Row arrow buttons (up / down) */
.ma-row-arrows {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.ma-row-arrow {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 3px;
    color: var(--color-muted);
    font-size: 0.55rem;
    line-height: 1;
    padding: 1px 4px;
    cursor: pointer;
    height: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ma-row-arrow:hover {
    background: var(--color-primary-soft);
    color: var(--color-primary);
    border-color: var(--color-primary);
}

/* Per-row delete button */
.ma-row-del {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    color: var(--color-muted);
    font-size: 0.95rem;
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
}

.ma-row-del:hover {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #dc2626;
}

/* Kind selector (Both / ≥ Lower / ≤ Upper) */
.ma-bound-kind {
    font-size: 0.78rem;
    padding: 2px 24px 2px 6px !important;
    height: 30px;
    background-position: right 4px center;
}

/* Compact "Add a dimension" picker — strip the card chrome so it
   reads as the bottom of the constraint list rather than its own
   card. */
.ma-add-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-muted);
    margin: 8px 0 4px 0;
}

/* Light horizontal rules above and below set the picker zone off
   from the constraints table above and the design bar below
   without making it look like a separate card. */
.ma-add-picker {
    border-top:    1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    padding: 10px 0;
    margin: 8px 0;
}

.ma-add-picker .ys-dim-picker,
.ma-add-picker .ys-dim-picker .card,
.ma-add-picker .ys-dim-picker > .card {
    border: 0;
    box-shadow: none;
    background: transparent;
}

.ma-add-picker .card-header { display: none; }
.ma-add-picker .card-body   { padding: 0; }

.ma-divider {
    border-top: 1px dashed var(--color-border);
    margin: 12px 0 8px 0;
}

/* Auto-balance toggle row sits just above the candidate-design bar. */
.ma-autobalance-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 6px 0 8px 0;
}

.ma-auto-balance-switch label {
    font-size: 0.85rem;
    color: var(--color-text);
    cursor: pointer;
}

/* Candidate design bar */
.ma-design-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 4px;
}

.ma-design-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text);
}

.ma-design-target {
    width: 70px !important;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ma-design-suffix {
    font-size: 0.85rem;
    color: var(--color-muted);
}

.ma-design-btn {
    /* Button anchors the left of the design bar; Target gets the
       auto-margin so it floats to the right. */
}

/* ---- Inline help icon + popover (core/ui_help.py) ---- */

.ys-help-wrap {
    display: inline-flex;
    align-items: center;
}

.ys-help-icon {
    background: transparent;
    border: 0;
    padding: 0 4px;
    color: var(--color-muted);
    cursor: pointer;
    font-size: 0.85em;
    line-height: 1;
    transition: color 0.12s ease;
}

.ys-help-icon:hover,
.ys-help-icon:focus { color: var(--color-primary); outline: none; }

.ys-help-popover {
    box-shadow: var(--shadow-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

/* Padding belt-and-braces: set on both Bootstrap's popover-body
   wrapper *and* the dcc.Markdown wrapper inside, so whichever ends
   up being the inner padding host gets it. */
.ys-help-popover .popover-body,
.ys-help-popover > .popover-body {
    padding: 18px 22px !important;
    color: var(--color-text);
    font-size: 0.85rem;
    line-height: 1.55;
}

.ys-help-md {
    padding: 6px 4px;     /* extra in case popover-body isn't matched */
    margin: 0;
}

.ys-help-md p              { margin: 0 0 12px 0; }
.ys-help-md p:last-child   { margin: 0; }
.ys-help-md ul, .ys-help-md ol { margin: 0 0 12px 24px; padding: 0; }
.ys-help-md li             { margin: 4px 0; }
.ys-help-md code           {
    background: #f1f5f9;
    padding: 1px 4px;
    border-radius: 3px;
    font-size: 0.85em;
}

.ma-design-hint {
    font-size: 0.72rem;
    color: var(--color-muted);
    margin: 4px 0 0 0;
}

/* Reset is now a real outline button sitting to the left of the
   Generate Candidate Design button in the design bar — no special
   styling required beyond the dbc default. */

/* Scatter overlay pickers — sit where the axis titles normally do. */
.ma-scatter-wrap {
    position: relative;
}

.ma-scatter-y-picker,
.ma-scatter-x-picker {
    position: absolute;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 2px 6px;
    box-shadow: var(--shadow-sm);
}

.ma-scatter-y-picker {
    top: 6px;
    left: 8px;
    width: 220px;
}

.ma-scatter-x-picker {
    bottom: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 260px;
}

.ma-scatter-axis-label {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--color-text);
}

.ma-scatter-y-picker .Select,
.ma-scatter-x-picker .Select,
.ma-scatter-y-picker .dash-dropdown,
.ma-scatter-x-picker .dash-dropdown {
    flex: 1;
}

/* Full dimension-picker (factory) used as an overlay on the
   Analysis-tab scatter. We position the wrapper absolutely on the
   plot, then strip the picker's card chrome (header, padding,
   border) so what shows through is just the dropdown + "Browse all
   dimensions (N)" link. */
.analysis-axis-y-picker,
.analysis-axis-x-picker {
    position: absolute;
    z-index: 50;
    background: rgba(255, 255, 255, 0.95);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 6px 8px;
    box-shadow: var(--shadow-sm);
}

.analysis-axis-y-picker { top: 6px;    left: 8px;  width: 260px;
                          max-width: calc(100% - 16px); }
.analysis-axis-x-picker { bottom: 6px; left: 50%;  width: 280px;
                          max-width: calc(100% - 16px);
                          transform: translateX(-50%); }

/* Box-card variant: anchor the X picker on the left so the bins +
   switch-grouping controls have room on the right. */
.analysis-axis-x-picker--bottom-left {
    left: 8px;
    transform: none;
}

/* On narrow box-plot cards the top-left Y picker and the top-right
   controls panel would collide; shrink the Y picker so they coexist. */
@container ys-main (max-width: 760px) {
    .ys-card--plot .analysis-axis-y-picker { width: 180px; }
}

.analysis-axis-y-picker .ys-dim-picker,
.analysis-axis-x-picker .ys-dim-picker,
.analysis-axis-y-picker .ys-dim-picker .card,
.analysis-axis-x-picker .ys-dim-picker .card {
    border: 0;
    box-shadow: none;
    background: transparent;
}

.analysis-axis-y-picker .card-header,
.analysis-axis-x-picker .card-header { display: none; }

.analysis-axis-y-picker .card-body,
.analysis-axis-x-picker .card-body { padding: 0; }

/* "Browse all dimensions (N)" link compacted for the overlay. */
.analysis-axis-y-picker .ys-dim-picker__browse-link,
.analysis-axis-x-picker .ys-dim-picker__browse-link {
    margin-top: 2px;
    font-size: 0.72rem;
}

/* Box-plot controls split:
     - bottom-right ``.analysis-box-controls`` holds Bins + Grouping
       (compact column).
     - top-right ``.analysis-box-top-controls`` holds plot style
       (Box / Violin) + Show points.
   Splitting them this way leaves the bottom edge mostly free so
   the X picker (bottom-left) has room when the card is narrow. */
.analysis-box-controls {
    position: absolute;
    bottom: 6px;
    right: 8px;
    z-index: 50;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 6px;
    box-shadow: var(--shadow-sm);
    max-width: calc(100% - 16px);
    min-width: 0;
}

.analysis-box-top-controls {
    position: absolute;
    top: 6px;
    right: 8px;
    z-index: 50;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 6px;
    box-shadow: var(--shadow-sm);
    max-width: calc(100% - 16px);
    min-width: 0;
    font-size: 0.78rem;
}

.analysis-box-top-controls .form-check {
    margin-bottom: 0;
}

/* Cap the X picker so the bottom row is shared with the controls. */
.ys-card--plot .analysis-axis-x-picker--bottom-left {
    max-width: calc(100% - 140px);
}

/* Bins row: label + spinbox on one line. */
.analysis-box-controls__row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.analysis-box-controls__label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--color-text);
}

.analysis-box-bins {
    width: 60px !important;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.analysis-box-switch-grouping {
    font-size: 0.72rem !important;
    padding: 2px 8px !important;
}

.analysis-box-style {
    font-size: 0.75rem !important;
    padding: 2px 24px 2px 6px !important;
    height: 28px !important;
}

.analysis-box-show-points label {
    font-size: 0.75rem;
    color: var(--color-text);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
}

/* Envelope-overlay panel for the secondary accommodation scatter.
   Bottom-right of the plot, matching the box-controls chrome. */
.ma-scatter2-overlay {
    position: absolute;
    bottom: 18px;
    right: 16px;
    z-index: 50;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 6px 10px;
    box-shadow: var(--shadow-sm);
    font-size: 0.78rem;
    min-width: 132px;
}
.ma-scatter2-overlay__label {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 4px;
}
.ma-scatter2-overlay__items .form-check {
    margin-bottom: 0;
    padding-left: 1.4em;
    line-height: 1.4;
}
.ma-scatter2-overlay__items label {
    cursor: pointer;
}

/* Bottom-right overlay on the scatter card stacks the Swap-axes
   button on top of the Show-legend checkbox. */
.analysis-scatter-overlay-stack {
    position: absolute;
    bottom: 18px;
    right: 16px;
    z-index: 50;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
}

.analysis-scatter-swap-btn {
    font-size: 0.72rem !important;
    padding: 2px 8px !important;
}

/* Show-legend checkbox. Positioning is now provided by
   ``.analysis-scatter-overlay-stack``; this rule only chrome-styles
   the checkbox pill. */
.analysis-scatter-legend-controls {
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 10px;
    box-shadow: var(--shadow-sm);
}

.analysis-scatter-legend-controls label {
    font-size: 0.78rem;
    color: var(--color-text);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    margin: 0;
}

/* Growth Curves — per-cohort empirical-quantiles table. Each
   ``gc-table-wrap`` holds one sex section (label + table). Add
   vertical breathing room between the Male and Female blocks. */
.gc-table-wrap + .gc-table-wrap {
    margin-top: 24px;
}

.gc-table__sex-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 6px;
}

/* ============================================================
   Correlations tab
   ------------------------------------------------------------
   Dim list + matrix table. Same row pattern as the accommodation
   bounds list but trimmed to dim name + coverage + arrows + ×.
   ============================================================ */

.corr-status {
    padding: 0 2px 12px 2px;
    font-size: 0.85rem;
    color: var(--color-text);
}

.corr-dim-list { margin-bottom: 10px; }

.corr-dim-header,
.corr-dim-row {
    display: grid;
    grid-template-columns:
        48px                  /* arrows */
        minmax(0, 1fr)        /* dimension name */
        72px                  /* coverage */
        28px;                 /* delete */
    align-items: center;
    gap: 10px;
    padding: 4px 6px;
}

.corr-dim-header {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    border-bottom: 1px solid var(--color-border);
}

.corr-dim-row + .corr-dim-row,
.corr-dim-header + .corr-dim-row {
    border-top: 1px solid #f1f5f9;
}

.corr-dim-row:hover { background: #f8fafc; }

.corr-dim-empty {
    padding: 14px 6px;
    color: var(--color-muted);
    font-size: 0.85rem;
}

.corr-dim-name {
    font-size: 0.88rem;
    color: var(--color-text);
}

.corr-dim-arrows {
    display: flex;
    gap: 2px;
}

.corr-row-arrow {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    width: 22px;
    height: 22px;
    font-size: 0.7rem;
    line-height: 1;
    color: var(--color-muted);
    cursor: pointer;
    padding: 0;
}
.corr-row-arrow:hover {
    color: var(--color-text);
    background: #f1f5f9;
}

.corr-row-del {
    background: transparent;
    border: none;
    color: var(--color-muted);
    font-size: 1rem;
    width: 24px;
    height: 24px;
    cursor: pointer;
    padding: 0;
}
.corr-row-del:hover { color: #dc2626; }

.corr-cov--good  { color: #047857; font-weight: 600; }
.corr-cov--warn  { color: #b45309; font-weight: 600; }
.corr-cov--bad   { color: #b91c1c; font-weight: 600; }

/* Add-picker block — matches the accommodation add-picker chrome. */
.corr-add-picker {
    margin: 8px 0 16px 0;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    gap: 14px;
}
.corr-add-label {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    flex: 0 0 auto;
}
.corr-add-picker > :last-child { flex: 1 1 auto; min-width: 0; }
.corr-add-picker .ys-dim-picker,
.corr-add-picker .ys-dim-picker .card {
    border: 0;
    background: transparent;
    box-shadow: none;
}
.corr-add-picker .card-header { display: none; }
.corr-add-picker .card-body   { padding: 0; }

/* Matrix table — standard row height, light pastel cell tints,
   no forced square aspect. */
.corr-matrix-wrap {
    margin-top: 12px;
    overflow-x: auto;
}

.corr-table {
    border-collapse: collapse;
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
}
.corr-table .corr-th,
.corr-table .corr-td {
    padding: 6px 10px;
    border: 1px solid #e5e7eb;
    text-align: right;
    white-space: nowrap;
}
.corr-th--corner {
    background: #f8fafc;
    border-top-color: transparent;
    border-left-color: transparent;
}
.corr-th--col {
    background: #f8fafc;
    font-weight: 600;
    color: var(--color-muted);
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    /* Rotate column headers so long dim names fit in narrow
       per-cell widths; the matrix stays compact horizontally even
       at 15 – 20 dims. The th itself stays a normal table cell —
       only the inner span is rotated. */
    vertical-align: bottom;
    text-align: left;
    padding: 6px 4px;
    height: 130px;
    min-width: 42px;
    max-width: 60px;
    overflow: visible;
}
.corr-th--col .corr-th__label {
    display: inline-block;
    transform: rotate(-50deg);
    transform-origin: left bottom;
    white-space: nowrap;
    /* The rotated text overflows the cell — give it room to spill
       upward without enlarging the cell. */
    width: 14px;
    line-height: 1;
}
.corr-th--row {
    background: #f8fafc;
    font-weight: 600;
    color: var(--color-text);
    text-align: left;
    font-size: 0.82rem;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.corr-td--clickable {
    transition: filter 60ms ease-out;
}
.corr-td--clickable:hover {
    filter: brightness(0.92);
    outline: 1px solid #475569;
    outline-offset: -1px;
}

.corr-bivariate-card { margin-top: 12px; }

/* Override the global ``.ys-card--plot > .card-body > *`` rule for
   the options strip in the bivariate card. Without this it inherits
   ``flex: 1 1 0`` and grows to half the card height, collapsing the
   plot to a sliver and overlapping the bottom-right resize handle
   so the drag stops registering. Keep it at natural height. */
.corr-bivariate-card .ys-card__plot-body > .corr-bivariate-options {
    flex: 0 0 auto !important;
    display: block !important;
    width: auto !important;
    padding: 8px 10px 0 10px;
    font-size: 0.8rem;
    color: var(--color-text);
}

.corr-age-adjust-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.corr-age-adjust-row .ys-help-icon { margin-left: 0; }

.corr-age-basis {
    margin: 6px 0 4px 22px;
    font-size: 0.8rem;
}
.corr-age-basis .form-check { margin-bottom: 2px; }

.corr-fit-diag { margin-top: 8px; }
.corr-diag-title {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-muted);
    margin-bottom: 4px;
}
.corr-diag-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}
.corr-diag-table th,
.corr-diag-table td {
    padding: 3px 6px;
    border-bottom: 1px solid #f1f5f9;
    text-align: left;
}
.corr-diag-table th { font-weight: 600; color: var(--color-muted); }
.corr-diag-num { text-align: right !important; }

/* ============================================================
   Report tab
   ------------------------------------------------------------
   "Add to report" button (sits in card headers next to the help
   icon) + the cell list rendered on the Report tab.
   ============================================================ */

.ys-add-to-report {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    width: 22px;
    height: 22px;
    color: var(--color-muted);
    cursor: pointer;
    margin-left: 8px;
    font-size: 0.7rem;
    line-height: 1;
    padding: 0;
}
.ys-add-to-report:hover {
    color: var(--color-primary);
    border-color: var(--color-primary);
    background: var(--color-primary-soft);
}

/* Push the card header trailing items (help icon, add button) to
   the right. */
.ys-card-header-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.ys-card-header-row > .ys-add-to-report,
.ys-card-header-row > .ys-add-to-report-wrap {
    margin-left: auto;
}
.ys-add-to-report-wrap {
    display: inline-flex;
    align-items: center;
}

/* Toolbar + title strip stay pinned to the top of the viewport
   while the cells below scroll. ``position: sticky`` works as long
   as no ancestor sets ``overflow`` other than ``visible`` — the
   card-body / card / page wrappers all keep overflow: visible. The
   strip needs a solid background so cells don't show through it. */
.report-sticky-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--color-surface);
    padding-bottom: 8px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
}

.report-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
}
.report-count {
    margin-left: auto;
    font-size: 0.8rem;
    color: var(--color-muted);
}

.report-empty {
    padding: 24px;
    color: var(--color-muted);
    text-align: center;
    border: 1px dashed var(--color-border);
    border-radius: 6px;
    font-size: 0.9rem;
}

.report-cells-list {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.report-cell {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    background: var(--color-surface);
    padding: 12px 14px;
}
.report-cell--note {
    background: #fffbeb;
    border-color: #fde68a;
}

.report-cell-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}
.report-cell-title {
    font-weight: 600;
    color: var(--color-text-heading);
    font-size: 0.95rem;
}

.report-cell-toolbar {
    margin-left: auto;
    display: flex;
    gap: 4px;
}
.report-cell-btn {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    width: 24px;
    height: 24px;
    color: var(--color-muted);
    cursor: pointer;
    font-size: 0.78rem;
    line-height: 1;
    padding: 0;
}
.report-cell-btn:hover {
    color: var(--color-text);
    background: #f1f5f9;
}
.report-cell-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}
.report-cell-btn--danger:hover { color: #dc2626; border-color: #fecaca; }

.report-caption,
.report-note-render {
    margin-top: 8px;
    padding: 6px 0;
    color: var(--color-text);
    font-size: 0.9rem;
}

.report-note-edit,
.report-caption-edit {
    margin-top: 8px;
    font-family: inherit;
    font-size: 0.9rem;
    padding: 6px 8px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
}

.report-table-wrap { overflow-x: auto; }

.report-prov {
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid #f1f5f9;
    font-size: 0.72rem;
    color: var(--color-muted);
    display: flex;
    flex-wrap: wrap;
    gap: 6px 12px;
}
.report-prov-chip {
    background: #f1f5f9;
    padding: 1px 6px;
    border-radius: 3px;
    font-variant-numeric: tabular-nums;
}
.report-prov-time { margin-left: auto; font-style: italic; }

.report-title-card {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 16px 18px;
    margin-bottom: 18px;
    background: linear-gradient(135deg, #ffffff 0%, #f0f7ff 100%);
    border: 1px solid var(--color-border);
    border-radius: 8px;
}
.report-title-logo {
    height: 60px;
    width: auto;
}
.report-title-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.report-title-headline {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-text-heading);
    letter-spacing: -0.01em;
}
.report-title-copyright {
    font-size: 0.78rem;
    color: var(--color-muted);
}

/* ------------------------------------------------------------
   Print / Save-as-PDF view for the Report tab.
   Hides app chrome (header, sidebar nav, in-card toolbars) so
   the printed page is just the title card + the captured cells
   in order. Browser Print dialog ("Save as PDF") gives a clean
   one-document deliverable without any new server-side deps.
   ------------------------------------------------------------ */
@media print {
    /* Background to white, kill chrome shadows. */
    html, body { background: #ffffff !important; }
    .app-header,
    .app-nav,
    .ys-resize-handle,
    .ys-help-icon,
    .ys-add-to-report,
    .report-toolbar,
    .report-cell-toolbar,
    .ys-card-header-row .ys-add-to-report,
    .corr-bivariate-options,
    #report-toast {
        display: none !important;
    }
    /* Show only the active page-container (router toggles its
       style attribute on the live tabs; ensure print sees only the
       Report container). */
    [data-page]:not([data-page="report"]) { display: none !important; }

    .app-body, .app-main, .ys-page, .ys-main, .ys-row, .ys-main-col {
        display: block !important;
        padding: 0 !important;
        margin: 0 !important;
        overflow: visible !important;
        height: auto !important;
        max-width: 100% !important;
    }
    .card, .ys-card { box-shadow: none !important; border: none !important; }
    .ys-card .card-header { border: none !important; padding: 0 !important; }

    .report-cell {
        page-break-inside: avoid;
        break-inside: avoid;
        border: 1px solid #d1d5db !important;
        margin-bottom: 14px !important;
    }
    .report-title-card {
        page-break-after: avoid;
        background: #ffffff !important;
    }

    /* Plotly canvases need a fixed height when printed; the live
       container heights are flex-driven and collapse on print. */
    .js-plotly-plot,
    .js-plotly-plot .plotly,
    .report-cell .js-plotly-plot { height: 420px !important; }
}

/* Jupyter-style insert-note strips between cells. Resting state is
   a faint hairline so the column reads as a list; hover reveals a
   small button so the affordance is discoverable without clutter. */
.report-insert-strip {
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    margin: -6px 0;
    opacity: 0;
    transition: opacity 0.12s ease;
}
.report-insert-strip::before {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 50%;
    height: 1px;
    background: var(--color-border);
}
.report-insert-strip:hover { opacity: 1; }

.report-insert-btn {
    position: relative;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-muted);
    font-size: 0.74rem;
    border-radius: 999px;
    padding: 2px 10px;
    cursor: pointer;
    line-height: 1;
}
.report-insert-btn:hover {
    color: var(--color-primary);
    border-color: var(--color-primary);
    background: var(--color-primary-soft);
}

/* Always show the first/last strips slightly to advertise the
   affordance when the list is empty / when the user hits the end. */
.report-cells-list > .report-insert-strip:first-child,
.report-cells-list > .report-insert-strip:last-child {
    opacity: 0.6;
}
.report-cells-list > .report-insert-strip:first-child:hover,
.report-cells-list > .report-insert-strip:last-child:hover {
    opacity: 1;
}

@media print {
    .report-insert-strip { display: none !important; }
}

.report-note-help {
    margin-top: 6px;
    font-size: 0.72rem;
    color: var(--color-muted);
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    align-items: center;
}
.report-note-help code {
    font-family: 'SF Mono', 'Menlo', monospace;
    background: #f1f5f9;
    padding: 1px 5px;
    border-radius: 3px;
    font-size: 0.7rem;
    color: var(--color-text);
}
.report-note-render {
    cursor: text;
}
.report-note-render p:last-child { margin-bottom: 0; }

/* Figure cells render at exact source pixel size — let the cell
   scroll horizontally if the captured figure is wider than the
   available width rather than stretching the figure. */
.report-cell--figure { overflow-x: auto; }

/* ============================================================
   Dimension info popup (modal)
   ------------------------------------------------------------
   Two-column layout: definition list on the left, landmark
   image floated on the right when present. Each field renders
   as a small uppercase label above its value; NA fields are
   omitted entirely upstream.
   ============================================================ */
.dim-info-body {
    padding: 4px 2px;
}
.dim-info-layout {
    display: flex;
    gap: 24px;
    align-items: flex-start;
}
.dim-info-text {
    flex: 1 1 0;
    min-width: 0;
}
.dim-info-image-wrap {
    flex: 0 0 auto;
    width: 260px;
    text-align: center;
}
.dim-info-image {
    max-width: 100%;
    max-height: 360px;
    height: auto;
    width: auto;
    object-fit: contain;
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 6px;
}
.dim-info-list {
    margin: 0;
    padding: 0;
}
.dim-info-label {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 0 0 2px 0;
}
.dim-info-value {
    margin: 0 0 16px 0;
    font-size: 0.92rem;
    color: var(--color-text);
    line-height: 1.5;
}
.dim-info-value:last-child { margin-bottom: 0; }
.dim-info-empty {
    color: var(--color-muted);
    font-style: italic;
    padding: 12px 0;
}

@media (max-width: 767.98px) {
    .dim-info-layout { flex-direction: column; }
    .dim-info-image-wrap { width: 100%; }
}

/* ============================================================
   Dim-picker hover info pane
   ------------------------------------------------------------
   The modal body splits into a left column (the picker tabs)
   and a right column (info pane). On hover of any dim card,
   ``assets/dim_hover.js`` writes the dim name to a per-modal
   ``dcc.Store``; the server callback re-renders the info pane
   with the same content the (i) info modal would show, image
   included. The info pane scrolls internally so long descriptions
   stay readable without cropping the cards.
   ============================================================ */
.dim-picker-split {
    display: flex;
    gap: 18px;
    align-items: stretch;
    /* The modal body itself scrolls (Dash sets ``scrollable=True``
       on the Modal); the split-pane sits inside that scroll
       container at its full visible height. */
    min-height: 0;
}
.dim-picker-split__main {
    flex: 1 1 0;
    min-width: 0;
}
.dim-picker-split__info {
    flex: 0 0 320px;
    max-height: calc(100vh - 220px);
    overflow-y: auto;
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 14px 16px;
    align-self: flex-start;
    position: sticky;
    top: 0;
}
.dim-info-pane-placeholder {
    color: var(--color-muted);
    font-size: 0.85rem;
    line-height: 1.5;
}
.dim-info-pane-title {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--color-text-heading);
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
}
/* The info-pane callback returns the same component tree the (i)
   modal does (``utilities.getDimenInfo.getInfoElements``), which is
   already styled by the ``.dim-info-*`` rules added earlier. The
   only adjustment needed is for the layout's image column to drop
   to a compact thumbnail at this width. */
.dim-picker-split__info .dim-info-layout {
    flex-direction: column;
    gap: 14px;
}
.dim-picker-split__info .dim-info-image-wrap {
    width: 100%;
    text-align: center;
}
.dim-picker-split__info .dim-info-image {
    max-height: 220px;
}

@media (max-width: 991.98px) {
    /* On narrower viewports stack the info pane below the picker
       tabs so neither column gets squeezed. */
    .dim-picker-split { flex-direction: column; }
    .dim-picker-split__info {
        flex-basis: auto;
        max-height: none;
        position: static;
    }
}

/* ============================================================
   In-card provenance strip
   ------------------------------------------------------------
   Same chip style the report-cell footer uses (white space → grey,
   label : value with monospace tabular-num). Sits at the bottom
   of a plot card so a screenshot of the card carries the
   dataset / dim / age context without needing a caption.
   ============================================================ */
.ys-prov-strip {
    padding: 4px 10px 2px;
    font-size: 0.72rem;
    color: var(--color-muted);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    border-top: 1px solid #f1f5f9;
    background: var(--color-surface);
}
.ys-prov-strip .ys-prov-chip {
    background: #f1f5f9;
    padding: 1px 6px;
    border-radius: 3px;
    font-variant-numeric: tabular-nums;
}
.ys-prov-strip .ys-prov-db {
    /* Database name spans more characters than a chip; leave it
       unwrapped at the left edge. */
    font-variant-numeric: tabular-nums;
}

/* When the strip sits inside a ``.ys-card__plot-body`` flex
   column, the global rule that grows every direct child equally
   would steal vertical space from the plot. Pin to natural
   height. */
.ys-card__plot-body > .ys-prov-strip {
    flex: 0 0 auto !important;
    display: flex !important;
    flex-direction: row !important;
}

/* ============================================================
   Card collapse toggle (chevron in card header)
   ------------------------------------------------------------
   Rendered when ``card_header(..., collapsible_id=...)`` is set.
   Sits at the LEFT edge of the header so the right-side slot
   (Add-to-report ``+``) stays at the upper right of the card.
   Pattern-matched callback in core/ui_components.py drives the
   toggle.
   ============================================================ */
.ys-card-collapse-toggle {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    width: 26px;
    height: 26px;
    color: var(--color-muted);
    cursor: pointer;
    padding: 0;
    font-size: 1.05rem;
    line-height: 1;
    margin-right: 6px;
}
.ys-card-collapse-toggle:hover {
    color: var(--color-text);
    border-color: var(--color-border-strong);
    background: #f1f5f9;
}

/* Card with a collapsed body: drop any fixed height so the card
   shrinks to just the header. Driven directly off the ``dbc.Collapse``
   element's state via ``:has(> .collapse:not(.show))``. */
.ys-card:has(> .collapse:not(.show)) {
    height: auto !important;
    min-height: 0 !important;
}

/* Propagate plot-card flex-fill through the ``dbc.Collapse`` wrapper.
   Scoped to ``.collapse.show`` / ``.collapsing`` — NOT bare ``.collapse``,
   because Bootstrap sets the closed-state ``.collapse`` to display:none
   and we musn't override that or the body keeps showing when closed. */
.ys-card--plot > .collapse.show,
.ys-card--plot > .collapsing {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.ys-card--plot > .collapse.show > .card-body,
.ys-card--plot > .collapsing > .card-body {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: var(--space-2);
}

/* PCA boundary cases — selected-dimension list + add picker.
   The "Add dimension" picker carries its own card chrome that's
   stripped by ``.pca-add-picker`` so the picker reads as the bottom
   row of the list rather than a nested card. */
.pca-dim-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 8px;
}
.pca-dim-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 8px;
    background: var(--color-surface-alt, #f1f5f9);
    border: 1px solid var(--color-border);
    border-radius: 6px;
}
.pca-dim-row__arrows {
    display: flex;
    flex-direction: column;
    gap: 0;
    flex: 0 0 auto;
}
.pca-dim-row__arrow {
    background: transparent;
    border: none;
    color: var(--color-muted);
    cursor: pointer;
    padding: 0 4px;
    line-height: 1.05;
    font-size: 0.72rem;
}
.pca-dim-row__arrow:hover {
    color: var(--color-text);
}
.pca-dim-row__name {
    flex: 1 1 auto;
    font-size: 0.9rem;
    color: var(--color-text);
    min-width: 0;
    font-weight: 500;
}
.pca-dim-row__minmax {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    color: var(--color-text);
    white-space: nowrap;
}
.pca-dim-row__sep,
.pca-dim-row__units {
    color: var(--color-muted);
}
.pca-dim-row__del {
    flex: 0 0 auto;
    background: transparent;
    border: none;
    color: var(--color-muted);
    cursor: pointer;
    padding: 2px 6px;
    line-height: 1;
}
.pca-dim-row__del:hover {
    color: #b91c1c;
}
.pca-dim-empty {
    font-size: 0.85rem;
}
/* "Add a dimension" block at the bottom of the PCA dim list —
   mirror of the accommodation tab so it reads as a section divider
   rather than a button. */
.pca-add-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-muted);
    margin: 8px 0 4px 0;
}
.pca-add-picker {
    border-top:    1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    padding: 10px 0;
    margin: 8px 0 0 0;
}
.pca-add-picker .ys-dim-picker,
.pca-add-picker .ys-dim-picker .card,
.pca-add-picker .ys-dim-picker > .card {
    border: 0;
    box-shadow: none;
    background: transparent;
}
.pca-add-picker .card-header { display: none; }
.pca-add-picker .card-body   { padding: 0; }

/* Smaller labels on the PCA-settings sidebar checklists / radios so
   the card stays compact and matches the entry-box rhythm. */
.pca-config-card .pca-config-check .form-check-label,
.pca-config-card .pca-config-check label {
    font-size: 0.78rem;
}

/* Z picker + 3D toggle overlay (top-right of the scatter card). */
.pca-scatter-z-overlay {
    position: absolute;
    top: 6px;
    right: 8px;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 8px;
    box-shadow: var(--shadow-sm);
    font-size: 0.78rem;
}
.pca-scatter-z-pick {
    min-width: 160px;
    font-size: 0.78rem;
}
.pca-3d-toggle .form-check {
    margin-bottom: 0;
}

.pca-num-input {
    width: 110px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.pca-cases-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 8px;
}

/* Transfer-to-Accommodation button now lives at the bottom of the
   Dimensions card, just under the add-dimension picker. */
.pca-transfer-row {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
}

/* "Show all points" toggle row shown beneath every scatter card.
   The card-body's flex-fill chain (``.ys-card--plot
   .ys-card__plot-body > *``) forces every child to flex-column with
   ``flex: 1 1 0`` — defeat both so the row stays a single line
   AND doesn't claim plot height. The card-body selectors here win
   on specificity / order. */
.ys-show-all-points-row,
.ys-card--plot .ys-card__plot-body > .ys-show-all-points-row {
    flex: 0 0 auto !important;
    display: flex !important;
    flex-direction: row !important;
    align-items: center;
    gap: 6px;
    width: auto !important;
    padding: 4px 6px 2px 6px;
    font-size: 0.78rem;
    color: var(--color-muted);
}
.ys-show-all-points-row .form-check {
    margin-bottom: 0;
}
.ys-show-all-points-row .ys-help-wrap {
    flex: 0 0 auto;
}
.ys-show-all-points-check .form-check-label {
    color: var(--color-text);
}

/* Generic plot wrapper: position: relative anchor for top-right
   overlay chips on scatter cards that don't already have a
   ``.ma-scatter-wrap``. Fills the flex chain like ma-scatter-wrap
   but without the picker-padding. */
.ys-card--plot .ys-card__plot-body > .ys-plot-wrap {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    position: relative;
    display: flex;
    flex-direction: column;
}

/* Top-right "All points" overlay chip — sibling of the plot wrap,
   absolutely positioned at the top-right corner of the plot area.
   Anchored opposite the Y picker (top-left). */
.ys-show-all-overlay {
    position: absolute;
    top: 6px;
    right: 8px;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 4px;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 8px;
    box-shadow: var(--shadow-sm);
    font-size: 0.78rem;
}
.ys-show-all-overlay .form-check {
    margin-bottom: 0;
}
.ys-show-all-overlay .ys-help-wrap {
    flex: 0 0 auto;
}

.pca-sec-scatter__hint {
    flex: 0 0 auto !important;
    font-size: 0.78rem;
    color: var(--color-muted);
    padding: 4px 6px 6px 6px;
    line-height: 1.35;
}

/* Numeric input + trailing units label, used in the Predict-Y row. */
.ys-input-with-units {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
.ys-input-with-units > .form-control,
.ys-input-with-units > .ys-prediction {
    flex: 1 1 auto;
    min-width: 0;
}
.ys-input-units {
    font-size: 0.78rem;
    color: var(--color-muted);
    white-space: nowrap;
    flex: 0 0 auto;
}
.ys-input-units:empty {
    display: none;
}

/* Variable name at the top of every non-plot output card. */
.ys-var-label {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-text-heading);
    margin-bottom: var(--space-2);
    padding-bottom: var(--space-1);
    border-bottom: 1px solid var(--color-border-soft, var(--color-border));
    letter-spacing: 0.01em;
}
.ys-var-label:empty {
    display: none;
}
