@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';

/* ============================================================
   Responsive breakpoints — single source of truth (P1.1)
   ------------------------------------------------------------
   Mobile-first: style the phone first, then enhance upward. These cutoffs
   mirror FluentUI Blazor's GridItem scale so CSS media queries and any
   C# FluentGrid usage agree on what "phone / tablet / desktop" mean.

     xs   < 600px           phone            -> @media (max-width: 599.98px)
     sm   600 - 959.98px    tablet portrait
     md   960 - 1279.98px   tablet / laptop
     lg   >= 1280px         desktop

   Rules of thumb for new media queries:
     - Phone-only tweak:        @media (max-width: 599.98px)
     - Below desktop (<= sm):   @media (max-width: 959.98px)
     - Touch devices, any size: @media (pointer: coarse)

   Intentional one-off cutoffs kept on purpose (do not "fix" these blindly):
     - 480px : Login 2-col -> 1-col split, finer than xs; auth page only.
     - 768px : data-grid horizontal-scroll band-aid; retired per page in P2.
   ============================================================ */

/* App-owned token layer. Every .razor/.css file styles with --dpab-*; only this block names the
   Fluent design tokens, so a Fluent theme change is a one-block edit. Declared on body because the
   alias resolves where it is declared. */
body {
    --dpab-text: var(--colorNeutralForeground1);
    --dpab-text-hint: var(--colorNeutralForeground3);
    --dpab-text-on-brand: var(--colorNeutralForegroundOnBrand);
    --dpab-brand: var(--colorBrandBackground);
    --dpab-brand-text: var(--colorBrandForeground1);
    --dpab-divider: var(--colorNeutralStroke2);
    --dpab-stroke: var(--colorNeutralStroke1);
    --dpab-stroke-hover: var(--colorNeutralStroke1Hover);
    --dpab-stroke-strong: var(--colorNeutralStrokeAccessible);
    --dpab-stroke-focus: var(--colorStrokeFocus2);
    --dpab-surface-1: var(--colorNeutralBackground1);
    --dpab-surface-2: var(--colorNeutralBackground2);
    --dpab-surface-3: var(--colorNeutralBackground3);
    --dpab-surface-4: var(--colorNeutralBackground4);
    --dpab-surface-floating: var(--colorNeutralBackground1);
    --dpab-fill: var(--colorNeutralBackground1);
    --dpab-fill-subtle: var(--colorNeutralBackground3);
    --dpab-fill-subtle-hover: var(--colorNeutralBackground3Hover);
    --dpab-fill-stealth: var(--colorTransparentBackground);
    --dpab-fill-strong: var(--colorNeutralForeground2);
    --dpab-danger: var(--colorStatusDangerForeground1);
    --dpab-warning: var(--colorStatusWarningForeground1);
    --dpab-success: var(--colorStatusSuccessForeground1);
    --dpab-info: var(--colorNeutralForeground2);
    --dpab-danger-fill: var(--colorStatusDangerBackground3);
    --dpab-danger-fill-light: var(--colorStatusDangerBackground1);
    --dpab-success-fill: var(--colorStatusSuccessBackground3);
    --dpab-success-fill-light: var(--colorStatusSuccessBackground1);
    --dpab-warning-fill: var(--colorStatusWarningBackground3);
    --dpab-font: var(--fontFamilyBase);
    --dpab-font-mono: var(--fontFamilyMonospace);
    --dpab-font-size-xs: var(--fontSizeBase100);
    --dpab-line-height-xs: var(--lineHeightBase100);
    --dpab-font-size-sm: var(--fontSizeBase200);
    --dpab-line-height-sm: var(--lineHeightBase200);
    --dpab-font-size-base: var(--fontSizeBase300);
    --dpab-line-height-base: var(--lineHeightBase300);
    --dpab-font-size-md: var(--fontSizeBase400);
    --dpab-line-height-md: var(--lineHeightBase400);
    --dpab-font-size-lg: var(--fontSizeBase500);
    --dpab-line-height-lg: var(--lineHeightBase500);
    --dpab-font-size-lg3: var(--fontSizeBase600);
    --dpab-line-height-lg3: var(--lineHeightBase600);
    --dpab-font-size-xl: var(--fontSizeHero700);
    --dpab-line-height-xl: var(--lineHeightHero700);
    --dpab-font-size-xl5: var(--fontSizeHero800);
    --dpab-line-height-xl5: var(--lineHeightHero800);
    --dpab-font-size-xl6: var(--fontSizeHero900);
    --dpab-line-height-xl6: var(--lineHeightHero900);
    --dpab-radius: var(--borderRadiusMedium);
    --dpab-radius-layer: var(--borderRadiusLarge);
    --dpab-stroke-width: var(--strokeWidthThin);
    --dpab-unit: 4px;
}

