:root {
    --env-banner-height: 0px;
}

/* Cogniform dynamic-form layout tokens — override the Cogniform Blazor renderer's built-in
   spacing/column defaults. DynamicFormRenderer reads each as var(--token, fallback), so tune the
   submission form's look here; removing a token reverts that value to the renderer's own default.
     col-min/max-width : columns pack as many of at least min-width as fit, each growing to
                         max-width (1fr fills the row; a fixed length caps it and leaves a gap).
     col-gap / row-gap : horizontal / vertical gaps between fields.
     field-padding     : inner padding of each field's bordered box.
     label-gap         : gap between a field's label and its control. */
:root {
    --cogniform-form-col-min-width: 700px;
    --cogniform-form-col-max-width: 1fr;
    --cogniform-form-col-gap: 16px;
    --cogniform-form-row-gap: 12px;
    --cogniform-form-field-padding: 6px 10px;
    --cogniform-form-label-gap: 1px;
    /* Submission detail header/banner background + loop accent. Defaults to the app theme's primary (the
       brand purple used by the nav/icons); override here to give the Cogniform pages a distinct banner. */
    --cogniform-header-bg: var(--mud-palette-primary);
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

/* The validation summary on the in-place edit forms (Item/Asset detail), matching the job entry
   form's box. Styled on the list, which ValidationSummary only emits when there is something to
   report, so a clean form shows no empty red box. */
.eb-form-validation .validation-errors {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--mud-palette-error);
    border-radius: 4px;
    background-color: var(--mud-palette-error-hover);
}

.eb-form-validation .validation-errors .validation-message {
    color: var(--mud-palette-error-text);
}

/* Compact in-place edit forms (Item/Asset detail). Cards flow into a masonry (CSS multi-column) layout,
   so cards of different heights pack tightly top-to-bottom into columns instead of aligning into rows
   that leave gaps (the shortcoming of the job entry form's row layout). Sized by column WIDTH, not a
   fixed count: the browser fits as many ~24rem columns as the screen allows — roughly three on a wide
   screen — so the cards fill the width instead of leaving a blank strip, and fall to two then one on
   narrower screens. The field text is also shrunk toward the density of the read view. */
.eb-compact-form {
    column-width: 24rem;
    column-gap: 1.5rem;
}

/* Each card is a whole, non-splitting column item with a gap beneath it — the masonry packing. */
.eb-compact-form .eb-form-card {
    break-inside: avoid;
    margin: 0 0 1.5rem;
    width: 100%;
}

/* ValidationSummary on the account pages renders its <ul> with the .alert classes
   so validation errors appear in the same colored box as status messages; drop the
   list bullets the <ul> would otherwise show inside the box. */
ul.validation-alert {
    list-style: none;
}

ul.validation-alert li + li {
    margin-top: 0.25rem;
}

.button-link {
    text-decoration: unset;
}

#blazor-error-ui {
    background: lightyellow;
    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;
    }

.title {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-bottom: 0.625rem;
}

    .title.title-secondary {
        padding-top: 0.313rem;
        padding-bottom: 0;
        color: var(--bs-secondary-color);
    }

.title-header-text {
    font-size: 2.5rem;
    line-height: 3rem;
    font-weight: 600;
    letter-spacing: 0rem;
    padding: 0.3125rem 0;
    /* Optical alignment: at this size a capital's left side bearing is ~0.05em
       (2px), making the title look indented next to body-size text below it. */
    margin-left: -0.05em;
}

.title-content-text {
    font-size: 1.75rem;
    font-weight: 400;
    line-height: 2.5rem;
    letter-spacing: 0rem;
}

.password-policy-hint {
    /* The title block above contributes 25px of built-in spacing (its own and its
       header's padding plus the layout flex gap) plus line-height slack; pull up
       through it so the visible gap above matches the 0.75rem below. */
    margin-top: -1.25rem;
    margin-bottom: 0.75rem;
    color: var(--bs-gray-700);
}

/* Layout for a section index built out of NavigationCards: a titled column with a centred,
   wrapping card grid. Global rather than scoped to one page because the admin index, its
   Reference Data child and the reports index all use it, and copies of a layout drift. The home page keeps its
   own scoped variant, which adds hero styling these pages do not want. */
.admin-container {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.welcome-gridlayout {
    display: flex;
    flex-direction: column;
    gap: 3rem;
}

.welcome-cards {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    justify-content: center;
}

/* Full-screen blocking scrim shared by the idle-logoff warning overlay below, the
   account submit/passkey busy overlays (account-submit.css) and BusyOverlay.razor.
   One recipe so the dim level, stacking, and geometry cannot drift between them.
   Deliberately sets no `display`: each consumer controls its own visibility. */
.fullscreen-scrim {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    align-items: center;
    justify-content: center;
    z-index: 10000;
}

/* Idle-logoff warning modal (IdleTimeoutProvider.razor). Global stylesheet rather
   than scoped CSS so styles apply under dotnet watch, which does not regenerate the
   scoped-CSS bundle for newly added .razor.css files. The element also carries
   .fullscreen-scrim; it is conditionally rendered, so it is simply flex when present. */
.idle-timeout-overlay,
.busy-overlay {
    display: flex;
}

/* A screen's busy scrim (BusyOverlay.razor), also conditionally rendered. It stacks
   over the nav and app bar but under MudBlazor's dialogs and snackbars, and under the
   idle-logoff warning: a message raised mid-save stays readable, and a user waiting
   on a long load can still answer "stay signed in". Inside a dialog it covers only the
   dialog box: the dialog's open animation leaves a transform on it, which makes the box
   the scrim's containing block. The dialog's own backdrop already dims the page. */
.busy-overlay {
    z-index: calc(var(--mud-zindex-appbar) + 1);
}

/* An import dialog's preview (FSR History, Inventory Import): its counts on one line, wrapping on a narrow
   screen, and its lists scrolling inside the dialog rather than pushing the dialog's buttons off screen. */
.import-preview-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.import-preview-table {
    max-height: 14rem;
    overflow-y: auto;
}

.import-preview-table th {
    white-space: nowrap;
}

.import-preview-list {
    max-height: 6rem;
    overflow-y: auto;
}

/* Read to assistive technology, not shown: the scrim's spinner says nothing on its own. */
.busy-overlay-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.idle-timeout-modal {
    /* Surface, not the page field: this is a panel floating on a scrim, and --bs-body-bg
       tracks the tinted background, which left the modal looking washed rather than white. */
    background: var(--mud-palette-surface);
    border-radius: 12px;
    padding: 2rem;
    text-align: center;
    max-width: 400px;
    width: 90%;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);
}

.idle-timeout-modal h3 {
    margin: 0 0 0.5rem;
    font-size: 1.25rem;
    color: var(--bs-body-color);
}

.idle-timeout-modal p {
    margin: 0 0 0.25rem;
    color: var(--bs-gray-700);
    font-size: 0.95rem;
}

