﻿/* ============================================================================
   Vesta Web — shell styles. NO COLOUR VALUES LIVE HERE (issue #892, D2c Wave 8).

   Every colour is var(--*) from the ONE generated token stylesheet
   (Vesta.Core.Theming.ThemeCss → GET /api/theme/tokens.css, with the default
   theme inlined into index.html for the pre-boot paint). Derived tints are
   color-mix() over a token, never a second value.

   This file used to open with a :root block that re-declared --accent,
   --selected, --workspace-accent and --skeleton, with --accent frozen at the
   Dark theme's blue for all nine themes, and carried 110 further literals. If a
   colour here is wrong, fix the palette in Vesta.Core/Theming: editing CSS
   fixes one product and forks the other.
   ========================================================================== */

* {
    box-sizing: border-box;
}

html, body {
    height: 100%;
    margin: 0;
    font-family: 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-size: 13px;
    color: var(--text-1);
    background: var(--surface-app);
}

#app {
    height: 100%;
}

button {
    font-family: inherit;
    font-size: inherit;
    color: inherit;
}

.flex-spacer {
    flex: 1;
}

.icon {
    display: block;
    flex: none;
}

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
}

    .icon-btn:hover:not(:disabled) {
        background: color-mix(in srgb, var(--text-1) 6%, transparent);
        color: var(--text-1);
    }

    .icon-btn:disabled {
        color: var(--text-3);
        cursor: default;
    }

/* ---------------------------------------------------------------- app shell */

.app-shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

.app-header {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 44px;
    padding: 0 14px;
    background: var(--surface-card);
    border-bottom: 1px solid var(--line);
    flex: none;
}

.app-header__brand {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--accent);
}

.app-header__brand-web {
    font-weight: 400;
    color: var(--workspace-accent);
}

.app-header__divider {
    width: 1px;
    height: 20px;
    background: var(--line);
}

.app-header__project {
    font-weight: 600;
}

/* ------------------------------------------------------------ workspace tabs */

.ws-tabs {
    display: flex;
    gap: 2px;
    padding: 8px 12px 0;
    flex: none;
    /* Single desktop-like row: many workspaces scroll horizontally, never wrap. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}

.ws-tab {
    display: flex;
    align-items: center;
    flex: none;
    max-width: 140px;
    padding: 5px 10px;
    font-size: 12px;
    white-space: nowrap;
    border: 1px solid var(--line);
    border-bottom: none;
    border-radius: 6px 6px 0 0;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
    /* Breathing room when scrollIntoView brings a tab to either edge. */
    scroll-margin-left: 12px;
    scroll-margin-right: 12px;
}

.ws-tab__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

    .ws-tab:hover {
        color: var(--text-1);
    }

.ws-tab--active {
    background: var(--surface-card);
    color: var(--text-1);
    font-weight: 600;
    box-shadow: inset 0 2px 0 var(--workspace-accent);
}

/* ------------------------------------------------------------ workspace area */

.workspace-area {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    margin: 0 12px 12px;
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: 0 6px 6px 6px;
}

/* Slim empty bar shown when the workspace isn't time-varying. */
.control-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    height: 36px;
    padding: 0 12px;
    border-bottom: 1px solid var(--line);
    color: var(--text-3);
    flex: none;
}

/* -------------------------------------------------------------- time slider */

.time-slider {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    padding: 0 12px;
    border-bottom: 1px solid var(--line);
    flex: none;
}

.time-slider__play {
    color: var(--text-1);
}

.time-slider__range {
    flex: 1;
    min-width: 80px;
    margin: 0 8px;
    -webkit-appearance: none;
    appearance: none;
    height: 8px;
    border-radius: 4px;
    background: var(--skeleton);
    outline: none;
    cursor: pointer;
}

    .time-slider__range::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: var(--workspace-accent);
        border: 2px solid var(--surface-card);
        box-shadow: 0 1px 3px color-mix(in srgb, var(--shadow-color) 25%, transparent);
        cursor: pointer;
    }

    .time-slider__range::-moz-range-thumb {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: var(--workspace-accent);
        border: 2px solid var(--surface-card);
        box-shadow: 0 1px 3px color-mix(in srgb, var(--shadow-color) 25%, transparent);
        cursor: pointer;
    }

    .time-slider__range::-moz-range-track {
        height: 8px;
        border-radius: 4px;
        background: var(--skeleton);
    }

.time-slider__speed {
    min-width: 40px;
    padding: 3px 8px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: transparent;
    color: var(--text-2);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

    .time-slider__speed:hover {
        background: color-mix(in srgb, var(--text-1) 6%, transparent);
        color: var(--text-1);
    }

.time-slider__loop--active {
    color: var(--workspace-accent);
    background: color-mix(in srgb, var(--workspace-accent) 12%, transparent);
}

    .time-slider__loop--active:hover:not(:disabled) {
        color: var(--workspace-accent);
        background: color-mix(in srgb, var(--workspace-accent) 20%, transparent);
    }

.time-slider__label {
    min-width: 64px;
    text-align: right;
    font-weight: 600;
    color: var(--text-1);
    white-space: nowrap;
}

/* Jump to a date (D2c Wave 4): the web mechanism for the desktop control bar's date
   dialog. Sits beside the clock label and stays chrome-toned, not plot-toned. */
.time-slider__date {
    padding: 2px 6px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: transparent;
    color: var(--text-2);
    font-size: 12px;
    cursor: pointer;
}

    .time-slider__date:hover {
        color: var(--text-1);
    }

/* ------------------------------------------------------------- preset bar */

.preset-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 42px;
    padding: 0 12px;
    border-bottom: 1px solid var(--line);
    flex: none;
}

.preset-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 14px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
    font-weight: 600;
}

    .preset-chip:hover {
        border-color: var(--workspace-accent);
        color: var(--workspace-accent);
    }

.preset-chip--active {
    background: var(--workspace-accent);
    border-color: var(--workspace-accent);
    color: var(--workspace-accent-ink);
}

    .preset-chip--active:hover {
        color: var(--workspace-accent-ink);
    }

/* -------------------------------------------------------- workspace content */

/* Every length below comes from Vesta.Core's WorkspaceGridLayout, published as CSS custom
   properties on .workspace-area by WorkspaceArea.razor (audit layout D5). The fallbacks are only
   for a fragment rendered outside that component; the numbers of record live in Core. */

.workspace-content {
    display: flex;
    flex: 1;
    min-height: 0;
    gap: var(--ws-gap, 8px);
    padding: var(--ws-gap, 8px);
    background: var(--surface-app);
    border-radius: 0 0 6px 6px;
}

.charts-area {
    display: flex;
    flex-direction: column;
    gap: var(--ws-gap, 8px);
    flex: 1;
    min-width: 0;
    min-height: 0;
}

/* Grid preset: N-aware uniform grid (1 full, 2 columns, 3–4 = 2×2, 5+ = 2 columns and a third
   row). The template itself is written inline from WorkspaceGridLayout.GridColumns/GridRows, so
   the panel arity cannot drift from the desktop canvas's. */

.charts-grid {
    display: grid;
    gap: var(--ws-gap, 8px);
    flex: 1;
    min-height: 0;
    /* Belt and braces for a count past the explicit template: implicit rows share the fixed
       height instead of growing past the workspace. */
    grid-auto-rows: minmax(0, 1fr);
}

.charts-grid__cell {
    min-width: 0;
    min-height: 0;
}

/* The "+ Add panel" affordance is NOT a panel: its own row below the grid, so it never re-shapes
   the panels (audit layout D5). */
.charts-add-row {
    flex: none;
    display: flex;
    /* A control's height, not a panel's — this row is host-specific chrome, so the token is the
       shell's own control height rather than one of Core's panel-layout constants. */
    height: var(--target-dialog);
}

/* Focus preset: focused panel on top, filmstrip of the other panels below. */