body {
    --body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
    font-family: var(--dpab-font);
    font-size: var(--dpab-font-size-base);
    line-height: var(--dpab-line-height-base);
    margin: 0;
}

.navmenu-icon {
    display: none;
}

.main {
    min-height: calc(100dvh - 86px);
    color: var(--dpab-text);
    align-items: stretch !important;
}

.body-content {
    flex: 1 1 auto;
    min-width: 0;
    align-self: stretch;
    height: calc(100dvh - 86px) !important;
    display: flex;
}

.content {
    padding: 0.5rem 1.5rem;
    align-self: stretch !important;
    width: 100%;
}

/* Shared responsive page container.
   Keeps the desktop max-width cap (set inline per page) but scales the
   horizontal padding down on tablets/phones so content uses the full
   available width instead of wasting it on fixed 24px gutters. */
.page-container {
    width: 100%;
    margin: 0 auto;
    padding: 24px;
    box-sizing: border-box;
}

/* Tablet (<= sm): trim the desktop gutters. Cutoff aligned to the md/sm
   boundary documented at the top of this file (was an ad-hoc 1024px). */
@media (max-width: 959.98px) {
    .page-container {
        padding: 16px;
    }
}

/* Phone (xs): reclaim nearly all horizontal space. */
@media (max-width: 599.98px) {
    .page-container {
        padding: 12px 8px;
        /* The container is a flex item centered in the page; min-width:0 lets it
           shrink to the viewport instead of being stretched to a wide child's
           min-content (e.g. a 720px data grid), which would scroll the whole page. */
        min-width: 0;
        max-width: 100% !important;
    }
}

/* ==============================================
   P3 — Detail-page section column (xs)
   ----------------------------------------------
   DetailPageLayout stacks its sections in a vertical FluentStack, which renders
   `align-items: start` inline — so cards size to their content (a narrow form
   card, an overflowing grid card) rather than the column width. Stretch them to
   full width, and let wide children shrink and scroll internally instead of
   widening the page.
   ============================================== */
@media (max-width: 599.98px) {
    .detail-sections {
        align-items: stretch !important;
        width: 100% !important;
        min-width: 0 !important;
    }

    .detail-sections > * {
        min-width: 0 !important;
        max-width: 100% !important;
    }
}

.manage {
    width: 100dvw;
}

/* The v4 footer was a semantic <footer>; v5 renders the shell footer as a FluentLayoutItem, so it carries .app-footer. */
footer,
.app-footer {
    display: flex;
    background: var(--dpab-surface-4);
    color: var(--dpab-text);
    align-items: center;
    padding: 10px 10px;
}

    footer a,
    .app-footer a {
        color: var(--dpab-text);
        text-decoration: none;
    }

        footer a:focus,
        .app-footer a:focus {
            outline: 1px dashed;
            outline-offset: 3px;
        }

        footer a:hover,
        .app-footer a:hover {
            text-decoration: underline;
        }

