html, body {
    font-family: 'Roboto', sans-serif;
    margin: 0;
    padding: 0;
    height: 100%;
}

/* AVAMS brand colors: green primary, yellow accent */
:root {
    --avams-green: #2E7D32;
    --avams-green-dark: #1B5E20;
    --avams-yellow: #FBC02D;
    --avams-yellow-light: #FFD54F;   /* the logo's lighter gold: subtitles on green, active nav */
}

/* ══ DESIGN LANGUAGE ══════════════════════════════════════════════════════
   De-Materialised 2026-09-05. Read AvamsTheme.cs first — it owns the palette,
   the elevation ramp and the type scale. This file owns the things MudBlazor
   exposes only as CSS: borders, padding and the geometry of individual parts.

   The governing idea: MATERIAL SEPARATES SURFACES WITH BLUR, THIS SEPARATES
   THEM WITH A LINE. AvamsTheme flattened the elevation ramp; without the rules
   below, cards would have lost their shadow and gained nothing, leaving the
   page as one undifferentiated sheet. The two changes only work as a pair.

   Selectors deliberately mirror MudBlazor's own, character for character,
   because these override rules rather than add to them — matching specificity
   and letting source order decide is more predictable than an !important war.
   app.css is loaded after MudBlazor.min.css in index.html, so it wins.       */

/* ── Surfaces ─────────────────────────────────────────────────────────────
   Scoped to elevation 1-4, which is exactly the range that USED to be
   separated by shadow (this codebase: Elevation="2" x42, "1" x11, "4" x2).

   Elevation="0" papers — 32 of them — are deliberately excluded. Those are
   layout wrappers that were never meant to read as a surface, and bordering
   them would draw 32 boxes the design never asked for.                      */
.mud-paper.mud-elevation-1,
.mud-paper.mud-elevation-2,
.mud-paper.mud-elevation-3,
.mud-paper.mud-elevation-4,
.mud-table.mud-elevation-1,
.mud-table.mud-elevation-2 {
    border: 1px solid var(--mud-palette-lines-default);
}

/* Chrome separates by position, not by outline: a full border around the app
   bar reads as a stray box rather than as structure. One edge each. */
.mud-appbar {
    border: 0;
    /* jam-frontend reskin: the bar is a SURFACE now (white in light mode,
       the Surface neutral in dark), so its edge is the same hairline every
       other surface uses. The rgba(0,0,0,.20) darkening that lived here was
       tuned for the old solid-green bar. */
    border-bottom: 1px solid var(--mud-palette-lines-default);
    box-shadow: none;
}

/* MudBlazor hardcodes the Material three-layer drop shadow on .mud-dialog,
   AFTER the .mud-elevation-* rules and at equal specificity — so the flattened
   ramp in AvamsTheme never reached a dialog and every modal in the app kept the
   stock Material shadow. This is elevation 8 from that ramp: dialogs are
   genuine overlays and still need real depth, just the modern soft kind. */
.mud-dialog {
    box-shadow: 0 8px 16px -4px rgba(15, 23, 42, .12),
                0 2px 4px -2px rgba(15, 23, 42, .06);
}

.mud-drawer {
    border: 0;
    border-right: 1px solid var(--mud-palette-lines-default);
}

/* ── Density ──────────────────────────────────────────────────────────────
   MudBlazor's defaults are mobile-first touch targets: 16px table cells and
   18.5px input padding are sized for thumbs. This is an internal tool driven
   with a mouse on a desktop, where that padding just means fewer rows visible
   per screen — the opposite of what an asset register is for.               */
.mud-table-cell {
    padding: 10px 14px;            /* was 16px all round */
}

.mud-table-dense * .mud-table-row .mud-table-cell {
    padding: 5px 12px;             /* was 6px 24px 6px 16px */
    padding-inline-start: 12px;
    padding-inline-end: 12px;
}

/* MudBlazor re-pads the final dense cell to 16px via a :last-child rule of
   equal specificity, which left the Actions column inset differently from
   every other column. Matched selector-for-selector so source order decides. */
.mud-table-dense * .mud-table-row .mud-table-cell:last-child {
    padding-right: 12px;
    padding-inline-end: 12px;
}

/* Column headers as small caps: the strongest single cue that a table is a
   data instrument rather than a list of cards. */