.charts-focus {
    display: flex;
    flex-direction: column;
    gap: var(--ws-gap, 8px);
    height: 100%;
}

.charts-focus__main {
    flex: 1;
    min-height: 0;
}

.filmstrip {
    display: flex;
    align-items: stretch;
    gap: var(--ws-gap, 8px);
    flex: none;
    overflow-x: auto;
}

.filmstrip--focus {
    height: var(--ws-focus-filmstrip-height, 86px);
}

.filmstrip--compare {
    height: var(--ws-compare-filmstrip-height, 90px);
}

.filmstrip__label {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--ws-filmstrip-label-width, 58px);
    flex: none;
    padding: 4px;
    border: 1px dashed var(--line);
    border-radius: 4px;
    color: var(--text-3);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: center;
}

/* Compare preset: two equal slots (A/B) over the filmstrip. */

.charts-compare {
    display: flex;
    flex-direction: column;
    gap: var(--ws-gap, 8px);
    height: 100%;
}

.charts-compare__slots {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--ws-gap, 8px);
    flex: 1;
    min-height: 0;
}

.compare-slot {
    position: relative;
    min-width: 0;
    min-height: 0;
    border-radius: 6px;
}

.compare-slot--target {
    outline: 2px dashed var(--workspace-accent);
    outline-offset: 2px;
}

.compare-slot--hover {
    outline-style: solid;
}

/* While a thumbnail drag is in flight, slot children must not steal dragenter/leave. */
.charts-compare--dragging .compare-slot * {
    pointer-events: none;
}

.compare-slot__badge {
    position: absolute;
    top: -7px;
    left: 8px;
    z-index: 2;
    width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--workspace-accent);
    color: var(--workspace-accent-ink);
    font-size: 10px;
    font-weight: 700;
}

.compare-slot__drop-hint {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    background: color-mix(in srgb, var(--workspace-accent) 14%, transparent);
    color: var(--workspace-accent);
    font-weight: 700;
    letter-spacing: 0.04em;
    pointer-events: none;
}

/* ---------------------------------------------------------------- viz panel */

.viz-panel {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow: hidden;
}

.viz-panel--active {
    border-color: var(--workspace-accent);
}

.viz-panel__header {
    display: flex;
    align-items: center;
    gap: 2px;
    height: 30px;
    padding: 0 6px 0 10px;
    border-bottom: 1px solid var(--line);
    flex: none;
}

.viz-panel__title {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.viz-panel__body {
    flex: 1;
    min-height: 0;
}

.viz-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 100%;
    padding: 10px;
    color: var(--text-3);
}

.viz-placeholder__sketch {
    width: 100%;
    height: 100%;
    min-height: 0;
    max-height: 220px;
}

.viz-placeholder__kind {
    flex: none;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.viz-placeholder--compact {
    padding: 4px;
    gap: 0;
}

/* ---------------------------------------------------------------- viz table */

.viz-table {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    font-size: 12px;
}

.viz-table__scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
}

.viz-table__table {
    width: 100%;
    border-collapse: collapse;
    table-layout: auto;
}

.viz-table__table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface-app);
    box-shadow: inset 0 -1px 0 var(--line);
    font-weight: 600;
    text-align: left;
    color: var(--text-2);
}

.viz-table__table tbody tr:nth-child(even) {
    background: color-mix(in srgb, var(--text-1) 2.5%, transparent);
}

.viz-table__row {
    cursor: pointer;
    user-select: none; /* shift-click spans rows; without this it drag-highlights text */
}

    .viz-table__row:hover {
        background: color-mix(in srgb, var(--text-1) 5%, transparent);
    }

    /* Linked-selection highlight - see the --selected token. */
    .viz-table__row--selected,
    .viz-table__table tbody tr.viz-table__row--selected:nth-child(even) {
        background: var(--selected-wash);
        box-shadow: inset 3px 0 0 var(--selected);
    }

    .viz-table__row--selected:hover {
        background: var(--selected-wash-strong);
    }

.viz-table__selected {
    color: var(--selected);
    font-weight: 600;
}

.viz-table__cell {
    height: 26px;
    max-width: 180px;
    padding: 0 10px;
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.viz-table__cell--num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.viz-table__footer {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 8px 0 10px;
    border-top: 1px solid var(--line);
    background: var(--surface-app);
    flex: none;
    font-size: 11px;
    color: var(--text-2);
}

.viz-table__range {
    font-variant-numeric: tabular-nums;
}

.viz-table__time {
    max-width: 130px;
    padding: 2px 4px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: var(--surface-card);
    color: var(--text-1);
    font-family: inherit;
    font-size: 11px;
}

    .viz-table__time:disabled {
        color: var(--text-3);
    }

.viz-table__page-btn {
    padding: 3px 10px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: var(--surface-card);
    color: var(--text-2);
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
}

    .viz-table__page-btn:hover:not(:disabled) {
        border-color: var(--workspace-accent);
        color: var(--workspace-accent);
    }

    .viz-table__page-btn:disabled {
        color: var(--text-3);
        cursor: default;
    }

.viz-table__error {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--text-3);
}

.viz-table__skeleton {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
}

/* --------------------------------------------------------------- thumbnails */

.viz-thumb {
    display: flex;
    flex-direction: column;
    width: 132px;
    flex: none;
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: 4px;
    overflow: hidden;
    cursor: pointer;
}

    .viz-thumb:hover {
        border-color: var(--workspace-accent);
    }

.viz-thumb--selected {
    border-color: var(--workspace-accent);
    box-shadow: inset 0 0 0 1px var(--workspace-accent);
}

.viz-thumb__sketch {
    flex: 1;
    min-height: 0;
}

.viz-thumb__label {
    flex: none;
    padding: 2px 6px 4px;
    font-size: 10px;
    color: var(--text-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* -------------------------------------------------------------- stats panel */

.stats-panel {
    display: flex;
    flex-direction: column;
    width: 200px;
    flex: none;
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: 6px;
}

.stats-panel--collapsed {
    width: 32px;
    align-items: center;
    padding-top: 6px;
}

.stats-panel__header {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 6px 0 10px;
    border-bottom: 1px solid var(--line);
    font-weight: 600;
    flex: none;
}

.stats-panel__body {
    padding: 10px;
    overflow-y: auto;
}

.stats-panel__hint {
    margin: 0 0 10px;
    font-size: 11px;
    color: var(--text-3);
}

/* The statistics grid: metric label column plus one narrow column per variable. Long
   variable names are clipped rather than allowed to widen the docked panel. */
.stats-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

.stats-table th,
.stats-table td {
    padding: 3px 4px;
    border-bottom: 1px solid var(--line);
    text-align: right;
    white-space: nowrap;
}

.stats-table thead th {
    max-width: 66px;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
}

.stats-table__metric {
    text-align: left;
    font-weight: 400;
    color: var(--text-3);
}

.stats-table__value--missing {
    color: var(--text-3);
}

/* "Selected (n)" divider, and the metric rows beneath it, carry the selection colour so
   the panel's selected block is legible at a glance next to the highlighted tiles. */
.stats-table__group th {
    padding-top: 8px;
    text-align: left;
    color: var(--selected);
    font-weight: 600;
    border-bottom: 1px solid var(--selected);
}

.stats-table__row--selected td,
.stats-table__row--selected th {
    background: var(--selected-wash);
}

/* ---------------------------------------------------------------- side pane */

/* `min-height: 0` is load-bearing, not tidiness: a grid item's automatic minimum size is its
   CONTENT size, so without this the pane would still push past its (now explicit) row on a long
   dataset tree and the body below would never overflow — see .wsp__canvas in WorkspacePage.razor.css
   for the report this pair fixes. */
.side-pane {
    display: flex;
    flex-direction: column;
    /* THE GRID OWNS THE WIDTH. This carried a hard-coded 340-pixel width against a --pane-w of
       328px (300px below 1280px), so the pane hung 12-40px past its own track and .wsp__canvas's
       overflow: hidden cut the overhang off — including the right edge where the body's scrollbar
       lives, which would have made the scroll region below unreachable. */
    min-width: 0;
    min-height: 0;
    background: var(--surface-card);
    border-left: 1px solid var(--line);
}

/* The desktop's sidebar tab strip: Advisor · Data View (Core SidePaneCatalog.Order). The active
   underline is the Advisor accent, the same member the hub buttons wear. */
.side-pane__tabs {
    display: flex;
    flex: none;
    border-bottom: 1px solid var(--line);
}

.side-tab {
    flex: 1;
    padding: 8px 0;
    border: none;
    background: transparent;
    color: var(--text-2);
    font-family: var(--font-ui);
    font-size: var(--fs-body);
    font-weight: 600;
    cursor: pointer;
    box-shadow: inset 0 -2px 0 transparent;
}

    .side-tab:hover {
        color: var(--text-1);
    }

    .side-tab:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

.side-tab--active {
    color: var(--advisor-accent);
    box-shadow: inset 0 -2px 0 var(--advisor-accent);
}

.side-pane__body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;
    min-height: 0;
    padding: 12px;
    overflow-y: auto;
}

.side-pane__hint {
    margin: 0 0 4px;
    font-size: 11px;
    color: var(--text-3);
}

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

.tree-skeleton__row--indent {
    padding-left: 16px;
}

.tree-skeleton__row--indent2 {
    padding-left: 32px;
}

/* ---------------------------------------------------------------- skeletons */

.skeleton {
    display: inline-block;
    height: 10px;
    border-radius: 5px;
    background: var(--skeleton);
}

.skeleton-row {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
}

/* --------------------------------------------------------- loading state */

.app-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    flex: 1;
    color: var(--text-2);
}