.alert {
    border: 1px dashed var(--dpab-brand);
    padding: 5px;
    margin: 10px 0;
}

.alert-danger {
    border: 1px dashed var(--dpab-danger);
}

.alert-success {
    border: 1px dashed var(--dpab-success);
}

.alert-warning {
    border: 1px dashed var(--dpab-warning);
}

#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;
    margin: 20px 0;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.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::before {
        content: "An error has occurred. "
    }

.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 20vh auto 1rem auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #e0e0e0;
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: #1b6ec2;
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

code {
    color: #c02d76;
}

/* Phone (xs): mobile nav drawer + single-column shell. Cutoff aligned to the
   xs/sm boundary documented at the top of this file (was an ad-hoc 600px, which
   overlapped the sm+ header rule at exactly 600px). */
@media (max-width: 599.98px) {
    .header-gutters {
        margin: 0.5rem 3rem 0.5rem 1.5rem !important;
    }

    [dir="rtl"] .header-gutters {
        margin: 0.5rem 1.5rem 0.5rem 3rem !important;
    }

    .main {
        flex-direction: column !important;
        row-gap: 0 !important;
    }

    /* Reduce body content gutters on phones to reclaim screen width. */
    .content {
        padding: 0.5rem 0.5rem;
    }

    /* Mobile nav becomes an overlay drawer so it floats over the page
       instead of pushing the content down when opened. */
    nav.sitenav {
        position: fixed;
        top: 50px;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1000;
        width: 100%;
        height: auto;
        overflow-y: auto;
        background: var(--dpab-surface-1);
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    }

    #main-menu {
        width: 100% !important;
    }

        #main-menu > div:first-child:is(.expander) {
            display: none;
        }

    .navmenu {
        width: 100%;
    }

    #navmenu-toggle {
        appearance: none;
    }

        #navmenu-toggle ~ nav {
            display: none;
        }

        #navmenu-toggle:checked ~ nav {
            display: block;
        }

    .navmenu-icon {
        cursor: pointer;
        z-index: 10;
        display: block;
        position: absolute;
        top: 15px;
        left: unset;
        right: 20px;
        width: 20px;
        height: 20px;
        border: none;
    }

    [dir="rtl"] .navmenu-icon {
        left: 20px;
        right: unset;
    }
}

/* ==============================================
   PWA: Standalone (installed) mode adjustments
   ============================================== */
@media (display-mode: standalone) {
    body {
        padding-top: env(safe-area-inset-top);
        padding-bottom: env(safe-area-inset-bottom);
        padding-left: env(safe-area-inset-left);
        padding-right: env(safe-area-inset-right);
    }

    html {
        overscroll-behavior: contain;
    }
}

/* FluentUI DataGrid: vertically center all cell content */
.fluent-data-grid td,
.fluent-data-grid th {
    display: flex;
    align-items: center;
}

/* Mobile: wide data grids scroll horizontally instead of clipping their
   right-hand columns/actions. The first column (usually the name/identifier)
   stays pinned so rows remain readable while scrolling sideways.

   FluentUI Blazor v4 renders <FluentDataGrid> as `<table class="fluent-data-grid">`
   with `display: grid`, `display: contents` rows, and real `<th>`/`<td>` cells
   (NOT the `<fluent-data-grid>` / `<fluent-data-grid-cell>` web-component tags).
   The grid already scrolls horizontally via its own `overflow-x: auto`, so we
   must NOT set `display: block` here (that would collapse the CSS grid). We only
   pin the first column. Verified live: the first cell stays at scrollLeft 0 while
   the grid scrolls sideways. Cells are transparent by default, hence the explicit
   opaque background so scrolled columns don't bleed through the pinned one. */