.idle-timeout-countdown {
    font-size: 2rem;
    font-weight: 600;
    color: var(--bs-warning);
    margin: 0.5rem 0 1.5rem;
    font-variant-numeric: tabular-nums;
}

.main-content {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    width: 100%;
}

.block-content {
    width: 31.25rem;
    height: auto;
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    max-width: 100%;
}


.or {
    display: flex;
    flex-direction: row;
    align-items: center;
}

    .or .or-line {
        border-bottom: 1px solid var(--bs-body-color);
        width: 100%;
        opacity: 0.2;
    }

    .or .or-text {
        padding: 0 0.625rem 0px 0.625rem;
        font-size: 0.75rem;
        line-height: 1.125rem;
        color: var(--bs-body-color);
        opacity: 0.7;
    }

.links-area {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    padding-top: 0.625rem;
}

.links-container {
    display: flex;
    gap: 0.25rem;
    justify-content: center;
    align-items: center;
}

    .links-container .dot {
        height: 0.3125rem;
        width: 0.3125rem;
        margin: 0 0.1875rem;
        border-radius: 50%;
        background-color: var(--bs-body-color);
        opacity: 0.7;
    }

.manage-layout {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.info-message::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0.15;
    background: var(--bs-primary);
    z-index: -1;
}

.info-message {
    padding: 0.625rem 1rem 0.625rem 1rem;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
}

    .info-message.info-panel {
        margin-bottom: 0.625rem;
    }

.manage-content {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.large-input {
    height: 4.3125rem;
    font-size: 1.875rem;
}

.title-content-text-secondary {
    font-size: 1.25rem;
    line-height: 1.75rem;
}

.button-group {
    display: flex;
    flex-direction: row;
    gap: 0.625rem;
}

.confirm-button {
    margin-top: 0.3125rem;
}

.display-desktop {
    display: block;
}

.display-mobile {
    display: none;
}

.icon-external-provider {
    width: 1rem;
    height: 1rem;
}

.icon-google {
    background-image: url("/images/account/providers/google-logo.svg")
}

.icon-facebook {
    background-image: url("/images/account/providers/facebook-logo.svg")
}

.icon-microsoft {
    background-image: url("/images/account/providers/microsoft-logo.svg")
}

.icon-twitter {
    background-image: url("/images/account/providers/x-logo.svg")
}

/* Treat tablet-width screens (≤1024px, e.g. iPads) as "mobile": hide the desktop-only
   header controls and show the in-drawer mobile equivalents instead. Breakpoint is 1024
   (not 768) so iPad-class devices get the compact layout. */
@media (max-width: 1024px) {
    .display-desktop {
        display: none;     /* Desktop-only elements (e.g. header user info / logout). */
    }

    .display-mobile {
        display: block;    /* Mobile-only elements (e.g. the in-drawer user menu). */
    }

}

/* Hide LastPass icons globally - 2024 selectors */
div[data-lastpass-icon-root],
div[data-lastpass-root],
span[data-lastpass-icon-root],
span[data-lastpass-root] {
    display: none !important;
    visibility: hidden !important;
    opacity: 0 !important;
    width: 0 !important;
    height: 0 !important;
}

/* Fixed form action footer - used for form save/cancel buttons */
.form-action-footer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: white;
    border-top: 1px solid var(--bs-border-color);
    padding: 0.75rem 1rem;
    z-index: 1020;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
}

.form-action-footer .button-container {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* When desktop sidebar is open (MudDrawer mini-variant), offset footer past the 240px sidebar */
@media (min-width: 769px) {
    .panel-open .form-action-footer {
        left: 240px;
    }
}

/* Spacer so content isn't hidden behind fixed footer */
.form-action-footer-spacer {
    height: 10rem;
}

/* Larger spacer when validation errors are expanded */
.form-action-footer-spacer.validation-expanded {
    height: 28rem;
}

/* Environment banner - displays current environment at top of page */
.environment-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    /* Above MudBlazor's drawer (--mud-zindex-drawer: 1100): the full-height nav is fixed and would
       otherwise paint over this top strip where they touch, clipping the banner. Kept below dialogs
       (1400) so a modal/scrim can still cover it. */
    z-index: 1250;
    text-align: center;
    padding: 0.25rem 1rem;
    font-size: 0.6125rem;
    font-weight: 600;
    color: #fff;
    letter-spacing: 0.05em;
}

.environment-banner.env-dev {
    background-color: #0d6efd;
}

.environment-banner.env-qa {
    background-color: #fd7e14;
}

.environment-banner.env-test {
    background-color: #198754;
}

.environment-banner.env-uat {
    background-color: #6f42c1;
}

.environment-banner-spacer {
    height: 1.25rem;
    flex-shrink: 0;
}

/* ============================================================
   Mobile-first auth page adaptations (phones ≤480px)
   - 48px touch targets for field workers
   - Scaled typography and tightened spacing
   - Vertically stacked links with large tap areas
   ============================================================ */
@media (max-width: 480px) {
    /* Title scaling */
    .title {
        gap: 0.25rem;
        padding-bottom: 0.375rem;
    }

    .title-header-text {
        font-size: 1.75rem;
        line-height: 2.125rem;
        padding: 0.125rem 0;
    }

    .title-content-text {
        font-size: 1.25rem;
        line-height: 1.75rem;
    }

    .title-content-text-secondary {
        font-size: 1rem;
    }

    /* Spacing */
    .block-content {
        gap: 0.5rem;
    }

    /* Links — stack vertically with large tap targets */
    .links-container {
        flex-direction: column;
        gap: 0;
        align-items: stretch;
    }

    .links-container a {
        display: flex;
        min-height: 2.75rem;
        align-items: center;
        justify-content: center;
        font-size: 0.9375rem;
        font-weight: 600;
        color: var(--bs-primary);
    }

    .links-container .dot {
        display: none;
    }

    /* "Or" divider — hide on mobile */
    .or {
        display: none;
    }

    .links-area {
        padding-top: 0.25rem;
    }

    /* Status messages */
    .block-content .alert {
        font-size: 0.9375rem;
        padding: 0.875rem 1rem;
        border-radius: 0.375rem;
        line-height: 1.5;
    }

    /* CTA prominence on static pages (Lockout, AccessDenied) */
    .cta-links-container {
        margin-top: 0.75rem;
    }

    .cta-links-container a {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 3.5rem;
        padding: 0 1.5rem;
        font-size: 1.125rem;
        background-color: var(--bs-primary);
        color: var(--bs-white) !important;
        border-radius: 0.375rem;
        font-weight: 600;
        text-decoration: none;
        width: 100%;
    }
}

