/* Companion.Ui.Lists primitives - shared styling for the
   composable list primitives under Components/Ui/Lists/. Loaded
   globally from App.razor so consuming pages don't have to wire it
   per island. All selectors are intentionally low-specificity so
   pages can override via scoped CSS without resorting to !important. */

/* ------- Toolbar ------- */

.list-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    align-items: center;
    margin-block-end: var(--space-4);
}

.list-toolbar__start {
    flex: 1 1 16rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
}

.list-toolbar__middle {
    flex: 0 1 auto;
}

.list-toolbar__end {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-inline-start: auto;
}

/* When the toolbar wraps on a narrow screen, the end cluster's
   margin-inline-start: auto used to pin it to the RIGHT edge of its own
   line while the controls above sat LEFT - two lines, opposite
   alignments. Below the mobile breakpoint the trailing cluster becomes
   its own full-width, left-aligned row instead, so the whole toolbar
   reads as one left-anchored stack. */
@media (max-width: 599px) {
    .list-toolbar {
        /* Tighter row rhythm when the toolbar wraps - the stacked lines
           should read as one unit, not separated groups. */
        gap: var(--space-2) var(--space-3);
    }

    .list-toolbar__end {
        flex-basis: 100%;
        justify-content: flex-start;
        margin-inline-start: 0;
    }

    /* With the full-screen toggle living in the leading cluster, the
       trailing line is the row count alone - align its text flush with
       the control edge above instead of the desktop inline padding, and
       size it as the caption it is. */
    .list-toolbar__end .list-view__total-count {
        padding-inline: 0;
        font-size: var(--font-size-sm);
    }
}

/* ------- Search box ------- */

.list-search {
    display: flex;
    align-items: center;
    min-inline-size: 16rem;
    flex: 1 1 16rem;
}

.list-search__input {
    inline-size: 100%;
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    /* 16px (not --font-size-sm/14px) so iOS Safari does NOT zoom the
       viewport when the user taps the search box. The visual difference
       at desktop sizes is negligible; the mobile UX cost of zoom-on-
       focus is severe (every tap leaves the user pinched-out and
       reorienting). Same reasoning applied to filter-panel inputs. */
    font-size: var(--font-size-md);
}

/* ------- Column picker ------- */

.list-column-picker__trigger {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    color: inherit;
}

.list-column-picker__trigger-icon {
    display: inline-flex;
    align-items: center;
}

.list-column-picker__trigger-icon svg {
    fill: currentColor;
}

/* ------- Refresh ------- */

.list-refresh__trigger {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    color: inherit;
}

.list-refresh__trigger:disabled {
    cursor: progress;
    opacity: 0.6;
}

.list-refresh__trigger-icon {
    display: inline-flex;
    align-items: center;
}

.list-refresh__trigger-icon svg {
    fill: currentColor;
}

/* ------- Full-screen toggle ------- */
/* Not offered on a phone. The page is already very nearly the whole screen there, and below this
   breakpoint the table has stopped being a table at all - it stacks into per-row cards - so
   expanding buys a sliver of height and no width whatsoever. The same breakpoint the card layout
   uses, so the control disappears exactly when the thing it would expand no longer exists.
   Only the ENTRY point is hidden: the fixed exit button still renders for anyone who expanded on a
   larger screen and then rotated or resized down, so nobody can be trapped. */
@media (max-width: 599px) {
    .list-fullscreen__trigger {
        display: none;
    }
}

/* Same chrome as the refresh trigger so the list controls read as one set. */
.list-fullscreen__trigger {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    color: inherit;
}

/* Pinned to the overlay's top-right corner while expanded - the place a reader
   looks to get out of a full-window view. Not part of the toolbar in that
   state: the toolbar scrolls with the content on a narrow window, and an exit
   control that can scroll out of reach is a trap. */
.list-fullscreen__close {
    /* Fixed, not absolute: the overlay scrolls as one document, and an absolute
       exit control would scroll away with the content. */
    position: fixed;
    inset-block-start: max(0.75rem, env(safe-area-inset-top));
    inset-inline-end: 0.75rem;
    z-index: 1;
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-chrome-surface);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-chrome-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    box-shadow: var(--shadow-md);
}

.list-fullscreen__close:hover,
.list-fullscreen__close:focus-visible {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
}

.list-fullscreen__close-icon {
    display: inline-flex;
    align-items: center;
}

.list-fullscreen__close-icon svg {
    fill: currentColor;
}

.list-fullscreen__trigger-icon {
    display: inline-flex;
    align-items: center;
}

.list-fullscreen__trigger-icon svg {
    fill: currentColor;
}

/* The expanded list. A fixed overlay rather than the native Fullscreen API: it
   keeps the app's own surface tokens, theme and focus behaviour, and works
   identically in every browser. The z-index sits above page chrome but BELOW a
   native <dialog>, which renders in the top layer - so the filter and column
   panels still open over an expanded list. */
.list-view--fullscreen {
    position: fixed;
    inset: 0;
    z-index: 900;
    display: flex;
    flex-direction: column;
    /* Edge to edge: the point of expanding is to give the table every pixel, so
       no padding here - a gap would show the page behind and waste the width
       the operator asked for. Inner elements keep their own spacing. */
    margin: 0;
    padding: 0;
    background: var(--color-surface);
    /* auto, NOT hidden. In the normal case the table region below absorbs the leftover
       height, so the content is exactly one viewport tall and this never scrolls. It
       matters when a filter panel or the column picker opens: those are in-flow items
       frozen at content height, and `hidden` would clip a tall one with no way to reach
       the rest of it. `auto` keeps that recoverable at no cost to the normal case. */
    overflow: auto;
    /* Keeps a scroll gesture inside the overlay instead of chaining to the page
       behind it - the same job a body-scroll lock would do, without JS. */
    overscroll-behavior: contain;
}

/* The table region absorbs whatever height the toolbar and pager leave.
   min-block-size: 0 is the load-bearing declaration, not decoration: a flex item's
   min-height defaults to `auto`, which refuses to shrink below the content, so
   without it `flex: 1` computes to the full table height and the overlay overflows
   again no matter what overflow the parent is given. */
.list-view--fullscreen .list-view__scroll {
    flex: 1 1 auto;
    min-block-size: 0;
    overflow: auto;
}

/* The toolbar is the one thing that needs breathing room from the window edge,
   and room on the right so it never runs under the close button. */
.list-view--fullscreen > .list-toolbar {
    padding-inline: 0.75rem;
    padding-block-start: max(0.75rem, env(safe-area-inset-top));
    padding-inline-end: 11rem;
}

/* The floating horizontal scrollbar is suppressed while expanded. It exists because on
   a normal page the table's own horizontal bar sits at the bottom of a long list, out
   of reach without scrolling to the end. Expanded, the table region is a bounded
   scroller whose native bar is already on screen at the bottom of the region - so the
   proxy would duplicate a control that is right there, and being sticky to the overlay
   it would sit on top of the last row.
   !important because lists.js writes `display: block` as an INLINE style when it
   decides the proxy has earned its place, and an inline declaration outranks this
   rule - so without it the suppression documented above is advisory only, and the
   proxy reappears over the last row exactly when the layout is already tight. */
.list-view--fullscreen .list-view__hscroll {
    display: none !important;
}

/* The Load-more control is a flex SIBLING of the table region, and has to reserve its
   own height rather than be laid over the end of the list.
   `flex: 0 0 auto` is the load-bearing part: the default `flex-shrink: 1` puts the
   band into the shrink pool alongside the (very tall) scroll region, and a band
   squeezed below its content height leaves the button hanging over the rows behind
   it. Growing is refused for the same reason the scroller claims the leftover space.
   The margin becomes padding so the band's own surface - not a transparent gap -
   separates it from the last row, and the safe-area inset keeps the button clear of
   the iPhone home-indicator gesture zone. */
.list-view--fullscreen .list-view__load-more {
    flex: 0 0 auto;
    margin-block-start: 0;
    padding-block: var(--space-3) max(var(--space-3), env(safe-area-inset-bottom, 0px));
    padding-inline: 0.75rem;
    background: var(--color-surface);
    border-block-start: 1px solid var(--color-border-subtle);
}

/* Zero-height would still intersect, but a real pixel makes the observer's behaviour
   independent of how a browser treats an empty box. */
.list-view__load-more-sentinel {
    block-size: 1px;
}

/* ------- Toolbar action (e.g. report Export, editor Add row) ------- */
/* Anchor or button slotted into the toolbar via ListView's ToolbarStart (or
   by a bespoke list-shaped surface); matches the trigger buttons' size +
   (below) chrome-navy pill treatment so it reads as one of the list controls
   rather than a stray link. */