@media (max-width: 768px) {
    /* Actually make the grid scroll. Only the list-page grids set `overflow:auto`
       inline; in-detail sub-grids (Seats, Activity, Integrations, Documents, …) did
       not, so their fixed-px columns spilled out of the card ("partially out of
       bounds"). This contains them so the first column below can pin while scrolling. */
    table.fluent-data-grid {
        overflow-x: auto;
    }

    table.fluent-data-grid th:first-child,
    table.fluent-data-grid td:first-child {
        position: sticky;
        left: 0;
        z-index: 1;
        background: var(--dpab-surface-1);
        box-shadow: 1px 0 0 var(--dpab-divider);
    }
}

/* Table */

table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
}

    table td,
    table th {
        padding: var(--spacingVerticalS) var(--spacingHorizontalM);
        text-align: left;
        vertical-align: middle;
        border-bottom: var(--dpab-stroke-width) solid var(--dpab-divider);
    }

/* Screen-reader-only text (e.g. labels for icon-only header cells) */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ==============================================
   P1.2 — Touch-target floor (>= 44px hit area)
   ----------------------------------------------
   On touch devices the smoke test measured header controls at ~31-32px.
   WCAG / platform guidance is a 44px minimum. We size the *inner control*
   of FluentUI web components through their exposed `control` part — this is
   standard author ::part() styling (NOT Blazor scoped ::deep, which cannot
   pierce a web component's shadow DOM). Host-element fallbacks are added in
   case a given component does not expose that part. Plain HTML controls
   (<select>, nav <a>) are sized directly.

   Scope: `pointer: coarse` so mouse-driven desktops keep their dense layout.
   ============================================== */
@media (pointer: coarse) {
    fluent-button::part(control),
    fluent-anchor-button::part(control),
    fluent-link::part(control) {
        min-height: 44px;
    }

    /* Host-box fallback so the clickable element grows even where the
       `control` part name differs. */
    fluent-button,
    fluent-anchor-button,
    fluent-link {
        min-height: 44px;
    }

    /* Language picker is a plain <select> (no shadow DOM). */
    .language-selector select {
        min-height: 44px;
    }

    /* Nav links: stretch each row to a comfortable tap height. Includes the
       landing-page link cards and section headings, which are plain <a>s and so
       are matched by none of the fluent-* rules above. */
    .sitenav a,
    .navmenu a,
    .nav-card,
    .nav-section-heading {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    /* …except a nav card, whose icon must stay top-aligned next to a wrapping
       description; the min-height above still applies. */
    .nav-card {
        align-items: flex-start;
    }
}

/* ==============================================
   P1.3 — Mobile type & input ergonomics (xs)
   ----------------------------------------------
   1) Inputs render at >= 16px on phones so iOS Safari does not auto-zoom
      the page when a field gains focus.
   2) Headings drop one step at xs via mobile type tokens layered on top of
      the FluentUI type ramp.
   ============================================== */
@media (max-width: 599.98px) {
    body {
        --type-mobile-h1: 1.5rem;
        --type-mobile-h2: 1.25rem;
        --type-mobile-h3: 1.1rem;
    }

    /* >= 16px input text to defeat iOS focus-zoom. Targets the inner control
       of FluentUI fields plus any raw HTML inputs (FlowEditor, tables). */
    fluent-text-input::part(control),
    fluent-textarea::part(control),
    fluent-select::part(control),
    input,
    textarea,
    select {
        font-size: 16px;
    }

    body h1 {
        font-size: var(--type-mobile-h1);
        line-height: 1.25;
    }

    body h2 {
        font-size: var(--type-mobile-h2);
        line-height: 1.3;
    }

    body h3 {
        font-size: var(--type-mobile-h3);
        line-height: 1.3;
    }
}

/* ==============================================
   P1.4 — Dialogs fit the phone
   ----------------------------------------------
   Cap every FluentDialog so it can never exceed the viewport, and let it go
   near full-width on phones. Targets the dialog panel `control` part. The
   global max-width only *caps* oversized dialogs (it never stretches a small
   one), so it is safe at every width. */
fluent-dialog::part(control) {
    max-width: 96vw;
}

@media (max-width: 599.98px) {
    fluent-dialog::part(control) {
        width: 96vw;
        max-width: 96vw;
    }
}

/* ==============================================
   P1.5 — Raw (non-FluentDataGrid) table scroll container
   ----------------------------------------------
   Wrap a hand-rolled <table> in <div class="table-scroll"> so wide tables
   scroll horizontally inside their own box instead of forcing page-level
   horizontal overflow on phones. */
.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}

/* ==============================================
   DataInspector — the debugging table behind a chart
   ----------------------------------------------
   Deliberately plain and dense: this is a surface for reading many rows and
   comparing them, not a presentation view. Its container owns the scrolling
   (max-height + overflow in the component), so this only styles the grid itself.
   ============================================== */
.inspector-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.inspector-table th,
.inspector-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--dpab-divider);
    white-space: nowrap;
}

