/* PlatinumWeigh PWA — base styles layered on top of MudBlazor.
   Uses MudBlazor CSS custom-properties (--mud-palette-*) so everything
   adapts automatically when the user switches dark / light mode. */

:root {
    /* Radius scale — one card standard everywhere; pills stay full-round. */
    --pw-radius-card: 16px;
    --pw-radius-control: 8px;
    --pw-radius-pill: 999px;

    /* Caption/metadata type scale — two steps below body (0.875rem), not a dozen one-offs. */
    --pw-font-caption: 0.75rem;
    --pw-font-micro: 0.6875rem;

    /* z-index scale. MudBlazor's own portaled overlays (menus/dialogs/snackbars) occupy
       roughly 1200–1400; app-level fixed elements stay below that band except the boot
       splash, which must sit above literally everything until it is dismissed. */
    --pw-z-app-banner: 1000;
    --pw-z-assistant-fab: 1050;
    --pw-z-splash: 9999;

    /* Waste Calculator tile — a teal blend derived from theme tokens (Info + Success) so it
       tracks light/dark mode automatically instead of a hardcoded hex pair. */
    --pw-tile-calculator-start: color-mix(in srgb, var(--mud-palette-info) 55%, var(--mud-palette-success) 45%);
    --pw-tile-calculator-end: color-mix(in srgb, var(--mud-palette-info-darken) 55%, var(--mud-palette-success-darken) 45%);
}

html, body {
    margin: 0;
    font-family: Inter, 'Segoe UI', system-ui, Roboto, Helvetica, Arial, sans-serif;
}

h1:focus { outline: none; }
h1:focus-visible { outline: 2px solid var(--mud-palette-primary); outline-offset: 4px; }

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

/* ── Shared content-card treatment ────────────────────────────────────────
   One flat, bordered surface style for every static content card (charts,
   calculator result, auth gates) — replaces five previously-divergent
   Elevation/shadow combinations (2, 3, 4, and none) with a single consistent
   look: border at rest, no drop shadow (never both — see the ghost-card ban
   in the platinum-ui skill). Pair with Elevation="0" on MudCard usages. */
.pw-card {
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--pw-radius-card);
    overflow: hidden;
}

/* ── Antigravity treatment ────────────────────────────────────────────────
   The palette lives in PlatinumWeighTheme.cs; this is the shape half of the
   same idea. Two things carry it: pills instead of soft rectangles on anything
   you click, and hairlines instead of drop shadows everywhere.

   Kept OUT of the dark theme deliberately. Dark mode already reads as its own
   product and separates surfaces by lightness rather than by border; forcing a
   1px hairline onto a dark card just makes it look sketched. */
.mud-button-root:not(.mud-icon-button):not(.mud-button-fab) {
    border-radius: var(--pw-radius-pill);
    padding-inline: 20px;
}
.mud-chip { border-radius: var(--pw-radius-pill); }

/* Elevation is the app's own decision (`.pw-card`, tables, the weight panel),
   so MudBlazor's ambient shadows are turned off rather than fought
   component-by-component. A surface separates itself with a hairline. */
:root:not(.pw-dark) .mud-paper:not(.mud-drawer):not(.mud-appbar):not(.mud-popover) {
    box-shadow: none;
}
:root:not(.pw-dark) .mud-appbar {
    box-shadow: none;
    border-bottom: 1px solid var(--mud-palette-divider);
}
:root:not(.pw-dark) .mud-drawer {
    border-right: 1px solid var(--mud-palette-divider);
}

/* An outlined input on a near-white ground wants the hairline, not the heavier
   default border MudBlazor draws at rest. */
:root:not(.pw-dark) .mud-input-outlined .mud-input-outlined-border {
    border-color: var(--mud-palette-lines-default);
}

/* ── Data tables ──────────────────────────────────────────────────────────
   A table IS a content card, so it gets the same flat bordered surface as
   `.pw-card` rather than a shadow of its own. The 36 tables in this app were
   split across three treatments — Elevation 0, Elevation 2, and 14 that set
   none at all and inherited MudBlazor's default 1 — so two list screens that
   do the same job (Clients and Carriers, say) did not look like the same
   product. The markup is normalised to Elevation="0"; this rule is what makes
   a table that forgets to say so still land in the right place. */