.list-toolbar__action {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

/* Button consumers can be disabled (anchors can't), and the shared pill
   :hover below matches disabled elements too - without these the dead
   control looks and lights up exactly like a live one. Mirrors the
   disabled treatment its sibling triggers already carry. */
/* A submit button hosted in the list toolbar (the editors' Save) must sit on
   the same line as its sibling triggers - the global app-shell submit rule
   adds a top margin meant for form layouts, which pushed Save down relative
   to the Filters/Columns/Refresh cluster. */
.app-shell__main .list-toolbar .list-toolbar__action[type="submit"] {
    margin-top: 0;
}

.list-toolbar__action:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

    .list-toolbar__action:disabled:hover {
        background: transparent;
        color: var(--color-chrome-surface);
    }

/* In-flow disclosure region below the toolbar - twin of
   .list-filter-panel__surface. A full-width block-flow element, so it
   never overflows the viewport on small screens (the old absolutely-
   positioned popover did). */
.list-column-picker__surface {
    margin-block: 0.5rem 1rem;
    padding: 1rem;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    background: var(--color-surface-raised);
    color: var(--color-text);
    box-shadow: var(--shadow-md);
}

.list-column-picker__surface[hidden] {
    display: none;
}

.list-column-picker__rows {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.list-column-picker__row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
}

.list-column-picker__row[draggable="true"]:hover {
    background: var(--color-surface-sunken);
    cursor: grab;
}

.list-column-picker__grip {
    color: var(--color-text-muted);
    cursor: grab;
}

.list-column-picker__visibility {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    text-align: start;
}

.list-column-picker__order {
    display: flex;
    gap: 0.125rem;
}

.list-column-picker__nudge {
    /* 44px target (WCAG 2.5.5 Target Size - Enhanced, AAA) - matches the
       rest of the toolbar's interactive controls (search, refresh, kebab
       trigger). The AA floor is 24px (2.5.8); the toolbar opts into 44px. */
    min-block-size: 2.75rem;
    min-inline-size: 2.75rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--color-border);
    border-radius: 0.25rem;
    background: var(--color-surface);
    /* The ↑ / ↓ glyphs are text, so without this they keep the UA's black
       ButtonText and all but vanish against the dark-theme surface. */
    color: var(--color-text);
    cursor: pointer;
}

.list-column-picker__nudge[disabled] {
    cursor: not-allowed;
    opacity: 0.4;
}

.list-column-picker__nudge:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
}

.list-column-picker__announce {
    margin-block-start: 0.25rem;
    margin-block-end: 0;
    /* Live-region status text - 14 px is the readable floor on Retina
       phones; 12 px (--font-size-xs) is too small for status content
       that operators may need to read at a glance. */
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.list-column-picker__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-block-start: 0.75rem;
    padding-block-start: 0.5rem;
    border-block-start: 1px solid var(--color-border-subtle);
}

.list-column-picker__action {
    min-block-size: 2.25rem;
    padding-inline: 0.875rem;
    border-radius: 0.375rem;
    border: 1px solid var(--color-border);
    cursor: pointer;
    font: inherit;
}

/* WCAG 2.4.7: the Reset/Apply buttons had no focus ring (browser default
   only), inconsistent with the nudge buttons above. */
.list-column-picker__action:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.list-column-picker__action--secondary {
    background: var(--color-surface);
    color: inherit;
}

.list-column-picker__action--primary {
    background: var(--color-brand);
    border-color: var(--color-brand);
    /* Theme/branding-aware: a tenant light-brand override would make
       hardcoded #fff unreadable (WCAG 1.4.3). */
    color: var(--color-text-on-accent);
}

.list-column-picker__action[disabled] {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ------- Header cells (shared) ------- */
/* Every <th> in a <ListView>'s <thead> carries .list-view__th - both
   sortable headers (which compose this with .list-sortable-header) and
   non-sortable ones (select column, actions column, columns without a
   SortKey). Keeps the row visually homogeneous regardless of column
   kind. */

.list-view__th {
    text-align: start;
    vertical-align: middle;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface-sunken);
}

.list-view__table td {
    padding: 0.5rem 0.75rem;
    vertical-align: top;
}

/* ------- Sortable header ------- */
/* Sort-only - filter chrome moved out to ListFilterPanel. Active
   columns render an up/down arrow next to the label; the aria-sort
   attribute carries the canonical direction for AT users. */

.list-sortable-header__sort {
    background: transparent;
    border: 0;
    padding-block: 0.125rem;
    padding-inline: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-align: start;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-block-size: 2.75rem;  /* WCAG 2.5.5 AA target size */
}

.list-sortable-header__sort:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
}

.list-sortable-header__arrow {
    color: var(--color-text-muted);
    font-size: 0.85em;
    font-weight: 400;
}

/* Neutral "sortable but not active" hint - dimmer than the active arrow
   so the active sort still reads as the dominant one. */
.list-sortable-header__arrow--inactive {
    opacity: 0.45;
}

.list-sortable-header__sort:hover .list-sortable-header__arrow--inactive {
    opacity: 0.8;
}

.list-sortable-header--active .list-sortable-header__sort {
    font-weight: 600;
}

/* `.visually-hidden` lives in app.css - single canonical definition. */

/* ------- Bulk action bar ------- */

.list-bulk-action-bar {
    position: sticky;
    inset-block-end: 0;
    z-index: var(--z-sticky);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    margin-block-start: 1rem;
    /* env(safe-area-inset-bottom) keeps the bar above the iPhone home
       indicator gesture-bar zone. Without this the dark strip sits
       under the swipe area and Apply / Cancel actions are partially
       obscured on Face-ID iPhones. The +0.75rem reserves the original
       internal padding on devices with no safe area. */
    padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: var(--color-text);
    color: var(--color-surface);
    border-radius: 0.5rem;
    box-shadow: var(--shadow-md);
}

.list-bulk-action-bar__status {
    margin: 0;
    flex: 1 1 auto;
}

/* WCAG 2.4.11 Focus Not Obscured (Minimum): the table has a sticky <th>
   header pinned to the top AND a sticky bulk-action bar pinned to the
   bottom of the scrollport, so a row control tabbed to could be scrolled
   behind either one. Reserve clearance on both sides so focus
   scroll-into-view stops short of the header (top) and the bar (bottom).
   Harmless on lists without a bar (only affects scroll alignment). */
.list-view__table tbody :is(a, button, input, select, [tabindex]) {
    scroll-margin-block-start: 3.5rem;
    scroll-margin-block-end: 5rem;
}

.list-bulk-action-bar__warning {
    color: var(--color-warning);
}

.list-bulk-action-bar__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.list-bulk-action-bar__deselect {
    background: transparent;
    border: 1px solid var(--color-surface);
    color: inherit;
    border-radius: 0.375rem;
    padding: 0.375rem 0.75rem;
    cursor: pointer;
    min-block-size: 2.75rem;
}

.list-bulk-action {
    min-block-size: 2.75rem;
    padding: 0.375rem 0.875rem;
    border-radius: 0.375rem;
    border: 1px solid transparent;
    cursor: pointer;
    background: var(--color-brand);
    color: var(--color-text-on-accent);
}

.list-bulk-action--destructive {
    background: var(--color-danger);
}

.list-bulk-action[disabled] {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ------- Row + select-all checkboxes -------
   Native checkboxes default to ~13px on iOS, ~16px on desktop browsers
   - far below WCAG AAA's 24×24 visual minimum and well below the
   44×44 touch target. Size the box at 24px (1.5rem) so the checkmark is
   visible to a thumb, and on coarse pointers stretch the surrounding
   tap area to 44×44 via outset padding (preserves the cell layout, no
   horizontal scroll on tight columns). */

.list-row-checkbox,
.list-select-all-checkbox {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    cursor: pointer;
}

/* Coarse-pointer (phones / tablets): inflate the hit area to 44×44
   without enlarging the rendered glyph or pushing siblings apart. The
   surrounding <td> uses 0.5rem padding so the net target ends up at
   ~2.75rem after the negative margin trick. */
@media (pointer: coarse) {
    .list-row-checkbox,
    .list-select-all-checkbox {
        min-inline-size: 2.75rem;
        min-block-size: 2.75rem;
    }
}

/* ------- Status cell ------- */

.list-status-cell__badge {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
    font-size: 0.875rem;
    border: 1px solid currentColor;
}

.list-status-cell__badge--on {
    color: var(--color-success);
    background: color-mix(in srgb, var(--color-success) 14%, var(--color-surface));
}

.list-status-cell__badge--off {
    color: var(--color-text-muted);
    background: transparent;
}

.list-status-cell__badge--na {
    color: var(--color-text-muted);
    background: transparent;
    border-style: dashed;
}

/* ------- Actions cell (kebab) ------- */

.list-actions-cell__menu {
    position: relative;
}

.list-actions-cell__trigger {
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
    color: var(--color-text);
    font: inherit;
    min-block-size: 2.75rem;
    min-inline-size: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.list-actions-cell__trigger:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
}

.list-actions-cell__panel[popover] {
    /* The popover is promoted to the top layer when open, so its
       containing block is the viewport. Clear the browser default
       centering and let the JS helper place us next to the trigger. */
    position: fixed;
    inset: auto;
    margin: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    padding: 0.25rem;
    min-inline-size: 12rem;
    box-shadow: var(--shadow-md);
    list-style: none;
}

.list-actions-cell__panel li {
    margin: 0;
}

.list-actions-cell__item {
    /* Flex-centred with an explicit min height so labels stay legible and
       un-clipped under 200% zoom / large-font preferences (WCAG 2.4.11),
       and the hit area matches the trigger button. */
    display: flex;
    align-items: center;
    min-block-size: var(--target-size-touch);
    inline-size: 100%;
    padding: var(--space-2) var(--space-3);
    border: 0;
    background: transparent;
    text-align: start;
    cursor: pointer;
    border-radius: 0.25rem;
    color: inherit;
    font: inherit;
    font-size: var(--font-size-sm);
}

.list-actions-cell__item:hover {
    background: var(--color-surface-sunken);
}

/* Outset focus ring (2px) to match every other list control - the popover
   is fixed-positioned in the top layer, so there is no overflow reason for
   the inset offset this rule previously used (WCAG 2.4.7 consistency). */
.list-actions-cell__item:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
    background: var(--color-surface-sunken);
}