/* Input labels: room for descenders
   ─────────────────────────────────
   It is the floating *label* that clips, not the value. Measured in the running app on the job entry
   form: every label reports clientHeight 18px against scrollHeight 20px, so each one loses 2px off
   the bottom — enough to cut the tails of g, y, p and the parentheses in "Weight (lb/ft)". The value
   text was never affected (its slot measured 40px tall against 40px of content).

   Two MudBlazor rules combine to cause it. `.mud-input-label-inputcontrol` sets `line-height:
   1.15rem` — 18.4px at the label's 1rem font, where the face needs about 20px for full ascent plus
   descent — and `.mud-input-label` sets `overflow: hidden` (there for the `text-overflow: ellipsis`
   that truncates a long label). The clip is what turns the 1.6px shortfall into a visible cut.

   Padding cannot fix it: `.mud-input-label-outlined` sets `padding: 0px 5px !important`, and an
   !important shorthand beats any ordinary padding-bottom regardless of specificity — verified by
   probing the live page, where a rule at 0-4-0 still lost. `line-height` carries no !important, so
   an ordinary rule wins on specificity alone. 0-4-0 here against MudBlazor's 0-3-0, so it holds
   regardless of stylesheet order and needs no !important of its own.

   1.35 (21.6px) is the smallest step that clears the descenders: it takes every label to
   clientHeight 22px against scrollHeight 22px — measured, not estimated. The label grows by 4px,
   inside a field that already reserves the room, so the field's own height does not change. */
.mud-input-control
    > .mud-input-control-input-container
    > .mud-input-label.mud-input-label-inputcontrol {
    line-height: 1.35;
}

/* MudDataGrid / MudTable base styling (.eb-mud-grid)
   ──────────────────────────────────────────────────
   Modern SaaS grid treatment: rounded container with a subtle shadow,
   a muted uppercase header, light row dividers, soft hover, and branded
   links/action icons. Colors reference the --mud-palette-* variables that
   MudThemeProvider emits from AppTheme.cs, so a theme change there propagates
   straight to the grid — AppTheme.cs is the single source of truth. */

/* Container. Grows to its rows and shrinks to the fold: basis auto measures the content, and shrink —
   against the definite height AuthenticatedLayout gives .mud-main-content — is what bounds it. Not
   flex: 1, which filled unconditionally and left a grid of four rows holding a viewport of blank card.
   min-height: 0 so the shrink can happen at all — a flex item's automatic minimum size otherwise floors it
   at its content. The floor that keeps a squeezed grid usable is in the short-viewport media query below
   rather than here, because a flat floor is itself the bug this rule fixes: filter a grid down to three
   rows and a 12rem floor left 90px of blank card under the last one. */
.eb-mud-grid.mud-table {
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    flex: 0 1 auto;
    min-height: 0;
}

/* Below this height the page header and toolbar can leave the grid a sliver, and because the card clips
   for its border radius, a floor on the scroll container inside it is invisible — the card shrinks past it
   and cuts the rows off with nothing overflowing the pane to scroll (measured at 330px tall: rows cut 43px
   below the card's edge). The floor goes on the card, so it overflows the pane and the pane's own
   overflow-y takes over. Scoped to short viewports because at a normal height it would pad a small grid
   with blank card instead. */
@media (max-height: 700px) {
    .eb-mud-grid.mud-table {
        min-height: 12rem;
    }
}

/* Shrinking the container is what bounds the scroll region MudBlazor needs for Virtualize and a sticky
   FixedHeader, so Height="100%" stays on the grids that use those. Basis auto so it measures its rows;
   the inline height:100% MudDataGrid writes here from Height becomes cyclic against a content-sized
   parent and resolves to auto, so it no longer fights the flex box.

   Precondition: .eb-mud-grid must sit in a flex column of definite height, which every consumer does
   today by being a page rendered into .page-body. Without one nothing shrinks and the grid degrades to
   its natural height — the page then scrolls, which is the acceptable failure.

   No floor here: the card above carries it, and one here only fights it — the card clips, so a floor
   inside it just pushes the rows past the clip. Shrinking to nothing is fine because this box scrolls.

   min-width, because ColumnResizeMode.Container writes an inline width: max-content here — and
   .mud-table-root is width: 100%, so 100% of a max-content box is the intrinsic column width and the
   table stopped short of the card it sits in. A floor of 100% keeps the table spanning the card while
   leaving max-content free to grow past it once a column is dragged wider, which is what the inline
   overflow-x: auto on the paper is there to scroll. */
.eb-mud-grid .mud-table-container {
    overflow-y: auto;
    border-radius: inherit;
    flex: 0 1 auto;
    min-width: 100%;
}

/* The toolbar must not absorb the shrink meant for the rows. MudToolBar lays its children out with no gap, so
   two actions side by side (say Return All Out and Add) would sit flush; the gap spaces them, and the collapse
   chevron, the way the Details card's header actions are spaced. */
.eb-mud-grid .mud-table-toolbar {
    flex: 0 0 auto;
    gap: 0.5rem;
}

/* A grid held still while one of its rows is added or edited (GridEditFreeze): its filters and sorts are set aside
   until Save or Cancel, so their controls go. Hidden rather than disabled, which also takes the buttons out of the
   tab order, and with visibility rather than display, so the columns keep their widths and the grid doesn't
   shift when the edit begins. The column titles, which also sort on click, stop answering the mouse; the column
   resizers are left alone. */
.eb-mud-grid.eb-grid-frozen .mud-table-head .filter-button,
.eb-mud-grid.eb-grid-frozen .mud-table-head .sort-direction-icon,
.eb-mud-grid.eb-grid-frozen .mud-table-head .mud-sort-index {
    visibility: hidden;
}

.eb-mud-grid.eb-grid-frozen .mud-table-head .sortable-column-header {
    pointer-events: none;
}

/* MudBlazor locks scrolling behind a dialog by putting overflow: hidden on <body>, which stopped working
   the moment .mud-main-content became the scroller instead of the window — the page scrolled under the
   modal. Both class names are MudBlazor's: it picks the -no-padding variant when no scrollbar gutter
   needs compensating. */
body.scroll-locked .mud-main-content,
body.scroll-locked-no-padding .mud-main-content {
    overflow: hidden;
}

/* A filter cell must not decide the column's width. MudBlazor renders a DateTime filter as a date picker
   plus a time picker side by side, which measures ~420px, and in an auto-layout table the widest cell in a
   column wins — so a column of "2026-08-18 01:23" ended up four times wider than its content. The primary
   date columns filter from their page toolbar instead; this caps the ones that remain, such as the audit
   columns that are hidden by default. */
.eb-mud-grid .filter-header-cell .mud-input-control,
.eb-mud-grid .filter-header-cell .mud-picker {
    min-width: 0;
}

.eb-mud-grid .filter-header-cell .mud-picker {
    max-width: 7.5rem;
}

/* The date columns replace MudBlazor's stock date+time pair with a single picker; this keeps it the width
   of the value it filters rather than the width of the picker's natural input. */
.eb-mud-grid .filter-header-cell .eb-grid-date-filter {
    max-width: 8.5rem;
}

/* Filter inputs sit inside the head, so they'd otherwise inherit its uppercase bold treatment. */
.eb-mud-grid .mud-table-head .mud-input input {
    text-transform: none;
    font-weight: 400;
    letter-spacing: normal;
    font-size: 0.8125rem;
}