.inspector-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--dpab-surface-floating);
    text-align: left;
    font-weight: 600;
}

/* ==============================================
   P2 — Responsive list: phone card view
   ----------------------------------------------
   ResponsiveList swaps a horizontally-scrolling FluentDataGrid for a stacked
   card-per-row list on xs. These styles only apply to the markup that component
   renders on phones (the grid path is untouched on sm+).
   ============================================== */
/* One column on a phone, filling out to three on a wide screen. `auto-fill` with a min track does the
   breakpoints itself: at 390px one 343px card fits and nothing changes, and the cap keeps a 1400px
   section from spreading a two-field card across half a metre of white space. Card lists used to render
   only under 600px, where this is a single column and behaves exactly as the old flex column did. */
.responsive-card-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
    gap: 12px;
    width: 100%;
    align-items: start;
}

@media (min-width: 1280px) {
    /* Three across, not four: each card carries labelled editor rows, and a fourth column squeezes the
       340px track to where the label and its control collide again — the problem cards were adopted to
       solve. */
    .responsive-card-list {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.responsive-card-list-footer {
    display: flex;
    justify-content: center;
    margin-top: 16px;
}

.rl-pager {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Shared list/grid empty state (ListEmptyState) — the 48px-pad, hint-coloured, centred block the
   server-paged grids had each copied by hand and ResponsiveList<T> now shows for empty lists. */
.list-empty-state {
    padding: 48px;
    text-align: center;
    color: var(--dpab-text-hint);
}

/* Skeleton placeholders (ResponsiveList loading rows + DetailPageLayout loading header) so the layout
   holds while data loads instead of a centred ring that reflows the page. */
.rl-skeleton-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px;
    border: 1px solid var(--dpab-stroke);
    border-radius: 8px;
}

.detail-skeleton-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
}

/* FluentDataGrid <LoadingContent> skeleton — used by the three server-paged grids. */
.grid-loading-skeleton {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px;
}

/* Card header: title on the left, an optional status badge on the right. */
.rl-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    /* Let a wide badge drop to its own line instead of crushing the title. Without this the title keeps
       whatever strip the badge leaves and `word-break` snaps it mid-word — "FraktExport" rendered as
       "FraktE / xport". Only bites now that cards sit in narrow columns beside a long status chip. */
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
}

.rl-card-title {
    font-weight: 600;
    font-size: 1.05rem;
    line-height: 1.3;
    word-break: break-word;
}

.rl-card-header-end {
    flex-shrink: 0;
}

/* A save bar that must stay reachable inside a very long section. The `.form-actions` rule below sticks
   only under 600px, which is right for a form but not for a list of 50+ editable cards: on a desktop the
   footer ends up thousands of pixels under the card being edited. Opt in per section, and only while
   there are unsaved changes — a permanently floating bar just steals a strip of every viewport. */
.form-actions.form-actions-sticky {
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: var(--dpab-surface-1);
    box-shadow: 0 -2px 8px rgb(0 0 0 / 8%);
}