.list-actions-cell__item--destructive {
    color: var(--color-danger);
}

/* Disabled controls are WCAG-contrast-exempt, but pair the dim with a
   muted text colour so the state reads at a glance; forced-colors gets an
   explicit GrayText cue. */
.list-actions-cell__item[disabled] {
    cursor: not-allowed;
    opacity: 0.6;
    color: var(--color-text-muted);
}

@media (forced-colors: active) {
    .list-actions-cell__item[disabled] {
        opacity: 1;
        color: GrayText;
    }
}

/* ------- Confirm dialog ------- */

.list-confirm-dialog {
    min-inline-size: 24rem;
    /* Bounded against the viewport so the dialog never overflows a phone. */
    max-inline-size: min(40rem, calc(100vw - 2rem));
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    /* A native <dialog> with no background falls back to the UA's Canvas/CanvasText
       system colours, which follow the OS `prefers-color-scheme` rather than this
       app's `[data-theme]` toggle - so on a light OS the dialog stayed white while
       the rest of the page was dark. Every other dialog in the solution sets these;
       this one did not. */
    background: var(--color-surface);
    color: var(--color-text);
    /* Modals are the most elevated surface in the app; without this they read as
       a flat rectangle pasted over the page. Shared with .details-dialog below so
       every native <dialog> in the solution sits at the same elevation. */
    box-shadow: var(--shadow-lg);
}

.list-confirm-dialog::backdrop {
    background: rgba(0, 0, 0, 0.4);
}

.list-confirm-dialog__form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1rem;
}

.list-confirm-dialog__title {
    margin: 0;
    font-size: 1.125rem;
}

.list-confirm-dialog__warning {
    margin: 0;
}

.list-confirm-dialog__input-label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

/* The prompt sentence ("Type X to confirm") - extra line-height gives the inline phrase chip room to breathe. */
.list-confirm-dialog__prompt {
    line-height: 1.9;
}

/* The exact phrase the operator must type, rendered as a distinct monospace chip inside the prompt so it stands out
   from the surrounding words and reads as verbatim text. user-select: all lets a single click select the whole phrase
   to copy it exactly. */
.list-confirm-dialog__phrase {
    font-family: var(--font-mono, ui-monospace, "Cascadia Code", Consolas, monospace);
    font-weight: 700;
    padding: 0.1rem 0.45rem;
    border-radius: 0.35rem;
    background: var(--color-surface-sunken);
    border: 1px solid var(--color-border);
    color: var(--color-brand);
    white-space: nowrap;
    user-select: all;
    -webkit-user-select: all;
}

.list-confirm-dialog__input {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
}

.list-confirm-dialog__error {
    color: var(--color-danger);
    margin: 0;
}

.list-confirm-dialog__actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
}

.list-confirm-dialog__cancel,
.list-confirm-dialog__confirm {
    min-block-size: 2.75rem;
    padding: 0.375rem 1rem;
    border-radius: 0.375rem;
    cursor: pointer;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    /* Without this a <button> keeps the UA's ButtonText - black - so Cancel
       rendered black-on-dark in dark mode. The Confirm button overrides both
       halves with the paired danger tokens below; Cancel needs the theme's own
       text colour. Set a background without a colour and one theme always loses. */
    color: var(--color-text);
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                filter var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-confirm-dialog__confirm {
    /* Danger surface/on-surface PAIR, not --color-danger + white: in
       dark mode --color-danger is a light red, so white text on it
       fails AA - the paired tokens stay AA in both themes. */
    background: var(--color-danger-surface);
    color: var(--color-danger-on-surface);
    border-color: var(--color-danger-surface);
}

/* Hover feedback matching the rest of the solution's buttons: the neutral
   Cancel lifts to the sunken surface, the danger Confirm darkens. */
.list-confirm-dialog__cancel:hover:not(:disabled) {
    background: var(--color-surface-sunken);
}

.list-confirm-dialog__confirm:hover:not(:disabled) {
    filter: brightness(0.92);
}

.list-confirm-dialog__cancel:active:not(:disabled),
.list-confirm-dialog__confirm:active:not(:disabled) {
    filter: brightness(0.92);
}

.list-confirm-dialog__confirm[disabled] {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ------- Details dialog (read-only twin of the confirm dialog) -------

   Shared by every "why did this run fail / what did it do" panel - the Jobs
   table and the report run-history table both render the same structure
   (title, failure category, hint, verbatim service text, optional connection
   deep-link, single Close). They previously diverged badly: the Jobs one had
   component-scoped styles while the report-history one had NO styles at all,
   so the dialog an operator actually meets after a failed report run was a
   raw native <dialog> with a browser-default button. Scoped .razor.css can't
   be shared, so the styles live here next to the confirm dialog they mirror.

   Geometry, radii, gaps and the button treatment are deliberately identical
   to .list-confirm-dialog above, so an operator who knows the typed-confirm
   flow recognises this chrome immediately. */

.details-dialog {
    min-inline-size: 24rem;
    /* Bounded so a long tenant-safe error summary wraps instead of stretching
       across a wide monitor, and still fits a phone. */
    max-inline-size: min(40rem, calc(100vw - 2rem));
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-lg);
}

.details-dialog::backdrop {
    background: rgba(0, 0, 0, 0.4);
}

.details-dialog__body {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1rem;
}

.details-dialog__title {
    margin: 0;
    font-size: 1.125rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-spacing-tight, -0.01em);
}

/* The failure category answers "what went wrong" - the strongest line in the
   body, separated from the title by a hairline so the two don't read as one
   run-on heading. */
.details-dialog__category {
    margin: 0;
    padding-block-start: 0.75rem;
    border-block-start: 1px solid var(--color-border-subtle, var(--color-border));
    font-weight: var(--font-weight-semibold);
}

/* The remediation hint is guidance, not a finding - muted so the category
   above and the verbatim text below carry the emphasis. */
.details-dialog__hint {
    margin: 0;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

/* Verbatim service text: a monospace notice block, same shape as the report
   dataset warnings, so quoted machine output is visually distinct from
   Companion's own prose and can't be mistaken for it. */
.details-dialog__summary {
    margin: 0;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--color-border-subtle, var(--color-border));
    border-radius: 0.375rem;
    background: var(--color-surface-sunken);
    font-family: var(--font-mono, ui-monospace, "Cascadia Code", Consolas, monospace);
    font-size: 0.875rem;
    overflow-wrap: anywhere;
}

.details-dialog__link {
    color: var(--color-brand);
    font-weight: var(--font-weight-medium);
}

.details-dialog__actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    padding-block-start: 0.25rem;
}

/* Byte-for-byte the .list-confirm-dialog__cancel treatment: a read-only panel's
   Close is a neutral dismiss, never a primary or danger commit. */
.details-dialog__close {
    min-block-size: 2.75rem;
    padding: 0.375rem 1rem;
    border-radius: 0.375rem;
    cursor: pointer;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    font: inherit;
    font-weight: var(--font-weight-semibold);
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                filter var(--motion-duration-fast) var(--motion-easing-standard);
}

.details-dialog__close:hover {
    background: var(--color-surface-sunken);
}

.details-dialog__close:active {
    filter: brightness(0.92);
}

.details-dialog__close:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 3px;
}

