/* CBS Dashboard app styles ("The Control Room", see DESIGN.md) */

/* ---------------------------------------------------------------------------
   Self-hosted IBM Plex Sans (variable weight 100-700). The plant network must
   not depend on fonts.googleapis.com; the app uses weights 400/500/600/700,
   all covered by this single variable file per subset.
--------------------------------------------------------------------------- */
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 100 700;
    font-stretch: 100%;
    font-display: swap;
    src: url('../fonts/ibm-plex-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 100 700;
    font-stretch: 100%;
    font-display: swap;
    src: url('../fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------------------------------------------------------------------------
   Shared fixed-header table height: pages with a full-height MudTable set
   Height="var(--page-table-height)" and override --page-table-offset (the
   vertical space consumed by app bar, tabs, toolbars, pager) per page instead
   of hardcoding their own calc() expressions.
--------------------------------------------------------------------------- */
:root {
    --page-table-offset: 265px;
    --page-table-height: calc(100dvh - var(--page-table-offset));
}

/* Logout is a POST form (antiforgery), not a nav link; make its button match
   the MudNavLink siblings, including hover and keyboard focus behavior. */
.nav-logout-button {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 8px 16px 8px 16px;
    border: none;
    background: transparent;
    color: var(--mud-palette-drawer-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.nav-logout-button:hover {
    background-color: var(--mud-palette-action-default-hover);
}

.nav-logout-button .mud-icon-root {
    color: var(--mud-palette-drawer-icon);
}

/* Signed-in email row (NavMenu.razor): MudTooltip's root and MudNavLink's flex children
   default to min-width:auto/no width cap, so a long email grew past the 220px drawer instead
   of clipping (a permanent horizontal scrollbar on every page). min-width:0 unlocks shrinking
   at each step of the flex path; the email span then ellipsizes instead of overflowing. */
.nav-account-tooltip {
    width: 100%;
    min-width: 0;
}

.nav-account-link .mud-nav-link-text {
    min-width: 0;
}

.nav-account-email {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Circuit-error / reconnect bar. Pure CSS on a plain HTML element: it must
   render when the Blazor circuit is dead, so no Mud components or CSS
   variables set by the runtime are assumed. Colors are the CustomTheme brand
   tokens (CbsNavyDark #141F49, StopRed #C62828).
--------------------------------------------------------------------------- */
#blazor-error-ui {
    display: none; /* shown by the Blazor script when the circuit fails */
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 2000;
    padding: 12px 56px 12px 16px;
    background: #141F49; /* CbsNavyDark */
    border-top: 3px solid #C62828; /* StopRed */
    color: #FFFFFF;
    font-family: "IBM Plex Sans", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    font-size: 0.9375rem;
    box-shadow: 0 -2px 8px rgba(20, 31, 73, 0.4);
}

#blazor-error-ui .reload {
    margin-left: 12px;
    padding: 4px 12px;
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: 4px;
    color: #FFFFFF;
    font-weight: 500;
    text-decoration: none;
}

#blazor-error-ui .reload:hover {
    background: rgba(255, 255, 255, 0.12);
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: 50%;
    right: 12px;
    transform: translateY(-50%);
    padding: 2px 8px;
    border: none;
    background: transparent;
    color: #FFFFFF;
    font-size: 1.125rem; /* Title step */
    line-height: 1;
    cursor: pointer;
}

#blazor-error-ui .reload:focus-visible,
#blazor-error-ui .dismiss:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 2px;
}

/* The Tabular Numbers Rule: columns of production counts align digit-for-digit. */
.mud-table,
.mud-simple-table,
table {
    font-variant-numeric: tabular-nums;
}

/* Visible keyboard focus on interactive elements (WCAG 2.4.7). */
a:focus-visible,
button:focus-visible,
.mud-button-root:focus-visible {
    outline: 2px solid var(--mud-palette-secondary);
    outline-offset: 2px;
}

/* Routes.razor's FocusOnNavigate moves focus to the page h1 on every navigation, so keyboard
   and screen-reader users land on the new page's title rather than back at the drawer. It sat
   inert until page titles became real h1s. Chrome reports the programmatic focus as
   :focus-visible and paints a ring, which is noise after a mouse click and permanent on the
   unattended wallboard. 2.4.7 governs keyboard-operable components; a tabindex="-1" heading is
   only a landing spot, and the focus move is still announced. Scoped to that landing spot so
   every real control keeps its ring. */
h1[tabindex="-1"]:focus {
    outline: none;
}

/* Keep non-table content from overflowing the viewport horizontally. */
.mud-main-content {
    overflow-x: hidden;
}

/* Let report tables scroll horizontally within their own container instead. */
.mud-table-container {
    overflow-x: auto;
}

/* ---------------------------------------------------------------------------
   Viewport-fill pages ("no scroll" rule): primary reporting pages fill exactly
   one viewport. The page column is the viewport minus the app bar (48px), the
   main-content top offset (56px total) and bottom padding (12px); every level
   below opts into flex + min-height:0 so panels scroll internally instead of
   growing the page.
--------------------------------------------------------------------------- */

.page-fill {
    display: flex;
    flex-direction: column;
    height: calc(100dvh - 68px);
}

.page-fill-tabs {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.page-fill-tabs .mud-tabs-panels {
    flex: 1 1 auto;
    min-height: 0;
}

.page-fill-tabs .mud-tabs-panels > div {
    height: 100%;
}

/* Single-panel report pages (no applicable Summary tab) skip MudTabs, so the
   viewport-fill chain continues through this plain wrapper instead. Mirrors the
   fill behavior of .page-fill-tabs + its panel div. */
.page-fill-plain {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.page-fill-plain > * {
    height: 100%;
}

/* --- Centered full-viewport notice (error / access-denied pages) ---------- */

.centered-notice {
    height: 100vh;
}

.centered-notice-card {
    max-width: 500px;
}

/* Scrollable list panels inside management dialogs; cap height so the dialog
   itself never outgrows the viewport. */
.dialog-scroll-panel {
    max-height: min(60vh, 480px);
    overflow-y: auto;
}

/* --- Report body: the shared three-column Reporting layout ---------------- */

.report-body {
    display: grid;
    gap: 8px;
    height: 100%;
    min-height: 0;
}

.report-col {
    min-width: 0;
    min-height: 0;
}

/* Side columns (gain/loss and OEE stacks) scroll internally when tall. */
.report-col-side {
    display: flex;
    flex-direction: column;
    gap: 8px;
    overflow-y: auto;
    padding-right: 2px;
}

.report-col-center {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
}

.report-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 2px 0 8px;
}

/* MudSwitch carries margin-left:-11px so its thumb optically aligns with the content above and
   below when the label sits on the right - its default. LabelPosition.Start reverses the row, so
   the label takes the leading edge and that negative margin drags the text outside its column
   instead. The report's centre column sets overflow-y:auto, which makes overflow-x clip, and the
   Detail View switch rendered as "etail View" with the D cut off. A reversed switch should never
   carry a leading negative margin; the thumb it was compensating for is no longer on that side. */
.mud-switch.flex-row-reverse {
    margin-left: 0;
}

/* Drill trail in the report toolbar (Year › Month › Week › Day). Inherits the
   toolbar's flex-wrap so it collapses gracefully on narrow screens. */
.report-crumbs {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
    min-width: 0;
    font-size: 0.9375rem; /* Body step per DESIGN.md type ramp */
    white-space: nowrap;
}

.report-crumb-sep {
    color: var(--mud-palette-text-secondary);
    opacity: 0.7;
}

.report-crumb-current {
    font-weight: 600;
}

.report-charts {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Each chart shares the remaining column height equally. */
.report-chart {
    flex: 1 1 0;
    min-height: 240px;
    position: relative;
    overflow: hidden;
}

/* Absolutely fill the chart box: ApexCharts measures its parent once on load
   and pins a pixel height, so if it sat in normal flow any few-px mismatch
   after fonts/toolbar settle would push the column taller and produce a stray
   scrollbar. Out of flow, the chart can never feed back into column height. */
.report-chart > div {
    position: absolute;
    inset: 0;
}

/* ApexCharts parks its hidden y-axis hover tooltip ~7px BELOW the svg inside
   the canvas, which made every chart report ~19px of phantom overflow and put
   a scrollbar on the center column. The svg itself fits exactly; clip the
   stray helper. (On hover the tooltip is repositioned inside the plot area.) */
.report-chart .apexcharts-canvas {
    overflow: hidden;
}

.report-empty {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
}

/* Metric columns in the report side tables (moved out of per-component
   <style> blocks that leaked global `.mud-table` rules with order-dependent
   min-width conflicts). GainLossTable has 3 value columns at 84px. */
.gain-loss-table th.metric-col,
.gain-loss-table td.metric-col {
    text-align: right;
    font-variant-numeric: tabular-nums;
    min-width: 84px;
}

.gain-loss-table td.metric-col .mud-tooltip-root {
    justify-content: flex-end;
}

/* Marks a value that opens a rich drill-down tooltip (the fleet OEE matrix's
   machine label and Avail/Perf cells): a quiet dotted underline signals the
   detail is discoverable without adding chrome to every cell. */
.has-detail {
    text-decoration: underline dotted var(--mud-palette-text-secondary);
    text-underline-offset: 2px;
    cursor: help;
}

/* Numeric summary-table columns (ReportPage): right-aligned with tabular
   numerals so counts align digit-for-digit (DESIGN.md: The Tabular Numbers
   Rule). Applied by the per-line pages to their count cells and headers. */
.mud-table td.numeric-col,
.mud-table th.numeric-col {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.mud-table th.numeric-col .mud-table-sort-label {
    justify-content: flex-end;
}

/* Dense data cells for the report side tables (previously a global style leak
   from GainLossTable/OeeTable <style> blocks that padded EVERY table).
   Tabular numerals so the matrix's percentage/count columns align
   digit-for-digit (DESIGN.md: The Tabular Numbers Rule). */
.report-col-side .mud-table-cell,
.fleet-overview .mud-table-cell {
    padding: 3px 6px !important;
    font-variant-numeric: tabular-nums;
}

/* --- Summary Data tab: table fills the panel, page never scrolls ---------- */

.summary-fill {
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.summary-fill .summary-fill-table {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.summary-fill .summary-fill-table .mud-table-container {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* --- Home fleet overview cards -------------------------------------------- */

.fleet-card {
    cursor: pointer;
    height: 100%;
}

.fleet-card:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

.fleet-card-title,
.fleet-card-count {
    font-weight: 600;
}

/* Counts refresh in place every 300s; tabular figures keep the card from
   jittering when digits change (DESIGN.md Tabular Numbers Rule). */
.fleet-card-count {
    font-variant-numeric: tabular-nums;
}

/* The .mud-progress-linear.horizontal pair is specificity, not decoration: MudBlazor ships
   `.mud-progress-linear.horizontal.mud-progress-linear-small { height: 4px }` (0,3,0), which
   silently beat every bare height in this file — the fleet, glance and wall bars all rendered at
   MudBlazor's 4px rather than their designed heights, most damagingly on the shop-floor TV. The
   no-data tracks are plain divs and were never overridden, so without this the two would not even
   match each other. Matching specificity plus app.css's later source order lets the design win. */
.fleet-card-progress,
.fleet-card-progress.mud-progress-linear.horizontal {
    height: 6px;
}

/* No-measurement progress track. An unfilled bar is indistinguishable from a bar
   at zero, but "no OEE recorded" and "0% OEE" are different claims — rendering the
   first as the second is exactly the silently-wrong number DESIGN.md's trust-the-data
   principle forbids. The hatch is the calendar's own "off / not scheduled" idiom
   (.monthly-calendar .legend-off), so absence already reads as absence here; Line gray
   keeps it out of the status palette (The State-Owns-Color Rule). */
.progress-nodata {
    border-radius: var(--mud-default-borderradius);
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    background-image: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 3px,
        var(--mud-palette-lines-default) 3px,
        var(--mud-palette-lines-default) 4px);
}

.fleet-card-secondary {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Delta vs pace: state-owns-color classes instead of inline styles. */
.fleet-delta-ahead {
    color: var(--mud-palette-success);
}

.fleet-delta-behind {
    color: var(--mud-palette-warning);
}

.fleet-delta-none {
    color: var(--mud-palette-text-secondary);
}

/* Home git-commit feed: fixed-height internally-scrolling list. */
.commit-feed-scroll {
    max-height: calc(100dvh - 320px);
    min-height: 200px;
    overflow-y: auto;
}

/* Data bridge status indicator: anchor for its popover. */
.bridge-status-anchor {
    position: relative;
}

.bridge-status-popover {
    width: 300px;
    max-width: 100vw;
}

/* --- Threshold state cells (report tables) --------------------------------
   State-owns-color tints derived from the DESIGN.md functional palette:
   Run Green #1E8E3E, Warn Amber #B26A00, Stop Red #C62828 at ~12% background
   with full-strength (default ink) text. The historical 4-band scale is kept
   as two amber intensities (state-warn / state-warn-strong) rather than
   collapsing to 3 states, so existing threshold granularity stays visible.
   Non-color cue (WCAG 1.4.1): non-good states carry a small corner glyph
   (! / !! / x) in the full-strength state color; good cells stay unmarked. */

.state-good,
.state-warn,
.state-warn-strong,
.state-bad {
    position: relative;
}

.state-good {
    background-color: rgba(var(--mud-palette-success-rgb), 0.12);
}

.state-warn {
    background-color: rgba(var(--mud-palette-warning-rgb), 0.12);
}

.state-warn-strong {
    background-color: rgba(var(--mud-palette-warning-rgb), 0.22);
}

.state-bad {
    background-color: rgba(var(--mud-palette-error-rgb), 0.12);
}

.state-warn::before,
.state-warn-strong::before,
.state-bad::before {
    position: absolute;
    top: 1px;
    left: 3px;
    font-size: 0.625rem; /* DESIGN.md typography Glyph micro-step */
    line-height: 1;
    font-weight: 700;
    pointer-events: none;
}

.state-warn::before {
    content: "!";
    color: var(--mud-palette-warning);
}

.state-warn-strong::before {
    content: "!!";
    color: var(--mud-palette-warning);
}

.state-bad::before {
    content: "\00d7"; /* multiplication sign, reads as an "x" mark */
    color: var(--mud-palette-error);
}

/* Layout helpers previously carried as inline styles in report tables.
   !important lets center-col win over the component-scoped metric-col
   right-alignment (component <style> blocks load after this sheet). */
.mud-table th.center-col,
.mud-table td.center-col,
.center-col {
    text-align: center !important;
}

.mud-table td.center-col .mud-tooltip-root {
    justify-content: center !important;
}

.row-total,
.mud-table tr.row-total td {
    font-weight: 600;
}

/* Threshold legend popover (report toolbar "Legend" button, ThresholdLegend):
   four state bands as rows, one column per metric family. Swatches reuse the
   real state-* cell classes — tint plus corner glyph — so the legend can never
   drift from the rendered tables. Text runs at the Label step. */
.threshold-legend {
    padding: 10px 14px;
}

.threshold-legend table {
    border-collapse: collapse;
}

.threshold-legend th,
.threshold-legend td {
    padding: 3px 8px;
    font-size: 0.8125rem; /* Label step per DESIGN.md type ramp */
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: 0.02em;
    text-align: left;
    white-space: nowrap;
}

.threshold-legend thead th {
    color: var(--mud-palette-text-secondary);
}

.threshold-legend .legend-range {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.threshold-legend td.legend-range {
    font-weight: 400;
}

.threshold-legend-swatch {
    display: inline-block;
    width: 22px;
    height: 15px;
    margin-right: 6px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    vertical-align: text-bottom;
}

/* Back affordance above a drilled-in single-machine gain/loss table. */
.fleet-drill-back {
    margin-bottom: 4px;
}

/* --- Narrow screens: release the fill; pages scroll normally -------------- */

@media (max-width: 959.98px) {
    .page-fill {
        height: auto;
    }

    .page-fill-tabs .mud-tabs-panels > div,
    .page-fill-plain > * {
        height: auto;
    }

    .report-body {
        display: flex;
        flex-direction: column;
        height: auto;
    }

    .report-col-side {
        overflow-y: visible;
    }

    .report-chart {
        min-height: 320px;
    }

    .report-empty {
        min-height: 320px;
    }

    .summary-fill .summary-fill-table .mud-table-container {
        max-height: 70vh;
    }
}

/* Screen-reader-only text: visually hidden but announced (e.g. empty action-column headers). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ==========================================================================
   Motion ("Calm Motion" — see DESIGN.md's Motion section)
   The app's entire motion vocabulary, in one block. Five moves, nothing else:
     1. Hover/focus color shifts on interactive chrome — 120ms ease
     2. State-machine swaps (loading/content/empty/error) — 200ms ease-out fade
     3. Progress-bar value/color changes — 200ms ease-out
     4. Value tick when a refreshed number CHANGES — 600ms ease-out steel wash
     5. Nav line-status dot color flips — 150ms ease
   Opacity and color only: no layout motion, no size motion, no elevation
   motion (the Flat Floor rule extends to animation). Everything declared here
   is switched off under prefers-reduced-motion at the end of the block.
   ========================================================================== */

/* 1. Hover/focus shifts: color, background, border, and opacity only. */
.mud-button-root,
.mud-nav-link,
.nav-logout-button,
.mud-table-hover .mud-table-row,
.metric-help-icon,
.has-detail,
.machine-schedule-stripe .week-set-button,
.machine-schedule-stripe .day-cell-interactive {
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease, opacity 120ms ease;
}

/* Scheduler shift bars hover with a brightness filter rather than a color swap. */
.machine-schedule-stripe .shift-bar {
    transition: filter 120ms ease;
}

/* 2. State swaps: a data region's loading/content/empty/error branch fades in
   when it mounts. Blazor only recreates the branch's elements when the state
   actually changes, so in-place data refreshes never replay the fade. */
@keyframes motion-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.state-fade-in {
    animation: motion-fade-in 200ms ease-out;
}

/* Content branch of the shared report cascade (ReportPage): same fade, plus
   the height links the viewport-fill chain needs through this wrapper. */
.report-content-fade {
    height: 100%;
    min-height: 0;
    animation: motion-fade-in 200ms ease-out;
}

/* 3. Determinate progress bars ease to a new value/state color instead of
   jumping when a background refresh lands (Mud's bar moves via transform). */
.glance-progress .mud-progress-linear-bar,
.fleet-card-progress .mud-progress-linear-bar {
    transition: transform 200ms ease-out, background-color 200ms ease-out;
}

/* 4. Value tick: one quiet steel wash fading out behind a displayed number
   that CHANGED after a background refresh (GlanceBand / Home fleet cards).
   The components gate the class on previous-value tracking — never on first
   paint, never when a refresh confirms the same number — so the tick stays
   a rare "the world moved" cue rather than render noise. */
@keyframes motion-value-tick {
    from { background-color: rgba(46, 107, 181, 0.16); } /* CbsSteel #2E6BB5 wash */
    to { background-color: transparent; }
}

.value-tick {
    animation: motion-value-tick 600ms ease-out;
    border-radius: 4px; /* rounded.sm */
}

/* 5. Nav line-status dots ease between state colors instead of flipping. */
.nav-line-status > button > .mud-nav-link-text::before {
    transition: background-color 150ms ease;
}

/* Reduced motion: the whole vocabulary above (plus the scheduler add-glyph
   reveal) goes static. Scoped rather than a global * kill so essential
   activity indicators (spinners, skeleton shimmer) keep signalling work. */
@media (prefers-reduced-motion: reduce) {
    .mud-button-root,
    .mud-nav-link,
    .nav-logout-button,
    .mud-table-hover .mud-table-row,
    .metric-help-icon,
    .has-detail,
    .machine-schedule-stripe .week-set-button,
    .machine-schedule-stripe .day-cell-interactive,
    .machine-schedule-stripe .day-cell-addable::before,
    .machine-schedule-stripe .shift-bar,
    .glance-progress .mud-progress-linear-bar,
    .fleet-card-progress .mud-progress-linear-bar,
    .nav-line-status > button > .mud-nav-link-text::before {
        transition: none;
    }

    .state-fade-in,
    .report-content-fade,
    .value-tick {
        animation: none;
    }
}

/* ==========================================================================
   NewScheduler: MachineScheduleStripe (NewScheduler/Components/MachineScheduleStripe.razor)
   Moved out of the component so the rules are emitted once, not once per machine
   row. The --shift-color-* custom properties are set inline on the stripe root
   from the ShiftColors constants (single source of truth, shared with the
   production detail charts).
   ========================================================================== */

.machine-schedule-stripe {
    display: flex;
    height: 32px;
    width: 100%;
}

/* Name gutter: fixed 200px (matching the header row) holding the name plus the quiet
   weekly bulk-set button, so the Gantt track's alignment with the day headers is untouched. */
.machine-schedule-stripe .machine-name-cell {
    flex: 0 0 200px;
    width: 200px;
    display: flex;
    align-items: center;
    padding-right: 6px;
    box-sizing: border-box;
    min-width: 0;
    /* Pinned during the calendar's horizontal scroll on narrow viewports, matching the
       sticky .machine-name-column gutter in MonthlyCalendar. */
    position: sticky;
    left: 0;
    z-index: 1;
    background-color: var(--mud-palette-surface);
}

.machine-schedule-stripe .machine-name {
    flex: 1 1 auto;
    min-width: 0;
    padding-right: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 32px;
    font-size: 0.9375rem;
    color: var(--mud-palette-text-primary);
}

/* Quiet but legible at rest (touch screens have no hover, and disabled-gray reads as
   inoperable); hover/focus lifts it to full text color. */
.machine-schedule-stripe .machine-name-cell .week-set-button {
    color: var(--mud-palette-text-secondary);
    padding: 4px;
}

.machine-schedule-stripe:hover .week-set-button,
.machine-schedule-stripe .machine-name-cell .week-set-button:focus-visible {
    color: var(--mud-palette-text-primary);
}

/* Week utilization rollup in the name gutter: the row's resolved in-month hours for this week.
   Label-step, muted, tabular digits so totals align down the column. */
.machine-schedule-stripe .week-total {
    flex: 0 0 auto;
    font-size: 0.8125rem; /* DESIGN.md Label step */
    font-weight: 500;
    letter-spacing: 0.02em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
    padding-right: 2px;
}

/* Under-scheduled week: warn-amber is STATE here (materially below the pattern baseline -
   threshold documented on SchedulerHoursMath.UnderScheduledBaselineFraction), never
   decoration, and it is always paired with the "!" glyph below so the state survives
   without color (WCAG 1.4.1). */
.machine-schedule-stripe .week-total-low {
    color: var(--mud-palette-warning);
    font-weight: 600;
}

.machine-schedule-stripe .week-total-glyph {
    font-size: 0.625rem; /* DESIGN.md typography Glyph micro-step */
    font-weight: 700;
    line-height: 1;
    margin-left: 2px;
    vertical-align: top;
}

/* The Gantt track: 7 equal day columns as the backdrop, shift bars overlaid absolutely.
   flex:1 columns after the 200px name gutter mirror MonthlyCalendar's header row, so the
   midnight gridlines line up exactly with the day numbers above. */
.machine-schedule-stripe .schedule-track {
    position: relative;
    display: flex;
    flex-grow: 1;
    height: 100%;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    overflow: hidden;
    background-color: var(--mud-palette-surface);
}

/* Day-window backdrop + per-day click target. Faint alternating banding and a hairline
   gridline at each midnight boundary map a bar to the day(s) it covers. */
.machine-schedule-stripe .day-cell {
    flex: 1 1 0;
    height: 100%;
    border-left: 1px solid var(--mud-palette-lines-default);
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
}

.machine-schedule-stripe .day-cell:first-child {
    border-left: none;
}

.machine-schedule-stripe .day-cell-interactive {
    cursor: pointer;
}

.machine-schedule-stripe .day-cell-alt {
    background-color: var(--mud-palette-background-gray);
}

.machine-schedule-stripe .day-cell-interactive:hover {
    background-color: var(--mud-palette-action-default-hover);
}

.machine-schedule-stripe .day-cell-interactive:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

/* Out-of-month day: blank, non-interactive; keeps only the column gridline for alignment. */
.machine-schedule-stripe .day-cell-out {
    background-color: var(--mud-palette-surface);
}

/* A deliberate day-off (0h override) gets a subtle neutral diagonal hatch + label, so it
   reads as "intentionally off" rather than "pattern has no shift here". */
.machine-schedule-stripe .day-cell-off {
    background-image: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 5px,
        var(--mud-palette-lines-default) 5px,
        var(--mud-palette-lines-default) 6px);
}

.machine-schedule-stripe .day-off-label {
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--mud-palette-text-secondary);
    background-color: var(--mud-palette-surface);
    padding: 0 4px;
    border-radius: 4px;
}

/* At-rest "add hours here" affordance on empty, editable day cells (not a day off, which
   already carries its own hatch + "Off" label). A faint centred "+" matching the muted
   week-set-button treatment, lifting to full strength on hover/focus-visible. Where a shift
   bar overlays the cell the opaque bar hides the glyph, so it shows only in the empty gaps. */
.machine-schedule-stripe .day-cell-addable::before {
    content: "+";
    font-size: 0.9375rem; /* Body step */
    font-weight: 600;
    line-height: 1;
    color: var(--mud-palette-text-secondary);
    opacity: 0.28;
    pointer-events: none;
    transition: opacity 120ms ease;
}

.machine-schedule-stripe .day-cell-addable:hover::before,
.machine-schedule-stripe .day-cell-addable:focus-visible::before {
    opacity: 1;
}

/* Continuous shift bar, coloured by shift designation to match the production detail charts.
   Vertically inset so the day-grid backdrop stays visible above and below. A bar sitting
   inside a day column reads as a day shift; one straddling a midnight gridline reads as
   an overnight shift. */
.machine-schedule-stripe .shift-bar {
    position: absolute;
    top: 3px;
    bottom: 3px;
    border-radius: 4px;
    cursor: pointer;
    box-sizing: border-box;
    /* Centre the hours label; container-type lets the label's @container queries below
       size themselves off THIS bar's rendered width, so the "8 / 8hrs / 8 hours" label
       adapts per-bar regardless of viewport. */
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    container-type: inline-size;
    /* Hairline on the trailing edge keeps two same-coloured abutting shifts readable as
       two distinct bars without splitting either at midnight. */
    /* Dark hairline mixed at a fixed strength so it stays a readable seam against
       any shift-color chip (not a themed line token, which is too light to show here). */
    box-shadow: inset -1px 0 0 0 color-mix(in srgb, black 28%, transparent);
}

/* The shift-length overlay. Hidden by default (bars too narrow to hold even one digit),
   then progressively revealed as the bar widens: number only, number + "hrs", number +
   " hours". pointer-events:none so clicks/keyboard still reach the bar underneath. */
.machine-schedule-stripe .shift-bar-hours {
    font-size: 0.8125rem; /* DESIGN.md Label step - the smallest sanctioned text size */
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.02em;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    padding: 0 3px;
    pointer-events: none;
    opacity: 0;
}

.machine-schedule-stripe .shift-bar-hours::after {
    content: "";
}

@container (min-width: 18px) {
    .machine-schedule-stripe .shift-bar-hours {
        opacity: 1;
    }
}

@container (min-width: 38px) {
    .machine-schedule-stripe .shift-bar-hours::after {
        content: "hrs";
    }
}

@container (min-width: 64px) {
    .machine-schedule-stripe .shift-bar-hours::after {
        /* nbsp so the number and word don't wrap apart */
        content: "\00a0hours";
    }
}

/* Legacy bars carry a light hatched fill, so white would vanish - and the hatch lines cut
   through bare digits. Full-strength ink on a small surface pill keeps the label legible
   (the bar's 0.75 opacity dims the text too, so secondary-on-hatch fell below AA). */
.machine-schedule-stripe .shift-bar-legacy .shift-bar-hours {
    color: var(--mud-palette-text-primary);
    background-color: var(--mud-palette-surface);
    border-radius: 4px;
    padding: 1px 3px;
}

/* Days bars are a light steel tint (#6FA0D6): white fails contrast there, so the hours
   label uses ink. Nights/Afternoons stay navy/indigo, where white passes. */
.machine-schedule-stripe .shift-bar-days .shift-bar-hours {
    color: var(--mud-palette-text-primary);
}

/* Days = steel tint #6FA0D6, Nights = CBS Navy, Afternoons = indigo #3D53A6 —
   deliberately brand blues, never status colors (see DESIGN.md's shift palette;
   values injected inline on the stripe root from ShiftColors). */
.machine-schedule-stripe .shift-bar-days {
    background-color: var(--shift-color-days);
}

.machine-schedule-stripe .shift-bar-nights {
    background-color: var(--shift-color-nights);
}

.machine-schedule-stripe .shift-bar-afternoons {
    background-color: var(--shift-color-afternoons);
}

.machine-schedule-stripe .shift-bar-other {
    background-color: var(--mud-palette-text-secondary);
}

/* A manual per-day override: not a normal shift, so a distinct neutral steel fill + faint
   diagonal weave sets it apart from the Day/Night colours (tooltip names the adjusted hours). */
.machine-schedule-stripe .shift-bar-adjusted {
    background-color: var(--mud-palette-info);
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.28),
        rgba(255, 255, 255, 0.28) 4px,
        transparent 4px,
        transparent 9px);
}

/* Read-only legacy-table hours: neutral gray hatched fill with a dashed border so they
   cannot be mistaken for editable NewScheduler shifts (tooltip is prefixed "Legacy: "). */
.machine-schedule-stripe .shift-bar-legacy {
    background-color: var(--mud-palette-action-disabled-background);
    background-image: repeating-linear-gradient(
        -45deg,
        var(--mud-palette-text-disabled),
        var(--mud-palette-text-disabled) 2px,
        transparent 2px,
        transparent 7px);
    border: 1px dashed var(--mud-palette-text-disabled);
    cursor: default;
    opacity: 0.75;
}

.machine-schedule-stripe .shift-bar:hover {
    filter: brightness(0.92);
}

.machine-schedule-stripe .shift-bar:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
    z-index: 1;
}

/* MudTooltip wraps each bar in an inline root element; drop it from the box tree so the
   absolutely-positioned bar resolves against .schedule-track, not the wrapper. */
.machine-schedule-stripe .schedule-track .mud-tooltip-root.mud-tooltip-inline,
.machine-schedule-stripe .machine-name-cell .mud-tooltip-root.mud-tooltip-inline {
    display: contents;
}

/* ==========================================================================
   NewScheduler: MonthlyCalendar (NewScheduler/Components/MonthlyCalendar.razor)
   Moved out of the component so the rules are emitted once, not once per render.
   All generic child selectors (.day, .week, .weekday, .legend-*, etc.) are scoped
   under .monthly-calendar so the calendar's grammar never leaks page-wide. The
   shift-designation swatch colours read from --shift-color-* custom properties
   set inline on the legend root from the ShiftColors constants (same single
   source of truth as the stripe bars).
   ========================================================================== */

.monthly-calendar {
    width: 100%;
}

.monthly-calendar .calendar-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
}

.monthly-calendar .calendar-nav {
    display: flex;
    align-items: center;
    gap: 4px;
}

.monthly-calendar .calendar-title {
    min-width: 12ch;
    text-align: center;
}

.monthly-calendar .calendar-toolbar-right {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}

.monthly-calendar .calendar-group-filter {
    max-width: 220px;
}

.monthly-calendar .calendar-legend {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.monthly-calendar .legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--mud-palette-text-secondary);
}

.monthly-calendar .legend-swatch {
    width: 18px;
    height: 12px;
    border-radius: 4px; /* rounded.sm */
    flex: 0 0 auto;
}

.monthly-calendar .legend-days {
    background-color: var(--shift-color-days);
}

.monthly-calendar .legend-nights {
    background-color: var(--shift-color-nights);
}

.monthly-calendar .legend-afternoons {
    background-color: var(--shift-color-afternoons);
}

/* Hatch geometry matches .shift-bar-adjusted above exactly, so the swatch and the
   bar read as the same material. */
.monthly-calendar .legend-adjusted {
    background-color: var(--mud-palette-info);
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.28),
        rgba(255, 255, 255, 0.28) 4px,
        transparent 4px,
        transparent 9px);
}