.rl-card-fields {
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* Cap how far a label can sit from its value. `.rl-field` is space-between, which is right on the
       390px phone this was built for. Card lists used to render ONLY under 600px, where the card is
       already narrower than this cap and it does nothing. The coverage section is the first to show
       cards on a desktop, and there an uncapped row threw the label and its control ~700px apart with
       nothing in between — two things that belong together, read as unrelated. */
    max-width: 640px;
}

/* Label left (muted), value right — wraps gracefully on narrow screens. */
.rl-field {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

/* Baseline alignment is right for a one-line value but drops the label to the bottom edge of a
   multi-line one, so a textarea row reads as if the label belongs to whatever sits below it. */
.rl-field:has(fluent-text-area) {
    align-items: flex-start;
}

.rl-field-label {
    color: var(--dpab-text-hint);
    font-size: 0.85rem;
    flex-shrink: 0;
}

.rl-field-value {
    text-align: right;
    word-break: break-word;
}

.rl-card-actions {
    margin-top: 14px;
}

/* ==============================================
   P3 — Forms & detail pages (xs)
   ----------------------------------------------
   .form-row     A two-column FluentStack field row (Orientation.Horizontal) that
                 stacks to one full-width column on phones. Tag the row with
                 Class="form-row". `!important` is required because FluentStack
                 sets flex-direction inline — same proven approach as `.main`.
   .form-actions A form's button footer. On phones it sticks to the bottom of the
                 viewport so Save stays reachable on long forms, and its buttons
                 stretch to a comfortable full-width tap target.
   ============================================== */
@media (max-width: 599.98px) {
    .form-row {
        flex-direction: column !important;
        /* FluentStack renders `align-items: start` inline (it does NOT stretch its
           children), and the row itself isn't stretched by its parent stack — so
           without these it collapses to content width (~50%) once stacked. Force
           the row to fill its container and stretch its columns. */
        align-items: stretch !important;
        width: 100% !important;
        gap: 16px !important;
    }

    /* Field columns take the full width and stop competing for horizontal space. */
    .form-row > * {
        flex: 0 0 auto !important;
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0;
    }

    .form-actions {
        position: sticky;
        bottom: 0;
        z-index: 5;
        background: var(--dpab-surface-1);
        /* !important so the safe-area inset wins over the footer's inline
           `padding: 16px 24px` (inline beats a normal stylesheet declaration).
           Without it the bar can sit under the iOS home-indicator gesture area. */
        padding-bottom: max(16px, env(safe-area-inset-bottom)) !important;
    }

    /* Cancel/Save share the row at a comfortable tap width. */
    .form-actions fluent-button {
        flex: 1 1 auto;
    }
}

/* ==============================================
   P4 — App shell: bottom navigation (xs)
   ----------------------------------------------
   A fixed bottom navigation bar surfaces the top-level routes on phones (the
   mobile-first hallmark). The hamburger drawer (NavMenu) stays for the long tail.
   Rendered by BottomNav only when ViewportState.IsPhone; these styles position it
   and reserve space so it never hides content, the footer, or the reconnect banner.
   ============================================== */
@media (max-width: 599.98px) {
    .bottom-nav {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 900;
        display: flex;
        justify-content: space-around;
        align-items: stretch;
        height: calc(56px + env(safe-area-inset-bottom));
        padding-bottom: env(safe-area-inset-bottom);
        background: var(--dpab-surface-1);
        border-top: 1px solid var(--dpab-divider);
        box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.08);
    }

    .bottom-nav-item {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        min-height: 44px;
        text-decoration: none;
        color: var(--dpab-text);
        font-size: 0.7rem;
    }

    .bottom-nav-item.active {
        color: var(--dpab-brand);
    }

    /* The bottom nav is fixed over the viewport bottom, and the scroll container is
       `.body-content` (overflow:auto, height-capped) — NOT the window — so a
       padding-bottom on `.content` alone never clears the bar. Shrink the scroll
       viewport by the nav height so its content can scroll fully clear of it. */
    .body-content {
        height: calc(100dvh - 86px - 64px - env(safe-area-inset-bottom)) !important;
    }

    /* A little breathing room above the bar. */
    .content {
        padding-bottom: calc(56px + env(safe-area-inset-bottom) + 16px);
    }

    /* The bottom nav replaces the footer chrome on phones. */
    .app-footer {
        display: none;
    }

    /* Keep the reconnect banner above the bottom nav — it stays critical during a
       disconnect but must not overlap or be hidden behind the bar. */
    #blazor-error-ui {
        bottom: calc(56px + env(safe-area-inset-bottom));
        margin: 0;
    }
}