@media (forced-colors: active) {
    .details-dialog,
    .details-dialog__close,
    .details-dialog__summary {
        border: 1px solid CanvasText;
    }
}

/* ------- Summary / empty / load-failed ------- */

.list-summary {
    margin-block-start: 0.5rem;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

.list-summary__selection {
    margin-inline-start: 0.5rem;
}

.list-empty-state,
.list-load-failed-state {
    padding: 1.5rem;
    border: 1px dashed var(--color-border);
    border-radius: 0.5rem;
    text-align: center;
}

/* Rich-layout variant - opt-in when the caller supplies a Title and/or
   Icon. Gives empty states more visual presence on landing-style pages
   so a "nothing here yet" surface reads as intentional design rather
   than a placeholder. */
.list-empty-state--rich {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-8) var(--space-6);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.list-empty-state__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--radius-full);
    background: var(--color-brand-subtle);
    color: var(--color-brand);
    margin-bottom: var(--space-1);
}

    .list-empty-state__icon :is(svg) {
        width: 1.5rem;
        height: 1.5rem;
    }

.list-empty-state__title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
}

.list-empty-state--rich .list-empty-state__message {
    margin: 0;
    max-width: 36ch;
    color: var(--color-text-muted);
    line-height: var(--line-height-relaxed);
}

.list-empty-state--rich .list-empty-state__action {
    margin-top: var(--space-2);
}

@media (forced-colors: active) {
    .list-empty-state--rich {
        border: 1px solid CanvasText;
        box-shadow: none;
    }

    .list-empty-state__icon {
        background: Canvas;
        border: 1px solid CanvasText;
        forced-color-adjust: none;
    }
}

.list-load-failed-state {
    border-color: var(--color-danger);
    color: var(--color-danger);
}

.list-load-failed-state__retry {
    margin-block-start: 0.5rem;
    min-block-size: 2.75rem;
    padding-inline: 1rem;
}

/* ------- Export button -------
   Matches .list-refresh__trigger / .list-column-picker__trigger so the
   Export sits in the toolbar as a peer of the other action buttons
   rather than as a stray oversized control. Padding, inline-flex, and
   font/color inheritance must stay in lock-step with those rules. */

.list-export-button {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    color: inherit;
}

.list-export-button[disabled] {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ------- Load-more (cursor pagination) -------
   Anchor below the table when cursor pagination has a next page. Shares
   the canonical toolbar-button shape so the affordance reads as a list
   control rather than a default-blue browser hyperlink. The wrapping
   <nav> centres the control under the table, separated from the last
   row by a comfortable margin so it isn't mistaken for a row link. */

.list-view__load-more {
    display: flex;
    justify-content: center;
    margin-block-start: var(--space-4);
}

.list-view__load-more-link {
    /* Sizing + layout - colour, border, and hover are picked up from
       the shared chrome-navy rule below. */
    min-block-size: 2.75rem;
    padding-inline: 1rem;
    border: 1px solid var(--color-chrome-surface);
    border-radius: 0.375rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    text-decoration: none;
}

.list-view__load-more-link[disabled],
.list-view__load-more-link[aria-busy="true"] {
    cursor: progress;
    opacity: 0.7;
}

/* Busy indicator shown in place of the Load more button while an expanded list
   auto-loads - a silent pause during a slow fetch reads as "the list ended". */
.list-view__load-more-status {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding-block: var(--space-3);
    color: var(--color-text-muted, var(--color-text));
}

.list-view__load-more-spinner {
    inline-size: 0.875rem;
    block-size: 0.875rem;
    border: 2px solid currentColor;
    border-block-end-color: transparent;
    border-radius: 50%;
    animation: list-view-load-more-spin 800ms linear infinite;
}

@keyframes list-view-load-more-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    /* Keep the load-more spinner rotating (just slower) rather than freezing -
       a small rotating ring reads as "loading more" and is not a
       vestibular-motion trigger; a frozen ring reads as "stuck". */
    .list-view__load-more-spinner {
        animation: list-view-load-more-spin 1.2s linear infinite;
    }
}

/* ------- Page-number pagination -------
   Offset/page twin of the cursor "Load-more" control above. The numbered
   links + prev/next share the canonical chrome-navy pill treatment (the
   same family as the toolbar actions / load-more) so the strip reads as a
   list control rather than a stray <ul> of blue hyperlinks. The current
   page renders filled (you are here) and the boundary prev/next render
   muted + non-interactive. The summary line mirrors .list-summary. */

.pagination {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    margin-block-start: var(--space-4);
}

/* The bar holds the page links and the rows-per-page selector on one row;
   wraps to stacked on narrow viewports. The summary sits below it. */
.pagination__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
}

.pagination__list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.pagination__link {
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-chrome-surface);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-chrome-surface);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                color var(--motion-duration-fast) var(--motion-easing-standard);
}

.pagination__link:hover,
.pagination__link:focus-visible {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
}

/* Current page is filled by default; it links to itself, so drop the
   pointer affordance and keep the fill on hover/focus. */
.pagination__link--current,
.pagination__link--current:hover,
.pagination__link--current:focus-visible {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
    cursor: default;
}

/* Disabled prev/next at the boundaries - a <span>, not a link: muted,
   non-interactive, and must not pick up the hover fill. */
.pagination__link--disabled,
.pagination__link--disabled:hover {
    background: transparent;
    color: var(--color-chrome-surface);
    opacity: 0.5;
    cursor: not-allowed;
}

/* Ellipsis between non-adjacent page numbers - same footprint as a link
   cell so the strip stays evenly spaced, but muted and non-interactive. */
.pagination__ellipsis {
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-muted);
}

.pagination__summary {
    margin: 0;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

@media (prefers-reduced-motion: reduce) {
    .pagination__link,
    .pagination__page-size-select,
    .pagination__page-size::after {
        transition: none;
    }
}

/* Rows-per-page selector - a compact dropdown styled to match the page pills
   (chrome-navy outline, same 2.75rem box) so it reads as part of the strip.
   appearance:none drops the browser's own arrow - the variably-sized native
   chrome that made the box look oversized - and the wrapper draws a small CSS
   caret instead. */
.pagination__page-size {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.pagination__page-size::after {
    content: "";
    position: absolute;
    inset-inline-end: 0.7rem;
    inline-size: 0.45rem;
    block-size: 0.45rem;
    border-inline-end: 1.5px solid var(--color-text-muted);
    border-block-end: 1.5px solid var(--color-text-muted);
    transform: translateY(-25%) rotate(45deg);
    pointer-events: none;
    transition: border-color var(--motion-duration-fast) var(--motion-easing-standard);
}

/* Caret darkens with the border on hover/focus. */
.pagination__page-size:hover::after,
.pagination__page-size:focus-within::after {
    border-inline-end-color: var(--color-chrome-surface);
    border-block-end-color: var(--color-chrome-surface);
}

.pagination__page-size-select {
    appearance: none;
    -webkit-appearance: none;
    margin: 0;
    box-sizing: border-box;
    block-size: 2.75rem;
    /* Floor width so the (left-aligned) value never reaches the caret - with
       appearance:none the select otherwise shrinks to fit its content and a
       3-digit "100" would collide with the chevron. */
    min-inline-size: 4.75rem;
    padding-block: 0;
    padding-inline: 0.75rem 1.75rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: inherit;
    font: inherit;
    line-height: 1.2;
    cursor: pointer;
    transition: border-color var(--motion-duration-fast) var(--motion-easing-standard),
                box-shadow var(--motion-duration-fast) var(--motion-easing-standard);
}

/* Stays white; the border (and caret, above) darken on hover, with a focus
   ring for keyboard users. */
.pagination__page-size-select:hover {
    border-color: var(--color-chrome-surface);
}

.pagination__page-size-select:focus-visible {
    border-color: var(--color-chrome-surface);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-chrome-surface) 30%, transparent);
    outline: none;
}

/* ------- Mobile: table → cards transformation -------
   Pages opt into this by adding the `list-table--mobile-cards` class
   on the <table>; primitives don't impose it because tablet layouts
   may prefer a trimmed column set instead. */