/* Header */
.eb-mud-grid .mud-table-head .mud-table-row .mud-table-cell {
    background-color: var(--mud-palette-gray-lighter);
    border-bottom: 1px solid var(--mud-palette-table-lines);
    color: var(--mud-palette-gray-darker);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Column filter (funnel) icon. Idle it matches the muted header text (--mud-palette-gray-darker),
   so an unused filter reads as chrome rather than an action. When a column is actively filtering,
   MudDataGrid adds a `filtered` class to the button (and swaps the outlined funnel glyph for a solid
   one); we recolor that state to the brand red accent so "this column is filtering" is unmistakable.
   The `.filtered` selector is one class more specific than the idle rule, so it wins without !important. */
.eb-mud-grid .mud-table-head .mud-icon-button {
    color: var(--mud-palette-gray-darker);
}
.eb-mud-grid .mud-table-head .mud-icon-button.filtered {
    color: var(--brand-red);
}

/* Header width. A column can never be narrower than its header, so the header must not hold space it does
   not need. MudDataGrid reserves room in every header for a sort button, a multi-sort number, a filter
   button and a drag handle — 89px before the title. The buttons are made compact; the drag handle goes,
   since the whole header is already the drag target; and the multi-sort number takes room only when it
   shows one. The title wraps onto a second line rather than holding the column at its one-line width when
   someone drags the column narrower. */
.eb-mud-grid .mud-table-head .column-options .mud-icon-button {
    padding: 2px;
}

.eb-mud-grid .mud-table-head .column-options .drag-icon-options,
.eb-mud-grid .mud-table-head .column-options .mud-sort-index.invisible {
    display: none;
}

/* The drag handle carried the only grab cursor; the header it stood for takes it over. */
.eb-mud-grid .mud-table-head .mud-drop-item-draggable {
    cursor: grab;
}

.eb-mud-grid .mud-table-head .column-header {
    align-items: center;
}

.eb-mud-grid .mud-table-head .sortable-column-header {
    white-space: normal;
}

/* Body rows — compact by default. height:auto lets the cell padding below decide the row height,
   so a full grid shows more rows per screen (this was formerly the Job Board's local opt-out, now
   the app-wide default). Grids that virtualize on a fixed pixel ItemSize opt back into a pinned
   height with .eb-mud-grid-fixed-row. */
.eb-mud-grid tbody tr.mud-table-row,
.eb-mud-grid tbody tr.mud-table-row td {
    height: auto;
    box-sizing: border-box;
}

/* Virtualization escape hatch. MudDataGrid Virtualize computes scroll geometry from ItemSize (px),
   so a virtualized grid must render rows at that exact height or the scroll maths desync from the
   rendered rows. Re-pin the shared 72px here rather than reverting the global compact default. */
.eb-mud-grid-fixed-row tbody tr.mud-table-row,
.eb-mud-grid-fixed-row tbody tr.mud-table-row td {
    height: 72px;
}

.eb-mud-grid .mud-table-body .mud-table-row .mud-table-cell {
    color: var(--mud-palette-text-primary);
    border-bottom: 1px solid var(--mud-palette-divider-light);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
}

/* Last row: the container border handles the bottom edge */
.eb-mud-grid .mud-table-body .mud-table-row:last-child .mud-table-cell {
    border-bottom: none;
}

/* Row hover is handled by MudBlazor's built-in Hover="true" treatment, which
   uses var(--mud-palette-table-hover) and is correctly gated by
   @media (hover:hover) — no custom rule needed (would duplicate it and break
   touch). */

/* Selected row */
.eb-mud-grid tbody tr.mud-table-row.eb-grid-selected-row,
.eb-mud-grid tbody tr.mud-table-row.eb-grid-selected-row:hover {
    background-color: rgba(var(--mud-palette-primary-rgb), 0.18);
}

/* Links */
.eb-mud-grid .mud-table-body .mud-table-cell a {
    color: var(--mud-palette-secondary);
    font-weight: 500;
}

.eb-mud-grid .mud-table-body .mud-table-cell a:hover {
    color: var(--mud-palette-primary);
}

/* LinkPropertyColumn's text: the cell that opens the row's own page reads as its heading. */
.eb-mud-grid .mud-table-body .mud-table-cell .eb-grid-link {
    font-weight: 700;
}

/* A grid's money columns, read as one band: a pale steel tint (the theme's tertiary), figures right-aligned
   in equal-width digits so amounts line up by place value. --lead marks the figure the page is about. */
.eb-mud-grid .mud-table-cell.eb-value-col {
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 14%, transparent);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* The band runs through the header too: one step deeper than the cells, in the header's muted navy. The
   drag-and-drop wrappers MudDataGrid puts round a header are stretched so the title can sit right. */
.eb-mud-grid .mud-table-head .mud-table-row .mud-table-cell.eb-value-col {
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 28%, var(--mud-palette-gray-lighter));
    color: var(--mud-palette-primary);
}

.eb-mud-grid .mud-table-head .mud-table-cell.eb-value-col .mud-drop-zone,
.eb-mud-grid .mud-table-head .mud-table-cell.eb-value-col .mud-drop-item {
    width: 100%;
}

/* The sort control moves to the title's left, as a number column's does, so the title lines up with the
   figures beneath it. */
.eb-mud-grid .mud-table-head .mud-table-cell.eb-value-col .column-header {
    justify-content: flex-end;
}

.eb-mud-grid .mud-table-head .mud-table-cell.eb-value-col .column-options {
    order: -1;
}

.eb-mud-grid .mud-table-body .mud-table-cell.eb-value-col--lead {
    color: var(--mud-palette-primary);
    font-weight: 600;
}

/* An absent value in a money column: present but quiet, so the column still reads as a column. */
.eb-value-empty {
    color: var(--mud-palette-text-disabled);
}

/* GridLayoutHost's wrapper adds no box, so the page's own layout CSS reaches the grid as before. */
.eb-grid-layout-host {
    display: contents;
}

/* Row action icons */
.eb-mud-grid .mud-table-body .mud-icon-button {
    color: var(--mud-palette-gray-default);
    transition: color 0.15s ease;
}

.eb-mud-grid .mud-table-body .mud-icon-button:hover {
    color: var(--mud-palette-gray-darker);
    background-color: var(--mud-palette-divider-light);
}

/* ==========================================================================
   Inventory (Module B) — shared layout for the asset search, management
   screens, and value report. Global rather than per-component scoped because
   every inventory page uses the same intro / toolbar / grid-block shell.
   ========================================================================== */
.inventory-intro {
    color: var(--mud-palette-text-secondary);
    margin: 0 0 1rem;
}

.inventory-toolbar {
    margin-bottom: 1rem;
}

/* A page title and its primary actions sharing one line — the shape the Job Board established and
 * every Reference Data screen now follows.
 *
 * space-between rather than margin-left:auto on the actions, so the pair stays correct if anything
 * is ever added between them. align-items: center because the title is set much larger than the
 * buttons, and aligning on baselines would leave them sitting oddly low against it. flex-wrap so a
 * narrow viewport drops the actions onto their own line instead of crushing the title.
 *
 * The bottom margin lives here so the title is set off from the grid below on every screen — the
 * pages carry no description paragraph, so nothing else supplies that gap. */