/* Matches .shift-bar-legacy above: gray hatch + dashed border = read-only legacy hours. */
.monthly-calendar .legend-legacy {
    background-color: var(--mud-palette-action-disabled-background);
    background-image: repeating-linear-gradient(
        -45deg,
        var(--mud-palette-text-disabled),
        var(--mud-palette-text-disabled) 2px,
        transparent 2px,
        transparent 7px);
    border: 1px dashed var(--mud-palette-text-disabled);
    opacity: 0.75;
}

.monthly-calendar .legend-off {
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    background-image: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 3px,
        var(--mud-palette-lines-default) 3px,
        var(--mud-palette-lines-default) 4px);
}

.monthly-calendar .calendar-scroll {
    max-height: 70vh;
    overflow: auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    background-color: var(--mud-palette-surface);
}

/* min-width forces narrow viewports into the sanctioned horizontal-scroll pattern
   (DESIGN.md tables rule) instead of crushing the 7 day columns into slivers. */
.monthly-calendar .calendar-grid {
    display: flex;
    flex-direction: column;
    min-width: 660px;
}

/* Opaque background so rows scrolling under the sticky header never show through
   (the 200px name gutter included). */
.monthly-calendar .weekday-header {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--mud-palette-surface);
}

/* The name gutter stays pinned during the horizontal scroll min-width now allows,
   so a machine's bars never lose their row label. */