@media (max-width: 599px) {
    .list-table--mobile-cards thead {
        display: none;
    }

    .list-table--mobile-cards tr {
        display: block;
        border: 1px solid var(--color-border);
        border-radius: 0.5rem;
        padding: 0.75rem;
        margin-block-end: 0.5rem;
    }

    /* Label ABOVE value, each on its own line - not a two-column row.
       The row form put the label and the value side by side and let the label
       keep its intrinsic width (`flex: none`), so a long label starved the
       value: "High confidence phishing action" left "Quarantine" a few pixels
       to wrap in, and it came out one character per line. Neither column can be
       given a fixed share either, because these cards carry both one-word
       verdicts and full sentences. Stacking sidesteps the negotiation - every
       value gets the full card width regardless of how long its label is. */
    .list-table--mobile-cards td,
    .list-table--mobile-cards th[scope="row"] {
        display: block;
        padding: 0.375rem 0;
        /* Deliberately overrides the column alignment classes a caller may put
           on the cell (`report-data__num` right-aligns, `report-data__center`
           centres - both correct in the desktop grid). Stacked, a right-aligned
           number sitting under a left-aligned label reads as belonging to the
           next field rather than to its own. This selector already outranks a
           bare class, but the intent is worth stating: alignment is the card
           layout's call below this breakpoint, not the column's. */
        text-align: start;
        /* A long unbroken value (URL, email, GUID) must wrap to the card rather
           than push it past the viewport edge and force a horizontal scroll.
           `anywhere` breaks the token only when there is no softer break. */
        overflow-wrap: anywhere;
    }

    .list-table--mobile-cards td::before,
    .list-table--mobile-cards th[scope="row"]::before {
        content: attr(data-label);
        /* Its own line, and set as a caption rather than as an equal partner:
           at full width a same-size bold label competes with the value it
           introduces. */
        display: block;
        margin-block-end: 0.125rem;
        font-size: 0.8125rem;
        font-weight: 600;
        line-height: 1.4;
        color: var(--color-text-muted);
        text-align: start;
    }

    /* Element values (links, badges, wrappers) default to min-width:auto, which
       floors them at their content width and reintroduces the overflow the
       cell's overflow-wrap exists to prevent. */
    .list-table--mobile-cards td > *,
    .list-table--mobile-cards th[scope="row"] > * {
        min-width: 0;
        max-width: 100%;
    }
}

/* ------- Mobile: select-all bar -------
   Consequence of the `thead { display: none }` above. Both select-all
   checkboxes live in a header cell, so hiding the header row took the
   whole-page selection control with it while the per-row checkboxes
   survived as card fields: under 599px an operator could tick rows one
   at a time but could never select them all. ListSelectAllBar renders
   that control immediately above the table, and this pair of rules keeps
   exactly one select-all on screen - the bar is hidden by default and
   only appears at the widths where the header row is gone. */

/* The bar IS a <label>, so both rules must outrank app.css's global
   `:is(.public-shell__main, .app-shell__main) label { display: block }`
   at specificity (0,1,1) - a bare `.list-select-all-bar` (0,1,0) loses to
   it wherever a list actually renders, which puts a SECOND select-all on
   screen at desktop widths and leaves `display: flex` inert on phones.
   Qualifying with the element name takes both rules to (0,1,1) and
   lists.css loads after app.css, so they win on source order. */
label.list-select-all-bar {
    display: none;
}

@media (max-width: 599px) {
    label.list-select-all-bar {
        display: flex;
        align-items: center;
        /* The checkbox is stretched to a 2.75rem tap target on coarse
           pointers, which already supplies most of the visual gap. */
        gap: 0.25rem;
        padding-block: 0.5rem;
        /* The same global label rule supplies a bottom margin meant for
           form fields; the bar sits directly on the card list. */
        margin-block-end: 0;
        /* The whole row is a <label>, so the text shares the hit target. */
        cursor: pointer;
    }

    .list-select-all-bar__text {
        /* Matches the card field labels the bar sits above. */
        font-size: 0.8125rem;
        font-weight: 600;
        line-height: 1.4;
        color: var(--color-text-muted);
    }
}

@media (prefers-reduced-motion: reduce) {
    .list-status-cell__badge {
        transition: none;
    }
}

/* ------- ListView container ------- */

/* The ListView<TRow> Razor composes the toolbar / search / picker
   primitives above. The wrapper claims the full width of its slot so
   sparse result sets still occupy the page instead of collapsing into
   a content-width strip the way an unscoped <table> does by default. */

.list-view {
    inline-size: 100%;
}

.list-view__scroll {
    inline-size: 100%;
    /* Contain horizontal overflow to the table region - the alternative
       (page-level horizontal scroll) was the original choice but breaks
       the page chrome on operator surfaces with wider column counts
       (the platform Jobs view + Recurring Jobs page). The sticky `<th>`
       now pins to this scroll container instead of the viewport, which
       matches the conventional behaviour of every modern data-table
       library. The CSS Overflow spec forces `overflow-y` to `auto` once
       `overflow-x` is set; rows that exceed the viewport height get an
       inner vertical scrollbar - acceptable here because operator
       lists are short (≤ a few hundred rows after filtering). */
    overflow-x: auto;
}

/* Opt-in modifier - historically clamped the table wrapper to the
   viewport and made the table-vertical scroll move inside the wrapper.
   That introduced two papercuts: an inner scrollbar that shrunk the
   visible table height every time a filter popover opened, and an
   overflow-clipping context that trimmed absolutely-positioned
   popovers when the trigger was near the wrapper edge. Both went
   away by letting the page scroll naturally; the modifier is kept
   as a no-op so existing callers stay valid. */
.list-view__scroll--viewport-clamped {
    /* No-op: page-self scroll is the chosen behaviour. */
}

/* Floating horizontal scrollbar - a proxy scroller (lists.js / enableStickyHScroll)
   pinned to the bottom of the viewport and synced to .list-view__scroll, so a wide
   table on a long list can be scrolled sideways without first scrolling the page to
   the bottom of the list to reach the native scrollbar. lists.js toggles it on only
   when the table overflows horizontally; it's hidden on the mobile card layout. */
.list-view__hscroll {
    position: sticky;
    inset-block-end: 0;
    inline-size: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    /* Sit just below the sticky bulk-action bar (which shares inset-block-end: 0), so
       an active selection's action bar keeps the bottom edge; still above the rows. */
    z-index: calc(var(--z-sticky) - 1);
    background: var(--color-surface);
    border-block-start: 1px solid var(--color-border-subtle);
    /* Hidden until JS measures a horizontal overflow (or on non-interactive SSR). */
    display: none;
    /* Force a PERSISTENT, visibly-rendered scrollbar. Without this the proxy relied on
       the OS scrollbar of a 1px-tall spacer, which on auto-hiding / overlay scrollbars
       (Windows 11 + Chromium/Edge, macOS) renders nothing until you scroll it - and you
       can't grab a 1px-tall bar - so the floating scrollbar appeared to do nothing on
       long, wide lists. Firefox: a thin, coloured (non-overlay) bar. */
    scrollbar-width: thin;
    scrollbar-color: var(--color-border-strong) transparent;
}

/* Chromium/WebKit: defining ::-webkit-scrollbar opts the element out of overlay
   scrollbars, so the horizontal bar is always drawn (classic, always-visible) while
   the proxy is shown. */
.list-view__hscroll::-webkit-scrollbar {
    height: 0.75rem;
}

.list-view__hscroll::-webkit-scrollbar-thumb {
    background: var(--color-border-strong);
    border-radius: 999px;
    /* Inset the thumb from the track edges without shrinking the hit target. */
    border: 3px solid transparent;
    background-clip: padding-box;
}

.list-view__hscroll::-webkit-scrollbar-track {
    background: transparent;
}

.list-view__hscroll-spacer {
    block-size: 1px;
}

/* No horizontal scrolling in the stacked card layout - hide the proxy there. */
@media (max-width: 599px) {
    .list-view__hscroll {
        display: none !important;
    }
}

@media (prefers-reduced-motion: reduce) {
    .list-view__hscroll {
        scroll-behavior: auto;
    }
}

.list-view__table {
    inline-size: 100%;
    border-collapse: collapse;
}

/* Zebra striping for every <ListView>-driven table - alternating row
   background plus a hover tint. Custom-property fallbacks let a host
   theme override without touching the shared rule. The platform-users
   table previously carried its own scoped copy of this rule; the
   lift-and-share keeps the audit log + future list surfaces visually
   consistent. */
.list-view__table tbody tr {
    background: var(--color-surface);
    transition: background-color var(--motion-duration-fast, 120ms) var(--motion-easing-standard, ease);
}

@media (prefers-reduced-motion: reduce) {
    .list-view__table tbody tr {
        transition: none;
    }
}

.list-view__table tbody tr:nth-child(even) {
    /* Soft brand-tinted zebra - token flips for dark theme so the
       stripe stays visible against either content surface. */
    background: var(--color-brand-50);
}

.list-view__table tbody tr:hover {
    background: color-mix(in srgb, var(--color-text) 6%, transparent);
}

/* ------- List filter panel ------- */
/* Unified filter chrome: a trigger button in the toolbar +
   <ListFilterPanelSurface> region rendered in normal page flow
   between the toolbar and the table. The surface auto-closes on
   outside-click / Escape / Apply / Reset / re-clicking the
   trigger (state lives in <ListView>). No backdrop, no modal -
   the table stays visible underneath. */