.app-loading__spinner {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 3px solid var(--skeleton);
    border-top-color: var(--workspace-accent);
    animation: app-loading-spin 0.9s linear infinite;
}

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

/* -------------------------------------------------- empty workspace hint */

.charts-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    border: 1px dashed var(--line);
    border-radius: 6px;
}

.charts-empty__hint {
    margin: 0;
    color: var(--text-3);
}

/* -------------------------------------------------- workspace tab badges */

.ws-tab__count {
    flex: none;
    margin-left: 6px;
    font-size: 10px;
    font-weight: 700;
    color: var(--workspace-accent);
}

/* ------------------------------------------------------- Data View tree */

/* `flex: none` so the tree keeps its full height inside the scrolling .side-pane__body. As a
   shrinkable column child it would instead be squeezed to the pane's height, and its fixed-height
   rows would spill out of a box that never scrolled. */
.data-tree {
    display: flex;
    flex-direction: column;
    flex: none;
    font-size: 12px;
}

.tree-row {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    flex: none;
    padding: 0 6px;
    border-radius: 4px;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

    .tree-row:hover {
        background: color-mix(in srgb, var(--text-1) 4%, transparent);
    }

.tree-row--context,
.tree-row--context:hover {
    background: color-mix(in srgb, var(--workspace-accent) 14%, transparent);
}

.tree-row--depth1 {
    padding-left: 22px;
}

.tree-row--depth2 {
    padding-left: 38px;
}

.tree-row--dataset .tree-row__label {
    font-weight: 600;
}

.tree-row__chevron {
    color: var(--text-3);
    transition: transform 0.12s ease;
}

.tree-row__chevron--expanded {
    transform: rotate(90deg);
}

.tree-row__chevron-spacer {
    width: 12px;
    flex: none;
}

.tree-row__icon {
    color: var(--text-2);
}

.tree-row__label {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------------------------------------------------------- context menu */

.context-backdrop {
    position: fixed;
    inset: 0;
    z-index: 40;
}

.context-menu {
    position: fixed;
    z-index: 41;
    min-width: 210px;
    padding: 4px;
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--shadow-color) 12%, transparent);
}

.context-menu__item {
    display: block;
    width: 100%;
    padding: 6px 10px;
    border: none;
    border-radius: 4px;
    background: transparent;
    text-align: left;
    font-size: 12px;
    cursor: pointer;
}

    .context-menu__item:hover {
        background: color-mix(in srgb, var(--workspace-accent) 12%, transparent);
    }

/* ---------------------------------------------------------------- modals */

.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--scrim);
}

.modal-card {
    display: flex;
    flex-direction: column;
    width: min(560px, calc(100vw - 48px));
    max-height: min(70vh, 640px);
    background: var(--surface-card);
    border-radius: 8px;
    box-shadow: 0 12px 40px color-mix(in srgb, var(--shadow-color) 22%, transparent);
    overflow: hidden;
}

.modal-card__header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--line);
    flex: none;
}

.modal-card__title {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.modal-card__close {
    font-size: 12px;
}

.modal-card__body {
    padding: 14px;
    overflow-y: auto;
    font-size: 12px;
}

.modal-section {
    margin-bottom: 14px;
}

    .modal-section:last-child {
        margin-bottom: 0;
    }

.modal-section__label {
    margin-bottom: 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-3);
}

.modal-section__text {
    margin: 0;
    white-space: pre-wrap;
    line-height: 1.5;
}

.modal-fact {
    margin: 0 0 12px;
    font-weight: 600;
}

.valid-times {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 260px;
    margin-top: 4px;
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow-y: auto;
}

.valid-times__item {
    flex: none;
    font-size: 11px;
    color: var(--text-2);
}

/* -------------------------------------------------- Blazor boot & error UI */

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px color-mix(in srgb, var(--shadow-color) 20%, transparent);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: var(--status-err);
    padding: 1rem;
    color: var(--status-err-ink);
}

    .blazor-error-boundary::after {
        content: "An error has occurred.";
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: var(--line);
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: var(--workspace-accent);
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }


/* ------------------------------------------------------------ chart panels */
/* Arctic plot chrome (recon §0.1): white chart area, proportional title sizing via
   container queries (3.5% of panel width, clamped), Calibri with sans-serif fallback.
   SciChart injects its own wrapper div with no intrinsic size, so the direct child of
   .viz-chart__surface is forced to fill (the DisableAspect sizing fix). */

.viz-chart {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    background: var(--plot-bg);
    container-type: size;
}

.viz-chart__title {
    margin-top: 5px;
    padding: 0 8px;
    text-align: center;
    overflow-wrap: break-word;
    color: var(--plot-title);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: clamp(11px, 3.5cqw, 24px);
    line-height: 1.2;
}

.viz-chart__note {
    padding: 1px 8px;
    text-align: center;
    color: var(--plot-axis);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 10px;
}

/* C3 spec-kind chrome: refusal panels, the correlogram/periodogram verdict strip, and the
   mono footer stat line shared by the temporal figures. Chart-internal ink stays the fixed
   Arctic values the rest of this block uses (charts are deliberately not shell-themed). */
.viz-chart__note--refusal {
    padding: 14px 16px;
    font-size: 11px;
    line-height: 1.5;
    max-width: 52ch;
    margin: auto;
}

.viz-chart__verdict {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    align-items: baseline;
    justify-content: center;
    padding: 2px 8px;
    color: var(--plot-axis);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 11px;
}

.viz-chart__verdict-stat {
    font-family: Consolas, "Cascadia Code", monospace;
    font-size: 10px;
}

.viz-chart__footer-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 12px;
    justify-content: center;
    padding: 2px 8px 4px;
    color: var(--plot-axis);
    font-family: Consolas, "Cascadia Code", monospace;
    font-size: 10px;
}

.viz-correlogram__panels {
    display: flex;
    flex-direction: column;
    background: none;
}

.viz-correlogram__panel {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    position: relative;
}

.viz-correlogram__panel-header {
    padding: 0 8px;
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--plot-axis);
}