.monthly-calendar .machine-name-header,
.monthly-calendar .machine-name-column {
    flex: 0 0 200px;
    width: 200px;
    position: sticky;
    left: 0;
    z-index: 1;
    background-color: var(--mud-palette-surface);
}

.monthly-calendar .weekday {
    flex: 1;
    background-color: var(--mud-palette-background-gray);
    padding: 6px 8px;
    text-align: center;
    color: var(--mud-palette-text-secondary);
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
}

.monthly-calendar .week {
    display: flex;
    min-height: 28px;
}

.monthly-calendar .day {
    flex: 1;
    background-color: var(--mud-palette-surface);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    border-left: 1px solid var(--mud-palette-lines-default);
    padding: 2px 6px;
}

.monthly-calendar .day-number {
    text-align: right;
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
}

.monthly-calendar .other-month .day-number {
    color: var(--mud-palette-text-disabled);
}

/* Today: the control room's anchor point - primary-weight day number on a steel wash. */
.monthly-calendar .day.today {
    background-color: var(--mud-palette-primary-hover);
}

.monthly-calendar .day.today .day-number {
    color: var(--mud-palette-primary);
    font-weight: 600;
}

.monthly-calendar .group-header-row {
    flex: 0 0 auto;
    padding: 4px 8px;
    background-color: var(--mud-palette-background-gray);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    color: var(--mud-palette-text-secondary);
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
}