.mud-table-root .mud-table-head .mud-table-cell {
    /* .mud-table-root prefix is REQUIRED, not decorative. MudBlazor ships
       `.mud-table-root .mud-table-head .mud-table-cell{color:...;font-weight:500}`
       at three classes; a two-class override lost `color` and `font-weight`
       while `font-size`/`letter-spacing`/`text-transform` still applied, so
       headers rendered as uppercase text at the wrong weight and colour. */
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

/* NO INPUT-HEIGHT OVERRIDE HERE, deliberately.
   A `.mud-input-root-outlined{padding:...}` rule used to live at this spot and
   did nothing: MudBlazor ships `.mud-input>input.mud-input-root-outlined` (two
   classes + an element) and `.mud-input-root-margin-dense` (three + element),
   both of which outrank a single-class selector. Worse, forcing it to win would
   misalign every floating label, because MudBlazor positions the resting label
   with `transform:translate(14px,20px)` tuned to the 18.5px padding it ships.
   Density belongs to MudBlazor's own Margin="Margin.Dense", which adjusts the
   padding AND the matching label transform together. Applied at the call sites.
   (Code review, 2026-09-08.) */


.mud-nav-link {
    padding-top: 6px;              /* was 8px */
    padding-bottom: 6px;
    line-height: 1.6;              /* was 1.75 */
}

.mud-list-item {
    padding-top: 6px;              /* was 8px */
    padding-bottom: 6px;
}

/* ── Geometry ─────────────────────────────────────────────────────────────
   Chips default to a full pill. At the sizes used here — status and priority
   labels inside table rows — a pill reads as a tag someone attached, while a
   small rounded rectangle reads as a value belonging to the column.         */
.mud-chip {
    border-radius: 5px;
}


/* ── Boot loader (shown while the WASM payload downloads) ─────────────────
   The SVG MUST be square, and that is not a style preference.

   With no width/height an <svg> falls back to the CSS replaced-element default
   of 300 × 150. A percentage radius resolves against the normalised diagonal
   — sqrt(w² + h²) / sqrt(2) — so r="40%" came out at ~95px while cy="50%" was
   only 75px from the top. The circle ran from -20 to 170 inside a 150-tall box,
   so its top and bottom were clipped and it rendered as two parentheses.

   The stroke-dasharray below is the proof of intent: 3.141 * 100% * 0.8 equals
   251.28%, which is the circumference 2*pi*0.4s only when width equals height.
   Sizing the element squarely is what makes that arithmetic describe a circle. */
.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;               /* square — see above */
    margin: 20vh auto 1rem auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #FBC02D;                    /* unfilled track — AVAMS yellow */
        stroke-width: 0.6rem;
        /* Rotation belongs on the circle, not the <svg>. It used to sit on the
           parent, where the element's own inline positioning transform silently
           replaced it — transform does not merge — so the arc grew from 3
           o'clock instead of 12. */
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
        transition: stroke-dasharray 0.05s ease-in-out;
    }

        .loading-progress circle:last-child {
            stroke: #2E7D32;                /* progress — AVAMS green */
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    /* 20vh clears the SVG's top margin; the extra 3.25rem drops the label into
       the middle of an 8rem circle. Tied to the two values above — change one
       and this needs changing with it. */
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
    color: #1B5E20;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

#blazor-error-ui {
    background: #FFF9C4;
    border-top: 3px solid #2E7D32;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* ── Mobile-friendly tweaks ───────────────────────────────────────────── */
@media (max-width: 600px) {
    .mud-table-cell { padding: 4px 8px; }
    .mud-paper.pa-4 { padding: 12px !important; }
    .mud-typography-h4 { font-size: 1.3rem !important; }
    .mud-typography-h5 { font-size: 1.1rem !important; }
    .mud-typography-h6 { font-size: 1rem !important; }
}

/* Make MudChip wrap on small screens */
.mud-chip { white-space: normal; }

/* ── Equipment-per-Room bars (dashboard) ──────────────────────────────────
   Replaces MudChart's bar chart, which renders into a hard-coded
   viewBox="0 0 650 350" and offers no bar-width setting — with only a few
   rooms its bars come out as hairlines and cannot be widened via its API.
   Plain markup gives full control and keeps labels as real text.         */
.avams-roombars {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 8px;
    max-height: 300px;
    overflow-y: auto;
}

.avams-roombar {
    display: grid;
    grid-template-columns: minmax(64px, 30%) 1fr auto;
    align-items: center;
    gap: 12px;
}

.avams-roombar-label {
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.avams-roombar-track {
    position: relative;
    height: 24px;
    border-radius: 4px;
    background: rgba(46, 125, 50, 0.12);   /* tint of the bar hue */
    overflow: hidden;
}

.avams-roombar-fill {
    display: block;
    height: 100%;
    min-width: 3px;
    border-radius: 4px;
    background: #2E7D32;                   /* AVAMS primary green */
    transition: width 0.35s ease;
}

.avams-roombar-value {
    min-width: 1.75rem;
    text-align: right;
    font-size: 0.9375rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;    /* digits stay column-aligned */
    color: var(--mud-palette-text-primary);
}

@media (prefers-reduced-motion: reduce) {
    .avams-roombar-fill { transition: none; }
}

/* ── Sign-in page ─────────────────────────────────────────────────────────
   Split layout: brand panel left, form right. Rendered by LoginLayout, which
   has no app bar or drawer, so this owns the whole viewport and the form is
   centred against the page rather than against whatever the drawer left over.
   Below 960px the panels stack.                                           */
.avams-login {
    --login-green-dark: #0f3d1e;
    --login-green-mid: #1d6b34;
    --login-green-light: #2f8f45;
    --login-gold: #d9a83a;
    --login-gold-stroke: #a5741c;
    --login-offwhite: #dcebe0;
    --login-border: #d8e0da;

    display: grid;
    grid-template-columns: 38% 62%;
    min-height: 100vh;
    min-height: 100dvh;              /* avoids mobile browser chrome cropping */
}

.avams-login__brand {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3rem 2.5rem;
    padding-top: calc(3rem + env(safe-area-inset-top));
    padding-bottom: calc(3rem + env(safe-area-inset-bottom));
    color: #fff;
    background: linear-gradient(160deg, var(--login-green-dark) 0%, var(--login-green-mid) 55%, var(--login-green-light) 100%);
}

/* Twelve AV/asset icon outlines spread across the whole panel (position,
   size, rotation and opacity all set per-icon inline in the markup, since
   each placement is hand-picked rather than a repeatable rule). Cropped at
   the panel edges by its overflow:hidden. */
.avams-login__icons {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
}

.avams-login__bgicon {
    position: absolute;
    color: #fff;
}

.avams-login__content {
    position: relative;
    z-index: 1;
    /* width, not just max-width: as a flex item with no explicit width this
       shrink-fit to its content, and the h1's unwrapped text width differs
       per headline phrase — which shifted this block's width, and so the
       tagline's own line-wrapping, on every rotation. A fixed width makes it
       phrase-independent; max-width keeps it from overflowing a panel
       narrower than 380px. */
    width: 380px;
    max-width: 100%;
}

.avams-login__headline-wrap {
    overflow: hidden;
    /* A fixed height, not min-height: "From Acquisition To Disposal" wraps to
       3 lines at this font-size/width, and min-height let the box grow to fit
       it, pushing the static tagline below down. Fixed at 3 lines x 2.5rem/
       1.15 line-height (in rem, not em, so it's independent of the wrap's own
       inherited font-size) means every phrase reserves the same box and
       nothing below it ever moves. */
    height: 8.7rem;
    width: 100%;
    display: flex;
    /* Top-aligned, not centered: centering made a 1-line phrase like "Track
       It All" sit lower than a 3-line one, so the visible text itself jumped
       between phrases even though the box height didn't. Anchoring every
       phrase to the same top edge keeps the text itself still. */
    align-items: flex-start;
}

.avams-login__headline-accent {
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--login-gold);
    margin-bottom: 1rem;
}

.avams-login__headline {
    margin: 0;
    font-size: 2.5rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    line-height: 1.15;
    text-transform: uppercase;
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.3s ease, transform 0.3s ease;
}

.avams-login__headline-line {
    display: block;
}

.avams-login__headline-white {
    color: #fff;
}

.avams-login__headline-gold {
    color: var(--login-gold);
    -webkit-text-stroke: 1px var(--login-gold-stroke);
    paint-order: stroke fill;
}

.avams-login__headline.is-out {
    opacity: 0;
    transform: translateY(-14px);
}

.avams-login__tagline {
    margin: 1.25rem 0 0;
    max-width: 34ch;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.6;
    color: var(--login-offwhite);
}

@media (prefers-reduced-motion: reduce) {
    .avams-login__headline { transition: none; }
}

.avams-login__form {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3rem 2rem;
    padding-top: calc(3rem + env(safe-area-inset-top));
    padding-bottom: calc(3rem + env(safe-area-inset-bottom));
    background: #fff;
    box-shadow: inset 0 0 140px rgba(15, 23, 17, 0.06);
}

.avams-login__form-accent-bar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--login-green-mid), var(--login-gold));
    z-index: 1;
}

/* Three blurred colour blobs that drift slowly rather than sit static. */
.avams-login__form-glow {
    position: absolute;
    width: 380px;
    height: 380px;
    border-radius: 50%;
    filter: blur(70px);
    animation: avams-login-drift 14s ease-in-out infinite;
}

.avams-login__form-glow--green {
    top: -120px;
    right: -100px;
    background: rgba(29, 107, 52, 0.20);
}

.avams-login__form-glow--gold {
    bottom: -120px;
    left: -100px;
    width: 340px;
    height: 340px;
    background: rgba(207, 154, 46, 0.18);
    animation-delay: -5s;
}

.avams-login__form-glow--mint {
    top: -60px;
    left: -80px;
    width: 220px;
    height: 220px;
    background: rgba(159, 216, 174, 0.16);
    animation-delay: -9s;
}

@keyframes avams-login-drift {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(18px, -14px); }
}