.viz-correlogram__panel > :last-child {
    flex: 1 1 0;
    min-height: 0;
}

.viz-chart__surface {
    flex: 1 1 0;
    min-height: 0;
    position: relative;
    background: var(--plot-bg);
}

/* Double-buffered surface layers: the outgoing chart stays visible while the incoming
   surface initializes BEHIND it (z-index — NOT opacity: SciChart never completes its
   first draw inside an opacity:0 subtree), then stacks above once its first frame
   rendered — so the un-drawn WebGL canvas (which compositors paint BLACK) is never
   shown. Layers have opaque white backgrounds, so the top ready layer fully covers
   retired ones until they are trimmed. */
.viz-chart__layer {
    position: absolute;
    inset: 0;
    background: var(--plot-bg);
    z-index: 1;
}

.viz-chart__layer--pending {
    z-index: 0;
}

    /* SciChart.Blazor renders TWO sibling wrapper divs (placeholder + chart), both
       sizeless, above the scichart root div (position:relative, NO height of its own).
       Overlay the siblings absolutely — stacking them in flow pushes the chart below
       the clipped panel — and force the inner chain to fill so DisableAspect surfaces
       don't collapse to 0 height.
       WHITE-OUT RULE: backgrounds may only go on elements BEHIND the canvas (the layer,
       the two wrappers, and the canvas's own CSS background). Painting the deeper
       scichart divs white covers the chart — the front div-root overlays the canvas. */
    .viz-chart__layer > div {
        position: absolute;
        inset: 0;
        background: var(--plot-bg);
    }

    .viz-chart__layer > div > div,
    .viz-chart__layer > div > div > div {
        width: 100% !important;
        height: 100% !important;
    }

    .viz-chart__layer canvas {
        background-color: var(--plot-bg);
    }

.viz-chart__error {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--text-3);
}

/* In-panel chart controls (joinpoint "Show:" / "Results:" dropdowns — desktop
   JoinpointPlot.xaml's LayoutGroup row above the surface). */
.viz-chart__controls {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 2px 8px;
    color: var(--plot-axis);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 11px;
}

    .viz-chart__controls:empty {
        display: none;
    }

.viz-chart__control {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

    .viz-chart__control select {
        max-width: 160px;
        padding: 1px 4px;
        border: 1px solid var(--plot-grid);
        border-radius: 3px;
        background: var(--plot-bg);
        color: var(--plot-title);
        font: inherit;
    }

/* HTML stand-in for the desktop SciChart bottom legend (horizontal, series markers). */
.viz-chart__legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 4px 12px;
    padding: 2px 8px 4px;
    color: var(--plot-axis);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 11px;
}

.viz-chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
}

.viz-chart__legend-chip {
    width: 10px;
    height: 10px;
    flex: 0 0 auto;
    border-radius: 2px;
}

.viz-chart__legend-chip--round {
    border-radius: 50%;
}

/* Dashed reference-line legend entry: a hollow chip whose color rides on border-color. */
.viz-chart__legend-chip--dashed {
    height: 0;
    border-top: 2px dashed;
    border-radius: 0;
}

.viz-chart__skeleton {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
}

/* --------------------------------------------------- 3D scatter (true 3D) extras */
/* Mirrors 3DScatterPlot.xaml: surface left, stats column right (Count + Mean/σ per
   axis). The surface is a plain host div driven by vestaScatter3d.js (direct
   scichart.js 3D interop — the Blazor wrapper has no 3D engine); scichart scales its
   canvas to the host, so the host just fills the panel. The hint line mirrors the
   desktop's "Shift + Left Click to Rotate" overlay. */

.viz-scatter3d__body {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
}

.viz-scatter3d__body > .viz-chart__surface {
    flex: 1 1 0;
    min-width: 0;
}

.viz-scatter3d__canvas {
    position: absolute;
    inset: 0;
    background: var(--plot-bg);
}

.viz-scatter3d__hint {
    position: absolute;
    left: 10px;
    bottom: 6px;
    z-index: 1;
    pointer-events: none;
    color: var(--plot-axis);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 10px;
}

.viz-scatter3d__stats {
    flex: 0 0 auto;
    max-width: 40%;
    padding: 6px 10px;
    overflow-y: auto;
    overflow-wrap: break-word;
    color: var(--plot-title);
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 11px;
    line-height: 1.5;
}

.viz-scatter3d__stats-var {
    margin-top: 5px;
    font-weight: 600;
}

/* ------------------------------------------------- chart gallery (DEV, /dev/charts) */
/* Fixed 2×2 overlay hosting all four chart components — verification harness and demo
   view while the cloud samples carry no chart specs (see ChartGallery.razor). */

.chart-gallery {
    position: fixed;
    inset: 24px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    background: var(--plot-tooltip-bg);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 6px 24px color-mix(in srgb, var(--shadow-color) 18%, transparent);
    overflow: hidden;
}

.chart-gallery__header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px 12px;
    border-bottom: 1px solid var(--line);
}

.chart-gallery__caption {
    font-weight: 600;
    white-space: nowrap;
}

/* Same look as the modal card's ✕ (modal-card__close). */
.chart-gallery__close {
    font-size: 12px;
    margin-left: 4px;
}

.chart-gallery__empty {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    color: var(--text-3);
}

.chart-gallery__grid {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 8px;
    padding: 8px;
}

.chart-gallery__cell {
    min-height: 0;
    min-width: 0;
    background: var(--plot-bg);
    border: 1px solid var(--line);
    border-radius: 4px;
    overflow: hidden;
}

/* --------------------------------------------------- chart probe (TEMPORARY) */
/* SciChart.Blazor de-risk overlay, mounted only from /dev/charts?probe=1. */

.chart-probe {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 1000;
    width: 480px;
    background: var(--plot-bg);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 6px 24px color-mix(in srgb, var(--shadow-color) 18%, transparent);
    overflow: hidden;
}

.chart-probe__header {
    padding: 6px 12px;
    font-weight: 600;
    border-bottom: 1px solid var(--line);
    background: var(--surface-app);
}

.chart-probe__body {
    height: 300px;
    background: var(--plot-bg);
}

/* ------------------------------------ self-contained spec visualizations */
/* Results table, contingency table, rose diagram and sensitivity heatmap — the
   spec kinds served whole by …/charts/specpayload. Chart-style titles reuse
   .viz-chart__title; tables reuse the .viz-table skeleton/error/scroll looks.
   Layout mirrors the desktop views (ResultsTable.xaml, ContingencyTable.xaml.cs,
   RoseDiagram.xaml.cs, GridHeatmap.xaml.cs) — but unlike the desktop's scroll
   viewers, matrix cells SHRINK to fit the tile (container-query solved cell size,
   desktop size as the cap) and the panel scrolls only after cells hit the floor. */

.viz-results-table__title {
    margin: 8px 8px 4px;
    text-align: center;
    font-weight: 600;
    font-size: 13px;
    color: var(--plot-title);
}

/* ---- contingency table (confusion matrix) ---- */

.viz-ctable {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    background: var(--plot-bg);
    container-type: size;
    font-family: Calibri, "Segoe UI", sans-serif;
    color: var(--plot-title);
}

.viz-ctable__scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;              /* last resort only, once cells hit the floor */
    padding: 8px 12px;
    container-type: size;
}

/* Same fit-the-tile cell sizing as .viz-heatmap__layout: solve the cell size from the
   container minus the chrome budget (y title + y labels + AUC side table horizontally;
   rotated x-label slot row + x title vertically), floor 24px (the two-line cell text
   hides below threshold), cap = the desktop ContingencyTable 64px. --ct-n inline. */