.list-filter-panel__trigger {
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font: inherit;
    color: inherit;
}

.list-filter-panel__trigger--active {
    border-color: var(--color-brand);
    color: var(--color-brand);
}

.list-filter-panel__trigger-icon {
    display: inline-flex;
    align-items: center;
}

.list-filter-panel__trigger-icon svg {
    fill: currentColor;
}

.list-filter-panel__trigger-badge {
    background: var(--color-brand);
    color: var(--color-surface);
    border-radius: 999px;
    padding: 0.125rem 0.5rem;
    font-size: 0.8em;
    line-height: 1;
}

.list-filter-panel__surface {
    margin-block: 0.5rem 1rem;
    padding: 1rem;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    background: var(--color-surface-raised);
    color: var(--color-text);
    box-shadow: var(--shadow-md);
    /* Pin the whole filter area to one size so labels (which inherit) and
       native form controls (which use `font: inherit`) render identically -
       browsers otherwise default <select>/<input> to ~16px next to 14px
       labels. Matches the list table cell size. Note the inputs
       themselves override this to 16px to dodge iOS zoom-on-focus. */
    font-size: var(--font-size-sm);
}

/* Tighter padding on phones so a four-filter panel doesn't push the
   table entirely below the fold on a 568 px iPhone SE. The
   list-filter-panel__body grid already collapses to single column at
   the same breakpoint; pairing it with the slimmer surface keeps the
   above-the-fold density usable. */
@media (max-width: 599px) {
    .list-filter-panel__surface {
        padding: var(--space-3);
    }
}

.list-filter-panel__surface[hidden] {
    display: none;
}

.list-filter-panel__body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
}

.list-filter-panel__row {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.list-filter-panel__row-label {
    font-weight: 500;
    color: var(--color-text);
}

.list-filter-panel__text-input,
.list-filter-panel__select {
    inline-size: 100%;
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    color: inherit;
    font: inherit;
    /* Override the 14px panel font on the actual inputs - iOS Safari
       zooms the viewport whenever a focused text input is under 16px,
       so labels stay at 14px (visual hierarchy) but the input itself
       reads at 16px. */
    font-size: var(--font-size-md);
}

/* Multi-select choice filter: a disclosure button faced like the sibling
   selects, opening a popup of native checkboxes. */
.list-filter-panel__multi {
    position: relative;
}

.list-filter-panel__multi-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    text-align: start;
    cursor: pointer;
}

.list-filter-panel__multi-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.list-filter-panel__multi-caret {
    flex: none;
    color: var(--color-text-muted);
}

.list-filter-panel__multi-popup {
    position: absolute;
    inset-block-start: calc(100% + var(--space-1));
    inset-inline: 0;
    /* --z-overlay, not --z-dropdown. Nothing clips this popup - the filter surface is
       a plain in-flow sibling ABOVE .list-view__scroll, so the scroll region's
       overflow never contains it, and no ancestor establishes a stacking context.
       It simply lost the paint order: --z-dropdown (100) sits BELOW --z-sticky (200),
       and a long option list hangs down over the table's sticky <th>, which then
       painted on top of it. --z-overlay (300) is the scale's designated "above every
       sticky surface in the content area, below modals" layer - the same reasoning
       AppShell's sidebar uses. */
    z-index: var(--z-overlay);
    max-block-size: 16rem;
    overflow-y: auto;
    padding: var(--space-1);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    /* Literal to match the sibling controls' 0.375rem, which are also literal. */
    border-radius: 0.375rem;
    box-shadow: var(--shadow-lg);
}

.list-filter-panel__multi-option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--target-size-minimum);
    padding-inline: var(--space-2);
    border-radius: var(--radius-sm);
}

.list-filter-panel__multi-option:hover {
    background: var(--color-surface-sunken);
}

.list-filter-panel__multi-option input {
    flex: none;
    accent-color: var(--color-brand);
}

.list-filter-panel__multi-option input:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.list-filter-panel__multi-option label {
    flex: 1;
    cursor: pointer;
    font-size: var(--font-size-sm);
}

.list-filter-panel__date-range {
    display: grid;
    /* Side-by-side from/to where the panel is wide enough; stacked on
       narrow screens instead of squeezing the datetime controls. */
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.5rem;
    margin-block-start: 0.5rem;
}

.list-filter-panel__date-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.list-filter-panel__date-field span {
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--color-text-muted);
}

.list-filter-panel__date-field input {
    /* Full width like every other panel input - a datetime-local input
       otherwise sizes to its content and sits visibly narrower than the
       select above it. */
    inline-size: 100%;
    min-block-size: 2.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    color: inherit;
    font: inherit;
    /* iOS zoom-on-focus guard - see .list-filter-panel__text-input. The
       font-size must come AFTER the `font` shorthand: the shorthand resets
       font-size, so the earlier order quietly restored the 14px panel size. */
    font-size: var(--font-size-md);
}

.list-filter-panel__date-field input::-webkit-calendar-picker-indicator {
    cursor: pointer;
}

.list-filter-panel__footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-block-start: 1rem;
}

.list-filter-panel__action {
    min-block-size: 2.75rem;
    padding-inline: 1rem;
    border: 1px solid var(--color-border);
    border-radius: 0.375rem;
    background: var(--color-surface);
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.list-filter-panel__action--primary {
    background: var(--color-brand);
    color: var(--color-surface);
    border-color: var(--color-brand);
}

.list-filter-panel__action:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
}

@media (max-width: 599px) {
    .list-filter-panel__body {
        grid-template-columns: 1fr;
    }
}

/* ------- Row count badge ------- */
/* "1,234 rows" indicator in the toolbar. Live-region updates so AT
   users hear the new count after every filter Apply. */
.list-view__total-count {
    display: inline-flex;
    align-items: center;
    padding-inline: 0.75rem;
    color: var(--color-text-muted);
    font: inherit;
}

/* ------- Bulk action bar - always-visible disabled state ------- */
.list-bulk-action-bar--empty {
    opacity: 0.55;
}

.list-bulk-action-bar--empty button[disabled] {
    cursor: not-allowed;
}

/* ============================================================
 * Crisp-tech visual treatment - higher-specificity surface skinning
 * for the table chrome (hairline header, zebra stripe, chrome-navy
 * trigger pills). Every value resolves through a `--color-*` /
 * `--shadow-*` / `--radius-*` token so the cascade tracks
 * light / dark / HC / forced-colors automatically.
 * ============================================================ */

/* Hairline header cell + cleaner zebra stripe. Header + body cells
   share --font-size-sm so the rows scan as one typographic family;
   the header stays distinct via weight + muted colour rather than
   size. */
.list-view__table .list-view__th,
.list-view__table td {
    font-size: var(--font-size-sm);
}

.list-view__table .list-view__th {
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border-subtle);
    color: var(--color-text-muted);
    font-weight: var(--font-weight-semibold);
    /* Pin header cells to the top of the viewport while the user scrolls
       a long table. Background is solid (not transparent) so body rows
       don't bleed through; z-index sits at --z-sticky so popovers (modal/
       tooltip) still paint above the header. The bulk-action bar uses the
       same layer but sticks to the opposite edge (inset-block-end), so
       the two don't collide. */
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-sticky);
}

@media (forced-colors: active) {
    .list-view__table .list-view__th {
        background: Canvas;
    }
}

/* ── Column resize (session-only) ──────────────────────────────────────────
   Handles are appended to each header cell by lists.js (enableColumnResize);
   the header cell is position:sticky (above), which anchors the absolutely
   positioned handle to its own right edge. The table only switches to fixed
   layout once the user actually resizes, so untouched tables keep their
   content-based sizing. */
.list-view__resize-handle {
    position: absolute;
    inset-block: 0;
    inset-inline-end: -3px;
    inline-size: 7px;
    cursor: col-resize;
    touch-action: none;
    user-select: none;
    /* Sit above the sticky header's own stacking so the grab target is on top. */
    z-index: calc(var(--z-sticky) + 1);
}

/* The handle overhangs its header's inline-end edge by 3px so the grab strip
   straddles the column boundary. The LAST header has no neighbour: its
   overhang pokes past the table's edge, and because the sticky <th> is the
   handle's containing block those 3px join the scroll region's scrollable
   overflow - every list showed a horizontal scrollbar (and armed the
   floating proxy) even when the columns fit. Keep the last handle flush
   with the table edge; wide tables still overflow via their real width. */
.list-view__th:last-child .list-view__resize-handle {
    inset-inline-end: 0;
}

/* A hairline grip fades in on hover / during a drag so the affordance is
   discoverable without adding permanent visual noise to every header. */