/* Dot grid, masked so it fades out behind where the card sits. */
.avams-login__form-dotgrid {
    position: absolute;
    inset: 0;
    background-image: radial-gradient(circle, rgba(15, 61, 30, 0.12) 1px, transparent 1.5px);
    background-size: 22px 22px;
    -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 0%, transparent 28%, black 62%);
    mask-image: radial-gradient(circle at 50% 50%, transparent 0%, transparent 28%, black 62%);
}

.avams-login__form-ring {
    position: absolute;
    top: -80px;
    right: -80px;
    width: 280px;
    height: 280px;
    border-radius: 50%;
    border: 3px solid var(--login-green-mid);
    opacity: 0.18;
}

.avams-login__form-ring--small {
    top: 40px;
    right: 20px;
    width: 130px;
    height: 130px;
    border-width: 2px;
    border-color: var(--login-gold);
}

.avams-login__form-watermark {
    position: absolute;
    bottom: -30px;
    left: -30px;
    color: var(--login-green-mid);
    opacity: 0.08;
}

@media (prefers-reduced-motion: reduce) {
    .avams-login__form-glow { animation: none; }
}

.avams-login__card.mud-paper {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 400px;
    border-radius: 16px;
    padding: 2.5rem 2rem;
    box-shadow: 0 24px 60px -20px rgba(15, 61, 30, 0.28), 0 10px 24px -10px rgba(0, 0, 0, 0.14);
}

.avams-login__card.mud-paper::before {
    content: '';
    position: absolute;
    top: 0;
    left: 20px;
    right: 20px;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(47, 143, 69, 0.35), transparent);
}

.avams-login__brandmark {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.avams-login__brandmark-img {
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    border-radius: 10px;
    /* drop-shadow, not box-shadow: the artwork's rounded corners are real
       transparent alpha, and box-shadow would trace the square border-box
       behind them. */
    filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.18));
}

.avams-login__brandmark-word {
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--mud-palette-text-primary);
}

.avams-login__subtext,
.avams-login__footnote {
    color: var(--login-gold);
}

.avams-login__footnote-row {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

.avams-login__footnote-icon {
    color: var(--login-gold);
    flex: 0 0 auto;
    margin-top: 1px;
}

.avams-login__field-group {
    display: flex;
    flex-direction: column;
}

.avams-login__field-label {
    font-size: 0.8125rem;
    font-weight: 700;
    margin-bottom: 6px;
    color: var(--mud-palette-text-primary);
}

.avams-login__card .mud-input-outlined-border {
    border-radius: 10px;
    border-color: var(--login-border);
}

.avams-login__card .mud-input-outlined:focus-within .mud-input-outlined-border {
    box-shadow: 0 0 0 3px rgba(29, 107, 52, 0.12);
}

.avams-login__password-wrap {
    position: relative;
}

.avams-login__password-toggle {
    position: absolute;
    right: 4px;
    /* A fixed px offset, not 50% of the wrapper: MudBlazor reserves helper/
       error-text space below the field even when empty, so centering on the
       wrapper's full height sat lower than the visible box's own middle. */
    top: 24px;
    transform: translateY(-50%);
}

.avams-login__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.avams-login__trouble-link {
    color: var(--login-green-mid);
    text-decoration: none;
    font-size: 0.875rem;
}

.avams-login__trouble-link:hover {
    text-decoration: underline;
}

.avams-login__submit.mud-button-filled {
    background-color: var(--login-green-mid);
    color: #fff;
}

.avams-login__submit.mud-button-filled:hover {
    background-color: var(--login-green-dark);
}

@media (max-width: 820px) {
    .avams-login {
        grid-template-columns: 1fr;      /* stack: brand above form */
    }

    /* Compact header on small screens so the form stays above the fold. */
    .avams-login__brand         { padding: 2rem 1.5rem; }
    .avams-login__content       { width: 100%; }
    .avams-login__headline      { font-size: 1.65rem; }
    .avams-login__headline-wrap { height: 5.7rem; }
    .avams-login__tagline       { display: none; }
    .avams-login__form          { padding: 2rem 1.5rem 3rem; }
}

/* ── Rooms module ─────────────────────────────────────────────────────────
   Room cards are clickable, so they need the affordance a MudTable row gets
   for free: a hover lift that says "this opens something".                */
.avams-room-card {
    transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}

.avams-room-card:hover {
    /* Was a 2px lift on an 18px black blur. The lift is now a hint and the
       real affordance is the border adopting the brand green — a colour
       change reads as "interactive" without the card appearing to detach. */
    transform: translateY(-1px);
    border-color: var(--mud-palette-primary);
    box-shadow: 0 4px 12px -2px rgba(15, 23, 42, 0.10);
}

@media (prefers-reduced-motion: reduce) {
    .avams-room-card,
    .avams-room-card:hover { transition: none; transform: none; }
}

/* ── Brand mark placements ────────────────────────────────────────────────
   One asset (img/avams-logo.png) reused at several sizes. The radius is a
   percentage because the artwork is a rounded "app icon" square whose own
   corner radius scales with its size — a fixed px radius leaves the source's
   corner pixels visible against a coloured surface.                        */

/* App bar: small mark beside the wordmark. Hidden on very narrow screens,
   where the drawer toggle and title already fill the row. */
.avams-appbar-logo {
    width: 32px;
    height: 32px;
    flex: 0 0 auto;
}

@media (max-width: 400px) {
    .avams-appbar-logo { display: none; }
}

/* Dashboard header band. */
.avams-dashboard-header {
    background: linear-gradient(135deg, var(--avams-green) 0%, var(--avams-green-dark) 100%);
    color: #fff;
    border-radius: 6px;                 /* matches LayoutProperties.DefaultBorderRadius */
}

.avams-dashboard-logo {
    width: 64px;
    height: 64px;
    /* drop-shadow, not box-shadow: the artwork's rounded corners are real
       transparent alpha, and box-shadow would trace the square border-box
       behind them. */
    filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.28));
    flex: 0 0 auto;
}

.avams-dashboard-subtitle {
    color: var(--avams-yellow-light);
    margin-top: 2px;
}

@media (max-width: 600px) {
    .avams-dashboard-logo { width: 48px; height: 48px; }
}

/* First-login / change-credentials: logo with the purpose badge overlaid, so
   the screen is branded without losing the icon that says what it is for. */
.avams-credentials-mark {
    position: relative;
    display: inline-block;
    line-height: 0;
}

.avams-credentials-logo {
    width: 72px;
    height: 72px;
}

.avams-credentials-badge {
    position: absolute;
    right: -6px;
    bottom: -6px;
    padding: 4px;
    border-radius: 50%;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-primary);
    box-shadow: 0 2px 6px -1px rgba(15, 23, 42, 0.14);
    font-size: 1.25rem;
}

/* ── Reports: always light, in every theme ────────────────────────────────
   A report is a document. It is designed to be printed, and it is read on
   screen as a preview of that printed page, so it stays white whatever theme
   the rest of the app is in.

   Setting `background: white` alone was not enough. MudBlazor components inside
   the report resolve their own colours from --mud-palette-* custom properties,
   so under the dark theme the stat tiles rendered as black boxes and body text
   came out near-white — on a white page. Re-declaring the palette variables on
   the container makes every descendant resolve light values instead, without
   touching the app's actual theme.                                          */
