html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    background-color: var(--colorNeutralBackground2, #f6f7f9);
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.detail-card {
    flex: 1 1 360px;
    min-width: 280px;
    padding: 0;
    height: 100%;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
}

/* KPI card layout is intentionally global. These reusable cards are rendered inside
   FluentCard web components, and their critical flex layout must not depend on a
   component-isolation scope being attached during prerender/circuit hydration. */
.bvc-layout,
.ac-layout,
.tc-layout {
    display: flex;
    align-items: center;
    gap: 18px;
    height: 100%;
}

.bvc-body,
.ac-body,
.tc-body {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.bvc-icon,
.ac-icon,
.tc-icon {
    display: grid;
    place-items: center;
    width: 54px;
    height: 54px;
    flex: 0 0 54px;
    border-radius: 14px;
}

.bvc-title,
.ac-title,
.tc-title {
    display: block;
    font-size: 14px;
    line-height: 1.35;
}

.bvc-value,
.ac-value,
.tc-value {
    display: block;
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bvc-supporting,
.ac-alert,
.tc-trend {
    display: block;
    font-size: 13px;
    line-height: 1.35;
}

.summary-card {
    flex: 1 1 0;
    min-width: 180px;
    padding: 16px;
}

.card-header {
    display: flex;
    align-items: center;
    width: 100%;
    align-self: stretch;
    box-sizing: border-box;
    background: transparent !important;
    background-color: transparent !important;
    margin: 0 0 12px;
    min-height: 44px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--colorNeutralStroke2);
}

.card-header-text {
    display: block;
    flex: 1 1 auto;
    width: auto;
    margin: 0;
    text-align: left;
    align-self: center;
}

.card-header fluent-button {
    white-space: nowrap;
}


.card-body {
    padding: 0 16px 16px;
    flex: 1 1 auto;
}

.card-divider {
    margin: 0 0 12px;
}

.detail-row {
    align-items: center;
    gap: 16px;
    width: 100%;
    flex-wrap: wrap;
}

.detail-label {
    flex: 0 0 120px;
}

.detail-value {
    flex: 1 1 auto;
    text-align: left;
}

.contact-card {
    max-width: 100%;
    overflow-x: auto;
}

.company-logo-preview,
.company-logo-placeholder {
    width: 72px;
    height: 72px;
    flex: 0 0 auto;
    object-fit: contain;
    border: 1px solid var(--colorNeutralStroke2, #e0e0e0);
    border-radius: 4px;
    background: var(--colorNeutralBackground2, #fafafa);
    padding: 6px;
}

@media (max-width: 640px) {
    .detail-label,
    .detail-value {
        flex-basis: 100%;
    }
}

.button-success,
.button-danger,
.button-warning,
.button-info {
    color: #ffffff !important;
}

.button-success {
    background-color: #107c10 !important;
    border-color: #107c10 !important;
}

.button-danger {
    background-color: #d13438 !important;
    border-color: #d13438 !important;
}

.button-warning {
    background-color: #ffaa44 !important;
    border-color: #ffaa44 !important;
    color: #1a1a1a !important;
}

.button-info {
    background-color: #0078d4 !important;
    border-color: #0078d4 !important;
}

/* FluentAutocomplete's dropdown (<fluent-popover-b>) renders in a portal outside the component's
   own DOM subtree, so component-scoped (::deep) CSS can never reach it — only a global rule like
   this one can. <fluent-popover-b> itself is `display: contents` (confirmed via computed style:
   its getBoundingClientRect() is always 0x0x0x0), so it never establishes a box for a percentage
   width to resolve against; a % width on its children is silently a no-op. <fluent-field> (the
   popover's first real box-generating descendant) is what actually needs the explicit width — by
   default it shrink-wraps to whichever option's rendered text happens to be widest, which is why
   the dropdown default-renders much narrower than the search input above it. CSS anchor sizing
   (anchor-size(width)) was tried first and does nothing here — the library positions this popover
   via its own JS logic, not the native CSS anchor API, so there's no anchor-name link for
   anchor-size() to resolve against.
   Fixed px (not a relative unit) because it's the only thing proven to survive: matches the
   FluentAutocomplete Width="100%" usage inside this app's two Medium dialogs (New Job Order,
   Add Job Order Item) — if a much narrower or wider host is added later, revisit this. */
fluent-popover-b fluent-field {
    width: 500px !important;
    box-sizing: border-box !important;
}

/* Filter rows: a label-less search box beside labelled selects, bottom-aligned
   (<FluentStack Class="filter-row" VerticalAlignment="Bottom">). Two things push a labelled field's control
   above the row's bottom edge, measured in a browser (the search box has neither):
   1. Fluent's default Margin on every field is the overridable class "my-3-o" (12px above and below;
      see the FluentComponentBase.Margin docs), and bottom alignment lines up the margin box, so the
      control ends up 12px higher. Cancelled below (no !important needed: this selector out-ranks the
      single-class ".my-3-o").
   2. A labelled <fluent-field> is a 3-row grid ('label' 'input' 'message'), and the message row keeps
      its top margin + row-gap even when the message <div> is empty (~4px). The library already hides
      the empty message for label-position="before"; this does the same for the default "above" and
      drops the row from the grid so no gap is left. A real validation message makes the div
      non-empty, so this never hides one.
   Point 2 is limited to fields whose label sits ABOVE. A checkbox/switch/radio field puts its label
   beside the control (label-position="after"), with its own grid ('input label' / 'message message');
   overriding that would drop the label on top of the box. The margin reset (point 1) is harmless for
   those, so it applies to every field.
   .field-grid: same reset for a plain CSS grid/flex of form fields whose parent owns the spacing
   (gap), e.g. the item form's Basic Information and Units of Measurement blocks. Without it each field
   adds its own 12px margin and the fields sit at different heights (a text area, a text input and an
   autocomplete differ by a few px, and a checkbox beside a select lands ~15px low). */
:is(.filter-row, .field-grid) fluent-field {
    margin-block: 0;
}

:is(.filter-row, .field-grid) fluent-field:not([label-position='after'], [label-position='before'], [label-position='below']) {
    grid-template-areas: 'label' 'input';
}

:is(.filter-row, .field-grid) fluent-field:not([label-position='after'], [label-position='before'], [label-position='below']) > [slot='message']:empty {
    display: none;
}

/* <fluent-avatar> is a Web Component — before its JS registers/upgrades it, the browser has no
   shadow-DOM clipping to apply yet, so an avatar photo passed via Image (e.g.
   CurrentUserService.AvatarDataUri, a full-resolution uploaded photo) can flash at its full native
   pixel size for a frame or two, right as the interactive circuit takes over from the
   server-prerendered markup. :not(:defined) targets exactly that pre-upgrade window; once the
   element upgrades, its own real styling takes over and this stops matching. 64px covers the
   largest AvatarSize this app actually uses (ProfileSettings' Size64) — every other usage
   (Size24/32/40) just renders slightly smaller than its final size for that one frame, which is
   harmless since the whole point is only to bound it, not match it exactly.
   App.razor's #app-boot-loader is the other half of this fix — it covers the very first paint
   until <fluent-avatar> is confirmed defined, so this rule is mostly a safety net for any
   later-mounted avatar (e.g. opening a dialog) rather than the main fix for first load. */
fluent-avatar:not(:defined) {
    display: inline-flex;
    overflow: hidden;
    width: 64px;
    height: 64px;
    max-width: 64px;
    max-height: 64px;
    border-radius: 50%;
    vertical-align: middle;
}

fluent-avatar:not(:defined) img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Row "Actions" menus (e.g. Printing Services list) rendered narrower than their longest item,
   clipping labels ("Hide from customer portal" -> "Hide from custome"). FluentMenu v5 exposes no
   Width parameter, its default max-width doesn't actually let the list grow to fit a long item
   (the item content slot is flex-clamped), and the list renders in a popover outside the
   triggering component's DOM subtree so component-scoped (::deep) CSS can't reach it — same
   reason the FluentAutocomplete rule above is global. Give the list a wider floor so the longest
   labels this app uses fit without truncation. */
fluent-menu-list {
    min-width: 248px;
}

/* FluentMenu v5's popover anchors its LEFT edge to the trigger and opens rightward; its built-in
   position-try flip isn't kicking in reliably, so a row "Actions" (⋮) menu in the right-hand
   column of a wide table opens off the right edge of the viewport with its labels clipped.
   Right-align the popover to its trigger instead (open leftward — the conventional direction for
   an overflow menu), and keep a flip fallback so a menu near the LEFT edge still opens rightward.
   Light-DOM rules on the custom element win over its shadow `:host` styles; `anchor()` is only
   honoured on browsers with native CSS anchor positioning (Chromium/Edge) — elsewhere the whole
   declaration is dropped and Fluent's own fallback positioning stands, so this is safe. */
fluent-menu-list[popover] {
    inset-inline-start: auto;
    inset-inline-end: anchor(end);
    position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}

/* Flat cards app-wide: every FluentCard renders without a drop shadow, whatever its Shadow
   parameter says (and on hover for selectable cards). One global rule instead of
   Shadow="CardShadow.None" on each card, so new pages are flat by default. Overlays that are not
   cards (dialogs, drawers, menus, the mobile sidebar) keep their own shadows. */
.fluent-card,
.fluent-card:hover {
    box-shadow: none !important;
}

/* Reusable KPI cards (Components/Shared/KpiCards): one 16px corner radius for all of them, matching
   the dashboard panels. Has to live here, not in each card's .razor.css: the card class is passed to
   FluentCard (a child component) via Class="...", so the scoped rules never match the card element. */
.fluent-card.basic-value-card,
.fluent-card.alert-card,
.fluent-card.comparison-card,
.fluent-card.mini-chart-card,
.fluent-card.split-value-card,
.fluent-card.status-count-card,
.fluent-card.target-progress-card,
.fluent-card.trend-card {
    border-radius: 16px !important;
}

/* Topbar profile menu (AdminTopBar.razor, FluentPopover AnchorId="profile-trigger").
   FluentPopover v5's adjustDialogPosition() picks its horizontal side backwards: it opens
   rightward whenever there is room on the trigger's LEFT, so an anchor at the far right of the
   topbar always opens off-screen (exposed once the NAM AI button that used to sit to its right
   was removed). The positioned element is the shadow <div part="dialog">, styled inline by that
   JS, so !important is needed to win over the inline left. Pin it to the right edge instead. */
fluent-popover-b[anchor-id="profile-trigger"]::part(dialog) {
    left: auto !important;
    right: 12px !important;
}