.list-view__resize-handle::after {
    content: "";
    position: absolute;
    inset-block: 18%;
    inset-inline-end: 3px;
    inline-size: 2px;
    border-radius: 1px;
    background: var(--color-text-muted);
    opacity: 0;
    transition: opacity var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-view__resize-handle:hover::after,
.list-view__table--resizing .list-view__resize-handle::after {
    opacity: 0.6;
}

/* Suppress text selection across the table while a drag is in progress. */
.list-view__table--resizing {
    user-select: none;
}

@media (min-width: 600px) {
    .list-view__table--resized {
        table-layout: fixed;
        /* max-content, NOT auto. With `auto` the table is shrink-to-fit against
           .list-view__scroll, so a column dragged past the window edge was squeezed
           back to fit and the widths the operator set silently evaporated. Under
           fixed layout max-content is the sum of the specified column widths - i.e.
           "hold the widths that were set and let the scroll container take the
           slack", which is what makes the table overflow instead of collapse. */
        inline-size: max-content;
        /* Still fill the region when every column is narrow. */
        min-inline-size: 100%;
    }

    /* Long unbreakable values (GUIDs, URLs) wrap inside the set width instead of
       overflowing and overlapping the neighbouring column.
       break-word, NOT anywhere: the two render identically here, but `anywhere`
       also counts the break points it introduces when computing min-content, which
       flattens the table's intrinsic floor to roughly one character and hands the
       browser permission to squeeze every column back inside the viewport - the
       collapse this rule was accused of causing. `break-word` leaves the intrinsic
       floor intact while still breaking the long token at paint time. */
    .list-view__table--resized td,
    .list-view__table--resized .list-view__th {
        overflow-wrap: break-word;
    }
}

/* Under the card breakpoint the header is hidden, so the handles are too. */
@media (max-width: 599px) {
    .list-view__resize-handle {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .list-view__resize-handle::after {
        transition: none;
    }
}

.list-view__table tbody tr {
    border-bottom: 1px solid var(--color-border-subtle);
    transition: background var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-view__table tbody tr:last-child {
    border-bottom: 0;
}

.list-view__table tbody tr:hover {
    background: var(--color-brand-50);
}

@media (forced-colors: active) {
    .list-view__table tbody tr:hover {
        background: Canvas;
        outline: 1px solid Highlight;
        outline-offset: -1px;
    }
}

/* Refined search input - pair with the modernised auth-form inputs
   so the visual language carries from sign-in into the dashboards. */
.list-search .list-search__input {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-border);
    border-radius: var(--radius-md);
    transition: border-color var(--motion-duration-fast) var(--motion-easing-standard),
                box-shadow var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-search .list-search__input:focus {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
    border-color: var(--color-brand);
}

/* Column-picker / filter trigger buttons - outlined chrome-navy pill
   that mirrors the .secondary submit button so toolbar chrome reads
   as one brand family with the page CTAs. Fills on hover; the
   --active variant (filters applied) renders filled by default.
   Export and Load-more join the same group so all list-control
   affordances share the chrome-navy treatment instead of drifting
   into per-control colours. */
.list-column-picker__trigger,
.list-filter-panel__trigger,
.list-refresh__trigger,
.list-fullscreen__trigger,
.list-toolbar__action,
.list-export-button,
.list-view__load-more-link {
    background: transparent;
    color: var(--color-chrome-surface);
    border-color: var(--color-chrome-surface);
    border-radius: var(--radius-md);
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                color var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-column-picker__trigger:hover,
.list-filter-panel__trigger:hover,
.list-refresh__trigger:hover,
.list-fullscreen__trigger:hover,
.list-toolbar__action:hover,
.list-export-button:hover,
.list-view__load-more-link:hover,
.list-view__load-more-link:focus-visible {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
}

.list-filter-panel__trigger--active {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
    border-color: var(--color-chrome-surface);
}

.list-column-picker__surface {
    border-color: var(--color-border-subtle);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
}

/* Status pills get a slimmer, rounded-pill treatment. */
.list-status-cell .list-status-cell__pill {
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-spacing-wide);
    text-transform: uppercase;
}

/* Bulk-action bar - softer crisp-tech surface instead of the
   slate-gray slab. Same shadow stack as cards. */
.list-bulk-action-bar {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
}

.list-bulk-action-bar__deselect {
    border-color: var(--color-border);
    color: var(--color-text);
}

/* Primary bulk-action chrome - matches the page submit buttons
   (--color-chrome-surface, the navy that sits behind the header /
   sidebar) so toolbar primaries read as one brand family with the
   form CTAs below the table. */
.list-bulk-action {
    background: var(--color-chrome-surface);
    color: var(--color-chrome-on-surface);
    border-radius: var(--radius-md);
    transition: filter var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-bulk-action:hover {
    filter: brightness(1.08);
}

.list-bulk-action:active {
    filter: brightness(0.92);
}

/* Total-row count adopts the muted-text token. */
.list-view__total-count {
    color: var(--color-text-muted);
}

/* ----- List-page hero ----------------------------------------------- */
/* Optional row of summary-stat tiles consumers compose above the
   toolbar. Lives inside the page body, not the AppShell chrome, so
   pages opt in by rendering <ListPageHero ...>. The grid wraps
   naturally and collapses to a single column at <600px. */

.list-page-hero {
    margin: 0 0 var(--space-6);
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.list-page-hero__header {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.list-page-hero__heading {
    margin: 0;
    font-size: var(--font-size-3xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
    color: var(--color-text);
    outline: none;
}

.list-page-hero__description {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-md);
    line-height: var(--line-height-relaxed);
    max-width: 56rem;
}

.list-page-hero__tiles {
    display: grid;
    /* min(14rem, 100%) - a bare minmax(14rem, 1fr) forces a 14rem track
       even when the column is narrower than 14rem (e.g. this hero inside
       a page whose secondary rail is open), which overflows and gives the
       whole page a horizontal scrollbar. Clamping the track minimum to the
       container width prevents that while keeping the 14rem target when
       there's room. */
    grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
    gap: var(--space-4);
}

.list-page-hero-tile {
    padding: var(--space-4) var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    transition: transform var(--motion-duration-normal) var(--motion-easing-standard),
                box-shadow var(--motion-duration-normal) var(--motion-easing-standard),
                border-color var(--motion-duration-normal) var(--motion-easing-standard);
}

    .list-page-hero-tile:hover {
        transform: translateY(-1px);
        box-shadow: var(--shadow-md);
        border-color: var(--color-border);
    }

@media (prefers-reduced-motion: reduce) {
    .list-page-hero-tile:hover {
        transform: none;
    }
}

.list-page-hero-tile__label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-muted);
    letter-spacing: var(--letter-spacing-wide);
    text-transform: uppercase;
}

.list-page-hero-tile__value {
    font-size: var(--font-size-3xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
    color: var(--color-text);
    /* Long single-token values (hostnames like login.microsoftonline.com,
       job ids, opaque slugs) have no natural break points, so the
       default `overflow-wrap: normal` lets them push past the tile
       edge. `anywhere` lets the browser break inside the token while
       still preferring whole-word breaks when they exist, and
       `min-width: 0` lets the tile flex item shrink below its content
       width so the grid track can clamp it. */
    overflow-wrap: anywhere;
    min-width: 0;
}

.list-page-hero-tile__trend {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    margin-top: var(--space-1);
}

/* Inline-SVG sparkline + delta caption. The SVG colour follows the tile
   tone via `currentColor`; the delta caption stays muted because a rise
   or fall isn't universally good/bad (fewer failures is good, a lower
   score is not), so sign-based colouring would mislead. */
.list-page-hero-tile__spark {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-1);
}

.list-page-hero-tile__spark-svg {
    width: 76px;
    height: 22px;
    flex: 0 0 auto;
    color: var(--color-text-muted);
    overflow: visible;
}

.list-page-hero-tile__spark-line {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.list-page-hero-tile__spark-dot {
    fill: currentColor;
    stroke: none;
}

.list-page-hero-tile__spark-delta {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.list-page-hero-tile[data-tile-tone="brand"] .list-page-hero-tile__spark-svg {
    color: var(--color-brand);
}

.list-page-hero-tile[data-tile-tone="success"] .list-page-hero-tile__spark-svg {
    color: var(--color-success);
}

.list-page-hero-tile[data-tile-tone="warning"] .list-page-hero-tile__spark-svg {
    color: var(--color-warning);
}

.list-page-hero-tile[data-tile-tone="danger"] .list-page-hero-tile__spark-svg {
    color: var(--color-danger);
}

@media (forced-colors: active) {
    .list-page-hero-tile__spark-svg {
        color: CanvasText;
    }
}

/* Tile tones - each sets the value colour so the eye finds the
   highest-priority counts first. Hairline + label remain neutral so
   the surface still reads as the same card system. */
.list-page-hero-tile[data-tile-tone="brand"] .list-page-hero-tile__value {
    color: var(--color-brand);
}

.list-page-hero-tile[data-tile-tone="success"] .list-page-hero-tile__value {
    color: var(--color-success);
}

.list-page-hero-tile[data-tile-tone="warning"] .list-page-hero-tile__value {
    color: var(--color-warning);
}

.list-page-hero-tile[data-tile-tone="danger"] .list-page-hero-tile__value {
    color: var(--color-danger);
}

@media (forced-colors: active) {
    .list-page-hero-tile[data-tile-tone="brand"] .list-page-hero-tile__value,
    .list-page-hero-tile[data-tile-tone="success"] .list-page-hero-tile__value,
    .list-page-hero-tile[data-tile-tone="warning"] .list-page-hero-tile__value,
    .list-page-hero-tile[data-tile-tone="danger"] .list-page-hero-tile__value {
        color: CanvasText;
    }
}

@media (max-width: 599px) {
    .list-page-hero__tiles {
        grid-template-columns: 1fr;
    }
}

/* ----- List density toggle ------------------------------------------ */
/* Two-segment pill: Comfortable / Compact. Renders inside the
   toolbar end slot; the active option carries data-active="True". */
.list-density-toggle {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    /* Match the 44px height of the other toolbar controls (refresh,
       column-picker) - it was the lone 36px outlier. */
    min-block-size: var(--target-size-touch);
    background: var(--color-surface);
}

.list-density-toggle__option {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--target-size-touch);
    padding: 0 var(--space-3);
    color: var(--color-text-muted);
    text-decoration: none;
    border: 0;
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                color var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-density-toggle__option + .list-density-toggle__option {
    border-left: 1px solid var(--color-border);
}

.list-density-toggle__option:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text);
}

.list-density-toggle__option:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.list-density-toggle__option[data-active="true"] {
    background: var(--color-brand-subtle);
    color: var(--color-brand);
}

@media (forced-colors: active) {
    .list-density-toggle__option[data-active="true"] {
        forced-color-adjust: none;
        background: Highlight;
        color: HighlightText;
    }
}

/* ----- Density-compact table override ------------------------------- */
/* Tables under a `[data-density="compact"]` container get tighter
   row + cell padding. Consumers wrap their table (or table host) in
   a div with the attribute; the attribute value is driven by the
   page reading ListPageBase.DensityParam. */
[data-density="compact"] .list-view__table .list-view__th,
[data-density="compact"] .list-view__table td {
    padding-block: 0.25rem;
    padding-inline: 0.625rem;
}

[data-density="compact"] .list-view__table {
    font-size: var(--font-size-sm);
}

/* ----- List view tabs ----------------------------------------------- */
/* Horizontal segmented filter tabs above the toolbar. Each tab is an
   anchor; the active tab carries data-active="True" + the brand pill
   treatment. Scrolls horizontally on narrow viewports rather than
   wrapping (keeps the visual order intact). */
.list-view-tabs {
    display: flex;
    gap: var(--space-1);
    margin: 0 0 var(--space-4);
    /* Drop the bottom padding so the underline hairline sits directly
       beneath the tab pill row - without this the ~8px gap between
       pill and hairline registers fat-thumb taps as no-ops on phones. */
    padding-bottom: 0;
    border-bottom: 1px solid var(--color-border-subtle);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    /* Edge-mask gradient hints that tabs continue off-screen on iOS
       Safari where the native scroll indicator hides while idle. The
       linear-gradient is the same on both edges; transparent fades the
       outermost 1rem of the strip so the user can see the partial last
       tab and infer scrollability. */
    mask-image: linear-gradient(to right, transparent 0, black 1rem, black calc(100% - 1rem), transparent 100%);
}

.list-view-tabs__tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    min-height: var(--target-size-minimum);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    border-radius: var(--radius-md);
    white-space: nowrap;
    transition: background var(--motion-duration-fast) var(--motion-easing-standard),
                color var(--motion-duration-fast) var(--motion-easing-standard);
}

.list-view-tabs__tab:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text);
}

.list-view-tabs__tab:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

.list-view-tabs__tab[data-active="true"] {
    background: var(--color-brand-subtle);
    color: var(--color-brand);
    font-weight: var(--font-weight-semibold);
}

.list-view-tabs__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    padding: 0 var(--space-2);
    background: var(--color-surface-sunken);
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    border-radius: var(--radius-full);
}