/* ==============================================
   P4 — App shell: phone header (xs)
   ----------------------------------------------
   On phones the header collapses (rendered by AppHeader): the search box becomes an
   icon that opens a full-width overlay, and install/notification/language move into
   a "⋯" overflow panel — instead of a ~120px search squeezed next to three buttons.
   On sm+ AppHeader renders the original inline header and none of this applies.
   ============================================== */
@media (max-width: 599.98px) {
    .app-header-xs {
        display: flex;
        align-items: center;
        width: 100%;
        gap: 8px;
        padding: 8px 12px;
        box-sizing: border-box;
    }

    /* The open search fills the header row; its results dropdown positions against
       SearchBar's own .search-container, so nothing extra is needed for it here. */
    .app-header-xs-search {
        flex: 1;
        min-width: 0;
    }

    .app-header-xs-overflow {
        position: relative;
    }

    /* Dropdown panel anchored under the "⋯" button, holding the secondary actions
       stacked. Sits above the backdrop, which dismisses it on an outside tap. */
    .app-header-xs-panel {
        position: absolute;
        top: calc(100% + 8px);
        right: 0;
        z-index: 1001;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 4px;
        min-width: 200px;
        padding: 8px;
        background: var(--dpab-surface-1);
        border: 1px solid var(--dpab-divider);
        border-radius: 8px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
    }

    .app-header-xs-backdrop {
        position: fixed;
        inset: 0;
        z-index: 1000;
        background: transparent;
    }
}

/* ==============================================
   Navigation cards (NavCardGrid)
   ----------------------------------------------
   The landing-page counterpart of a side-menu link: one card per page, laid out by
   DashboardGrid. `height: 100%` is what keeps a row of cards the same height when
   one description wraps to two lines and its neighbours do not.
   ============================================== */
.nav-card {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    height: 100%;
    box-sizing: border-box;
    /* 44px floor: these are primary touch targets on a phone. */
    min-height: 44px;
    padding: 14px 16px;
    border: 1px solid var(--dpab-stroke);
    border-radius: var(--dpab-radius);
    background: var(--dpab-surface-1);
    color: var(--dpab-text);
    text-decoration: none;
    transition: background 0.1s ease-in-out, border-color 0.1s ease-in-out;
}

.nav-card:hover,
.nav-card:focus-visible {
    border-color: var(--dpab-brand);
    background: var(--dpab-surface-2);
}

.nav-card-icon {
    flex: 0 0 auto;
    margin-top: 2px;
}

.nav-card-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* Let a long description wrap instead of widening the grid column. */
    min-width: 0;
}

.nav-card-title {
    font-weight: 600;
}

.nav-card-description {
    color: var(--dpab-text-hint);
    font-size: 0.85rem;
    line-height: 1.35;
}

/* A section heading on the /admin hub. The whole icon+title row is the link, so it
   clears the 44px touch floor (reinforced in the `pointer: coarse` block above) —
   a bare text anchor would render about half that. */
.nav-section-heading {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    font-size: 1.1rem;
    font-weight: 600;
}

/* Failed-job rows on the /admin/monitoring summary card. Each row is a link into the Hangfire
   dashboard, so it carries the same 44px touch floor as the nav cards above. */