.viz-ctable__layout {
    --ct-ylab-w: clamp(30px, 18cqw, 110px);
    --ct-xlab-h: min(72px, 26cqh);
    --ct-cell: clamp(24px,
        min((100cqw - 170px - var(--ct-ylab-w)) / var(--ct-n, 1),
            (100cqh - 46px - var(--ct-xlab-h)) / var(--ct-n, 1)),
        64px);
    display: grid;
    grid-template-areas:
        "ytitle ylabels grid  aucs"
        ".      .       xlabels ."
        ".      .       xtitle .";
    grid-template-columns: auto auto auto auto;
    align-content: start;
    /* Safe centering — see .viz-heatmap__layout. */
    width: max-content;
    margin-inline: auto;
}

.viz-ctable__y-title {
    grid-area: ytitle;
    align-self: center;
    margin-right: 6px;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-weight: 600;
    font-size: 13px;
}

.viz-ctable__y-labels {
    grid-area: ylabels;
    display: grid;
    grid-auto-rows: var(--ct-cell, 64px);
}

.viz-ctable__y-label {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    max-width: var(--ct-ylab-w, 200px);
    padding: 0 8px 0 4px;
    font-weight: 600;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.viz-ctable__grid {
    grid-area: grid;
    display: grid;
    grid-template-columns: repeat(var(--ct-n, 1), var(--ct-cell, 64px));
    grid-auto-rows: var(--ct-cell, 64px);
}

.viz-ctable__cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border: 0.5px solid var(--plot-grid);
    overflow: hidden;
    font-size: min(11px, calc(var(--ct-cell, 64px) * 0.28));
    line-height: 1.3;
    color: var(--plot-title);
    text-align: center;
}

    /* The percentage line hides below a cell-size threshold (count keeps priority). */
    .viz-ctable__cell span + span {
        font-size: clamp(0px, (var(--ct-cell, 64px) - 40px) * 10, 10px);
    }

.viz-ctable__aucs {
    grid-area: aucs;
    align-self: start;
    margin-left: 16px;
}

.viz-ctable__auc-title {
    margin: 0 4px 4px;
    text-align: center;
    font-weight: 700;
    font-size: 13px;
}

.viz-ctable__auc-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    height: var(--ct-cell, 64px);
}

.viz-ctable__auc-name {
    padding-left: 4px;
    font-weight: 600;
    font-size: 11px;
}

.viz-ctable__auc-value {
    padding-right: 4px;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

/* Column labels rotate -45° with their top-right corner anchored at the column
   center (the desktop's Canvas placement); the slot row leaves them room. */
.viz-ctable__x-labels {
    grid-area: xlabels;
    display: flex;
    height: var(--ct-xlab-h, 72px);
}

.viz-ctable__x-label-slot {
    position: relative;
    width: var(--ct-cell, 64px);
}

    .viz-ctable__x-label-slot span {
        position: absolute;
        top: 6px;
        right: 50%;
        transform: rotate(-45deg);
        transform-origin: 100% 0;
        white-space: nowrap;
        font-weight: 600;
        font-size: 13px;
    }

.viz-ctable__x-title {
    grid-area: xtitle;
    text-align: center;
    font-weight: 600;
    font-size: 13px;
}

/* ---- rose diagram (SVG polar histogram) ---- */

.viz-rose__canvas {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    justify-content: center;
}

    .viz-rose__canvas svg {
        width: 100%;
        height: 100%;
    }

.viz-rose__cardinal {
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 12px;
    font-weight: 600;
    fill: var(--plot-axis);
}

.viz-rose__stats {
    padding: 4px 8px 8px;
    text-align: center;
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 11px;
    color: var(--plot-axis);
}

/* ---- sensitivity heatmap (labeled cell grid + legend) ---- */

.viz-heatmap__scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;              /* last resort only, once cells hit the floor */
    padding: 8px 12px;
    container-type: size;
    font-family: Calibri, "Segoe UI", sans-serif;
}

/* Cell size solves to FIT the tile on both axes: available container space minus the
   chrome budget (y-label column, row title, legend horizontally; x-labels + column
   title vertically), divided by the matrix dimensions — floored at --hm-cell-min
   (default 14px; below that the scroll container takes over) and capped at the desktop
   GridHeatmap default 52px. A tile whose matrix cannot fit at the shared floor may
   lower --hm-cell-min for itself (CorrelationMatrixGrid does this in Fit mode only).
   --hm-cols/--hm-rows are set inline per tile by the Razor components. */
.viz-heatmap__layout {
    --hm-xlab-h: 22px;
    --hm-ylab-w: clamp(30px, 18cqw, 110px);
    --hm-cell: clamp(var(--hm-cell-min, 14px),
        min((100cqw - 126px - var(--hm-ylab-w)) / var(--hm-cols, 1),
            (100cqh - 42px - var(--hm-xlab-h)) / var(--hm-rows, 1)),
        52px);
    display: grid;
    grid-template-areas:
        "rtitle ylabels grid  legend"
        ".      .       xlabels ."
        ".      .       ctitle .";
    grid-template-columns: auto auto auto auto;
    align-content: start;
    /* Safe centering: auto margins collapse to 0 when the layout overflows the
       scroll container (justify-content:center would clip the leading y labels);
       max-content (NOT fit-content) so a squeezed layout never crushes the
       auto-sized label column to slivers. */
    width: max-content;
    margin-inline: auto;
}

/* Rotated (categorical / correlation) x labels need diagonal room below the grid.
   --hm-xlab-w mirrors the desktop columnLabelMaxWidth cap (GridHeatmap
   ComputeLayoutMetrics: min of the row-label budget and ~35% of the available
   height); --hm-xlab-est is the component's inline estimate of the longest
   label's width at the 12px cap font (the web stand-in for the desktop's
   MeasureText pass). Slot height = width·sin45° + line box (desktop
   MeasureColumnLabelHeight). Kept independent of --hm-cell — the cell size
   formula reads --hm-xlab-h, so a font-derived height would be a var cycle. */
.viz-heatmap__layout--rotated-x {
    --hm-xlab-w: min(var(--hm-xlab-est, 80px), 30cqh, 160px);
    --hm-xlab-h: calc(var(--hm-xlab-w) * 0.7071 + 18px);
}

.viz-heatmap__row-title {
    grid-area: rtitle;
    align-self: center;
    margin-right: 6px;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-weight: 600;
    font-size: 12px;
    color: var(--plot-axis);
}

.viz-heatmap__y-labels {
    grid-area: ylabels;
    display: grid;
    grid-auto-rows: var(--hm-cell, 52px);
}