.monthly-calendar .machine-schedule-row {
    display: flex;
    height: 32px;
    margin-bottom: 3px;
}

/* Coarse-pointer (shop-floor tablet) sizing: the 32px stripe rows are undersized touch
   targets (WCAG 2.5.5 / platform HIGs want ~44px), so under a coarse primary pointer the
   row - and with it every day-cell click target - grows to 44px, and the week-set button
   gains padding for a comfortable hit area. Desktop keeps the denser 32px rhythm. The
   shift-bar inset scales up so bars stay proportionally centred in the taller track.
   Pointer-media only - the roving-tabindex keyboard behaviour is untouched. */
@media (pointer: coarse) {
    .machine-schedule-stripe,
    .monthly-calendar .machine-schedule-row {
        height: 44px;
    }

    .machine-schedule-stripe .machine-name {
        line-height: 44px;
    }

    .machine-schedule-stripe .shift-bar {
        top: 5px;
        bottom: 5px;
    }

    .machine-schedule-stripe .machine-name-cell .week-set-button {
        padding: 8px;
    }
}

/* The Manage menu (fleet-config CRUD) sits in the toolbar's right cluster; keep it from
   flexing so a long legend wraps around it instead of squeezing it off-view. */
.monthly-calendar .calendar-manage-menu {
    flex: 0 0 auto;
}