.list-view-tabs__tab[data-active="true"] .list-view-tabs__count {
    background: var(--color-surface);
    color: var(--color-brand);
}

/* Attention tab - flags a tab that needs the operator's action (e.g. a pending
   review). The label takes the brand colour and the count badge fills with the brand
   accent so it reads as "act here", distinct from the muted informational counts.
   Placed after the active-state rules so it persists even when the tab is active. */
/* Disabled tab - an inert span for a view whose prerequisite is unmet
   (e.g. the sibling Microsoft 365 connection tabs before the Graph
   connection is tested). Muted and non-interactive; no hover state. */
.list-view-tabs__tab[data-disabled="true"] {
    color: var(--color-text-muted);
    opacity: 0.55;
    cursor: not-allowed;
}

    .list-view-tabs__tab[data-disabled="true"]:hover {
        background: transparent;
        color: var(--color-text-muted);
    }

.list-view-tabs__tab[data-attention="true"] {
    color: var(--color-brand);
    font-weight: var(--font-weight-semibold);
}

.list-view-tabs__tab[data-attention="true"] .list-view-tabs__count {
    background: var(--color-brand);
    color: var(--color-text-on-accent);
}

@media (forced-colors: active) {
    .list-view-tabs__tab[data-attention="true"] .list-view-tabs__count {
        forced-color-adjust: none;
        background: Highlight;
        color: HighlightText;
    }

    .list-view-tabs__tab[data-active="true"] {
        forced-color-adjust: none;
        background: Highlight;
        color: HighlightText;
    }
    .list-view-tabs__tab[data-active="true"] .list-view-tabs__count {
        background: Canvas;
        color: CanvasText;
    }
}

/* ----- List card view ----------------------------------------------- */
/* Responsive card grid as an alternative to the table layout. Auto-
   fits cards to the available width with a sensible minimum so the
   layout stays readable on every viewport. Mobile collapses to one
   column at < 600px (subsumes the existing list-view table-to-card
   transformation for pages that opt into the card view by default). */
.list-card-view {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-4);
}

.list-card-view__card {
    padding: var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
    transition: transform var(--motion-duration-normal) var(--motion-easing-standard),
                box-shadow var(--motion-duration-normal) var(--motion-easing-standard),
                border-color var(--motion-duration-normal) var(--motion-easing-standard);
}

    .list-card-view__card:hover {
        transform: translateY(-1px);
        box-shadow: var(--shadow-md);
        border-color: var(--color-border);
    }

@media (prefers-reduced-motion: reduce) {
    .list-card-view__card:hover {
        transform: none;
    }
}

@media (forced-colors: active) {
    .list-card-view__card {
        border-color: CanvasText;
    }
}

/* Compact density - tighter spacing, smaller min-width so more
   cards fit per row. Matches the table compact density triggered
   by ListDensityToggle. */
.list-card-view--compact,
.list-card-view[data-density="compact"] {
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: var(--space-3);
}

    .list-card-view--compact .list-card-view__card,
    .list-card-view[data-density="compact"] .list-card-view__card {
        padding: var(--space-4);
    }

@media (max-width: 599px) {
    .list-card-view {
        grid-template-columns: 1fr;
    }
}

/* Loading / empty / load-failed slots - neutral defaults so pages
   that don't supply a template still see something useful. */
.list-card-view__loading,
.list-card-view__empty {
    grid-column: 1 / -1;
    margin: 0;
    padding: var(--space-8) var(--space-4);
    color: var(--color-text-muted);
    text-align: center;
    font-size: var(--font-size-md);
}

.list-card-view__load-failed {
    grid-column: 1 / -1;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--color-danger);
    border-radius: var(--radius-lg);
    background: var(--color-danger-surface);
    color: var(--color-danger-on-surface);
}

/* Effective-quota cell on the platform tenants table: the numeric value sits
   next to a "custom" StatusPill when the tenant overrides the default. Lay
   them out inline with a gap so the pill doesn't butt against the number.
   (Global, not scoped: the cell is built via RenderTreeBuilder, which carries
   no CSS-isolation attribute.) */
.platform-tenants-table__quota {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
}

/* Live quota-usage cell: three stacked "Label used/limit" lines, each with an
   optional at-cap pill. Global (not scoped) because the cell is built via
   RenderTreeBuilder. */
.platform-tenants-table__usage {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.25rem);
    font-variant-numeric: tabular-nums;
}

.platform-tenants-table__usage-line {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    font-size: var(--font-size-sm, 0.875rem);
    white-space: nowrap;
}

