/* 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 {
    /* EnviroNexus tokens. MudBlazor's palette (PlatinumWeighTheme.cs) carries the same
       hexes into --mud-palette-*; these names are what app CSS reads directly. */
    --brand-primary: #16803C;
    --brand-primary-hover: #146C32;
    --brand-subtle: rgba(22, 128, 60, 0.10);
    --brand-on-subtle: #16803C;
    --bg-canvas: #F4F7F5;
    --bg-surface: #FFFFFF;
    --bg-surface-hover: #F3F7F4;
    --bg-sidebar: #EFF4F1;
    --bg-input: #F9FBFA;
    --border-subtle: #E2E8F0;
    --border-focus: #16803C;
    --text-primary: #0F172A;
    --text-secondary: #475569;
    --text-muted: #64748B;
    --enviro-card-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.05), 0 2px 6px -1px rgba(15, 23, 42, 0.03);
    --enviro-ease: cubic-bezier(0.16, 1, 0.3, 1);

    --pw-radius-card: 14px;
    --pw-radius-control: 10px;
    --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.pw-dark {
    --brand-primary: #22C55E;
    --brand-primary-hover: #16A34A;
    --brand-subtle: rgba(34, 197, 94, 0.14);
    --brand-on-subtle: #4ADE80;
    --bg-canvas: #0A0F0D;
    --bg-surface: #111916;
    --bg-surface-hover: #17221E;
    --bg-sidebar: #0D1411;
    --bg-input: #0D1411;
    --border-subtle: #1E2D26;
    --border-focus: #22C55E;
    --text-primary: #F1F5F9;
    --text-secondary: #94A3B8;
    --text-muted: #64748B;
    --enviro-card-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.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;
    }
}

/* ── Cards ────────────────────────────────────────────────────────────────
   One surface in both themes. Light mode used to strip every paper shadow,
   which left Settings sections floating on a flat white canvas. */
.pw-card,
.enviro-card,
.mud-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--pw-radius-card);
    box-shadow: var(--enviro-card-shadow);
    transition: transform 0.2s var(--enviro-ease), box-shadow 0.2s ease, border-color 0.2s ease;
}
.pw-card:hover,
.enviro-card:hover,
.mud-card:hover {
    border-color: rgba(34, 197, 94, 0.3);
    transform: translateY(-1px);
}
/* A card that contains a form should not jump while the operator is typing. */
.mud-dialog .mud-card:hover,
.mud-popover .mud-card:hover {
    transform: none;
}

.mud-button-root:not(.mud-icon-button):not(.mud-button-fab) {
    border-radius: var(--pw-radius-control);
    padding-inline: 18px;
    transition: transform 0.2s var(--enviro-ease), box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.mud-button-filled.mud-button-filled-primary:hover {
    transform: translateY(-1px);
    box-shadow: 0 6px 16px -3px rgba(22, 128, 60, 0.35);
}
.mud-button-filled.mud-button-filled-primary:active {
    transform: translateY(0) scale(0.98);
}
.mud-button-outlined.mud-button-outlined-error:hover {
    background-color: rgba(239, 68, 68, 0.1);
}
.mud-chip { border-radius: var(--pw-radius-pill); }

.mud-appbar {
    box-shadow: none !important;
    background: color-mix(in srgb, var(--bg-surface) 88%, transparent) !important;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border-subtle);
}
.mud-drawer {
    background: var(--bg-sidebar) !important;
    border-right: 1px solid var(--border-subtle);
}

/* Outlined fields: 46px, sage fill, green focus ring. Multiline keeps its own height. */
.mud-input.mud-input-outlined .mud-input-outlined-border {
    border-color: var(--border-subtle);
    border-radius: var(--pw-radius-control);
}
.mud-input.mud-input-outlined .mud-input-slot {
    background: var(--bg-input);
    border-radius: var(--pw-radius-control);
}
.mud-input.mud-input-outlined:not(:has(textarea)) .mud-input-slot {
    min-height: 46px;
    padding-top: 8px;
    padding-bottom: 8px;
}
.mud-input.mud-input-outlined.mud-input-focused .mud-input-outlined-border,
.mud-input-control.mud-input-focused .mud-input-outlined-border {
    border-color: var(--border-focus) !important;
    box-shadow: 0 0 0 3px var(--brand-subtle);
}

/* Section icon beside a card title — brand green on a soft badge. */
.mud-card > .mud-card-content > .mud-stack:first-child > .mud-icon-root {
    background: var(--brand-subtle);
    color: var(--brand-primary) !important;
    border-radius: 8px;
    padding: 6px;
    font-size: 1.25rem;
}