.report-page {
    background: #ffffff;
    color: #212121;
    padding: 24px;

    --mud-palette-text-primary:      #212121;
    --mud-palette-text-secondary:    #555555;
    --mud-palette-text-disabled:     #9e9e9e;
    --mud-palette-surface:           #ffffff;
    --mud-palette-background:        #ffffff;
    --mud-palette-background-gray:   #f5f5f5;
    --mud-palette-divider:           #e0e0e0;
    --mud-palette-divider-light:     #eeeeee;
    --mud-palette-lines-default:     #e0e0e0;
    --mud-palette-lines-inputs:      #bdbdbd;
    --mud-palette-table-lines:       #e0e0e0;
    --mud-palette-table-striped:     #fafafa;
    --mud-palette-table-hover:       #f5f5f5;
    --mud-palette-action-default:    #616161;
    --mud-palette-drawer-text:       #212121;
    --mud-palette-drawer-background: #ffffff;
}

/* Descendants that paint their own surface (stat tiles, tables) must follow the
   page, not the app theme. */
.report-page .mud-paper,
.report-page .mud-table,
.report-page .mud-table-container {
    background-color: #ffffff;
    color: #212121;
}

.report-page .mud-table-cell,
.report-page .mud-table-head .mud-table-cell {
    color: #212121;
    border-color: #e0e0e0;
}

.report-brand {
    color: #2E7D32;
    font-weight: 700;
    letter-spacing: 0.04em;
}

/* Muted meta text inside a report — deliberately NOT MudBlazor's "Secondary"
   colour, which is the brand yellow and unreadable on white. */
.report-muted { color: #555555; }

/* Report summary tiles: the rounded MudPaper card grid every report uses
   on screen, and a plain bordered "Overview" grid (same look as the
   Equipment Maintenance History Card's .mrc-overview) that's swapped in for
   print only — the cards' shadow/rounded corners don't reproduce well on
   paper, and this reads more like a printed form. Each report keeps its own
   on-screen cards untouched and adds one hidden .report-overview block with
   the same numbers; only visibility swaps at print time, per report. */
.report-overview { display: none; }

.report-overview-bar {
    background: #1B5E20;
    color: #fff;
    font-weight: 700;
    padding: 8px 14px;
    border-radius: 4px 4px 0 0;
    /* print-color-adjust asks the browser to keep backgrounds when printing,
       but not every print/PDF path honours it. box-shadow is a more
       reliable fallback: it isn't classified as a "background graphic" by
       the ink-saving default that strips background-color, so an inset
       shadow this large — filling the whole box — survives print even
       where the color-adjust property is ignored. Kept alongside
       background-color (not instead of) so screen rendering is unaffected. */
    box-shadow: inset 0 0 0 1000px #1B5E20;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
    color-adjust: exact;
}

.report-overview-grid {
    /* Fixed 2 columns, not auto-fit/minmax — auto-fit measures the
       container's available width to decide how many columns fit, and in
       print/PDF rendering that measurement collapsed to a single column,
       which is what stacked every cell into a vertical list. A fixed count
       has nothing to measure, so it can't collapse (same fix
       MaintenanceReport.razor's own .mrc-overview-grid relies on, just 2
       columns here instead of that report's 3). */
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    border: 1px solid #cfd8cf;
    border-top: none;
}

.report-overview-cell {
    border-right: 1px solid #cfd8cf;
    border-bottom: 1px solid #cfd8cf;
}

.report-overview-cell:nth-child(2n) { border-right: none; }

.report-overview-cell-label {
    background: #E8F5E9;
    color: #1B5E20;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 4px 10px;
    box-shadow: inset 0 0 0 1000px #E8F5E9;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
    color-adjust: exact;
}

.report-overview-cell-value {
    padding: 8px 10px;
    font-size: 0.92rem;
    font-weight: 700;
    color: #212121;
}

/* ── Report tables, filters and print (Phase 4, 2026-10-09) ──────────────
   Tables are drawn by Shared/ReportTable.razor with the inline styles each
   report always used, so the screen look is unchanged; the "report-table"
   class is what print hooks onto.

   LANDSCAPE: a named page, the trick the Maintenance History Card used on its
   own. A report passes Landscape="true" to ReportHeader and only that report
   prints landscape. The whole document joins the named page (body:has), not
   just .report-page: with only the report on it, the layout boxes around it
   stayed on the default page, and every landscape print began with a blank
   portrait page (the Maintenance card's did too).

   CLIPPING: a horizontal scroller (.report-table-wrap) is right on screen and
   wrong on paper, where it cut the table's right-hand columns off. Print
   shows the whole table, repeats the header row on every page and keeps a
   row on one page. */
@page report-landscape {
    size: landscape;
}

body:has(.report-landscape),
.report-landscape { page: report-landscape; }

/* The filter row above a report (screen only: .no-print): controls side by
   side, wrapping on narrow screens; the Clear button keeps its own width. */
.report-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}

.report-filters > * { flex: 1 1 180px; min-width: 170px; max-width: 260px; }
.report-filters > .report-filters-actions { flex: 0 0 auto; min-width: 0; }

/* The "Filters: ..." line (ReportHeader.razor) used to be the only place a
   printout said what it covers, since the filters row itself is .no-print.
   Now that ReportFiltersPanel shows the same thing on screen (its own Period
   cards/info bar and active-filter tags), this line is screen-redundant —
   so it's hidden on screen and print-only, the opposite of .no-print. */
.report-filter-summary { display: none; }
@media print {
    .report-filter-summary { display: block !important; }
}

@media print {
    .report-table-wrap { overflow: visible !important; }
    .report-table thead { display: table-header-group; }
    .report-table tr { break-inside: avoid; page-break-inside: avoid; }
}