.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* The actions travel together, so space-between has two children to separate rather than three to
 * spread. Also the hook for a screen that grows a second action later, as the Job Board did. */
.page-header-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

/* Tabs whose panels hold a grid (Pricing): the tabs fill the page body as a flex column, so the grid's
   .inventory-grid-block grows to the window as it does on a page without tabs. Child selectors only: the
   tab header buttons carry .mud-tab-panel too. */
.eb-fill-tabs,
.eb-fill-tabs > .mud-tabs-panels,
.eb-fill-tabs > .mud-tabs-panels > .mud-tab-panel {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
}

/* Tabs framed as one card (Rates, Inventory Settings): the tab strip and its panel share a single bordered
   surface, so a grid inside reads as part of the tab rather than a second card floating under a strip. The
   grid drops its own shadow and keeps a thin border; the panel's 16px padding is the gap between the tabs,
   any toolbar, and the grid. */
.eb-card-tabs {
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
    overflow: hidden;
}

.eb-card-tabs > .mud-tabs-tabbar {
    border-bottom: 1px solid var(--mud-palette-lines-default);
    border-radius: 0;
}

.eb-card-tabs > .mud-tabs-panels {
    background-color: var(--mud-palette-surface);
}

/* The 16px gap on every side. Here rather than through MudTabs' PanelClass, which MudBlazor 9 no longer
   accepts (the MUD0002 analyzer warning) and silently drops. */
.eb-card-tabs > .mud-tabs-panels > .mud-tab-panel {
    padding: 1rem;
}

.eb-card-tabs .eb-mud-grid.mud-table {
    border-radius: 8px;
    box-shadow: none;
}

.eb-card-tabs .inventory-grid-block {
    margin-top: 0;
}

/* Tabs and toggle groups whose selected choice must stand out (Pricing): the unselected ones go grey, the
   selected one takes the primary colour on a light primary wash, in bold, with icons following their label. */
.eb-select-tabs .mud-tab {
    color: var(--mud-palette-text-secondary);
}

.eb-select-tabs .mud-tab .mud-icon-root {
    color: inherit;
}

.eb-select-tabs .mud-tab.mud-tab-active {
    color: var(--mud-palette-primary);
    font-weight: 600;
    background-color: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
}

.eb-select-tabs .mud-tab-slider {
    height: 3px;
}

.eb-select-toggle .mud-toggle-item {
    color: var(--mud-palette-text-secondary);
    border-color: color-mix(in srgb, var(--mud-palette-primary) 25%, transparent);
}

.eb-select-toggle .mud-toggle-item.eb-toggle-selected {
    color: var(--mud-palette-primary);
    font-weight: 600;
    background-color: color-mix(in srgb, var(--mud-palette-primary) 14%, transparent);
}

/* An editor in a grid cell that reads as the cell's text until it is used (the Pricing grids): no border or
   underline at rest, a light wash on hover to show it can be edited, and the normal input once focused. */
.eb-inline-edit .mud-input {
    border-radius: var(--mud-default-borderradius);
    padding-inline: 0.25rem;
}

.eb-inline-edit .mud-input:hover,
.eb-inline-edit .mud-input:focus-within {
    background-color: var(--mud-palette-action-default-hover);
}

/* Custom column filter popups (multiselect + search, and ranges) used in the inventory grid's
   header filter menus. */
.eb-col-filter {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.25rem;
    min-width: 14rem;
}

.eb-col-filter-options {
    display: flex;
    flex-direction: column;
    max-height: 16rem;
    overflow-y: auto;
}

.eb-col-filter-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.eb-col-filter--range {
    min-width: 12rem;
}

/* The job form's Crew Members dropdown, spanning the Crew Members and Crew Lead fields. JobEntry sets the
   width each time the dropdown opens (setSpanWidthProperty); until it has, the dropdown fits its rows, and
   never narrower than its own field (DropdownWidth.Adaptive).
   Global because the dropdown renders in MudBlazor's popover layer, outside JobEntry's scoped CSS. */
.job-entry-crew-popover {
    width: var(--job-entry-crew-popover-width, auto);
}

/* CertificationAlert — the certificate-ribbon icon beside a crew member's name, and its tooltip. Global
   because the tooltip renders in MudBlazor's popover layer, outside any component's scoped CSS. */
.eb-cert-alert {
    display: inline-flex;
    align-items: center;
    margin-left: 0.25rem;
    vertical-align: middle;
    cursor: help;
}

.eb-cert-alert:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
    border-radius: 4px;
}

.eb-cert-alert-tip {
    text-align: left;
    line-height: 1.5;
}

.eb-cert-alert-tip-title {
    font-weight: 600;
    margin-bottom: 0.125rem;
}

/* A fixed width beside the header's buttons, and none of MudBlazor's uneven dense-form margins (8px
   above, 4px below), so the field centres on the buttons instead of sitting 2px low. Four classes to
   outrank MudBlazor's three-class margin rule. */
.page-header-actions .mud-input-control.mud-input-control-margin-dense.page-header-field {
    flex: 0 0 12rem;
    margin: 0;
}

/* The bounded flex column .eb-mud-grid needs (see the precondition note above) so its Height="100%"
   resolves to a real box and FixedHeader has something to stick inside. */
.inventory-grid-block {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    height: 32rem;
    /* Small gap below the page title — matches the app's 0.75rem title-to-content rhythm
       (see .password-policy-hint). Scoped to this inventory-only class so no other
       .title-content-text page is affected. */
    margin-top: 0.75rem;
}

/* Asset detail (B2) read view */
.asset-detail-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.asset-detail-title {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex: 1 1 auto;
    min-width: 0;
}

.asset-detail-description {
    color: var(--mud-palette-text-secondary);
    margin: 0 0 1.5rem;
}

/* A failed asset's banner (FF-3508): the failure on the left, Retire / Raise Repair PO on the right. */
.asset-detail-failure .mud-alert-message {
    flex: 1 1 auto;
}

.asset-detail-failure-body {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem;
}

.asset-detail-failure-actions {
    display: flex;
    gap: 0.5rem;
}

.asset-detail-card {
    padding: 1rem 1.25rem;
    height: 100%;
}

.asset-detail-card-title {
    font-weight: 600;
    margin-bottom: 0.5rem;
}

/* Label column then value column — the classic definition-list two-up. */
.asset-detail-list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.35rem 1rem;
    margin: 0;
}

.asset-detail-list dt {
    color: var(--mud-palette-text-secondary);
}

.asset-detail-list dd {
    margin: 0;
}

/* Branded dialog: a steel-tint header with the title in navy, hairline sections, and a
   footer. The same steel wash as the PO viewer's line headers, mixed from palette variables so it
   follows AppTheme (dark mode included). Rows inside pad to the header's gutter so everything shares
   one left edge. Applied with Class="eb-dialog" on a MudDialog. */