/* Accordion rows inside a settings card. */
.mud-expand-panel {
    border-radius: 8px !important;
    border: 1px solid var(--border-subtle);
    background: transparent;
    margin-top: 8px;
}
.mud-expand-panel .mud-expand-panel-header {
    padding: 12px 16px;
    border-radius: 8px;
    transition: background-color 0.15s ease;
}
.mud-expand-panel .mud-expand-panel-header:hover {
    background: var(--bg-surface-hover);
}
.mud-expand-panel .mud-expand-panel-icon {
    transition: transform 0.2s var(--enviro-ease);
}
.mud-panel-expanded .mud-expand-panel-icon,
.mud-expand-panel.mud-panel-expanded > .mud-expand-panel-header .mud-expand-panel-icon {
    transform: rotate(180deg);
}

/* Alerts: amber warning and crimson danger, never a royal-blue info chip. */
.mud-alert-filled-warning,
.mud-alert-outlined-warning,
.mud-alert-text-warning {
    border-color: #F59E0B;
}
.mud-alert-text-warning,
.mud-alert-outlined-warning {
    background: rgba(245, 158, 11, 0.08);
}

/* ── 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(--border-subtle);
    border-radius: var(--pw-radius-card);
    overflow: hidden;
    background: var(--bg-surface);
}
/* 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;
}

/* Raalebborg / custom matrix tables */
.pw-diversion-hint { color: #DC2626; }
html.pw-dark .pw-diversion-hint { color: #F87171; }
.pw-matrix-total td {
    background: rgba(22, 128, 60, 0.10);
    font-weight: 600;
}
html.pw-dark .pw-matrix-total td {
    background: rgba(34, 197, 94, 0.14);
}

/* ── Tab bars: separated pills ──────────────────────────────────────────
   Ported from PlatinumPOS/PlatinumWCA at Devon's request (2026-09-04): each
   tab is its own rounded pill with real space to its neighbour, instead of
   the header and panel sharing one packed box. Apply `Class="pw-tabs"` to
   the MudTabs; the panel keeps its boundary via `.pw-card` on
   `TabPanelsClass` instead — that's also why Elevation should be 0 here,
   matching the "elevation is .pw-card's job, not MudBlazor's ambient
   shadow" rule above. Only 2 tabs per bar in this app today, so (unlike
   POS's 6-7-tab bars) there's no need to port POS's wrap/no-scroll fix or
   its icon-only phone treatment — everything already fits at every width. */
.pw-tabs .mud-tabs-tabbar {
    background: transparent;
    box-shadow: none;
    margin-bottom: 16px;
}
.pw-tabs .mud-tabs-tabbar-wrapper {
    gap: 10px;
}
.pw-tabs .mud-tab-slider { display: none; }
.pw-tabs .mud-tab {
    border-radius: var(--pw-radius-control);
    border-bottom: none;
}
.pw-tabs .mud-tab:hover {
    background-color: var(--mud-palette-primary-hover);
}
.pw-tabs .mud-tab.mud-tab-active {
    background-color: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    font-weight: 700;
}
.pw-tabs .mud-tab.mud-tab-active:hover {
    background-color: var(--mud-palette-primary);
}

/* ── 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. The logo 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: 200px;
    height: 200px;
    margin-bottom: 0.75rem;
}
.splash-logo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 2;
    object-fit: contain;
}
/* Covers the centre node (black on the light mark, white on the dark one)
   once the orbs arrive, so that dot glows the same green in both modes. */
.splash-core {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 13px;
    height: 13px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    z-index: 4;
    background: #46D66E;
    opacity: 0;
    pointer-events: none;
    box-shadow:
        0 0 6px 2px rgba(70, 214, 110, 0.95),
        0 0 18px 7px rgba(52, 130, 68, 0.8),
        0 0 36px 14px rgba(52, 130, 68, 0.4);
}
/* 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(52,130,68,0.18) 0%, rgba(52,130,68,0) 70%);
    z-index: 1;
}
/* One tagline colour for both themes: slate reads on #0F172A and on #FFFFFF
   alike, so the splash needs no theme class of its own. */
.splash-wordmark {
    margin: 0;
    font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    font-size: 2.35rem;
    font-weight: 700;
    letter-spacing: -0.04em;
    line-height: 1;
}
.splash-wordmark .enviro { color: #2C6B3C; }
.splash-wordmark .nexus { color: #1B2430; }
#splash-screen.splash-dark .splash-wordmark .enviro { color: #7DCEA0; }
#splash-screen.splash-dark .splash-wordmark .nexus { color: #F4F7F5; }
.splash-tagline {
    font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    color: #64748b;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    margin: 0.65rem 0 0;
}
#splash-screen.splash-dark .splash-tagline { color: rgba(255,255,255,0.72); }
html.splash-on #app { visibility: hidden; }

/* ── 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;
}

/* The mark's stems are near-black. On a dark surface they vanish and a white
   joint ring reads as a hole, so the dark file is a light redraw of the same mark. */
.enx-mark-dark { display: none; }
.pw-dark .enx-mark-light { display: none; }
.pw-dark .enx-mark-dark { display: inline; }
#splash-screen.splash-dark .splash-logo.enx-mark-light { display: none; }
#splash-screen.splash-dark .splash-logo.enx-mark-dark { display: block; }

.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. */
.mud-drawer > .mud-drawer-content {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    overflow: hidden;
    padding-top: 4px;
}
.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;
    padding-top: 8px;       /* first item sits clear of the brand header */
    scroll-padding-top: 8px;
}
.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;
    border-left: 3px solid transparent;
    transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s 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: var(--brand-subtle) !important;
    transform: translateX(3px);
}
.mud-drawer .mud-nav-link:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}
/* A translucent tint of Primary itself, not PrimaryLighten: in the light theme PrimaryLighten is a
   near-white neutral (so it read fine), but in the dark theme it's just a LIGHTER SHADE OF THE SAME
   BLUE as Primary — active nav text ended up nearly the same colour as its own background. Deriving
   the tint from Primary's own rgb triplet (MudBlazor always exposes one) self-corrects in both themes
   with no separate dark-mode override. */
.mud-drawer .mud-nav-link.active {
    background: var(--brand-subtle) !important;
    color: var(--brand-on-subtle) !important;
    font-weight: 600 !important;
    border-left-color: var(--brand-primary);
    transform: none;
}
.mud-drawer .mud-nav-link.active .mud-icon-root {
    color: var(--brand-on-subtle) !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);
}