.mud-table.mud-table-outlined,
.mud-table {
    border: 1px solid var(--mud-palette-divider);
    border-radius: var(--pw-radius-card);
    overflow: hidden;
}
/* A table nested inside an already-bordered card would otherwise draw a second
   frame 1px inside the first. */
.pw-card .mud-table,
.mud-card .mud-table,
.mud-expand-panel .mud-table {
    border: none;
    border-radius: 0;
}
/* Header row: a tinted band rather than a rule, so the column names read as a
   header at a glance on a long list. */
.mud-table .mud-table-head .mud-table-cell {
    background: var(--mud-palette-background-grey);
    font-weight: 600;
    white-space: nowrap;
}
/* Figures line up in their columns. Without tabular figures a column of
   amounts is ragged, which is the single most common way a data screen reads
   as unfinished. */
.mud-table .mud-table-cell { font-variant-numeric: tabular-nums; }

/* ── One page rhythm ──────────────────────────────────────────────────────
   The title row, the optional sentence under it, and the first content block
   used eight different spacing combinations across the app (mb-1, mb-2, mb-3,
   mb-5, GutterBottom true/false, and none). Measured on a 1440px viewport the
   gap above the first table ranged from 8px to 28px depending only on which
   page you were standing on. One class, one number, set here.

   Deliberately ONLY a margin: the header is usually a MudStack that already
   owns its own row layout and spacing, and a second flex/gap declaration here
   would quietly fight it. This class is a spacing token, not a layout.

   The `!important` matches MudBlazor's own spacing utilities (`mb-3` and
   friends all carry it). Without it this rule loses to whatever utility class
   happens to still be on the element, which is exactly the drift being fixed. */
.pw-page-head { margin-bottom: 20px !important; }
/* An intro sentence belongs to the heading above it, so it closes the gap to
   the title and owns the space down to the content. */
.pw-page-head + .pw-page-intro {
    margin-top: -12px !important;
    margin-bottom: 20px !important;
    color: var(--mud-palette-text-secondary);
    max-width: 78ch;   /* a sentence of explanation stays readable rather than
                          running the full width of a 1440px screen */
}

/* ── Dashboard brand header ──────────────────────────────────────────── */
.rw-brand-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.rw-brand-text { flex: 1 1 auto; min-width: 0; }
.rw-brand-logo-wrap {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}
.rw-brand-logo {
    max-height: 72px;
    max-width: 180px;
    width: auto;
    height: auto;
    object-fit: contain;
}
@media (max-width: 480px) {
    .rw-brand-header { flex-direction: column; align-items: flex-start; }
    .rw-brand-logo   { max-height: 52px; max-width: 140px; }
}

/* ── Dashboard action tiles ───────────────────────────────────────────── */
.w8-tile {
    cursor: pointer;
    transition: transform .2s cubic-bezier(0.2, 0, 0, 1), box-shadow .2s cubic-bezier(0.2, 0, 0, 1);
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    border-radius: var(--pw-radius-card) !important;
}
.w8-tile:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, .08) !important;
}
.w8-tile:active {
    transform: translateY(0);
    box-shadow: 0 4px 10px rgba(0, 0, 0, .05) !important;
}
.w8-tile:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* Count badge on the Multi-Weigh tile: loads awaiting their second weigh. */
.rw-pending-pill {
    display: inline-flex;
    align-items: center;
    padding: 4px 12px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .22);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
}