/* ---------------------------------------------------------------------------
   Blazor circuit reconnect banner. The element in App.razor replaces the
   framework's full-screen default modal; blazor.web.js only toggles the state
   classes on it. Pure CSS on plain HTML: it must render while the circuit is
   dead, so no Mud components or runtime-set variables are assumed. Colors are
   DESIGN.md tokens (CBS Navy #1B2A63 / CbsNavyDark #141F49, WarnAmber
   #B26A00, StopRed #C62828).
--------------------------------------------------------------------------- */
#components-reconnect-modal {
    display: none; /* shown only while reconnecting or failed */
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2100; /* above the app bar and dialogs; the circuit is unusable anyway */
    background: #141F49; /* CbsNavyDark */
    border-bottom: 3px solid #B26A00; /* WarnAmber while retrying */
    color: #FFFFFF;
    font-family: "IBM Plex Sans", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    font-size: 0.8125rem; /* Label step */
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: block;
}

#components-reconnect-modal .reconnect-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    max-width: 960px;
    margin: 0 auto;
}

/* Small inline spinner while retrying; hidden once retries exhaust. */
#components-reconnect-modal .reconnect-spinner {
    width: 14px;
    height: 14px;
    flex: none;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #FFFFFF;
    border-radius: 50%;
    animation: reconnect-spin 0.9s linear infinite;
}

@keyframes reconnect-spin {
    to { transform: rotate(360deg); }
}

/* State-driven content: retrying shows the spinner + "Reconnecting"; failed and
   rejected show "Connection lost" + Reload. */
#components-reconnect-modal .reconnect-message-failed,
#components-reconnect-modal .reconnect-reload-button {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    border-bottom-color: #C62828; /* StopRed once retries exhaust */
}

#components-reconnect-modal.components-reconnect-failed .reconnect-spinner,
#components-reconnect-modal.components-reconnect-rejected .reconnect-spinner,
#components-reconnect-modal.components-reconnect-failed .reconnect-message-reconnecting,
#components-reconnect-modal.components-reconnect-rejected .reconnect-message-reconnecting {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-message-failed,
#components-reconnect-modal.components-reconnect-rejected .reconnect-message-failed {
    display: inline;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-reload-button,
#components-reconnect-modal.components-reconnect-rejected .reconnect-reload-button {
    display: inline-block;
}

#components-reconnect-modal .reconnect-reload-button {
    margin-left: auto;
    padding: 4px 14px;
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: 4px;
    background: transparent;
    color: #FFFFFF;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
}

#components-reconnect-modal .reconnect-reload-button:hover {
    background: rgba(255, 255, 255, 0.12);
}

#components-reconnect-modal .reconnect-reload-button:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Metric contextual help (MetricHelp component): a subtle inline info icon in
   report table headers with a plain-language definition popover.
--------------------------------------------------------------------------- */
.metric-help-icon {
    font-size: 0.8125rem; /* Label step; smaller than Size.Small default so headers stay scannable */
    vertical-align: text-top;
    margin-left: 2px;
    opacity: 0.45;
    cursor: help;
}

.metric-help-icon:hover,
.metric-help-icon:focus-visible {
    opacity: 0.85;
}

.metric-help-icon:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
    border-radius: 4px;
}

.metric-help-tip {
    max-width: 320px;
    text-align: left;
}

/* ---------------------------------------------------------------------------
   Nav-drawer line-status dots (LineStatusService). An 8px dot before a product
   group's title carries the line's live run/warn/stop state; the paired
   aria-label on the group keeps the cue from being colour-alone (WCAG 1.4.1).
   Scoped to the group header's own title (the direct button child) so nested
   MudNavLink labels never inherit a dot. Unknown / off-shift / errored lines
   carry no modifier class and therefore no dot. Colours are the DESIGN.md
   functional palette via the Mud tokens: Run Green (success), Warn Amber
   (warning), Stop Red (error).
--------------------------------------------------------------------------- */
.nav-line-status > button > .mud-nav-link-text::before {
    content: "";
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 8px;
    border-radius: 50%;
    vertical-align: middle;
    flex: none;
}

.nav-line-status--run > button > .mud-nav-link-text::before {
    background-color: var(--mud-palette-success);
}

.nav-line-status--warn > button > .mud-nav-link-text::before {
    background-color: var(--mud-palette-warning);
}

.nav-line-status--stop > button > .mud-nav-link-text::before {
    background-color: var(--mud-palette-error);
}

.metric-help-tip .mud-typography-body2 {
    margin-top: 4px;
}

/* --- Glance band (Production pages) ---------------------------------------
   The distance-legible "today" strip above each line's Production report:
   one row that wraps gracefully on narrow screens (no horizontal overflow),
   Headline-plus tabular numerals per DESIGN.md's type ramp, status color from
   the functional palette via the MudChip/progress Color (text carries the
   meaning, never color alone). */

.glance-band-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 32px;
}

.glance-band-status {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 130px;
}

.glance-band-numbers {
    display: flex;
    align-items: baseline;
    gap: 8px;
    white-space: nowrap;
}

.glance-count {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    /* DESIGN.md typography Display step - scales with viewport so the at-a-distance
       count stays the loudest thing on screen without overflowing small viewports. */
    font-size: clamp(2.25rem, 4vw, 3.5rem);
}

.glance-target {
    font-variant-numeric: tabular-nums;
    /* Secondary to the live count: scales between the documented Title (1.125rem)
       and Headline (1.5rem) type steps. */
    font-size: clamp(1.125rem, 1.8vw, 1.5rem);
}

.glance-band-progress {
    flex: 1 1 220px;
    min-width: 180px;
    display: flex;
    align-items: center;
    gap: 12px;
}

/* See .fleet-card-progress: the doubled selector out-specifies MudBlazor's 4px default. */
.glance-progress,
.glance-progress.mud-progress-linear.horizontal {
    flex: 1 1 auto;
    height: 10px;
}

.glance-percent {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ==========================================================================
   Downtime panel (Components/Products/DowntimePanel.razor)
   Persistent downtime surface rendered by ReportBody under the report's side
   rail: the drilled period's category split with quiet proportional bars,
   plus the top-reasons pareto. A category's share is context, not machine
   state, so the bars use the steel/line neutrals — never the status palette
   (DESIGN.md: The State-Owns-Color Rule). Label-step type, tabular numerals,
   Line borders, flat (no added elevation).
   ========================================================================== */

.downtime-panel {
    flex: 0 0 auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    background-color: var(--mud-palette-surface);
    padding: 8px 12px;
}

.downtime-panel-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding-bottom: 6px;
}