@media print {
    .no-print,
    .mud-appbar,
    .mud-drawer,
    .mud-drawer-overlay,
    .mud-popover-provider,
    .mud-snackbar-provider,
    .avams-chatbot-widget,
    .avams-chatbot-greeting { display: none !important; }

    .report-summary-cards { display: none !important; }
    .report-overview      { display: block !important; }

    /* MudBlazor reserves space for the docked sidebar/app bar via margin, not
       padding, on .mud-main-content — the earlier padding:0/margin-top:0 reset
       left margin-left untouched, so the printed page still had the drawer's
       width as blank space down the left edge (plus whatever margin-top the
       app bar's own height left at the top). Zero the whole box model instead
       of guessing which sides still carry it. */
    .mud-main-content { padding: 0 !important; margin: 0 !important; }

    /* The report's own container is centered with MaxWidth.ExtraExtraLarge —
       full-width and edge-to-edge on paper instead of leaving side margins
       that just make the top/left gap worse once the drawer's own offset is
       gone. */
    .mud-main-content .mud-container { max-width: none !important; padding: 0 !important; margin: 0 !important; }

    .report-page      { box-shadow: none !important; border: none !important; margin: 0 !important; }
    a                 { color: inherit; text-decoration: none; }
    body              { background: #ffffff !important; margin: 0 !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   jam-frontend RESKIN — cherry-picked into main 2026-09-13
   Origin: Branches/…_PRIVATE-jam-frontend (its 2026-08-28 reskin), previewed
   first in Branches/…_PREVIEW-on-main. Only the shell and widgets came across:
   dark "workspace" sidebar, white app bar with clock chip and profile menu,
   tinted stat cards, the Equipment Health widget, page-header bands, and the
   floating Ava chatbot widget. No route, role gate or data call changed.

   TWO DECISIONS DIFFER FROM THE BRANCH, on purpose:
     • GEOMETRY follows the de-materialised system above, not the branch's
       14px bubbles: cards and panels use --mud-default-borderradius (6px, set
       in AvamsTheme.LayoutProperties) and a 1px hairline; icon chips 8px.
     • COLOUR is token-driven. The branch hard-coded light hex values
       (#FFFFFF gradients, #E7EAF2 borders, pastel chip fills) and never ran
       dark mode. Every surface-dependent colour below is a palette variable
       or a color-mix() of a semantic token INTO the surface, so the same rule
       renders correctly under both palettes with no per-theme override.
       Brand greens/yellow go through the :root --avams-* tokens.
   ══════════════════════════════════════════════════════════════════════════ */

/* Same gradient/subtitle treatment as the Dashboard's own header
   (.avams-dashboard-header/.avams-dashboard-subtitle, reused as-is rather
   than duplicated), extended to other pages' headers. Those pages use a
   page-specific icon in a translucent box instead of the product logo, since
   the logo is the Dashboard's one branded moment. */
.avams-page-header-icon {
    width: 56px; height: 56px; border-radius: 8px; flex: 0 0 auto;
    background: rgba(255, 255, 255, 0.16);
    display: flex; align-items: center; justify-content: center;
}

/* The title+subtitle block next to the logo/icon is a flex item with the
   default flex-basis:auto, whose hypothetical main size (used for the wrap
   decision, before any shrinking) is the text's max-content width — i.e. as
   wide as the subtitle would be laid out on one unbroken line. A long enough
   subtitle is wider than the space left beside the icon, so flex-wrap puts
   the whole block on its own line below the icon instead of letting the
   subtitle wrap within its own box. flex-basis:0 (via the flex:1 1 0%
   shorthand) starts sizing from zero instead, sidestepping that trigger. */
.avams-dashboard-logo + div,
.avams-page-header-icon + div {
    min-width: 0;
    flex: 1 1 0%;
}

/* ── Dialog titles: vertically center the leading icon against its label ──
   MudIcon defaults to `vertical-align: baseline`, which pins the icon's box
   to the text's baseline rather than its visual center — on an h6 line the
   icon's midpoint then sits several px above the label's. Fixing it here
   covers every "icon + MudText" dialog title at once. */
.mud-dialog-title .mud-icon-root {
    vertical-align: middle;
    margin-top: -2px;
}

/* ── Sidebar shell: brand header (fixed) + scrollable nav + footer (fixed) */
.mud-drawer .mud-drawer-content { display: flex; flex-direction: column; height: 100%; }

.avams-sidebar-brand {
    display: flex; align-items: center; gap: 8px;
    padding: 12px 8px; flex: 0 0 auto;
}
.avams-sidebar-toggle { flex: 0 0 auto; color: var(--mud-palette-drawer-text) !important; }
.avams-sidebar-brand img { width: 32px; height: 32px; border-radius: var(--mud-default-borderradius); flex: 0 0 auto; }
/* No forced nowrap — the hamburger sharing this row with the logo leaves
   less width than the subtitle needs at 240px, so it wraps to a second
   line instead of clipping mid-word. */
.avams-sidebar-brand-text { min-width: 0; }
.avams-sidebar-brand-title {
    font-weight: 700; font-size: 1.02rem; line-height: 1.2;
    color: var(--mud-palette-drawer-text);
}
.avams-sidebar-brand-subtitle {
    font-size: 0.7rem; color: var(--mud-palette-drawer-icon);
    letter-spacing: .03em; margin-top: 2px; line-height: 1.3;
}

/* Collapsed (mini) sidebar: MudBlazor already hides each MudNavLink's own
   label text in this state, but the section captions, the Logout button's
   text, and the brand logo image aren't part of that mechanism, so they'd
   otherwise clip mid-word — or, for the 32px logo sharing a 56px-wide row
   with the hamburger button, simply not fit and appear to vanish. */
.mud-drawer--closed .avams-nav-section-label,
.mud-drawer--closed .avams-sidebar-brand-text,
.mud-drawer--closed .avams-sidebar-brand img {
    display: none;
}
/* With the logo hidden, the row holds only the hamburger — center it, and
   tighten the padding now that it only needs to fit one 40px icon button. */
.mud-drawer--closed .avams-sidebar-brand {
    justify-content: center;
    padding: 8px;
}

/* Collapsed rows have no label text, so they need noticeably less height
   and horizontal margin than expanded ones — without this, MudNavLink keeps
   its expanded padding and the icon list ends up needing more vertical space
   than the drawer has, producing a scrollbar that shouldn't be there. The
   padding itself has to shrink, not just min-height, since min-height is a
   floor and has no effect once natural content height already exceeds it. */
.mud-drawer--closed .mud-nav-link { padding: 3px 8px !important; min-height: 30px !important; margin: 0 8px; }

/* MudDivider's own ".my-2" utility (8px top+bottom) is sized for the
   expanded list; trimmed here so the icon strip fits a typical viewport. */
.mud-drawer--closed .mud-divider { margin-top: 2px !important; margin-bottom: 2px !important; }

/* Settings/Logout are both MudBlazor FullWidth Text buttons — sized for
   their own label text even with an empty label string, so at 56px either
   renders wider than the drawer, causing a horizontal scrollbar. Collapsed,
   each needs to be exactly as wide as one icon, and the two stack vertically
   (not side by side) since the footer now holds two buttons, not one. */
.mud-drawer--closed .avams-sidebar-footer {
    display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 0;
}
.mud-drawer--closed .avams-sidebar-footer-btn {
    width: 40px !important; min-width: 40px !important;
    padding: 6px !important; justify-content: center !important;
}
.mud-drawer--closed .avams-sidebar-footer-btn .mud-button-icon-start {
    margin: 0 !important;
}

.avams-nav-scroll { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; }

.avams-sidebar-footer {
    flex: 0 0 auto; padding: 12px 0; margin-top: auto;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

/* Lines up the Settings/Logout icons with every MudNavLink's own icon above
   them: a nav link sits at margin:2px 8px (.mud-drawer .mud-nav-link) with
   its own padding-left:16px baked into MudBlazor's default .mud-nav-link
   rule, an 8+16=24px inset from the drawer's inner edge that .avams-
   sidebar-footer's own (now zero) horizontal padding can't reproduce on its
   own — a plain FullWidth MudButton's default Text-variant padding is only
   6px 8px, 4px short of that. Collapsed-drawer state overrides this back to
   a centered icon below, so it only applies expanded. */
.avams-sidebar-footer-btn {
    padding-left: 24px !important;
}

/* The line between Settings and Logout specifically — distinct from the
   .avams-sidebar-footer border-top above, which separates the whole footer
   from the scrollable nav list, not the two buttons from each other. */
.avams-sidebar-footer-divider {
    border-color: rgba(255, 255, 255, 0.08) !important;
    margin: 4px 8px !important;
}
.mud-drawer--closed .avams-sidebar-footer-divider {
    width: 24px; margin: 4px auto !important;
}

/* Section captions ("Transactions", "Analytics", ...) — the drawer's own
   Color.Secondary resolves to the brand yellow, which reads as a jarring
   highlight rather than a quiet label on the dark-green background. */
.avams-nav-section-label { color: var(--mud-palette-drawer-icon) !important; }

/* MudDivider's default border color is tuned for a light drawer and all but
   disappears on a dark one. The drawer is dark green in BOTH palettes (see
   AvamsTheme), so a translucent white edge is right in both. */
.mud-drawer .mud-divider { border-color: rgba(255, 255, 255, 0.1) !important; }

/* Active/hover nav row — the active item is signalled by its icon and label
   turning the brand's yellow accent, not primarily by a background fill (a
   subtle rounded tint still marks the row so it doesn't rely on colour
   alone). The active class is plain ".active" (from Blazor's own NavLink,
   which MudNavLink wraps) — confirmed against the live DOM in the branch. */
.mud-drawer .mud-nav-link { border-radius: var(--mud-default-borderradius); margin: 2px 8px; }
.mud-drawer .mud-nav-link:hover:not(.active) { background-color: rgba(255, 255, 255, 0.07); }
.mud-drawer .mud-nav-link.active {
    background-color: rgba(255, 255, 255, 0.1) !important;
}
.mud-drawer .mud-nav-link.active .mud-nav-link-text,
.mud-drawer .mud-nav-link.active .mud-icon-root {
    color: var(--avams-yellow-light) !important;
}

/* ── AppBar: subtle separation now that it's surface-on-background, plus the
   rounded "chip" that shows the current time. */
.avams-appbar-light { border-bottom: 1px solid var(--mud-palette-lines-default); }

.avams-appbar-clock {
    display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
    background: var(--mud-palette-background-gray);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
    padding: 5px 12px; font-size: 0.82rem; color: var(--mud-palette-text-secondary);
}
.avams-user-avatar {
    width: 34px; height: 34px; border-radius: 50%;
    background: var(--mud-palette-primary); color: var(--mud-palette-primary-text);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 0.95rem; flex: 0 0 auto;
}

/* Top bar profile dropdown (MainLayout's identity block → MudMenu) */
.avams-user-avatar-lg {
    width: 48px; height: 48px; font-size: 1.25rem;
}
.avams-profile-menu-popover { min-width: 260px; }
.avams-profile-menu-header { max-width: 320px; }
.avams-profile-menu-name { line-height: 1.25; }
.avams-profile-menu-email {
    color: var(--mud-palette-text-secondary);
    line-height: 1.3;
    overflow-wrap: break-word;
}

/* ── Stat card rows: always a single line ─────────────────────────────────
   MudGrid's xs/md columns used to wrap each stat-card row onto multiple
   lines at narrower widths. This replaces that with a flex row where every
   card compresses equally (min-width:0 is what actually lets a flex item
   shrink below its own content's natural width) rather than ever wrapping.
   Padding/icon-chip/value size shrink together via clamp() so a 6-card row
   stays legible down to a phone-width viewport instead of just clipping. */
.avams-stat-row {
    display: flex;
    flex-wrap: nowrap;
    gap: clamp(4px, 1.2vw, 10px);
}
.avams-stat-row > * {
    flex: 1 1 0;
    min-width: 0;
}
/* Every flex level between the row and the text has to carry min-width:0
   for the shrink to actually reach the text. MudStack renders as plain
   ".d-flex" utility classes and has no stable class of its own. */
.avams-stat-row .avams-stat-card .d-flex,
.avams-stat-row .avams-stat-card .d-flex > div {
    min-width: 0;
}
.avams-stat-row .avams-stat-card {
    padding: clamp(8px, 2vw, 16px) !important;
}
.avams-stat-row .avams-stat-icon-chip {
    width: clamp(30px, 4vw, 44px);
    height: clamp(30px, 4vw, 44px);
    border-radius: clamp(6px, 1vw, 8px);
}
.avams-stat-row .avams-stat-icon-chip .mud-icon-root {
    font-size: clamp(1rem, 2.2vw, 1.5rem);
}
.avams-stat-row .mud-typography-h4,
.avams-stat-row .mud-typography-h5 {
    font-size: clamp(1.1rem, 2.6vw, 2.125rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Label text (body2/caption) truncates too — EXCEPT inside KpiCard, whose
   "caption" is a full-sentence Description meant to wrap onto several
   lines, not a short label like "Total"/"Planned". */
.avams-stat-row .mud-typography-body2,
.avams-stat-row .mud-typography-caption:not(.avams-kpi-description) {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* History Logs' summary cards use longer titles ("Assets Disposed/Retired")
   than the 1-2 word labels the rule above assumes — that clipped it mid-word
   ("Assets Disposed/Retir…"). Scoped to this page's row only via an added
   modifier class, so every other page's stat row keeps the original
   single-line ellipsis behavior. */
.avams-stat-row.avams-stat-row-wrap .mud-typography-body2 {
    white-space: normal;
    overflow: visible;
    text-overflow: unset;
    line-height: 1.2;
}

/* ── Cards: flat panels and tinted stat cards ─────────────────────────────
   Same radius and hairline as every other .mud-paper in the de-materialised
   system. The tint is a semantic token mixed INTO the surface — 10% at the
   top fading to the plain surface — so it is a faint wash on white and an
   equally faint wash on the dark surface, never a white card on a dark page.
   green→Primary, amber→Warning, red→Error, blue→Info: the same mapping the
   MudIcon colours inside the cards already use. */
.avams-flat-card {
    border-radius: var(--mud-default-borderradius) !important;
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: none !important;
}
.avams-stat-card {
    border-radius: var(--mud-default-borderradius) !important;
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: none !important;
}
.avams-stat-card.tint-green {
    background: linear-gradient(180deg, color-mix(in srgb, var(--mud-palette-primary) 10%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--mud-palette-primary) 30%, var(--mud-palette-lines-default));
}
.avams-stat-card.tint-amber {
    background: linear-gradient(180deg, color-mix(in srgb, var(--mud-palette-warning) 12%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--mud-palette-warning) 35%, var(--mud-palette-lines-default));
}
.avams-stat-card.tint-red {
    background: linear-gradient(180deg, color-mix(in srgb, var(--mud-palette-error) 10%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--mud-palette-error) 30%, var(--mud-palette-lines-default));
}
.avams-stat-card.tint-blue {
    background: linear-gradient(180deg, color-mix(in srgb, var(--mud-palette-info) 10%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--mud-palette-info) 30%, var(--mud-palette-lines-default));
}
/* Rooms page's 5th card (Installed AV Assets) — same token-driven recipe as
   the other tints above, mixing MudBlazor's Tertiary token instead of a
   literal hex, so it also renders correctly in dark mode with no override. */
.avams-stat-card.tint-purple {
    background: linear-gradient(180deg, color-mix(in srgb, var(--mud-palette-tertiary) 10%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--mud-palette-tertiary) 30%, var(--mud-palette-lines-default));
}

/* Maintenance Tickets summary cards: tinted from --tint-color (set inline
   per card to that exact status's StatusHexColor — Tickets.razor) instead of
   a fixed mud-palette token, so each card's wash/border is the exact same
   color as that status's chip in the table below, not just the same color
   family via the theme's Warning/Info/Success/Error. */
.avams-stat-card.tint-custom {
    background: linear-gradient(180deg, color-mix(in srgb, var(--tint-color) 10%, var(--mud-palette-surface)) 0%, var(--mud-palette-surface) 65%);
    border-color: color-mix(in srgb, var(--tint-color) 30%, var(--mud-palette-lines-default));
}

/* Card label/description text (Color="Color.Secondary" on a MudText)
   resolves to the brand's yellow accent — right for a genuine warning, wrong
   as the default caption tint under every card title. Scoped to card
   containers only, so amber still means something real elsewhere. */
.avams-stat-card .mud-secondary-text,
.avams-flat-card .mud-secondary-text {
    color: var(--mud-palette-text-secondary) !important;
}

/* Icon chips: the same token, mixed a little stronger, behind the icon. */
.avams-stat-icon-chip {
    width: 44px; height: 44px; border-radius: 8px; flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center;
}
.avams-stat-icon-chip.green { background: color-mix(in srgb, var(--mud-palette-primary) 18%, var(--mud-palette-surface)); }
.avams-stat-icon-chip.amber { background: color-mix(in srgb, var(--mud-palette-warning) 22%, var(--mud-palette-surface)); }
.avams-stat-icon-chip.red   { background: color-mix(in srgb, var(--mud-palette-error)   18%, var(--mud-palette-surface)); }
.avams-stat-icon-chip.blue  { background: color-mix(in srgb, var(--mud-palette-info)    18%, var(--mud-palette-surface)); }
.avams-stat-icon-chip.purple { background: color-mix(in srgb, var(--mud-palette-tertiary) 18%, var(--mud-palette-surface)); }

/* Empty-state illustration: a tinted icon circle + message, for a list with
   zero items (2026-11-03) — the teacher "My ..." pages (My Borrow Requests,
   My Room Requests, My Schedule) share this one global class instead of each
   duplicating it, since it's a plain page pattern, not a quickadd dialog.
   The icon names what the empty list is OF (equipment, a room, a class),
   not a generic "nothing here" glyph, so pass whichever MudIcon fits the
   caller's own subject. */
.avams-empty-state {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 12px; padding: 48px 20px; text-align: center;
}
.avams-empty-icon {
    width: 56px; height: 56px; border-radius: 50%; flex: 0 0 auto;
    background: color-mix(in srgb, var(--mud-palette-primary) 12%, var(--mud-palette-surface));
    color: var(--mud-palette-primary);
    display: flex; align-items: center; justify-content: center;
}
.avams-empty-state-text { color: var(--mud-palette-text-secondary); max-width: 360px; }

/* ── Dashboard: progress-bar status widget (Equipment Health panel) ─────── */
.avams-status-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.avams-status-row + .avams-status-row { border-top: 1px solid var(--mud-palette-divider-light); }
.avams-status-icon {
    width: 36px; height: 36px; border-radius: 8px; flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center;
}
.avams-status-icon.danger  { background: color-mix(in srgb, var(--mud-palette-error)   16%, var(--mud-palette-surface)); color: var(--mud-palette-error); }
.avams-status-icon.warning { background: color-mix(in srgb, var(--mud-palette-warning) 22%, var(--mud-palette-surface)); color: var(--mud-palette-warning); }
.avams-status-icon.success { background: color-mix(in srgb, var(--mud-palette-success) 18%, var(--mud-palette-surface)); color: var(--mud-palette-success); }
/* General/informational notifications (NotificationMenu's bell dropdown) —
   the one severity this widget didn't previously need a chip for. */
.avams-status-icon.neutral { background: color-mix(in srgb, var(--mud-palette-text-secondary) 16%, var(--mud-palette-surface)); color: var(--mud-palette-text-secondary); }
.avams-status-bar-track { height: 6px; border-radius: 3px; background: var(--mud-palette-background-gray); overflow: hidden; margin-top: 6px; }
.avams-status-bar-fill  { height: 100%; border-radius: 3px; }
.avams-status-bar-fill.danger  { background: var(--mud-palette-error); }
.avams-status-bar-fill.warning { background: var(--mud-palette-warning); }
.avams-status-bar-fill.success { background: var(--mud-palette-success); }

/* ── Floating chatbot widget (ChatbotWidget.razor) — Ava ──────────────────
   Fixed to the viewport, not the layout flow, so it follows the page on
   scroll and renders above content on every screen. z-index 1200 matches
   MudBlazor's drawer tier — above normal page content and the app bar, but
   still below the dialog/overlay tier (~1300+), so the widget sits behind the
   modal's dimmed backdrop once the chat is open.

   The circular clip (border-radius + overflow:hidden) lives on the INNER
   .avams-chatbot-widget-circle, not this outer box — the unread badge is
   pinned to THIS box's corner, and a square's corners fall outside its own
   inscribed circle, so putting overflow:hidden here would clip the badge.
   Round on purpose: it is an avatar, not a card. */
.avams-chatbot-widget {
    position: fixed;
    right: 28px;
    bottom: 28px;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    cursor: grab;
    z-index: 1200;
    flex-shrink: 0;
    transition: transform 0.2s ease;
    /* Vertical drag-to-reposition (see avamsInitChatbotDrag in index.html)
       owns all pointer movement on this element — without this, a touch
       drag also scrolls the page underneath it at the same time. */
    touch-action: none;
    user-select: none;
}
.avams-chatbot-widget:hover {
    transform: scale(1.05);
}
.avams-chatbot-widget:active { transform: scale(0.97); }

/* The artwork is RGBA (transparent background), so the disc behind it is the
   palette surface — white in light mode, the dark surface in dark mode — and
   the contrast ring is the same colour, which is what makes it read as a
   floating layer on either page background. */
.avams-chatbot-widget-circle {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    overflow: hidden;
    background: var(--mud-palette-surface);
    border: 3px solid var(--mud-palette-surface);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28), 0 2px 4px rgba(0, 0, 0, 0.15);
    transition: box-shadow 0.2s ease;
}
.avams-chatbot-widget:hover .avams-chatbot-widget-circle {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.32), 0 2px 6px rgba(0, 0, 0, 0.2);
}

.avams-chatbot-widget-pose {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    transition: opacity 0.2s ease;
}
/* Idle pose shown by default; wave pose crossfades in on hover and back out
   on mouse-leave — a plain CSS opacity swap, so it reverts on its own with
   no JS/C# state to manage. */
.avams-chatbot-widget-wave { opacity: 0; }
.avams-chatbot-widget:hover .avams-chatbot-widget-idle { opacity: 0; }
.avams-chatbot-widget:hover .avams-chatbot-widget-wave { opacity: 1; }

/* Unread indicator — "Ava has something you haven't seen yet" (currently the
   first-open welcome message; see ChatbotWidget.razor). Sits on the OUTER
   box, outside the inner circle's overflow:hidden clip. */
.avams-chatbot-widget-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--mud-palette-error);
    border: 2px solid var(--mud-palette-surface);
    z-index: 1;
}

/* One-time "notice me" pulse — an expanding, fading ring, played once on the
   first render of a genuinely new browser session (see the sessionStorage
   check in ChatbotWidgetState). A single iteration, never looping. Brand
   green, which is right in both palettes. */
@keyframes ava-intro-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(46, 125, 50, 0.55); }
    70%  { box-shadow: 0 0 0 20px rgba(46, 125, 50, 0); }
    100% { box-shadow: 0 0 0 0 rgba(46, 125, 50, 0); }
}
.avams-chatbot-widget-intro {
    animation: ava-intro-pulse 1.6s ease-out 1;
}

@media (max-width: 600px) {
    .avams-chatbot-widget { right: 20px; bottom: 20px; }
}

/* ── Ava's periodic greeting bubble (ChatbotWidgetState's greeting cycle) ──
   Always in the DOM (see ChatbotWidget.razor) with visibility toggled by a
   class, not @if — that's what makes the fade-out a real transition instead
   of an instant disappearance. position:fixed, same as the icon, so it floats
   above content and never shifts page layout. z-index sits one below the
   icon's 1200 so the icon itself is never obscured by the bubble. */
.avams-chatbot-greeting {
    position: fixed;
    right: 104px;
    bottom: 36px;
    max-width: 200px;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    padding: 10px 26px 10px 14px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
    font-size: 0.85rem;
    line-height: 1.35;
    cursor: pointer;
    z-index: 1199;
    opacity: 0;
    transform: translateX(8px);
    pointer-events: none;
    transition: opacity 0.35s ease, transform 0.35s ease;
}
.avams-chatbot-greeting-visible {
    opacity: 1;
    transform: translateX(0);
    pointer-events: auto;
}
/* Small triangular tail pointing right, toward Ava's icon. */
.avams-chatbot-greeting::after {
    content: "";
    position: absolute;
    right: -8px;
    top: 50%;
    transform: translateY(-50%);
    border-width: 8px 0 8px 8px;
    border-style: solid;
    border-color: transparent transparent transparent var(--mud-palette-surface);
}
.avams-chatbot-greeting-dismiss {
    position: absolute;
    top: 2px;
    right: 6px;
    border: none;
    background: transparent;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    color: var(--mud-palette-text-secondary);
    padding: 4px;
}
.avams-chatbot-greeting-dismiss:hover { color: var(--mud-palette-text-primary); }

@media (max-width: 600px) {
    .avams-chatbot-greeting { right: 88px; bottom: 26px; max-width: 160px; font-size: 0.8rem; }
}

/* ── Clickable dashboards (2026-10-05) ────────────────────────────────────
   DashLink renders a real <a href> around a card, list row or legend item
   when the viewer's role can open the target page (.avams-dash-link), and
   plain markup when it can't (.avams-dash-static), so only things that
   navigate look like they do. Hover tints toward Primary; keyboard focus
   (Tab, then Enter to follow) gets a visible ring. */
.avams-dash-link,
.avams-dash-static {
    display: block;
    min-width: 0;
}
.avams-dash-link {
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    border-radius: var(--mud-default-borderradius);
}
.avams-dash-link.avams-dash-inline,
.avams-dash-static.avams-dash-inline {
    display: inline;
}

/* A card inside a link fills it, so a row of linked cards stays even. */
.avams-dash-link > .mud-paper,
.avams-dash-static > .mud-paper,
.avams-dash-button > .mud-paper {
    height: 100%;
}
.avams-dash-link > .mud-paper,
.avams-dash-button > .mud-paper {
    transition: border-color .15s ease, box-shadow .15s ease;
}
.avams-dash-link:hover > .mud-paper,
.avams-dash-button:hover > .mud-paper {
    border-color: color-mix(in srgb, var(--mud-palette-primary) 55%, var(--mud-palette-lines-default)) !important;
    box-shadow: 0 2px 10px color-mix(in srgb, var(--mud-palette-primary) 18%, transparent) !important;
}

.avams-dash-link:focus-visible,
.avams-dash-button:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* The Pending Approvals card opens a menu, so it is a <button>: strip the
   native button look so it matches its sibling cards exactly. */
.avams-dash-button {
    display: block;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: inherit;
    cursor: pointer;
    border-radius: var(--mud-default-borderradius);
}

/* List rows, bars and legend items: a soft tint instead of a card border. */
.avams-dash-link.avams-dash-item {
    padding-left: 6px;
    padding-right: 6px;
    margin-left: -6px;
    margin-right: -6px;
    transition: background-color .15s ease;
}
.avams-dash-link.avams-dash-item:hover {
    background: color-mix(in srgb, var(--mud-palette-primary) 8%, transparent);
}
/* .avams-roombars scrolls, so a row poking 6px outside it would add a
   horizontal scrollbar: keep those rows inside. */
.avams-roombars > .avams-dash-item {
    margin-left: 0;
    margin-right: 0;
}
.avams-dash-link.avams-dash-inline:hover {
    text-decoration: underline;
}

/* "View all →" in a card header. */
.avams-dash-viewall {
    font-size: 0.75rem;
    font-weight: 500;
    white-space: nowrap;
    color: var(--mud-palette-primary) !important;
}

/* Table rows that open a record: the whole row takes the click; the name
   cell holds the real link for keyboard users. */
.avams-dash-row {
    cursor: pointer;
}
.avams-dash-row:hover > td {
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
}

/* Rows of the hand-built SVG charts (Top Equipment Borrowed). */
.avams-dash-svg-link {
    cursor: pointer;
    outline: none;
}
.avams-dash-svg-hit {
    fill: transparent;
}
.avams-dash-svg-link:hover .avams-dash-svg-hit {
    fill: color-mix(in srgb, var(--mud-palette-primary) 10%, transparent);
}
.avams-dash-svg-link:focus-visible .avams-dash-svg-hit {
    fill: color-mix(in srgb, var(--mud-palette-primary) 10%, transparent);
    stroke: var(--mud-palette-primary);
    stroke-width: 2px;
}

/* Custom chart legend whose items are links (staff dashboard). */
.avams-dash-legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 4px 14px;
    margin-top: 8px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}
.avams-dash-legend .avams-dash-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-radius: 999px;
}
.avams-dash-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    flex: 0 0 auto;
}

@media (prefers-reduced-motion: reduce) {
    .avams-dash-link > .mud-paper,
    .avams-dash-button > .mud-paper,
    .avams-dash-link.avams-dash-item {
        transition: none;
    }
}
/* View-only fields: values the system fills in (Responsible Staff, Assigned
   Professor, Expected EOL, Last Known Location, Changed By...). Kept ReadOnly
   rather than Disabled so the text can still be selected and copied. Only the
   value text is grayed; the field box and its label look like any other field. */
/* Matches MudBlazor's own ".mud-input > input.mud-input-root" plus one class,
   so it wins over the theme's primary text color without !important. */
.avams-view-only .mud-input > input.mud-input-root,
.avams-view-only .mud-input > textarea.mud-input-root {
    color: var(--mud-palette-text-secondary);
    -webkit-text-fill-color: var(--mud-palette-text-secondary);
    cursor: default;
}
.avams-view-only .mud-input-control-input-container label.mud-input-label {
    color: var(--mud-palette-text-secondary);
}