/* ── Selection list items (Weigh / MultiWeigh pickers) ───────────────── */
.w8-select-item {
    cursor: pointer;
    min-height: 64px;
    transition: background .12s ease, transform .1s ease, box-shadow .12s ease;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
.w8-select-item:hover {
    background: var(--mud-palette-action-default-hover) !important;
    transform: translateX(3px);
}
.w8-select-item:active { transform: translateX(1px); }
.w8-select-item:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

/* ── Live weight panel states ─────────────────────────────────────────── */
/* Applied as CSS classes; colour automatically tracks the palette vars.  */
.w8-weight-stable {
    background: linear-gradient(135deg,
        var(--mud-palette-primary),
        var(--mud-palette-primary-darken)) !important;
}
.w8-weight-moving {
    background: linear-gradient(135deg,
        var(--mud-palette-warning),
        var(--mud-palette-tertiary, #E88F1A)) !important;
}
.w8-weight-disconnected {
    background: var(--mud-palette-lines-default, #DEE3EA) !important;
}

/* Text on the coloured weight panel */
.w8-weight-live-text   { color: rgba(255, 255, 255, 0.92); }
.w8-weight-live-label  { color: rgba(255, 255, 255, 0.70); }
.w8-weight-idle-text   { color: var(--mud-palette-text-secondary); }

/* ── Big weight readout ───────────────────────────────────────────────── */
.weight-readout {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    letter-spacing: -2px;
    line-height: 1;
    font-size: 4rem;
}

/* ── Net mass + charge preview (MultiWeigh second weigh) ──────────────── */
/* Sits inside the confirmation card, so it reads as a panel rather than a  */
/* second card: tinted surface, no elevation, palette-driven in both themes.*/
.w8-net-card {
    border-radius: var(--pw-radius-card);
    /* Faintly tinted toward the brand rather than a flat grey, so the panel still reads as its own block
       on the light theme, where background-grey is nearly the card's own white. */
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, var(--mud-palette-background-grey));
    border: 1px solid color-mix(in srgb, var(--mud-palette-primary) 18%, var(--mud-palette-lines-default));
}
.w8-net-value {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    letter-spacing: -1px;
    line-height: 1.1;
    font-size: 2.25rem;
}
.w8-net-waiting {
    display: flex;
    align-items: center;
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
}
/* Label left, figure right, both on one baseline so the column of amounts lines up. */
.w8-money-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
}
.w8-money-row .w8-money-label {
    flex: 1 1 auto;
    color: var(--mud-palette-text-secondary);
}
.w8-money-row .w8-money-value {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}
.w8-money-total .w8-money-label,
.w8-money-total .w8-money-value {
    font-weight: 700;
    font-size: 1.15rem;
    color: var(--mud-palette-text-primary);
}

/* ── Step-indicator chips (Weigh / MultiWeigh stepper) ───────────────── */
.w8-step-done {
    opacity: 0.85;
}

/* ── KPI / summary stat cards ────────────────────────────────────────── */
/* A full-tint icon chip carries the status colour instead of a side stripe. */
.w8-stat-icon {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: var(--pw-radius-control);
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--mud-palette-primary) 14%, transparent);
    color: var(--mud-palette-primary);
}
.w8-stat-card.success .w8-stat-icon { background: color-mix(in srgb, var(--mud-palette-success) 14%, transparent); color: var(--mud-palette-success); }
.w8-stat-card.info    .w8-stat-icon { background: color-mix(in srgb, var(--mud-palette-info) 14%, transparent);    color: var(--mud-palette-info); }
.w8-stat-card.warning .w8-stat-icon { background: color-mix(in srgb, var(--mud-palette-warning) 14%, transparent); color: var(--mud-palette-warning); }

/* ── Page-level layout helpers ─────────────────────────────────────────────
   The bottom padding clears the floating AI-assistant button. It is fixed to
   the bottom-right, so anything the page renders in its last ~100px sits
   underneath it — on Mine Position that was two of the four operating-cost
   amounts, silently covered. 104px = the 56px FAB + its 24px offset + air. */
.w8-page-content {
    padding-top: 8px;
    padding-bottom: 104px;
}

/* ── Blazor WASM boot UI ──────────────────────────────────────────────── */
.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 20vh auto 1rem auto;
}
.loading-progress circle {
    fill: none;
    stroke: #e0e0e0;
    stroke-width: 0.6rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}