.downtime-panel-head .downtime-panel-title {
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.downtime-panel-head .downtime-panel-scope {
    color: var(--mud-palette-text-secondary);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.downtime-panel-head .downtime-panel-total {
    margin-left: auto;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Category rows: label | proportional bar | minutes, aligned as one grid so
   the bars share a common baseline and the minutes align digit-for-digit. */
.downtime-cats {
    display: grid;
    grid-template-columns: auto minmax(40px, 1fr) auto;
    align-items: center;
    column-gap: 8px;
    row-gap: 4px;
}

.downtime-cat-label {
    /* Label step per DESIGN.md type ramp. */
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
}

.downtime-cat-bar {
    height: 6px;
    border-radius: 4px;
    background-color: var(--mud-palette-lines-default);
    overflow: hidden;
}

/* CBS Steel (Info) fill: an informational accent, not a status color. */
.downtime-cat-fill {
    height: 100%;
    background-color: var(--mud-palette-info);
}

.downtime-cat-minutes,
.downtime-reason-minutes {
    font-size: 0.8125rem;
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

.downtime-reasons {
    display: flex;
    flex-direction: column;
    margin-top: 2px;
}

.downtime-reason {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 2px 0;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    font-size: 0.8125rem;
    line-height: 1.4;
}

.downtime-reason:last-child {
    border-bottom: 0;
}

.downtime-reason-label {
    min-width: 0;
    overflow-wrap: anywhere;
}

.downtime-panel .downtime-more {
    margin-top: 2px;
}

/* ==========================================================================
   Period scoreboard + fleet trend deltas
   (Components/Products/PeriodScoreboard.razor, FleetOverviewTable.razor)
   The drill-following "are we on target for THIS period?" strip rendered by
   ReportBody between the toolbar and the charts, plus the small "vs prior
   period" delta lines in the fleet matrices. Grammar mirrors the glance band
   one step down the type ramp: Headline-step produced count with a Title-step
   target (the glance band keeps Display for today's live count), tabular
   numerals throughout, Line border, flat surface (Flat Floor), 8px radius.
   Deltas are CONTEXT, not machine state: always ink-muted with a Glyph-step
   ▲/▼ as the non-color direction cue (State-Owns-Color rule).
   ========================================================================== */

.period-scoreboard {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 16px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    background-color: var(--mud-palette-surface);
    padding: 4px 12px;
    margin-bottom: 8px;
}

.period-scoreboard .period-scoreboard-chip {
    margin: 0;
}

/* Label step: quiet period name matching the breadcrumb wording. */
.period-scoreboard-label {
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.period-scoreboard-numbers {
    display: flex;
    align-items: baseline;
    gap: 6px;
    white-space: nowrap;
}

/* Headline step (1.5rem/600): the period verdict is the center column's
   loudest figure, second only to the page's Display-step glance count. */
.period-count {
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

/* Title step, muted: the target is the reference, not the headline. */
.period-target {
    font-size: 1.125rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

/* Body step, weighted: the attainment percent — the same "n1" figure the
   Overall gain/loss cell shows, never a differently-rounded rival. */
.period-percent {
    font-size: 0.9375rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Prior-period deltas: Label step (the smallest reading step — never below
   0.8125rem for reading content), neutral ink-muted. */
.period-delta,
.fleet-delta {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Matrix cells stack the delta under the value on its own quiet line. */
.fleet-delta {
    display: block;
}

/* Glyph step (0.625rem/700): the ▲/▼ direction mark is a non-color cue,
   not reading-size text. */
.delta-glyph {
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 1;
}

/* ==========================================================================
   Print
   Ctrl+P on a report should produce a clean data artifact, not a screenshot
   of the app shell: hide interactive chrome, release every viewport-fill /
   internal-scroll clamp so tables flow across pages, and force light
   ink-on-white regardless of the on-screen theme. Scoped to tables/text;
   ApexCharts canvases may raster imperfectly — acceptable.
   ========================================================================== */

@media print {

    /* Light ink-on-white, explicitly: a dark-mode user must still print
       black-on-white. Re-pin the palette variables (DESIGN.md neutral tokens)
       so every var()-driven color resolves light, then hard-force the big
       surfaces for anything the theme sets directly. */
    :root,
    :root[data-theme="dark"] {
        color-scheme: light;
        --mud-palette-background: #FFFFFF;
        --mud-palette-surface: #FFFFFF;
        --mud-palette-drawer-background: #FFFFFF;
        --mud-palette-text-primary: #1E2430;
        --mud-palette-text-secondary: #5A6472;
        --mud-palette-lines-default: #DCE2EA;
        --mud-palette-table-lines: #DCE2EA;
    }

    html,
    body,
    .mud-layout,
    .mud-main-content,
    .mud-container,
    .mud-paper,
    .mud-card,
    .mud-table,
    .mud-tabs,
    .mud-tabs-panels {
        background: #FFFFFF !important;
        color: #1E2430 !important;
        box-shadow: none !important;
    }

    /* Keep the low-opacity state tints and their corner glyphs on paper —
       they carry the report's meaning (browser default is to drop them). */
    * {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* App chrome: bars, drawer, overlays, snackbars, the reconnect strip. */
    .mud-appbar,
    .mud-drawer,
    .mud-drawer-overlay,
    .mud-overlay,
    .mud-snackbar,
    .mud-snackbar-provider,
    .mud-popover-provider,
    .mud-popover,
    #blazor-error-ui {
        display: none !important;
    }

    /* Interactive-only controls are meaningless on paper: buttons (report
       toolbar, Download CSV, Back/Jump, row actions), menus (the cross-line
       switcher, Legend, go-to-date), switches, the tab strip, and toolbar
       inputs. The drill crumbs and the "Updated HH:mm" freshness caption
       stay — they caption the printed data. */
    .mud-button-root,
    .mud-icon-button,
    .mud-menu,
    .mud-switch,
    .mud-tabs-tabbar,
    .mud-tabs-toolbar,
    .report-toolbar .mud-input-control {
        display: none !important;
    }

    /* Release the viewport-fill chain: pages grow with their content. */
    :root {
        --page-table-height: auto;
    }

    .mud-main-content {
        padding: 0 !important;
        margin: 0 !important;
        overflow: visible !important;
    }

    .page-fill,
    .page-fill-tabs,
    .page-fill-plain,
    .page-fill-tabs .mud-tabs-panels > div,
    .page-fill-plain > * {
        display: block !important;
        height: auto !important;
        min-height: 0 !important;
    }

    /* Reporting columns stack and stop scrolling internally — everything a
       side rail holds must reach the page, not just one viewport of it. */
    .report-body {
        display: block !important;
        height: auto !important;
    }

    .report-col-side,
    .report-col-center {
        display: block !important;
        overflow: visible !important;
        height: auto !important;
    }

    /* Un-clamp every fixed-header / fill-height table container so rows flow
       across printed pages ("height: auto !important" also beats the inline
       Height="var(--page-table-height)" style MudTable writes). */
    .mud-table-container,
    .summary-fill,
    .summary-fill .summary-fill-table,
    .summary-fill .summary-fill-table .mud-table-container {
        display: block !important;
        height: auto !important;
        max-height: none !important;
        overflow: visible !important;
    }

    /* Sticky headers print in normal flow (sticky positioning can duplicate
       or overlap header rows across page breaks). */
    .mud-table-sticky-header th,
    .mud-table-fixed-header th {
        position: static !important;
    }

    /* Keep a row or chart from being sliced across a page break. */
    .mud-table tr,
    .report-chart,
    .period-scoreboard {
        break-inside: avoid;
    }
}

/* ==========================================================================
   Dark theme ("Nights" palette — DESIGN.md Colors: Dark palette)
   Scoped under .theme-dark, which MainLayout sets on the MudLayout root when
   dark mode is active (and the wallboard root carries always). Everything the
   Mud palette vars already drive flips automatically via PaletteDark
   (CustomTheme.cs); this block covers only what sits OUTSIDE those vars:
   ApexCharts chrome (serialized options / library defaults), the scheduler's
   inline shift-color custom properties, and the color brand logo. Values here
   mirror code constants — change them together:
     shift colors  → NewScheduler/ShiftColors.cs (*Dark)
     paper ink     → CustomTheme.PaperDark (#10192C)
   Static SSR pages (/Account) never get the .theme-dark class; the color-scheme
   and logo rules are mirrored there under :root[data-cbs-theme="dark"]; see
   Components/Account/Shared/AccountLayout.razor and keep the pairs matched.
   ========================================================================== */

/* Native widgets (scrollbars, form controls) render dark inside the app. */
.theme-dark {
    color-scheme: dark;
}

/* The color CBS logo (navy on transparent) vanishes on dark surfaces; render
   it flat white, matching the app bar's white logotype. */
.theme-dark img[src$="CBSLogo.png"] {
    filter: brightness(0) invert(1);
}

/* --- ApexCharts chrome ----------------------------------------------------
   Axis/data-label/legend text, gridlines and tooltips follow the Mud palette
   tokens so chart chrome tracks a theme flip instantly (series colors are
   serialized into the chart options and are re-resolved in code — see
   ChartSeriesColors.PaletteFor / ReportBase.OnChartThemeChangedAsync).
   !important: ApexCharts writes these as inline styles/attributes. */

.theme-dark .apexcharts-text,
.theme-dark .apexcharts-title-text,
.theme-dark .apexcharts-datalabel,
.theme-dark .apexcharts-datalabel-label,
.theme-dark .apexcharts-datalabel-value,
.theme-dark .apexcharts-datalabels text {
    fill: var(--mud-palette-text-primary) !important;
}

.theme-dark .apexcharts-xaxis-label,
.theme-dark .apexcharts-yaxis-label,
.theme-dark .apexcharts-xaxis-title-text,
.theme-dark .apexcharts-yaxis-title-text {
    fill: var(--mud-palette-text-secondary) !important;
}

.theme-dark .apexcharts-legend-text {
    color: var(--mud-palette-text-primary) !important;
}

.theme-dark .apexcharts-gridline,
.theme-dark .apexcharts-grid-borders line,
.theme-dark line.apexcharts-xaxis-tick {
    stroke: var(--mud-palette-lines-default);
}

.theme-dark .apexcharts-toolbar svg {
    fill: var(--mud-palette-text-secondary);
}

.theme-dark .apexcharts-tooltip {
    background: var(--mud-palette-surface) !important;
    color: var(--mud-palette-text-primary) !important;
    border: 1px solid var(--mud-palette-lines-default) !important;
}

.theme-dark .apexcharts-tooltip-title {
    background: var(--mud-palette-background-gray) !important;
    border-bottom: 1px solid var(--mud-palette-lines-default) !important;
}

.theme-dark .apexcharts-xaxistooltip,
.theme-dark .apexcharts-yaxistooltip {
    background: var(--mud-palette-surface) !important;
    color: var(--mud-palette-text-primary) !important;
    border: 1px solid var(--mud-palette-lines-default) !important;
}

/* --- Scheduler shift bars -------------------------------------------------
   The stripe and calendar-legend roots carry the LIGHT ShiftColors inline;
   navy/indigo fall below 3:1 on the dark track, so re-pin the custom props to
   the ShiftColors.*Dark constants (author !important beats inline style).
   Days holds; Nights lifts to #5A6FC0; Afternoons to #8A7FD6 (DESIGN.md). */
.theme-dark .machine-schedule-stripe,
.theme-dark .monthly-calendar .calendar-legend {
    --shift-color-days: #6FA0D6 !important;
    --shift-color-nights: #5A6FC0 !important;
    --shift-color-afternoons: #8A7FD6 !important;
}

/* Bar hour-labels flip with the fill: dark Paper ink on the light Days /
   Afternoons / adjusted (steel) fills — near-white text-primary would fall to
   ~2.3:1 there. Nights keeps its white label (4.7:1 on #5A6FC0). */
.theme-dark .machine-schedule-stripe .shift-bar-days .shift-bar-hours,
.theme-dark .machine-schedule-stripe .shift-bar-afternoons .shift-bar-hours,
.theme-dark .machine-schedule-stripe .shift-bar-adjusted .shift-bar-hours {
    color: #10192C; /* CustomTheme.PaperDark */
    background-color: transparent;
}

/* ==========================================================================
   Wallboard (/Wall — DESIGN.md Layout: the sanctioned second density mode)
   Full-viewport, always-dark TV surface: plant clock + shift label up top,
   one glance-grammar row per authorized line. Type steps scale on viewport
   HEIGHT so four rows fill a TV; tabular numerals throughout; Line hairlines;
   flat (Flat Floor); motion limited to the shared value tick and the progress
   ease (Calm Motion).
   ========================================================================== */

.wallboard-root {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: clamp(16px, 3vh, 40px) clamp(16px, 3vw, 48px);
    background-color: var(--mud-palette-background);
    color: var(--mud-palette-text-primary);
    overflow: hidden;
}

.wallboard-header {
    flex: 0 0 auto;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 24px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    padding-bottom: 8px;
}

.wallboard-header-left,
.wallboard-header-right {
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
}

.wallboard-title {
    font-size: clamp(1.125rem, 3vh, 1.75rem);
}

/* Freshness + shift captions read from across a room too. */
.wallboard-header .mud-typography-caption {
    font-size: clamp(0.8125rem, 2vh, 1.125rem);
}

.wallboard-clock {
    font-size: clamp(1.5rem, 4.5vh, 2.75rem);
    font-weight: 600;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.wallboard-lines {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* One line per row; rows share the remaining viewport height equally. */
.wallboard-line {
    flex: 1 1 0;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(110px, 0.8fr) minmax(0, 1.7fr) minmax(0, 1.8fr) minmax(0, 1.3fr);
    align-items: center;
    column-gap: clamp(16px, 3vw, 48px);
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.wallboard-line:last-child {
    border-bottom: 0;
}

.wallboard-line-name {
    font-size: clamp(1.5rem, 5vh, 2.75rem);
    font-weight: 600;
    line-height: 1.1;
}

.wallboard-line-numbers {
    display: flex;
    align-items: baseline;
    gap: 12px;
    white-space: nowrap;
}

/* The wall's Display step: the count is the loudest thing on the TV. */
.wallboard-count {
    font-size: clamp(2.25rem, 9vh, 5.5rem);
    font-weight: 600;
    line-height: 1.05;
    font-variant-numeric: tabular-nums;
}

.wallboard-target {
    font-size: clamp(1.125rem, 3.5vh, 2rem);
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.wallboard-line-progress {
    display: flex;
    align-items: center;
    gap: 16px;
    min-width: 0;
}

/* See .fleet-card-progress: the doubled selector out-specifies MudBlazor's 4px default. */
.wallboard-progress,
.wallboard-progress.mud-progress-linear.horizontal {
    flex: 1 1 auto;
    height: clamp(10px, 1.8vh, 16px);
}

/* Same progress ease as the glance band / fleet cards (Calm Motion move 3). */
.wallboard-progress .mud-progress-linear-bar {
    transition: transform 200ms ease-out, background-color 200ms ease-out;
}

.wallboard-percent {
    font-size: clamp(1.125rem, 3.5vh, 2rem);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.wallboard-line-status {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
    max-width: 100%;
}

.wallboard-chip {
    margin: 0;
    font-size: clamp(0.875rem, 2.4vh, 1.25rem);
    height: auto;
    padding: 6px 16px;
}

.wallboard-secondary {
    font-size: clamp(0.8125rem, 2vh, 1.125rem);
    color: var(--mud-palette-text-secondary);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Loading / error branches span the data columns so the name keeps its slot. */
.wallboard-line-state {
    grid-column: 2 / -1;
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.wallboard-empty {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Single-line board (/Wall/{Line}): a TV given over to one line.
   The wall scale is tuned for four rows sharing the viewport, so on a dedicated board it left
   roughly 85% of a 1080p screen empty and rendered the count no larger than when four lines
   competed for the same glass — the extra screen bought nothing. One row has about four times
   the height available, so the viewport-height coefficients and caps rise to spend it.
   Every clamp FLOOR is left exactly as the shared scale, so small viewports and the narrow-screen
   stack below behave identically to the multi-line board (DESIGN.md: wall clamp floors are core
   ramp steps). Only the roomy case changes. */
.wallboard-root--single .wallboard-line-name {
    font-size: clamp(1.5rem, 11vh, 6rem);
}

.wallboard-root--single .wallboard-count {
    font-size: clamp(2.25rem, 26vh, 14rem);
}

.wallboard-root--single .wallboard-target {
    font-size: clamp(1.125rem, 7vh, 3.5rem);
}

.wallboard-root--single .wallboard-percent {
    font-size: clamp(1.125rem, 8vh, 4.5rem);
}

.wallboard-root--single .wallboard-progress {
    height: clamp(10px, 4vh, 34px);
}

.wallboard-root--single .wallboard-chip {
    font-size: clamp(0.875rem, 4vh, 2.25rem);
    padding: 10px 24px;
}

.wallboard-root--single .wallboard-secondary {
    font-size: clamp(0.8125rem, 3vh, 1.75rem);
}

/* Narrow screens (a wallboard opened on a phone/tablet): release the fixed
   viewport and stack each line's cells; the page scrolls normally. */
@media (max-width: 959.98px) {
    .wallboard-root {
        position: static;
        min-height: 100dvh;
        overflow: auto;
    }

    .wallboard-line {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 4px;
        padding: 12px 0;
    }

    .wallboard-line-state {
        grid-column: auto;
    }
}

/* Reduced motion: the wallboard's progress ease goes static like the rest of
   the vocabulary (the shared .value-tick is already covered in the Motion
   block above). */
@media (prefers-reduced-motion: reduce) {
    .wallboard-progress .mud-progress-linear-bar {
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   Markdown guides (Database Explorer → Guides tab)

   Markdig emits plain semantic HTML with no classes, so everything here is
   element-scoped under .cbs-markdown. Colours come from the MudBlazor palette
   so the pane follows the light/dark toggle without a second rule set.
   --------------------------------------------------------------------------- */

.cbs-markdown {
    color: var(--mud-palette-text-primary);
    font-size: 0.9375rem;
    line-height: 1.65;
}

/* The measure sits on the text elements, not the container: prose past ~78ch is
   hard to track back to the next line, but reference tables and SQL blocks are
   scanned rather than read and need every pixel of the pane. */
.cbs-markdown p,
.cbs-markdown ul,
.cbs-markdown ol,
.cbs-markdown blockquote,
.cbs-markdown h1,
.cbs-markdown h2,
.cbs-markdown h3,
.cbs-markdown h4,
.cbs-markdown h5,
.cbs-markdown h6 {
    max-width: 78ch;
}

/* Headings step down in weight rather than size alone; these documents are
   deeply nested and size alone stops distinguishing levels past h3. */
.cbs-markdown h1,
.cbs-markdown h2,
.cbs-markdown h3,
.cbs-markdown h4,
.cbs-markdown h5,
.cbs-markdown h6 {
    color: var(--mud-palette-text-primary);
    line-height: 1.25;
    margin: 2rem 0 0.75rem;
}

.cbs-markdown > :first-child {
    margin-top: 0;
}

.cbs-markdown h1 {
    font-size: 1.5rem;
    font-weight: 600;
    border-bottom: 1px solid var(--mud-palette-divider);
    padding-bottom: 0.4rem;
}

.cbs-markdown h2 {
    font-size: 1.25rem;
    font-weight: 600;
    border-bottom: 1px solid var(--mud-palette-divider);
    padding-bottom: 0.3rem;
}

.cbs-markdown h3 {
    font-size: 1.125rem;
    font-weight: 600;
}

.cbs-markdown h4,
.cbs-markdown h5,
.cbs-markdown h6 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
}

.cbs-markdown p,
.cbs-markdown ul,
.cbs-markdown ol {
    margin: 0 0 1rem;
}

.cbs-markdown li {
    margin-bottom: 0.35rem;
}

.cbs-markdown li > ul,
.cbs-markdown li > ol {
    margin-top: 0.35rem;
}

.cbs-markdown a {
    color: var(--mud-palette-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Inline code sits inside prose, so it gets a tint rather than a border —
   a border at this size reads as a table cell. */
.cbs-markdown code {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.86em;
    background-color: var(--mud-palette-action-default-hover);
    border-radius: 4px;
    padding: 0.12em 0.38em;
}

.cbs-markdown pre {
    background-color: var(--mud-palette-background-grey);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    padding: 0.85rem 1rem;
    margin: 0 0 1rem;
    overflow-x: auto; /* SQL samples run long; the page itself must not scroll */
}

.cbs-markdown pre code {
    background: none;
    padding: 0;
    font-size: 0.875rem;
    line-height: 1.55;
    white-space: pre;
}

/* The left rule is the markdown convention for an aside, but it stays neutral —
   an aside reads quieter than body text, so a brand-coloured bar would give it
   more emphasis than the prose it is subordinate to. The secondary text colour
   is what marks it as an aside; the rule only shows where it starts and ends. */
.cbs-markdown blockquote {
    margin: 0 0 1rem;
    padding: 0.1rem 0 0.1rem 1rem;
    border-left: 3px solid var(--mud-palette-lines-default);
    color: var(--mud-palette-text-secondary);
}

/* Pipe tables carry most of the reference detail and run far wider than the
   prose measure. display:block lets each table scroll inside itself — Markdig
   emits no wrapper element to hang the scroll container on, and the page body
   must never scroll horizontally (DESIGN.md). */
.cbs-markdown table {
    display: block;
    width: max-content;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    font-size: 0.875rem;
    margin: 0 0 1rem;
}

.cbs-markdown th,
.cbs-markdown td {
    border: 1px solid var(--mud-palette-lines-default);
    padding: 0.4rem 0.65rem;
    text-align: left;
    vertical-align: top;
}

.cbs-markdown th {
    background-color: var(--mud-palette-background-grey);
    font-weight: 600;
    white-space: nowrap;
}

.cbs-markdown tbody tr:nth-of-type(odd) {
    background-color: var(--mud-palette-action-default-hover);
}

.cbs-markdown hr {
    border: 0;
    border-top: 1px solid var(--mud-palette-divider);
    margin: 2rem 0;
}

.cbs-markdown img {
    max-width: 100%;
}

/* Task lists come from Markdig's advanced extensions; the default checkbox
   inherits list bullets and indentation it does not need. */
.cbs-markdown input[type="checkbox"] {
    margin-right: 0.4rem;
}

/* ---------------------------------------------------------------------------
   Database Explorer — Browse tree

   Areas and tables are buttons rather than links: they change what the pane
   shows, they do not navigate. Native buttons keep keyboard and screen-reader
   behaviour for free, so everything here is stripping the default chrome back
   off rather than rebuilding it.
   --------------------------------------------------------------------------- */

.db-tree {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    /* The tree scrolls independently — 339 tables must not push the detail pane
       off the bottom of the page. */
    max-height: 70vh;
    overflow-y: auto;
}

.db-tree-area + .db-tree-area {
    border-top: 1px solid var(--mud-palette-divider);
}

.db-tree-area-head,
.db-tree-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: inherit;
    font: inherit;
}

.db-tree-area-head {
    padding: 0.5rem 0.6rem;
    font-weight: 600;
    font-size: 0.875rem;
}

.db-tree-area-head:hover,
.db-tree-item:hover {
    background-color: var(--mud-palette-action-default-hover);
}

.db-tree-area-head:focus-visible,
.db-tree-item:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

.db-tree-area-title {
    flex: 1 1 auto;
    min-width: 0;
}

/* Row counts and table counts are reference figures, not the thing being read. */
.db-tree-count {
    flex: 0 0 auto;
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

.db-tree-scope {
    margin: 0;
    padding: 0 0.6rem 0.5rem 2rem;
    font-size: 0.8125rem;
    line-height: 1.45;
    color: var(--mud-palette-text-secondary);
}

.db-tree-list {
    list-style: none;
    margin: 0;
    padding: 0 0 0.4rem;
}

.db-tree-item {
    padding: 0.3rem 0.6rem 0.3rem 2rem;
    font-size: 0.875rem;
}

.db-tree-item-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* The selected table is marked by weight and a tinted row rather than a colour
   bar, so it reads as "you are here" without competing with the detail pane. */
.db-tree-item-active {
    background-color: var(--mud-palette-action-default-hover);
    font-weight: 600;
}

/* ---------------------------------------------------------------------------
   Table and column notes rendered as markdown inside table cells. Same
   .cbs-markdown rules as the guides, minus the block spacing a cell cannot
   afford and the prose measure a narrow column has already imposed.
   --------------------------------------------------------------------------- */

.cbs-note p,
.cbs-note ul,
.cbs-note ol {
    max-width: none;
}

.cbs-note > :last-child {
    margin-bottom: 0;
}

.cbs-note p {
    margin-bottom: 0.4rem;
}

/* ---------------------------------------------------------------------------
   /Admin/Logs viewer (LogViewer.razor).
   The pane is a column-reverse flexbox and the markup renders newest-first:
   together the browser keeps the scroll pinned to the visual bottom while
   auto-refresh appends lines, with no JS scroll management.
   --------------------------------------------------------------------------- */

.do-log-pane {
    display: flex;
    flex-direction: column-reverse;
    height: 65vh;
    overflow: auto;
    background: var(--mud-palette-background);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    padding: 8px 12px;
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.8125rem;
    line-height: 1.45;
}

.do-log-line {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.do-log-stamp {
    color: var(--mud-palette-text-secondary);
    margin-right: 1ch;
    user-select: none;
}

/* Continuation rows repeat near-identical stamps; fade them so entries read as blocks */
.do-log-cont .do-log-stamp {
    opacity: 0.45;
}

.do-log-warn {
    color: var(--mud-palette-warning);
}

.do-log-error {
    color: var(--mud-palette-error);
}

/* Stream/tail selects: enough width for their longest option without flexing full-row */
.do-log-control {
    min-width: 170px;
    max-width: 220px;
}