.hangfire-failure {
    display: flex;
    flex-direction: column;
    gap: 2px;
    box-sizing: border-box;
    min-height: 44px;
    padding: 8px 12px;
    border: 1px solid var(--dpab-stroke);
    border-radius: var(--dpab-radius);
    background: var(--dpab-surface-1);
    color: var(--dpab-text);
    text-decoration: none;
    /* Long exception text must wrap, never widen the card past the viewport. */
    min-width: 0;
    overflow-wrap: anywhere;
}

.hangfire-failure:hover,
.hangfire-failure:focus-visible {
    border-color: var(--dpab-brand);
    background: var(--dpab-surface-2);
}

.hangfire-failure-name {
    font-weight: 600;
    font-size: 0.875rem;
}

.hangfire-failure-time {
    font-size: 0.75rem;
    color: var(--dpab-text-hint);
}

.hangfire-failure-reason {
    font-size: 0.8125rem;
    color: var(--dpab-danger-fill);
}

/* ------------------------------------------------------------------------
   Freshworks section — "not linked" empty state.
   A state to act on, not a failure, so it reads as a panel with actions
   rather than a warning bar. Padding only: no fixed width, so it holds at
   390px like everything else on a detail page.
   ------------------------------------------------------------------------ */
.freshworks-empty {
    padding: 16px;
    margin: 8px 0 16px 0;
    border: 1px solid var(--dpab-stroke);
    border-radius: var(--dpab-radius);
    background: var(--dpab-surface-2);
}

.freshworks-empty code {
    background: var(--dpab-surface-3);
    padding: 1px 6px;
    border-radius: 3px;
    word-break: break-all;
}

/* Blazor FocusOnNavigate focuses the page <h1>; it is not interactive, so no focus ring. */
h1:focus {
    outline: none;
}

/* v5 header area is brand-coloured; the header's subtle buttons and icons resolve through
   colorNeutralForeground2, which is dark grey. Point it at white inside the header only
   (the search input keeps Foreground1). */
.app-header {
    --colorNeutralForeground2: #fff;
    --colorNeutralForeground2Hover: #fff;
    --colorNeutralForeground2Pressed: #fff;
    --colorNeutralForeground2Selected: #fff;
}

/* The v5 nav is only as tall as its items; give the whole sidebar column its background. */
.navmenu {
    background: var(--colorNeutralBackground3);
}

/* v5 dialogs are a fixed 600px. Inline dialogs that ask for a width do so with
   Style="--dialog-width: …" (FluentDialog turns that into a rule on the host); this makes the variable count. The [fuib] attribute out-ranks the library's own
   `fluent-dialog[fuib]::part(dialog)` rule, which its script-injected stylesheet loads after ours. */
fluent-dialog[fuib][style*="--dialog-width"]::part(dialog) {
    width: var(--dialog-width);
    max-width: 96vw;
}

/* A tall dialog (sample text, long forms) stays inside the viewport and scrolls its body. */
fluent-dialog[fuib][type]::part(dialog) {
    max-height: 92vh;
}

/* v5 badges are a fixed 20px, nowrap pill. Long labels (grant chips, statuses in narrow cells) must wrap
   and grow rather than spill out of the pill. */
fluent-badge {
    height: auto;
    min-height: 20px;
    white-space: normal;
    max-width: 100%;
}

/* v5 grid header cells do not clip: a long title in a narrow column ran into its neighbour. Ellipsis instead. */
.fluent-data-grid th {
    overflow: hidden;
}

.fluent-data-grid th > div {
    min-width: 0;
}

.fluent-data-grid th fluent-button[col-sort-button] {
    max-width: 100%;
    min-width: 0;
}

.fluent-data-grid th [col-title-text] {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* v5 nav category buttons centre their text, which shows once a long title wraps. */
.navmenu .fluent-navcategoryitem {
    text-align: start;
}