/* A determinate MudProgressLinear's "unfilled" track defaults to 20% opacity of its own accent colour
   (MudBlazor's own CSS, one compound selector per Color — e.g. `.mud-progress-linear-color-info
   .mud-progress-linear-background::before` — so a plain single-class override loses on specificity,
   not source order). Solid enough that a near-empty bar (an order barely started against a large
   expected weight, correctly reading 0.0%) still reads as substantially filled at a glance, since
   there's no visual distinction between "always-present track" and "a little real progress". Dimmed
   so an actual near-zero value looks empty; the solid "value" fill this doesn't touch is what still
   shows real progress once there is any. !important to beat MudBlazor's per-colour compound rule,
   same as the nav-link overrides above. */
.mud-progress-linear-background::before {
    opacity: 0.08 !important;
}

/* A determinate MudProgressLinear's fill is positioned by MudBlazor's own inline
   `transform: translateX(-(100-value)%)`, clipped by its `.mud-progress-linear-bars` wrapper's
   `overflow:hidden`. Whenever that wrapper's own pixel width is fractional — near-universal in a
   percentage-sized table column — the browser's sub-pixel rounding of the transform can leave roughly
   a device-pixel-wide sliver of the FULL-SATURATION fill colour visible right at the boundary it's
   meant to be clipped at. Invisible at 50% (an extra pixel is lost in a 40px-wide real fill) but
   glaring at 0%, where it is the ONLY colour showing: found live on Orders, two genuinely-untouched
   orders (0% and 0.2% complete) both showed a small lit sliver at the very start of the bar, reading
   as "this has some progress" when it has none. Reproduced by pinning the fill's transform and
   confirming the sliver tracks the 0%/100% boundary exactly, regardless of Color/theme — a rendering
   artifact, not a wrong Value (PercentComplete's own arithmetic was already correct).
   MudBlazor recomputes that inline transform itself on every render, so there is no CSS hook to add a
   safety margin to it directly. Added a layer up instead: wrap the component in `.pw-progress-clip` and
   make the component itself 2px WIDER than that wrapper's own clip window, so the component's internal
   0%/100% seam always sits just outside what the wrapper actually shows, however small the real
   sub-pixel error turns out to be. Verified this eliminates the sliver at 0%, ~42% and ~98% alike, with
   the rounded end-caps still rendering correctly. Every VALUE-DRIVEN (non-Indeterminate)
   MudProgressLinear should be wrapped in `<div class="pw-progress-clip">` for this to apply — an
   Indeterminate one has no Value/transform and isn't affected. */