.viz-heatmap__y-label {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    max-width: var(--hm-ylab-w, 200px);
    padding: 0 8px 0 4px;
    font-weight: 600;
    /* Desktop auto-size header font: scales with the cell, clamped 6-12px, so
       labels stay one-per-row instead of overlapping when cells shrink. */
    font-size: clamp(6px, calc(var(--hm-cell, 52px) * 0.6), 12px);
    color: var(--plot-axis);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.viz-heatmap__grid {
    grid-area: grid;
    display: grid;
    grid-template-columns: repeat(var(--hm-cols, 1), var(--hm-cell, 52px));
    grid-auto-rows: var(--hm-cell, 52px);
}

.viz-heatmap__cell {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0.5px solid var(--plot-grid);
    overflow: hidden;
    /* Desktop AutoSizeCells behavior: value text scales with the cell and vanishes
       below a ~27px cell (the steep ramp term); tooltips keep the exact value. */
    font-size: clamp(0px, min((var(--hm-cell, 52px) - 26px) * 11, var(--hm-cell, 52px) * 0.21), 11px);
    text-align: center;
}

.viz-heatmap__legend {
    grid-area: legend;
    display: flex;
    gap: 4px;
    margin-left: 10px;
}

.viz-heatmap__legend-bar {
    width: 24px;
    height: 100%;
    border: 0.5px solid var(--plot-grid);
}

.viz-heatmap__legend-labels {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    font-size: 11px;
    color: var(--plot-axis);
}

.viz-heatmap__x-labels {
    grid-area: xlabels;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: var(--hm-cell, 52px);
}

/* Reserve the diagonal room below the grid so rotated labels don't create
   scrollable overflow (same idea as the contingency table's 72px slot row). */
.viz-heatmap__layout--rotated-x .viz-heatmap__x-labels {
    height: var(--hm-xlab-h);
    align-items: start;
}

.viz-heatmap__x-label {
    padding-top: 4px;
    text-align: center;
    font-weight: 600;
    font-size: 12px;
    color: var(--plot-axis);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Categorical/correlation X labels rotate -45° (desktop BuildRotatedXAxisLabels):
   full text on the diagonal, the RIGHT end of the rotated box anchored at the
   column center (top-right transform origin, like the contingency table slots),
   ellipsis only past --hm-xlab-w (the desktop columnLabelMaxWidth), and the font
   scaling with the cell like the desktop's auto-size header font. */
.viz-heatmap__x-label-slot {
    position: relative;
}

    .viz-heatmap__x-label-slot span {
        position: absolute;
        top: 4px;
        right: 50%;
        max-width: var(--hm-xlab-w, 160px);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        transform: rotate(-45deg);
        transform-origin: 100% 0;
        font-weight: 600;
        font-size: clamp(6px, calc(var(--hm-cell, 52px) * 0.6), 12px);
        line-height: 1.1;
        color: var(--plot-axis);
    }

.viz-heatmap__column-title {
    grid-area: ctitle;
    padding-top: 4px;
    text-align: center;
    font-weight: 600;
    font-size: 12px;
    color: var(--plot-axis);
}

/* ---- variogram map (dense lag-space heatmap + gamma legend) ---- */
/* Unlike the labeled 52px-cell grids above, this is a dense 64×64 image-like surface:
   1fr cells inside a square box that fits the tile, no cell text, ±extent tick labels
   on both axes and a vertical gamma legend labeled G3 max (top) / min (bottom) —
   mirroring the desktop VariogramChartControl heatmap view + HeatmapColorMap. */

.viz-vgmap__body {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-areas:
        "ytitle yticks grid   legend"
        ".      .      xticks ."
        ".      .      xtitle .";
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    grid-template-rows: minmax(0, 1fr) auto auto;
    padding: 8px 12px;
    font-family: Calibri, "Segoe UI", sans-serif;
}

.viz-vgmap__y-title {
    grid-area: ytitle;
    align-self: center;
    margin-right: 4px;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-weight: 600;
    font-size: 12px;
    color: var(--plot-axis);
}

.viz-vgmap__y-ticks {
    grid-area: yticks;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-end;
    padding-right: 6px;
    font-size: 11px;
    color: var(--plot-axis);
}

.viz-vgmap__grid-box {
    grid-area: grid;
    min-width: 0;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    container-type: size;
}

/* One square scaled to fit BOTH tile dimensions (cqw/cqh resolve against the
   grid-box container) — height:100% + max-width:100% distorted the square in
   width-constrained tiles because aspect-ratio only applies to an auto dimension. */
.viz-vgmap__grid {
    display: grid;
    width: min(100cqw, 100cqh);
    aspect-ratio: 1;
    border: 0.5px solid var(--plot-grid);
}

.viz-vgmap__cell {
    min-width: 0;
    min-height: 0;
}

.viz-vgmap__legend {
    grid-area: legend;
    display: flex;
    gap: 4px;
    margin-left: 10px;
}

.viz-vgmap__legend-bar {
    width: 24px;
    height: 100%;
    border: 0.5px solid var(--plot-grid);
}

.viz-vgmap__legend-labels {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    font-size: 11px;
    color: var(--plot-axis);
}

.viz-vgmap__x-ticks {
    grid-area: xticks;
    display: flex;
    justify-content: space-between;
    padding-top: 2px;
    font-size: 11px;
    color: var(--plot-axis);
}

.viz-vgmap__x-title {
    grid-area: xtitle;
    padding-top: 2px;
    text-align: center;
    font-weight: 600;
    font-size: 12px;
    color: var(--plot-axis);
}

/* ---- variogram fit-summary card (VisualizationTab.xaml variogramFitSummaryTemplate) ---- */

.viz-fitcard__scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: 8px 12px;
    font-family: Calibri, "Segoe UI", sans-serif;
}

.viz-fitcard__card {
    max-width: 420px;
    margin: 0 auto;
    padding: 16px;
    border: 1px solid var(--plot-grid);
    border-radius: 10px;
    background: var(--plot-bg);
}

.viz-fitcard__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.viz-fitcard__accent {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--plot-primary);
}

/* CV badge slot — desktop parity: never shown (the saved model carries no CV results). */
.viz-fitcard__badge {
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--plot-primary);
    color: var(--plot-bg);
    font-size: 11px;
    font-weight: 600;
}

.viz-fitcard__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 12px;
}

.viz-fitcard__label {
    font-size: 10.5px;
    font-weight: 700;
    color: var(--plot-axis);
}

.viz-fitcard__value {
    margin-top: 2px;
    font-family: Consolas, "Cascadia Mono", monospace;
    font-size: 14px;
    font-weight: 600;
    color: var(--plot-title);
    overflow-wrap: break-word;
}

/* ------------------------------------------------- analysis summary (document tile) */

/* A summary is a DOCUMENT, so this tile scrolls VERTICALLY (desktop
   FlowDocumentScrollViewer; ResultsTableView scrolls the same way). Everything the
   server-rendered fragment brings is styled INSIDE .viz-summary so the export
   stylesheet never has to ship with it. */
.viz-summary {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    /* The PLOT background, like every chart tile (.viz-chart): the document's ink below is all
       --plot-* tokens, chosen against --plot-bg. Without this the tile inherited the shell's card
       surface and Arctic's dark title ink sat on Dark's dark card — the summary was unreadable in
       the workspace while the chart beside it was fine (staging, September 14, 2026). The desktop's
       AnalysisSummaryViewer paints the same white behind its FlowDocument. */
    background: var(--plot-bg);
}

.viz-summary__scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Compact scale of the desktop export document (17px body → 12.5px in a tile). */
.viz-summary__doc {
    padding: 10px 14px 18px;
    font-family: Calibri, "Segoe UI", sans-serif;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--plot-title);
    overflow-wrap: break-word;
}

    .viz-summary__doc h1 {
        margin: 0 0 8px;
        font-size: 17px;
        font-weight: 700;
        color: var(--plot-title);
    }

    .viz-summary__doc h2 {
        margin: 14px 0 6px;
        font-size: 14.5px;
        font-weight: 700;
        color: color-mix(in srgb, var(--plot-title) 55%, var(--plot-axis));
    }

    .viz-summary__doc h3 {
        margin: 12px 0 4px;
        font-size: 13px;
        font-weight: 700;
        color: var(--plot-axis);
    }

    .viz-summary__doc p {
        margin: 4px 0;
    }

    .viz-summary__doc ul {
        margin: 4px 0 8px 18px;
        padding: 0;
    }

    .viz-summary__doc li {
        margin: 2px 0;
    }

    /* Collapsible sections are native <details>; only the affordance is restyled. */
    .viz-summary__doc details {
        margin: 8px 0;
        padding: 6px 8px;
        border: 1px solid var(--plot-grid);
        border-radius: 4px;
        background: color-mix(in srgb, var(--plot-title) 4%, transparent);
    }

    .viz-summary__doc summary {
        cursor: pointer;
        font-size: 12.5px;
        font-weight: 700;
        color: var(--plot-primary);
    }

        .viz-summary__doc summary:hover {
            color: color-mix(in srgb, var(--plot-primary) 70%, var(--plot-title));
        }

    /* The generators' [red]/[blue]/[pink] color tags survive as these spans. */
    .viz-summary__doc .red {
        color: var(--plot-series-4);
    }

    .viz-summary__doc .blue {
        color: var(--plot-primary);
    }

    .viz-summary__doc .pink {
        color: var(--plot-series-7);
    }