.loading-progress circle:last-child {
    stroke: #1A73E8;
    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;
    color: #606A7B;
}
.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* ── Animated splash screen (canvas particle effect, light + dark modes) ──
   Kept deliberately IDENTICAL to PlatinumIntegration's `.splash-*` rules. The
   two products sit side by side on a customer's screens and the first thing
   either paints should be recognisably the same company — so the logo box, the
   glow and the tagline all match. Change one, change the other.

   Keep these in sync with the critical copy inlined in index.html's <head>. */
#splash-screen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: var(--pw-z-splash, 9999);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    transition: opacity 0.6s ease;
}
#splash-screen.splash-fade {
    opacity: 0;
    pointer-events: none;
}
/* Canvas fills the entire splash — splash.js sets #splash-screen's background colour. */
#splashCanvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
/* Overlay: logo + tagline centred on top of the canvas. */
.splash-overlay {
    position: relative;
    z-index: 10;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
}
/* A FIXED box, not an intrinsic size. platinum-brink.svg is sized by its
   containing block, so anything else lets it paint viewport-wide for the
   moment before app.css applies — the bug PlatinumIntegration hit and fixed. */
.splash-logo-wrap {
    position: relative;
    width: 120px;
    height: 120px;
    margin-bottom: 2rem;
}
.splash-logo {
    width: 100%;
    height: 100%;
    position: relative;
    z-index: 2;
}
/* Ambient glow behind the logo — a soft radial falloff rather than a blurred
   solid, so it reads the same on a black and on a white ground. */
.splash-glow {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 140%;
    height: 140%;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, rgba(0,51,102,0.15) 0%, rgba(0,51,102,0) 70%);
    z-index: 1;
}
/* One tagline colour for both themes: slate reads on #050505 and on #FFFFFF
   alike, so the splash needs no theme class of its own. */
.splash-tagline {
    font-family: Inter, 'Segoe UI', system-ui, sans-serif;
    color: #64748b;
    font-size: 0.9rem;
    font-weight: 500;
    letter-spacing: 0.5px;
    margin: 0;
}