.eb-dialog {
    --eb-dialog-gutter: 1.375rem;
    --eb-dialog-steel: color-mix(in srgb, var(--mud-palette-tertiary) 25%, var(--mud-palette-surface));
    --eb-dialog-rule: color-mix(in srgb, var(--mud-palette-tertiary) 45%, var(--mud-palette-surface));
    overflow: hidden;
}

.eb-dialog .mud-dialog-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.875rem var(--eb-dialog-gutter);
    background-color: var(--eb-dialog-steel);
    border-bottom: 1px solid var(--eb-dialog-rule);
}

.eb-dialog-title {
    margin: 0;
    font-family: var(--mud-typography-h6-family);
    font-size: 1.05rem;
    font-weight: 500;
    color: var(--mud-palette-primary);
}

.eb-dialog-subtitle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}

/* How many things the dialog acts on, beside the title. */
.eb-dialog-count {
    min-width: 1.5rem;
    padding: 0.1rem 0.6rem;
    border-radius: 999px;
    text-align: center;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mud-palette-primary-text);
    background-color: var(--mud-palette-primary);
}

/* The padded body a form's fields sit in, on the same gutter as the header and rows. */
.eb-dialog-body {
    padding: 1rem var(--eb-dialog-gutter);
}

/* Edge-to-edge rows (a serial list, a checklist) set above the padded body. A long list scrolls on its
   own, so a form's fields below it stay in view. */
.eb-dialog-rows {
    max-height: 15rem;
    overflow-y: auto;
}

/* Rows ruled off from the body below them. */
.eb-dialog-rows + .eb-dialog-body {
    border-top: 1px solid var(--eb-dialog-rule);
}

/* A serial list's description takes the slack, pushing any detail (e.g. the bin) to the right. */
.eb-serial-list-description {
    flex: 1 1 auto;
}

/* A confirm's message, beside the tag it names. */
.eb-dialog-message {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.eb-dialog .mud-dialog-content {
    padding: 0;
    max-height: 60vh;
    overflow-y: auto;
}

.eb-dialog-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-height: 3rem;
    padding: 0.5rem var(--eb-dialog-gutter);
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.eb-dialog-row:last-child {
    border-bottom: none;
}

.eb-dialog-row-secondary {
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}

/* A returned tool's row: serial, detail and its Pass/Fail toggle on one line, the failure note wrapping below. */
.eb-return-row {
    flex-wrap: wrap;
    row-gap: 0.25rem;
}

.eb-return-row .eb-return-note {
    flex-basis: 100%;
    margin-top: 0;
}

/* A plain MudIcon is narrower than a dense checkbox; pad it so the labels line up. */
.eb-dialog-row > .mud-icon-root {
    margin: 0 0.4rem;
}

.eb-dialog .mud-dialog-actions {
    gap: 0.5rem;
    padding: 0.75rem var(--eb-dialog-gutter);
    border-top: 1px solid var(--eb-dialog-rule);
}

/* A serial number as a stamped ID tag — navy outline on a steel tint — so serials read as the
   physical tools they name. */
.eb-serial-tag {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--mud-palette-primary);
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
    color: var(--mud-palette-primary);
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 18%, var(--mud-palette-surface));
}

@media (max-width: 600px) {
    .eb-dialog-row {
        flex-wrap: wrap;
        row-gap: 0.25rem;
    }
}

/* ============================================================================================
   Entity page vocabulary: List -> dense read-only View -> separate Editor.

   The shared layout classes for every entity detail (view) and entry (editor) page, promoted
   here from the Job pages' scoped CSS so all entities read as one system rather than as
   look-alike copies. Global rather than *.razor.css because the point is reuse across pages, and
   a global sheet reaches MudBlazor's rendered elements with a plain descendant selector — the
   ::deep combinator the scoped originals needed only works inside a component's own .razor.css.

   See docs/Coding-Standards.md "Entity Page Structure" for when and how to use these.
   ============================================================================================ */

/* ---- Detail (read-only view) --------------------------------------------------------------- */

/* Title on the left, actions (Edit, lifecycle) on the right, sharing one line — the same corner
   the list's "New" action sits in, so the primary action is in the same place on both screens. */
.eb-detail-header-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* The record's identifier as the page title: the heading face, bold and navy, so it reads as the
   page's name. Added beside .title-content-text, which keeps the size. */
.eb-page-title {
    font-family: var(--mud-typography-h1-family);
    font-weight: 700;
    color: var(--mud-palette-primary);
}

/* The line under the title naming the record the way its list row does — what it is, its state
   chips, then a few facts — on the detail page and its editor alike. */
.eb-identity {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: -0.5rem 0 1.5rem;
}

.eb-identity-main {
    font-size: 1.125rem;
    font-weight: 700;
}

/* Semantic token rather than a raw grey, for the AA contrast reason .eb-detail-list dt gives. */
.eb-identity-meta {
    color: var(--mud-palette-text-secondary);
}

/* The action cluster (Edit + any lifecycle/delete buttons) travels together. */
.eb-detail-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

/* The field groups: fixed columns rather than auto-fit, with align-items:start so a short card
   (few fields) is not padded out to the height of a tall one beside it. minmax(0, 1fr) so a long
   value cannot force a column wider than its share. */
.eb-detail-groups {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}