/* Tables the server rendered inline (template markdown tables) AND the marker tables
   this component renders: one ResultsTableView-like style for both. */
.viz-summary__doc table,
.viz-summary__table {
    width: 100%;
    margin: 8px 0 12px;
    border-collapse: collapse;
    table-layout: auto;
    font-size: 12px;
}

    .viz-summary__doc table th,
    .viz-summary__table th {
        padding: 4px 8px;
        border-bottom: 1px solid var(--plot-grid);
        background: var(--plot-tooltip-bg);
        color: var(--plot-axis);
        font-weight: 600;
        text-align: left;
        white-space: nowrap;
    }

    .viz-summary__doc table td,
    .viz-summary__table td {
        padding: 3px 8px;
        border-bottom: 1px solid var(--plot-grid);
        vertical-align: top;
    }

/* Numeric columns: applied to BOTH the th and its tds so the header sits over its
   own column's digits (a left-aligned header above right-aligned numbers reads as
   misaligned — reported in live testing). */
.viz-summary__num,
.viz-summary__table th.viz-summary__num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Joinpoint Time/Slope/APC cells list one entry per segment (newline-separated). */
.viz-summary__table--wrap td {
    white-space: pre-line;
}

/* Desktop DataGrid row tint for a significant term / the most parsimonious model
   (CreateRegressionSummaryTablePanel + CreateJoinpointSummaryTablePanel both set
   Colors.LightBlue). */
.viz-summary__row--tinted > td {
    background: color-mix(in srgb, var(--plot-primary) 25%, var(--plot-bg));
}

/* An interleaved interactive component. The contingency grid and correlation grid size
   their cells from their container (container-type: size), which needs a DEFINITE height
   — inside a scrolling document there is none, so the slot gets one. */
.viz-summary__block {
    margin: 8px 0 12px;
}

.viz-summary__block--matrix,
.viz-summary__block--ctable {
    height: 300px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    border: 1px solid var(--plot-grid);
    border-radius: 4px;
    background: var(--plot-bg);
}

/* Wrapper the payload-driven correlation grid renders into (controls + scroll). */
.viz-heatmap__grid-root {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    height: 100%;
}

/* ---- Hub / wizard buttons (September 14, 2026) ----
   The web twins of the desktop's AdvisorGhostButtonStyle, AdvisorPrimaryButtonStyle and
   AdvisorCloseButtonStyle (Vesta/Styles/AdvisorTokens.xaml), bound to the SAME palette values via
   the generated --advisor-* tokens: ghost = transparent + accent outline + accent ink, hover fills
   with the soft accent; primary = accent fill + accent ink, hover .92 / pressed .78 opacity; both
   fade to .45 when disabled. Global on purpose — PanelModal's footer, the Projects hub, and the
   step bodies' own action buttons all wear them, so every wizard looks like one product. The old
   per-component buttons read --accent, which is the TITLE-BAR chrome accent, not the hub accent. */
.hub-button {
    min-height: var(--target-dialog);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 0 14px;
    border: 1px solid var(--advisor-accent);
    border-radius: var(--r-md);
    background: transparent;
    color: var(--advisor-accent);
    font-family: var(--font-ui);
    font-size: var(--fs-comfortable);
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-standard),
                opacity var(--dur-fast) var(--ease-standard);
}

    .hub-button:hover:not(:disabled) {
        background: var(--advisor-accent-soft);
    }

    .hub-button:active:not(:disabled) {
        opacity: .85;
    }

    .hub-button:disabled {
        opacity: .45;
        cursor: default;
    }

    .hub-button:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

.hub-button--primary {
    border-color: var(--advisor-accent-fill);
    background: var(--advisor-accent-fill);
    color: var(--advisor-accent-ink);
}

    .hub-button--primary:hover:not(:disabled) {
        background: var(--advisor-accent-fill);
        opacity: .92;
    }

    .hub-button--primary:active:not(:disabled) {
        opacity: .78;
    }

/* Filled destructive: --status-err is split by polarity and ships its own derived ink. */
.hub-button--danger {
    border-color: var(--status-err);
    background: var(--status-err);
    color: var(--status-err-ink);
}

    .hub-button--danger:hover:not(:disabled) {
        background: var(--status-err);
        opacity: .92;
    }

/* A full-width variant for stacked action lists (Analyze's Summary step). */
.hub-button--block {
    width: 100%;
    box-sizing: border-box;
}

/* The ✕ — AdvisorCloseButtonStyle: 30×30, ink-2 glyph, pane-3 on hover, line-strong when pressed. */
.hub-close {
    width: var(--target-dialog);
    height: var(--target-dialog);
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: var(--r-md);
    background: transparent;
    color: var(--advisor-ink-2);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-standard);
}

    .hub-close:hover {
        background: var(--advisor-pane-3);
        color: var(--advisor-ink);
    }

    .hub-close:active {
        background: var(--advisor-line-strong);
    }

    .hub-close:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

/* ---- Jobs (A3) ---- */
/* A3's live job harness (/jobs). Tokens only — no colour literal anywhere below, per B0
   HANDOFF §5.3 — and Compact density (C0 D7): 11px body, 10px secondary, 24px controls. */

/* Shared with Home's header: the one text link in the app chrome. */
.app-header__link {
    padding: 0 6px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    border: none;
    background: transparent;
    font-size: 11px;
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
    cursor: pointer;
    border-radius: 3px;
}

    .app-header__link:hover {
        background: color-mix(in srgb, var(--accent) 14%, transparent);
    }

    .app-header__link:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

/* Hub connection pill. Three states, because "connecting" and "offline" mean different
   things to someone waiting on a cold-starting job. */
.jobs-conn {
    font-size: 10px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid transparent;
    white-space: nowrap;
}

.jobs-conn--connected {
    background: color-mix(in srgb, var(--status-ok) 18%, transparent);
    border-color: color-mix(in srgb, var(--status-ok) 40%, transparent);
    color: var(--status-ok);
}

.jobs-conn--connecting {
    background: color-mix(in srgb, var(--status-info) 18%, transparent);
    border-color: color-mix(in srgb, var(--status-info) 40%, transparent);
    color: var(--status-info);
}

.jobs-conn--offline {
    background: color-mix(in srgb, var(--text-3) 14%, transparent);
    border-color: color-mix(in srgb, var(--text-3) 34%, transparent);
    color: var(--text-3);
}

.jobs-who {
    font-size: 10px;
    color: var(--text-3);
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.jobs-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    background: var(--surface-app);
    color: var(--text-1);
    font-size: 11px;
}

/* ---- sign-in ---- */

.jobs-signin {
    align-self: center;
    width: 100%;
    max-width: 420px;
    margin-top: 32px;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface-card);
}

.jobs-signin__title {
    margin: 0 0 4px;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-1);
}

    /* App.razor's FocusOnNavigate focuses the route's h1 for screen readers. The heading is
       not interactive, so the UA focus ring on it is noise (and the one box on an otherwise
       calm panel). */
    .jobs-signin__title:focus {
        outline: none;
    }

.jobs-signin__lede {
    margin: 0 0 12px;
    font-size: 10px;
    color: var(--text-3);
    line-height: 1.45;
}

.jobs-signin__step {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
}

.jobs-signin__label {
    width: 44px;
    flex: none;
    font-size: 10px;
    color: var(--text-3);
}

.jobs-signin__input {
    flex: 1;
    min-width: 0;
    height: 24px;
    padding: 0 6px;
    font: inherit;
    font-size: 11px;
    color: var(--text-1);
    background: var(--surface-card-2);
    border: 1px solid var(--line);
    border-radius: 3px;
}

    .jobs-signin__input:focus-visible {
        outline: none;
        border-color: var(--accent);
        box-shadow: var(--shadow-focus);
    }

    .jobs-signin__input:disabled {
        color: var(--text-3);
    }

