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

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--mud-palette-success);
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

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

/* Full-screen blocking scrim shared by the idle-logoff warning overlay below, the
   account submit/passkey busy overlays (account-submit.css), and the app-wide busy
   overlay directly below. One recipe so the dim level, stacking, and geometry
   cannot drift between them. Deliberately sets no `display`: each consumer
   controls its own visibility.

   The default stacking here is "above everything", which is right for a scrim that
   is itself the thing demanding attention — the idle-logoff warning, and the account
   pages, which are static SSR and host no dialogs. A scrim that merely reports work
   in progress must NOT use it; see .component-busy-overlay. */
.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;
}

/* App-wide busy overlay for actions that submit a plain form and wait on a full
   page reload (e.g. TenantSwitcher.razor's business-switch forms), revealed via
   submitIndicator.js's data-submit-indicator-target — same is-submitting toggle
   as the account busy overlays, just targeted at an element outside the form. */
.app-busy-overlay {
    display: none;
    /* Owns the color so MudProgressCircular's Color.Inherit resolves to it. Without this the
       spinner inherits the page's near-black ink and draws an arc nobody can see on a dimmed
       scrim, leaving the caption to carry the whole message. */
    color: var(--bs-white);
}

.app-busy-overlay.is-submitting {
    display: flex;
    flex-direction: column;
}

.app-busy-label {
    margin-top: 1rem;
    color: var(--bs-white);
    font-size: 1.1rem;
}

/* Busy overlay for interactive (InteractiveServer) components, which gate on a C#
   flag rather than submitIndicator.js. Conditionally rendered, so it is simply flex
   when present — same arrangement as .idle-timeout-overlay below. Composes
   .fullscreen-scrim and reuses .app-busy-label for its caption.

   Stacked ABOVE MudBlazor's dialog layer (+2 clears the dialog at 1400 and the
   popovers it hosts at 1401), so a save started from a dialog reports itself over
   that dialog. Below it the indicator is unreadable rather than polite: the scrim
   centers its spinner on the viewport and a centered dialog sits exactly on top of
   it, so the one element that says "working" is the one element hidden. Still well
   under .fullscreen-scrim's own 10000, which the idle-logoff warning keeps — that
   scrim must be able to interrupt a busy one. The fallback covers surfaces that
   render no MudThemeProvider. */
.component-busy-overlay {
    display: flex;
    flex-direction: column;
    /* Owns the color for the same reason as .app-busy-overlay above. */
    color: var(--bs-white);
    z-index: calc(var(--mud-zindex-dialog, 1400) + 2);
}

/* 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 {
    display: flex;
}

.idle-timeout-modal {
    background: var(--bs-body-bg);
    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;
    z-index: 1030;
    text-align: center;
    padding: 0.25rem 1rem;
    font-size: 0.6125rem;
    font-weight: 600;
    color: var(--bs-white);
    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%;
    }
}

/* MudDataGrid / MudTable scroll contract (.hs-grid-page / .hs-grid-card)
   ─────────────────────────────────────────────────────────────────────
   Structure only — no surface, border, radius or shadow here. The card is the page's own
   MudPaper (Elevation="1" Class="pa-0") that every grid already sits in, so these rules
   decide who owns the scrollbar and nothing else; the look stays MudBlazor's theme, which
   is what keeps an opted-in grid identical to one that has not been converted yet.

   The contract is a chain of definite heights. AuthenticatedLayout gives .mud-main-content a
   definite height and its own overflow-y, .page-body passes that down as a flex column, and
   every link below re-declares min-height: 0 so the next one can shrink against it. A flex
   item's automatic minimum size otherwise floors it at its content height: the grid keeps its
   natural height, nothing overflows the card, and the pane scrolls instead of the rows.
   Omitting min-height: 0 at any single link degrades the whole chain back to page scrolling,
   with nothing to see in the stylesheet or the DOM. Ported from ebp-example, whose site.css
   documents the same contract. */

/* The page's root element, so the card below has a definite height to shrink against. A page
   that renders straight into .page-body with no wrapper of its own needs nothing — its
   children are already that flex column's items. */