/* ── Login page ──────────────────────────────────────────────────────── */
.rw-login-root {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: var(--mud-palette-background, #EEF2F5);
    padding: 24px 16px;
}

.rw-login-card {
    padding: 40px 40px 32px;
    width: 100%;
    max-width: 420px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

.rw-login-logo {
    max-width: 180px;
    max-height: 140px;
    width: auto;
    height: auto;
    object-fit: contain;
    align-self: center;
    margin-bottom: 4px;
}

.rw-login-footer {
    margin-top: 24px;
    text-align: center;
}

/* Version stamp pinned to the bottom of the nav drawer — visible on every page once signed in.

   `margin-top: auto` is what actually pins it. It used to sit 8px under the last nav link with the
   drawer's own credit line far below it, leaving a large dead gap between two things that read as one
   footer. The drawer nav is a flex column (below) so auto margin pushes this to the end of the region. */
.pw-nav-version {
    padding: 10px 16px 14px;
    text-align: center;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    margin-top: auto;
}

@media (max-width: 480px) {
    .rw-login-card { padding: 28px 20px 24px; }
    .rw-login-logo { max-width: 140px; max-height: 110px; }
}

/* ── First-login disclaimer gate ────────────────────────────────── */
.rw-disclaimer-card {
    padding: 32px 32px 24px;
    width: 100%;
    max-width: 640px;
    display: flex;
    flex-direction: column;
}
.rw-disclaimer-body {
    max-height: min(48vh, 440px);
    overflow-y: auto;
    padding-right: 8px;
}
.rw-disclaimer-h {
    font-weight: 700;
    margin-bottom: 4px;
}
@media (max-width: 480px) {
    .rw-disclaimer-card { padding: 24px 18px 20px; }
    .rw-disclaimer-body { max-height: 52vh; }
}

/* ── Nav drawer overrides ───────────────────────────────────────────────
   The drawer is a fixed column: header, nav region, pinned footer. It must
   NEVER scroll sideways, and on a 1080p screen the whole admin menu — Setup
   group expanded — has to fit without scrolling at all.

   The menu used to do both. `.mud-nav-link` is `width:100%`, so the old
   `margin: 2px 12px` pushed every link 24px past the drawer's right edge (36px
   for Setup's children, which added another `margin-left:24px` on top of
   MudBlazor's own padding-based indent). That put a horizontal scrollbar on the
   nav region — and because `overflow-y:auto` alone makes the computed
   `overflow-x` `auto` too, there was nothing stopping it. On Windows (classic,
   space-consuming scrollbars, unlike macOS overlay ones) that horizontal bar
   then ate ~15px of height and raised a *vertical* scrollbar on a menu that
   otherwise fitted — the "scrolls up and down for no apparent reason" part.

   So: the horizontal inset lives on the menu container as padding (border-box,
   so `width:100%` links land inside it), sub-items indent with padding only,
   overflow-x is pinned hidden, and the rows are compact enough that the worst
   case — 19 rows, admin with Setup open — fits a Full HD viewport. */
.rw-drawer-nav {
    flex: 1 1 auto;
    min-height: 0;          /* lets the flex child shrink rather than overflow */
    overflow-x: hidden;     /* never sideways — see above */
    overflow-y: auto;       /* fallback only, for genuinely short screens */
    display: flex;          /* so the version stamp can pin itself to the bottom */
    flex-direction: column;
}
.rw-drawer-nav > .mud-navmenu { flex: 0 0 auto; }
/* Header and footer are flex-fixed: only the nav region between them flexes,
   and they give the menu back the height MudBlazor's 64px min-height took. */
.rw-drawer-header {
    flex: 0 0 auto;
    min-height: 0;
    padding: 14px 20px;
}
.rw-drawer-footer {
    flex: 0 0 auto;
    padding: 8px 12px;
    font-size: 0.66rem;
    line-height: 1.35;
    color: var(--mud-palette-text-secondary);
}
.mud-drawer {
    overflow: hidden;       /* the nav region owns the only scrollable area */
}
.rw-drawer-nav > .mud-navmenu {
    padding: 0 8px;         /* the links' horizontal inset, inside the width.
                               Outer menu only — a nested one (Setup's children)
                               would stack another inset and re-indent them. */
}
.mud-drawer .mud-nav-link {
    border-radius: var(--pw-radius-control);
    margin: 1px 0;
    padding: 6px 12px;
    font-size: 0.85rem;
    line-height: 1.4;
    align-items: center;
    transition: background-color 0.2s ease, color 0.2s ease;
}
.mud-drawer .mud-nav-link .mud-nav-link-text {
    margin-inline-start: 10px;
    overflow-wrap: anywhere;   /* a long label wraps; it never widens the menu */
}
.mud-drawer .mud-nav-link .mud-icon-root {
    font-size: 1.2rem;
}
.mud-drawer .mud-nav-link:hover,
.mud-drawer .mud-nav-link:focus-visible {
    background-color: var(--mud-palette-action-default-hover) !important;
}
.mud-drawer .mud-nav-link:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}
.mud-drawer .mud-nav-link.active {
    background-color: var(--mud-palette-primary-lighten) !important;
    color: var(--mud-palette-primary) !important;
    font-weight: 700;
}
.mud-drawer .mud-nav-link.active .mud-icon-root {
    color: var(--mud-palette-primary) !important;
}
/* Setup's children indent with padding, not margin, so the row stays exactly as
   wide as the drawer (MudBlazor's own rule already sets padding-inline-start;
   this only tightens it and restores the right padding it unsets). */
.mud-drawer .mud-nav-group .mud-nav-link {
    padding-inline-start: 30px;
    padding-inline-end: 12px;
}

/* Section labels ("Operations" / "Oversight") — tight, so they cost a row of
   height rather than the ~28px MudBlazor's overline line-height would give. */
.rw-nav-section {
    display: block;
    padding: 8px 20px 2px;
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

/* Legend dot (small circle beside series label) */
.rw-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* ── Weekly activity sparkline (custom responsive SVG) ───────────────────
   A fixed 0–100 viewBox stretched edge-to-edge (preserveAspectRatio="none").
   Strokes use vector-effect:non-scaling-stroke so they stay crisp & uniform
   regardless of the non-uniform stretch; data dots are HTML so they stay
   perfectly round. This fills the full card width on every screen size. */
.rw-spark {
    position: relative;
    width: 100%;
    height: 250px;
}
.rw-spark--center {
    display: flex;
    align-items: center;
    justify-content: center;
}
.rw-spark-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}
.rw-spark-grid {
    stroke: var(--mud-palette-divider, #e2e8f0);
    stroke-width: 1;
    stroke-dasharray: 3 4;
    opacity: 0.5;
}
.rw-spark-dot {
    position: absolute;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--mud-palette-surface, #fff);
    transform: translate(-50%, -50%);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
    transition: width .12s ease, height .12s ease;
    cursor: default;
    z-index: 2;
}
.rw-spark-dot--dump    { border: 2.5px solid var(--mud-palette-error); }
.rw-spark-dot--removal { border: 2.5px solid var(--mud-palette-success); }
.rw-spark-dot:hover {
    width: 14px;
    height: 14px;
    z-index: 3;
}
.rw-spark-ymax {
    position: absolute;
    top: 10%;
    left: 0;
    transform: translateY(-50%);
    font-size: var(--pw-font-micro);
    font-weight: 600;
    line-height: 1;
    color: var(--mud-palette-text-secondary);
    background: var(--mud-palette-surface, #fff);
    padding: 1px 5px;
    border-radius: var(--pw-radius-control);
    opacity: 0.9;
    pointer-events: none;
}
.rw-spark-axis {
    position: relative;
    width: 100%;
    height: 16px;
    margin-top: 8px;
}
.rw-spark-axis span {
    position: absolute;
    transform: translateX(-50%);
    font-size: var(--pw-font-caption);
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}
@media (max-width: 480px) {
    .rw-spark { height: 210px; }
    .rw-spark-axis span { font-size: 0.62rem; }
}

/* ── Stockpile donut cards ────────────────────────────────────────────── */
.rw-stockpile-header {
    padding: 14px 20px;
}

.rw-stockpile-header--dump {
    background: linear-gradient(135deg, var(--mud-palette-error-darken) 0%, var(--mud-palette-error) 100%);
}

.rw-stockpile-header--removal {
    background: linear-gradient(135deg, var(--mud-palette-success-darken) 0%, var(--mud-palette-success) 100%);
}

/* Header title/total — one shared size (not four repeated one-off inline styles). */
.rw-stockpile-title {
    color: #fff;
    font-weight: 700;
    font-size: 0.9375rem;
}
.rw-stockpile-total {
    color: rgba(255, 255, 255, 0.85);
    font-size: var(--pw-font-caption);
}

/* Legend swatch (square colour indicator in the legend table) */
.rw-legend-swatch {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    flex-shrink: 0;
}

/* ── Dropdown popovers on a small screen ──────────────────────────────────
   Reported from a client site on an 8.68" rugged tablet (BlackView Active 5):
   the waste-type dropdown rendered off-screen in BOTH portrait and landscape,
   so the operator could not read or reach half the options.

   MudBlazor portals a popover to the body and positions it with inline
   left/top, so CSS cannot move it — but it CAN stop it needing to be moved.
   Two clamps do that, and they are deliberately global rather than scoped to
   one page: any dropdown on any screen is one long label away from the same
   bug, and this is a booth product used on small rugged hardware.

   - width: never wider than the viewport, so a long label wraps instead of
     pushing the box past the edge.
   - height: never taller than 55vh, so a 15-item list scrolls in place
     instead of running off the bottom (the landscape failure). */
.mud-popover {
    max-width: calc(100vw - 16px);
    max-height: 55vh;
    overflow-y: auto;
    overscroll-behavior: contain;   /* scrolling the list must not scroll the page behind it */
}

/* A long option must wrap rather than force the popover wide. MudBlazor sets
   nowrap on list-item text by default, which is what made the width unbounded. */
.mud-popover .mud-list-item-text,
.mud-popover .mud-list-item-text .mud-typography {
    white-space: normal;
    overflow-wrap: anywhere;
}

/* Touch target: a gloved hand on a weighbridge, not a mouse. */
@media (pointer: coarse) {
    .mud-popover .mud-list-item { min-height: 48px; }
}

/* ── Waste type option ────────────────────────────────────────────────────
   Two lines: the description, then the density it sets. The density is the
   figure the operator is really choosing by, so it must never be the part
   that gets truncated. */
.pw-waste-type-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 2px 0;
    min-width: 0;
}
.pw-waste-type-name {
    font-weight: 500;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.pw-waste-type-density {
    font-size: var(--pw-font-caption);
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}
/* The list is 15 options long; on a short landscape viewport it must scroll
   rather than extend past the bottom of the screen. */
.pw-waste-type-popover {
    max-height: min(55vh, 420px);
}

/* ── Waste Calculator ─────────────────────────────────────────────────── */
.rw-calc-options {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.rw-calc-chip {
    min-width: 56px;
    border-radius: var(--pw-radius-control) !important;
    font-weight: 600;
}
.rw-calc-chip--fill {
    min-width: 72px;
    font-size: 1.05rem;
}
.rw-calc-result-header {
    padding: 20px 24px 18px;
    background: linear-gradient(135deg,
        var(--mud-palette-primary) 0%,
        var(--mud-palette-primary-darken, #114a2e) 100%);
}
.rw-calc-summary td {
    padding: 4px 0;
    border: none;
    font-size: 0.875rem;
}
.rw-calc-summary td:last-child {
    font-weight: 600;
    text-align: right;
}

/* ── Page footer (every page) ──────────────────────────────────────────────
   The same credit line already sits pinned in the nav drawer, so on any screen
   wide enough to show the drawer this was the identical sentence twice on one
   view, with a rule across the page between them. The drawer is
   DrawerVariant.Responsive, which hides it below MudBlazor's Md breakpoint —
   so this footer carries the credit exactly where the drawer cannot. */
.rw-page-footer {
    padding: 12px 16px 20px;
    border-top: 1px solid var(--mud-palette-divider, #DEE3EA);
    text-align: center;
}
@media (min-width: 960px) {
    .rw-page-footer { display: none; }
}

/* ── Long-form dialog — bottom scroll affordance ─────────────────────────────
   MudBlazor's own .mud-dialog-content already scrolls internally on a short viewport (an 8" tablet in
   landscape is ~800px tall) — but nothing signals that on first open, so the last field(s) sit below the
   fold looking like they don't exist. Found 2026-08-16 on Tariffs' Add dialog (13 fields) via a live
   1280×800 check; Weighbridges' Add dialog (14+ fields for a Serial connection) had the identical shape.
   Apply `pw-scroll-dialog` to any MudDialog whose content can plausibly run long, with a trailing
   `<div class="pw-scroll-dialog-fade">` as the LAST child of DialogContent (after every conditional
   branch, not inside one — it has to be the true last child regardless of which branch renders).
   The classic sticky-footer-fade trick: as that last child, `position: sticky; bottom: 0` pins it to the
   visible bottom edge while there's still real content below the fold, and once scrolled all the way down
   its own normal-flow position already sits at that same edge — so it settles flush after the real last
   field rather than floating over it. No JS scroll-tracking needed to know when to hide. */
.pw-scroll-dialog .mud-dialog-content { padding-bottom: 4px; }
.pw-scroll-dialog-fade {
    position: sticky;
    bottom: 0;
    height: 26px;
    margin-top: -26px;
    background: linear-gradient(to bottom, transparent, var(--mud-palette-surface, #fff) 85%);
    pointer-events: none;
}

/* ── Blazor error boundary ────────────────────────────────────────────── */
.blazor-error-boundary {
    background: #b32121;
    padding: 1rem;
    color: white;
}
.blazor-error-boundary::after { content: "An error has occurred."; }

#blazor-error-ui {
    color: #fff;
    background: #C62828;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: var(--pw-z-app-banner, 1000);
}
#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}