.pw-progress-clip {
    position: relative;
    overflow: hidden;
    border-radius: var(--mud-default-borderradius);
}
.pw-progress-clip > .mud-progress-linear {
    width: calc(100% + 2px);
    margin-left: -1px;
}

/* 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: transform .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 {
    transform: translate(-50%, -50%) scale(1.556);
    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;
}

/* Analytics "Top Clients" ranked horizontal bars. The track derives its tint from Primary's own rgb
   triplet rather than --mud-palette-primary-lighten — that token is a near-white neutral in the light
   theme but just a LIGHTER SHADE OF THE SAME BLUE as Primary in the dark theme (the exact nav-link
   contrast bug fixed elsewhere in this file), so a track built from it would read fine in light mode and
   wrong in dark. */
.rw-hbar-row {
    display: flex;
    align-items: center;
    gap: 14px;
}
.rw-hbar-label {
    width: 190px;
    flex: 0 0 190px;
    font-size: 0.8125rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rw-hbar-track {
    flex: 1 1 auto;
    height: 20px;
    border-radius: 5px;
    overflow: hidden;
    background: rgba(var(--mud-palette-primary-rgb), 0.12);
}
.rw-hbar-fill {
    height: 100%;
    border-radius: 5px;
}
.rw-hbar-value {
    width: 78px;
    flex: 0 0 78px;
    text-align: right;
    font-size: 0.8125rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
@media (max-width: 600px) {
    /* The label needs somewhere to give on a phone; the track shrinks to fit instead. */
    .rw-hbar-label { width: 110px; flex-basis: 110px; }
}

/* ── 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;
}

/* Home — site photograph, today's figures, and the operation tiles. */
.rw-hero {
    position: relative;
    aspect-ratio: 1024 / 342;
    min-height: 220px;
    border-radius: 20px;
    overflow: hidden;
    background: #143026;
    color: #F8FAFC;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
    padding: 28px;
}
.rw-hero-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.rw-hero-shade {
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, rgba(10, 24, 16, 0.78) 0%, rgba(10, 24, 16, 0.22) 78%);
}
.rw-hero-copy,
.rw-hero-now { position: relative; z-index: 1; }
.rw-hero-title {
    margin: 0;
    font-size: clamp(1.7rem, 3vw, 2.5rem);
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
}
.rw-hero-place,
.rw-hero-date,
.rw-hero-weather { margin: 6px 0 0; font-size: 0.95rem; }
.rw-hero-clock {
    margin: 0;
    font-size: 2.5rem;
    font-weight: 700;
    letter-spacing: -0.04em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.rw-hero-now { text-align: right; }
.rw-hero-photo-btn {
    display: inline-block;
    margin-top: 14px;
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.16);
    cursor: pointer;
    font-size: 0.8rem;
    font-weight: 600;
}
.rw-hero-photo-btn input { display: none; }
.rw-kpis {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px;
}
.rw-kpi {
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-divider);
    border-radius: 16px;
    padding: 16px 18px 14px;
}
.rw-kpi-label {
    margin: 0;
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}
.rw-kpi-value {
    margin: 8px 0 0;
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.rw-kpi-delta {
    margin: 8px 0 0;
    font-size: 0.8rem;
    font-weight: 600;
}
.rw-kpi-delta span {
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
}
.rw-kpi-delta.is-good { color: #1B5E34; }
.rw-kpi-delta.is-bad { color: #9F2D24; }
html.pw-dark .rw-kpi-delta.is-good { color: #8ED6A8; }
html.pw-dark .rw-kpi-delta.is-bad { color: #F0A8A2; }
.rw-home-tile {
    min-height: 148px;
    border-radius: 16px;
    cursor: pointer;
    border: 1px solid transparent;
}
.rw-home-tile--green,
.rw-home-tile--green .mud-typography { color: #F4FBF6; }
.rw-home-tile--green { background: #1E6B3A; }
.rw-home-tile--quiet {
    background: var(--mud-palette-surface);
    border-color: var(--mud-palette-divider);
    color: var(--mud-palette-text-primary);
}
.rw-home-tile--quiet .mud-icon-root { color: #1E7A4C; }
@media (max-width: 960px) {
    .rw-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
    .rw-hero { flex-direction: column; align-items: flex-start; aspect-ratio: auto; min-height: 280px; }
    .rw-hero-now, .rw-hero-clock { text-align: left; }
}
@media (max-width: 520px) {
    .rw-kpis { grid-template-columns: 1fr; }
}
