/* 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;
}

/* ------- 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;
}

/* ------- Toolbar action link (e.g. report Export) ------- */
/* Anchor slotted into the toolbar via ListView's ToolbarStart; 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;
}

/* 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);
    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: middle;
}

/* ------- 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;
    max-inline-size: 40rem;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
}

.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);
    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;
}

/* ------- 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;
}

.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;
    }

    .list-table--mobile-cards td,
    .list-table--mobile-cards th[scope="row"] {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.25rem 0;
        text-align: start;
    }

    .list-table--mobile-cards td::before,
    .list-table--mobile-cards th[scope="row"]::before {
        content: attr(data-label);
        font-weight: 600;
        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);
}

.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);
}

/* 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;
        /* Grow past the container (horizontal scroll lives on .list-view__scroll)
           when a column is widened, while still filling it when columns are
           narrow. */
        inline-size: auto;
        min-inline-size: 100%;
    }

    /* Long unbreakable values (GUIDs, URLs) wrap inside the set width instead of
       overflowing and overlapping the neighbouring column. */
    .list-view__table--resized td,
    .list-view__table--resized .list-view__th {
        overflow-wrap: anywhere;
    }
}

/* 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-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-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;
}