/* A column is a stack, so its cards are content-height rather than sharing one height. */
.eb-detail-column {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

.eb-detail-group {
    min-width: 0;
}

.eb-detail-group .mud-card {
    width: 100%;
}

/* Spans whatever column count is in effect — 1 / -1 rather than a number because that count
   changes at the breakpoints below. For full-width groups such as long notes. */
.eb-detail-group-wide {
    grid-column: 1 / -1;
}

/* Every section title on an entity page — a field group's, a grid's or a stacked card's — in the
   heading face in navy, as .eb-dialog-title sets a dialog's title, so it reads as a heading rather
   than as one more line of field text. */
.eb-section-title,
.eb-detail-group .eb-detail-group-title,
.eb-entry-group .eb-entry-group-title {
    font-family: var(--mud-typography-h6-family);
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.5;
    color: var(--mud-palette-primary);
}

/* Two columns at tablet width, one at phone — matching the entry form and the Job pages so the
   whole app breaks at the same widths. */
@media (max-width: 1279px) {
    .eb-detail-groups {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 767px) {
    .eb-detail-groups {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The label/value field grid inside each card: label column sized to content, value column taking
   the rest. Left-aligned values (names, grades) scan better than right-aligned against a ragged
   left edge. */
.eb-detail-list {
    display: grid;
    grid-template-columns: minmax(7rem, auto) 1fr;
    gap: 0.5rem 1.5rem;
    margin: 0;
}

.eb-detail-list dt {
    color: var(--mud-palette-text-secondary);
    font-weight: 400;
}

.eb-detail-list dd {
    margin: 0;
}

/* Numeric values: tabular-nums keeps digits on a common width so figures line up by place value. */
.eb-detail-figure {
    font-variant-numeric: tabular-nums;
}

/* Free-text values: pre-line preserves typed line breaks while collapsing runs of spaces;
   overflow-wrap so an unbroken token (a long URL) cannot push the card past its column. */
.eb-detail-longtext {
    white-space: pre-line;
    overflow-wrap: anywhere;
}

/* The outer "Details" card that holds one outlined sub-card per field group, so the nesting reads as
   one card containing several rather than a row of cards competing for the same emphasis (the Job
   detail's treatment). Its own margin sets it off from the header row above. */
.eb-detail-details {
    margin-bottom: 1.5rem;
}

/* Top-level cards stacked below the Details card (Lines, Receiving, Documents) — the same 1.5rem
   rhythm the Details wrapper sets, so they do not sit flush. Margins collapse with the wrapper's
   margin-bottom, so the Details-to-first-card gap stays 1.5rem rather than doubling. */
.eb-detail-stacked-card {
    margin-top: 1.5rem;
}

/* MudCard's header-actions slot lays its children out with no gap, so Edit and Delete would sit flush.
   Scoped to the Details card so the sub-card headers (which carry no actions) are not affected. */
.eb-detail-details .mud-card-header-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Sub-cards read as separate objects from their thin outline alone (MudCard Outlined="true"), on the
   same white ground as the parent Details card — no fill. Sub-card headers carry far less weight than
   the Details heading above them, so their padding is tightened. */
.eb-detail-group .mud-card-header {
    padding-bottom: 0.25rem;
}

/* A card whose header is just a title: pull the body up under it instead of MudCard's default
   header-bottom + content-top padding (32px of air between a title and its first field). */
.eb-compact-card .mud-card-header {
    padding-bottom: 0;
}

.eb-compact-card .mud-card-content {
    padding-top: 0.5rem;
}

/* ---- Entry (editor) ------------------------------------------------------------------------ */

/* The validation summary. Styled on the list rather than the wrapper so a clean form shows no
   empty red box: ValidationSummary emits no <ul> until there is something to report. */
.eb-entry-validation .validation-errors {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--mud-palette-error);
    border-radius: 4px;
    background-color: var(--mud-palette-error-hover);
}

.eb-entry-validation .validation-message {
    /* The error colour itself, not error-text: error-text is the contrast colour for text on a solid error
       fill (white), which is illegible on the light error-hover background this banner uses. */
    color: var(--mud-palette-error);
}

/* Responsive line rows for the Tubular Specification editor (TubularSpecLinesEditor / LineFields): fields
   flow on one row on wide screens and wrap as the screen narrows, with the remove control kept at the
   row's end. (The PO editor uses the .eb-po-ledger grid below instead.) */
.eb-po-lines {
    display: flex;
    flex-direction: column;
}

.eb-po-line {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 6px 10px;
    padding: 8px 6px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.eb-po-field {
    flex: 1 1 auto;
    min-width: 0;
}

.eb-po-field.del { flex: 0 0 auto; align-self: center; }

/* PO editor lines as a ledger: one header row of column labels, then one row per line where every cell
   is the same outlined box. Read-only cells (N/A, line total, an inspection line's description) keep the
   box, greyed, so the grid never breaks. The totals sit under the Line Total column. Columns flex from a
   floor; when the card itself is too narrow for the floors (a container query, so a collapsed nav counts)
   rows fall back to stacked fields labelled from each cell's data-label. */
.eb-po-ledger {
    --eb-po-ledger-columns: minmax(120px, 1.5fr) minmax(120px, 1.5fr) minmax(80px, 1fr) minmax(120px, 2.2fr)
        minmax(72px, 0.8fr) 56px minmax(88px, 1fr) 36px minmax(92px, 1fr) 32px;
    container-type: inline-size;
    display: flex;
    flex-direction: column;
}

.eb-po-ledger-row {
    display: grid;
    grid-template-columns: var(--eb-po-ledger-columns);
    gap: 6px;
    align-items: start;
    padding: 4px 0;
}

/* The same steel-tint header as the PO viewer's lines grid (.eb-po-view-lines), so the two pages match. */
.eb-po-ledger-head {
    align-items: center;
    padding: 6px 8px;
    border-radius: 4px;
    margin-bottom: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mud-palette-primary);
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 25%, var(--mud-palette-surface));
    border-bottom: 1px solid color-mix(in srgb, var(--mud-palette-tertiary) 45%, var(--mud-palette-surface));
}

.eb-po-ledger-head .num { text-align: right; }
.eb-po-ledger-head .center { text-align: center; }

.eb-po-ledger .cell { min-width: 0; }
.eb-po-ledger .cell.center,
.eb-po-ledger .cell.del { align-self: center; justify-self: center; }

/* One input size for every cell — no Dense variants mixed with full-size fields. */
.eb-po-ledger .mud-input-control { margin-top: 0; }
.eb-po-ledger .num input { text-align: right; }

.eb-po-ledger .mud-input-adornment .mud-icon-button { padding: 2px; }
.eb-po-ledger .mud-input-adornment .mud-icon-root { font-size: 1.1rem; }

/* Read-only cells keep their box but read as not editable. */
.eb-po-ledger .eb-ro .mud-input {
    background-color: var(--mud-palette-background-gray);
}

.eb-po-ledger .eb-ro input {
    color: var(--mud-palette-text-secondary);
}

.eb-po-ledger .num .eb-ro input {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
}

.eb-po-ledger-row.eb-line-error {
    background-color: var(--mud-palette-error-hover);
    box-shadow: inset 3px 0 0 0 var(--mud-palette-error);
    border-radius: 2px;
}

/* Subtotal / Total: labels span the Unit Price + Tax columns, figures sit in the Line Total column with
   the same right inset as the boxes above, so the eye runs straight down to the total. */
.eb-po-ledger-totals {
    border-top: 1px solid var(--mud-palette-lines-default);
    margin-top: 4px;
    padding-top: 8px;
    row-gap: 2px;
    align-items: baseline;
    font-size: 0.875rem;
}

.eb-po-ledger-totals .label {
    grid-column: 7 / 9;
    text-align: right;
    color: var(--mud-palette-text-secondary);
}

.eb-po-ledger-totals .value {
    grid-column: 9 / 10;
    text-align: right;
    padding-right: 14px;
}

.eb-po-ledger-totals .total {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
    font-size: 1rem;
}

@container (max-width: 879px) {
    /* Two classes so it outranks the stacked .eb-po-ledger-row display below. */
    .eb-po-ledger-row.eb-po-ledger-head { display: none; }

    .eb-po-ledger-row {
        display: flex;
        flex-wrap: wrap;
        gap: 6px 10px;
        padding: 8px 0;
        border-bottom: 1px solid var(--mud-palette-lines-default);
    }

    .eb-po-ledger .cell { flex: 1 1 140px; }
    .eb-po-ledger .cell.center,
    .eb-po-ledger .cell.del { flex: 0 0 auto; }

    .eb-po-ledger .cell[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: 0.7rem;
        color: var(--mud-palette-text-secondary);
    }

    .eb-po-ledger-totals {
        display: grid;
        grid-template-columns: 1fr auto;
        border-bottom: none;
    }

    .eb-po-ledger-totals .label { grid-column: 1; }
    .eb-po-ledger-totals .value { grid-column: 2; padding-right: 0; }
}

/* The PO type toggle locks once the PO has lines; keep the chosen type legible while locked rather than
   greying both options out equally. !important because MudBlazor's disabled-button colours are. */
.eb-po-type-toggle .mud-toggle-item-selected:disabled {
    background-color: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary-text) !important;
    opacity: 0.75;
}

/* The PO viewer's lines: the editor's column order, figures right-aligned in tabular numerals, and the
   totals in the footer under the Line Total column. Description takes the slack; the rest size to content. */
/* Steel-tint header: a light wash of the theme's tertiary (steel blue) over the card surface, labels in
   the primary colour. Mixed from palette variables so it follows AppTheme, dark mode included. */
.eb-po-view-lines.mud-simple-table table thead th {
    white-space: nowrap;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mud-palette-primary);
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 25%, var(--mud-palette-surface));
    border-bottom: 1px solid color-mix(in srgb, var(--mud-palette-tertiary) 45%, var(--mud-palette-surface));
}