.jobs-signin__input--code {
    letter-spacing: 0.18em;
    font-variant-numeric: tabular-nums;
}

.jobs-signin__primary,
.jobs-submit__button {
    flex: none;
    height: 24px;
    padding: 0 10px;
    border: 1px solid transparent;
    border-radius: 3px;
    font-size: 11px;
    font-weight: 600;
    background: var(--accent);
    color: var(--accent-on);
    cursor: pointer;
}

    .jobs-signin__primary:hover:not(:disabled),
    .jobs-submit__button:hover:not(:disabled) {
        background: color-mix(in srgb, var(--accent) 84%, var(--text-1));
    }

    /* Clearly recessed: at 30% over a dark card the fill still read as "press me". */
    .jobs-signin__primary:disabled,
    .jobs-submit__button:disabled {
        background: transparent;
        border-color: var(--line-strong);
        color: var(--text-4);
        cursor: default;
    }

    .jobs-signin__primary:focus-visible,
    .jobs-submit__button:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

.jobs-signin__ghost {
    flex: none;
    height: 24px;
    padding: 0 8px;
    border: 1px solid var(--line-strong);
    border-radius: 3px;
    font-size: 10px;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
}

.jobs-signin__notice {
    margin: 8px 0 0;
    font-size: 10px;
    color: var(--text-3);
}

/* The server's own words, verbatim — see JobsSignInPanel's remarks. */
.jobs-error {
    margin: 8px 0 0;
    padding: 6px 8px;
    font-size: 10px;
    line-height: 1.45;
    border-radius: 3px;
    border-left: 2px solid var(--status-err);
    background: color-mix(in srgb, var(--status-err) 12%, transparent);
    color: var(--text-1);
}

/* ---- submit card ---- */

.jobs-submit,
.jobs-list {
    width: 100%;
    max-width: 1040px;
    align-self: center;
}

.jobs-submit {
    flex: none;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface-card);
}

.jobs-submit__title {
    margin: 0 0 8px;
    font-size: 12px;
    font-weight: 700;
    color: var(--text-1);
}

    /* See .jobs-signin__title:focus — FocusOnNavigate focuses the route's h1. */
    .jobs-submit__title:focus {
        outline: none;
    }

.jobs-submit__fields {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

.jobs-field {
    display: flex;
    align-items: center;
    gap: 5px;
}

.jobs-field__label {
    font-size: 10px;
    color: var(--text-3);
    white-space: nowrap;
}

.jobs-field__input {
    width: 68px;
    height: 24px;
    padding: 0 6px;
    font: inherit;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--text-1);
    background: var(--surface-card-2);
    border: 1px solid var(--line);
    border-radius: 3px;
}

    .jobs-field__input:focus-visible {
        outline: none;
        border-color: var(--accent);
        box-shadow: var(--shadow-focus);
    }

.jobs-field--check {
    gap: 4px;
}

/* ---- job list ---- */

.jobs-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.jobs-empty {
    margin: 0;
    padding: 16px;
    text-align: center;
    font-size: 10px;
    color: var(--text-3);
    border: 1px dashed var(--line);
    border-radius: 6px;
}

.job-card {
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface-card);
}

.job-card__head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}

.job-card__kind {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-1);
}

.job-card__id,
.job-card__mono {
    font-family: 'Cascadia Mono', Consolas, 'Courier New', monospace;
    font-size: 10px;
    color: var(--text-3);
}

.job-status {
    font-size: 10px;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}

.job-status--queued {
    background: color-mix(in srgb, var(--text-3) 16%, transparent);
    color: var(--text-3);
}

.job-status--starting {
    background: color-mix(in srgb, var(--status-info) 18%, transparent);
    color: var(--status-info);
}

.job-status--running {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
}

.job-status--succeeded {
    background: color-mix(in srgb, var(--status-ok) 18%, transparent);
    color: var(--status-ok);
}

.job-status--failed {
    background: color-mix(in srgb, var(--status-err) 18%, transparent);
    color: var(--status-err);
}

.job-status--cancelled,
.job-status--cancelling {
    background: color-mix(in srgb, var(--status-warn) 20%, transparent);
    color: var(--status-warn);
}

/* Progress track. The pre-terminal states are deliberately UNLIKE each other: a Batch
   cold start holds a job at Queued/0% for 1-2 minutes, and a page where that looks the
   same as "running, no progress yet" reads as hung. Queued = dim stripes on the track
   itself (so it is visible at 0% width), Starting = a pulsing track, Running = solid fill. */
.job-card__bar {
    position: relative;
    height: 6px;
    border-radius: 3px;
    overflow: hidden;
    background: var(--surface-card-2);
}

.job-card__fill {
    height: 100%;
    border-radius: 3px;
    background: var(--accent);
    transition: width 0.25s linear;
}

.job-card__bar--queued {
    background-image: repeating-linear-gradient(135deg,
        color-mix(in srgb, var(--text-3) 30%, transparent) 0 4px,
        transparent 4px 9px);
}

    .job-card__bar--queued .job-card__fill {
        background: var(--text-3);
    }

    .job-card__bar--starting .job-card__fill {
        background: var(--status-info);
    }

/* Indeterminate: a sliver crossing the track. "Starting" has no percentage to show (the
   worker has the job but has not begun reporting), and anything spanning the whole track
   reads as a completed bar. */
.job-card__bar--starting::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 28%;
    border-radius: 3px;
    background: var(--status-info);
    animation: job-bar-shuttle 1.5s ease-in-out infinite;
}

@keyframes job-bar-shuttle {
    /* -100%..357% of the sliver's OWN width, i.e. just off one end to just off the other. */
    from {
        transform: translateX(-100%);
    }

    to {
        transform: translateX(357%);
    }
}

@media (prefers-reduced-motion: reduce) {
    /* Left where the keyframes start it: a static sliver, still unlike a filled bar. */
    .job-card__bar--starting::after {
        animation: none;
    }
}

.job-card__bar--succeeded .job-card__fill {
    background: var(--status-ok);
}

.job-card__bar--failed .job-card__fill {
    background: var(--status-err);
}

.job-card__bar--cancelled .job-card__fill,
.job-card__bar--cancelling .job-card__fill {
    background: var(--status-warn);
}

.job-card__row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 5px;
}

.job-card__msg {
    flex: 1;
    min-width: 0;
    font-size: 10px;
    color: var(--text-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.job-card__pct {
    flex: none;
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    color: var(--text-3);
}

.job-card__cancel {
    flex: none;
    height: 20px;
    padding: 0 8px;
    font-size: 10px;
    border: 1px solid var(--line-strong);
    border-radius: 3px;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
}

    .job-card__cancel:hover:not(:disabled) {
        border-color: var(--status-warn);
        color: var(--status-warn);
    }

    .job-card__cancel:disabled {
        color: var(--text-4);
        border-color: var(--line);
        cursor: default;
    }

    .job-card__cancel:focus-visible {
        outline: none;
        box-shadow: var(--shadow-focus);
    }

.job-card__error {
    margin: 6px 0 0;
    padding: 5px 7px;
    font-size: 10px;
    line-height: 1.45;
    border-radius: 3px;
    border-left: 2px solid var(--status-err);
    background: color-mix(in srgb, var(--status-err) 12%, transparent);
    color: var(--text-1);
}

.job-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin: 6px 0 0;
}

.job-card__meta-item {
    display: flex;
    align-items: baseline;
    gap: 4px;
    min-width: 0;
}

    .job-card__meta-item dt {
        font-size: 10px;
        color: var(--text-4);
    }

    .job-card__meta-item dd {
        margin: 0;
        font-size: 10px;
        color: var(--text-2);
        font-variant-numeric: tabular-nums;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