.hs-grid-page {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* The card. Grows to its rows and shrinks to the fold: basis auto measures the content, and the
   shrink — against the definite height the chain above supplies — is what bounds it. Not
   flex: 1, which would fill unconditionally and leave a four-row grid holding a viewport of
   blank card. overflow: hidden keeps the rows inside the paper's radius. */
.hs-grid-card {
    display: flex;
    flex-direction: column;
    flex: 0 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* Below this height the page header and toolbar can leave the grid a sliver, and because the
   card clips for its 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. The floor
   goes on the card, so it overflows the pane and the pane's own overflow-y takes over: this is
   what "the page scrolls once the viewport gets very short" is made of. Scoped to short
   viewports because at a normal height it would pad a small grid with blank card instead. */
@media (max-height: 700px) {
    .hs-grid-card {
        min-height: 12rem;
    }
}

/* A panel that cannot join the chain. JobRunsPanel sits in a MudGrid, and a wrapping flex row
   gives vertical shrink nothing reliable to resolve against, so a viewport-relative cap bounds
   the scroll region directly instead. Its rows scroll while the console page goes on scrolling
   the pane, which is the right behavior for a column of dashboard cards. */
.hs-grid-panel {
    display: flex;
    flex-direction: column;
    max-height: 60vh;
    overflow: hidden;
}

/* Expansion panels in the chain (.hs-grid-expand)
   ───────────────────────────────────────────────
   A card inside a MudExpansionPanel is six elements below the page rather than one: MudBlazor
   emits .mud-expansion-panels > .mud-expand-panel > .mud-expand-panel-content, and MudCollapse
   adds .mud-collapse-container > .mud-collapse-wrapper > .mud-collapse-wrapper-inner inside
   that. Each link has to carry the contract or the chain degrades to page scrolling, so these
   rules re-declare it — but only the panel marked .hs-grid-expand actually holds a card, and
   the rest of a page's panels are left alone. */
.hs-grid-page .mud-expansion-panels {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* MudBlazor sizes a panel flex: 1 0 100% for the row layout it assumes. In the column above,
   a basis of 100% that cannot shrink makes every panel a viewportful of its own. Content-sized
   instead, so a form is never cut off at its own Save button. */
.hs-grid-page .mud-expand-panel {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    min-height: 0;
}

/* The header is the one part that must never be squeezed: it is what reopens a shut panel. */
.hs-grid-page .mud-expand-panel-header {
    flex: 0 0 auto;
}

/* The panel holding a card is the one that shrinks, and the chain runs on through it. Every
   other panel in the column is content-sized, so this one absorbs all of the shrink on its own:
   without a floor a tall form squeezes it to nothing, and a zero-height card reads as offerings
   that failed to load. Past the floor it overflows the pane and the pane's own overflow-y takes
   over, the same way .hs-grid-card degrades on a short viewport. The floor is conditioned on
   MudBlazor's expanded class so a shut panel is still just its header. */
.hs-grid-page .mud-expand-panel.hs-grid-expand {
    flex: 0 1 auto;
}

.hs-grid-page .mud-expand-panel.hs-grid-expand.mud-panel-expanded {
    min-height: 16rem;
}

.hs-grid-page .hs-grid-expand .mud-expand-panel-content,
.hs-grid-page .hs-grid-expand .mud-collapse-wrapper,
.hs-grid-page .hs-grid-expand .mud-collapse-wrapper-inner {
    display: flex;
    flex-direction: column;
    flex: 0 1 auto;
    min-height: 0;
}

/* .mud-collapse-container keeps display: grid, deliberately. MudBlazor animates the collapse by
   transitioning grid-template-rows between minmax(0, 0fr) and minmax(0, 1fr), so overriding the
   display here costs the animation and leaves a panel that never closes — measured: it collapses
   the content to nothing instead. It only has to shrink as a flex item; the 1fr row track already
   passes a definite height down to the wrapper. */
.hs-grid-page .hs-grid-expand .mud-collapse-container {
    flex: 0 1 auto;
    min-height: 0;
}

/* An expanded MudCollapse makes its own wrapper a scroll container. That would put a second
   scrollbar around the card and the text above it; the card's own is the one that belongs to the
   rows, so this one stands down. Four classes to outrank MudBlazor's own two-class rule. */
.hs-grid-page .hs-grid-expand .mud-collapse-entered .mud-collapse-wrapper {
    overflow: hidden;
}

/* MudDataGrid and MudTable share every .mud-table* class — MudBlazor emits no structural
   .mud-data-grid rules — so one set of selectors covers both. A MudTable is its own paper and
   so carries .hs-grid-card itself; a descendant selector leaves that case to the card rules
   above rather than matching the element twice. */
.hs-grid-card .mud-table,
.hs-grid-panel .mud-table {
    display: flex;
    flex-direction: column;
    flex: 0 1 auto;
    min-height: 0;
    /* The card already paints the surface; painting it again here would double the tint of any
       translucent palette. */
    background-color: transparent;
}

/* The scroll port. 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. No floor here — the card above carries it, and one here only
   fights it, because the card clips and a floor inside merely pushes the rows past the clip.

   overflow-y auto rather than scroll: a permanent gutter on a grid short enough not to need one
   reads as a disabled scrollbar.

   Nothing sets a width here, and that is the point. MudBlazor's auto table layout lets the columns
   wrap and the table condense to the card, and its own overflow-x: auto on this element then
   scrolls sideways only once the columns cannot get any thinner — condense first, scroll last. The
   one thing that breaks it is ColumnResizeMode.Container, which writes an inline
   width: max-content here: a max-content box sizes every column to its longest unwrapped line, so
   the cells stop wrapping and every grid scrolls sideways whether it needed to or not. No grid
   declares that mode, and none should without accepting the trade. */
.hs-grid-card .mud-table-container,
.hs-grid-panel .mud-table-container {
    overflow-y: auto;
    border-radius: inherit;
    flex: 0 1 auto;
    min-height: 0;
}

/* Grid chrome must not absorb the shrink meant for the rows. The pagination row needs this most:
   MudBlazor leaves it display: initial and position: sticky, so as a flex item it would be
   squeezed instead of pinned below the rows. */
.hs-grid-card .mud-table-toolbar,
.hs-grid-card .mud-table-pagination,
.hs-grid-panel .mud-table-toolbar,
.hs-grid-panel .mud-table-pagination {
    flex: 0 0 auto;
}

/* The pinned header has to outrank the rows sliding under it. MudBlazor gives its sticky header
   cells z-index 2 and its buttons position: relative with z-index 2 as well — equal z-index, so
   paint order falls back to document order, and tbody comes after thead. The rows therefore win,
   and a scrolled row's action buttons draw on top of the header: measured on Manage Pharmacies,
   probing down the Actions column through the header band hit a row's button at two of five
   points. Only cells carrying positioned content show it, which is why it reads as the Actions
   column overflowing the table rather than as a stacking bug. One step is enough — nothing in the
   rows goes above 2, and popovers and tooltips portal out to the body.

   Keyed on MudBlazor's own sticky-header class rather than on .hs-grid-card, for two reasons: it
   applies only where a header is actually pinned, and it holds whether the card is the paper
   wrapping a MudDataGrid or a MudTable that is its own paper — a .hs-grid-card descendant selector
   misses the second, where the two classes land on one element. Five classes deep because
   MudBlazor's own rule is four and a tie would leave this depending on stylesheet order. */
.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-row .mud-table-cell {
    z-index: 3;
}

/* Boolean columns (.hs-col-flag)
   ─────────────────────────────
   A column whose cells only ever read "True" or "False" is sized entirely by its header, so a
   two-word title like "Email Confirmed" reserves far more width than the data needs and pushes
   later columns off screen. Shrink the column to its content and let the header wrap. */
.mud-table-cell.hs-col-flag {
    width: 1px;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
    white-space: normal;
}

/* MudBlazor locks scrolling behind a dialog by putting overflow: hidden on <body>, which stops
   working the moment .mud-main-content becomes 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;
}