.eb-po-view-lines .num { text-align: right; font-variant-numeric: tabular-nums; }
.eb-po-view-lines .center { text-align: center; }
.eb-po-view-lines .select { width: 44px; }
.eb-po-view-lines .nowrap { white-space: nowrap; }
.eb-po-view-lines .item { min-width: 130px; }
.eb-po-view-lines .desc { min-width: 140px; }

/* Eleven columns: halve MudSimpleTable's 16px side padding so they fit without a scrollbar. */
.eb-po-view-lines.mud-simple-table table th,
.eb-po-view-lines.mud-simple-table table td {
    padding-left: 8px;
    padding-right: 8px;
}
.eb-po-view-lines td.total { font-weight: 600; }

/* What failed, under a failed tool's status chip. */
.eb-po-view-lines .eb-po-failure-note {
    max-width: 14rem;
    margin-top: 0.25rem;
    white-space: normal;
    text-align: left;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}

/* Specific enough to beat MudSimpleTable's own cell border, so the totals read as one block. */
.eb-po-view-lines.mud-simple-table table tfoot tr td {
    border-bottom: none;
    padding-top: 2px;
    padding-bottom: 2px;
}

/* MudSimpleTable drops the last body row's bottom border; rule the top of the totals instead so the
   lines visibly end before Subtotal/Total begin (the editor's totals divider). */
.eb-po-view-lines.mud-simple-table table tfoot tr:first-child td {
    border-top: 1px solid var(--mud-palette-lines-default);
    padding-top: 10px;
}
.eb-po-view-lines tfoot .label { color: var(--mud-palette-text-secondary); }

.eb-po-view-lines tfoot tr.grand td {
    font-size: 1rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

/* The PO editor's Ship-To summary: the business unit's address as plain text until overridden. */
.eb-po-shipto-summary {
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
}

/* The Tubular Specification line's fields (TubularSpecLinesEditor). */
.eb-po-field.eb-spec-size { flex-basis: 100px; }
.eb-po-field.eb-spec-weight { flex-basis: 100px; }
.eb-po-field.eb-spec-grade { flex-basis: 110px; }
.eb-po-field.eb-spec-connection { flex-basis: 180px; }
.eb-po-field.eb-spec-oem { flex-basis: 140px; }
.eb-po-field.eb-spec-count { flex-basis: 95px; }
.eb-po-field.eb-spec-location { flex: 2 1 180px; }

/* In the line dialog the fields get a roomier form layout, three to a row, instead of the line
   list's single compact row. */
.eb-spec-dialog .eb-po-line { border-bottom: none; }
.eb-spec-dialog .eb-po-field { flex: 1 1 30%; }

/* Tighter adornments so a selected value is readable rather than truncated behind the dropdown arrow /
   clear (X) adornments. */
.eb-po-lines .mud-input-adornment .mud-icon-button {
    padding: 2px;
}

.eb-po-lines .mud-input-adornment .mud-icon-root {
    font-size: 1.1rem;
}

/* A line whose picked asset is no longer inspection-eligible — highlighted live while editing so the
   user can fix it in place instead of waiting for a save to fail. */
/* The editor's field groups, laid out like the detail page's so a field is edited in the position
   it was read. Same fixed columns, same breakpoints, same align-items:start. */
.eb-entry-groups {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}

.eb-entry-column {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

.eb-entry-group {
    min-width: 0;
}

.eb-entry-group .mud-card {
    width: 100%;
}

.eb-entry-group-wide {
    grid-column: 1 / -1;
}

@media (max-width: 1279px) {
    .eb-entry-groups {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 767px) {
    .eb-entry-groups {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Save on the right, Cancel beside it. Sticky so the pair stays reachable on a tall form without
   scrolling back down; the surface colour keeps the fields from showing through behind it. */
.eb-entry-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    position: sticky;
    bottom: 0;
    padding: 0.75rem 0;
    background-color: var(--mud-palette-background);
}

/* Supporting text in the theme's secondary colour — e.g. the when-and-who under a returned line's status,
   or the note under an asset's move without inspection. */
.eb-secondary-text {
    color: var(--mud-palette-text-secondary);
}

/* Where a moved serial will land, under the job picker in the Move to Another Job dialog. */
.move-to-job-landing {
    margin: 0.5rem 0 1rem;
    padding: 0.5rem 0.75rem;
    border: 1px dashed var(--eb-dialog-rule);
    border-radius: 6px;
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 10%, var(--mud-palette-surface));
}

/* ---- Job page tabs and layouts -----------------------------------------------------------------
   Shared by the job detail page and the job edit form, so a field is edited in the place it was read.
   Global rather than scoped: MudTabs renders its own root, which never carries a page's scope.

   The strip sits on the page field and each panel holds outlined cards and grids, so the tab is a
   heading over its content rather than another card around it. */
.job-tabs > .mud-tabs-tabbar {
    background-color: transparent;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    margin-bottom: 1rem;
}

.job-tabs > .mud-tabs-panels {
    background-color: transparent;
}

/* A tab's count badge, in the flow rather than floated past the label: MudTab's ripple clips the tab, so
   a badge hanging off the end was cut off. Raised so it still reads as a superscript. */
.job-tabs .mud-tab-badge .mud-badge-wrapper {
    position: static;
}

.job-tabs .mud-tab-badge .mud-badge {
    position: relative;
    inset: auto;
    top: -0.5rem;
}

/* Two columns, the main content wider than its companion; one on a narrow screen. */
.job-split {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
    margin-bottom: 1rem;
}

/* Job Info's four cards, each as tall as its own content. */
.job-columns {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1rem;
    align-items: start;
}

@media (max-width: 1279px) {
    .job-columns {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 767px) {
    .job-split,
    .job-columns {
        grid-template-columns: minmax(0, 1fr);
    }
}
