/* ============================================================================
   UI DATA TABLE — the shared data surface.

   Layer: core/ (see the layering policy at the top of core/ui-system.css).
   Everything here is a primitive: the frame, toolbar, scroll area, header,
   rows, cells and the workspace chrome that wwwroot/js/tableWorkspace.js
   injects after render. No selector may be named after a page or a domain
   noun — CoreStylesheetPolicyTests enforces that.

   Structure a framed table renders (Components/Common/Patterns/UiDataTable.razor):

     .ui-table-frame                  positioning context + rounded surface
       .ui-table-lead                 caller-owned toolbar slot (optional)
       .enhanced-table-toolbar        injected workspace chrome (optional)
       .ui-table-scroll               the scroll port (also .enhanced-table-scroll)
         table.ui-table               the table itself
       .enhanced-table-footer         injected paging (optional)

   Sizing is driven by one local token layer (--uidt-*) declared on the frame,
   so density is a token swap rather than a per-element override. Colour comes
   from the global --sp-* tokens, which already carry a dark theme: no rule in
   this file needs an html[data-theme="dark"] counterpart.
   ========================================================================= */

.ui-table-frame {
    --uidt-radius: var(--sp-radius-lg);
    --uidt-border: var(--sp-color-border);
    --uidt-border-soft: color-mix(in srgb, var(--sp-color-ink-muted) 28%, var(--sp-color-border));
    --uidt-surface: var(--sp-color-surface);
    --uidt-surface-head: color-mix(in srgb, var(--sp-color-surface-muted) 82%, var(--sp-color-surface));
    --uidt-surface-sunken: color-mix(in srgb, var(--sp-color-surface-muted) 45%, var(--sp-color-surface));
    --uidt-surface-zebra: color-mix(in srgb, var(--sp-color-surface-muted) 34%, var(--sp-color-surface));
    --uidt-surface-hover: color-mix(in srgb, var(--sp-color-brand) 7%, var(--sp-color-surface));
    --uidt-surface-selected: color-mix(in srgb, var(--sp-color-brand) 13%, var(--sp-color-surface));
    --uidt-ink: var(--sp-color-ink-strong);
    --uidt-ink-muted: var(--sp-color-ink-muted);
    --uidt-ink-faint: var(--sp-color-ink-subtle);
    --uidt-accent: var(--sp-color-brand);

    /* Density scale. Swapped wholesale by .ui-table-density-* / the workspace
       density toggle; every padding and type step below reads from here. */
    --uidt-cell-pad-y: 0.375rem;
    --uidt-cell-pad-x: 0.625rem;
    --uidt-font-size: 0.8125rem;
    --uidt-line-height: 1.35;
    --uidt-head-pad-y: 0.4375rem;
    --uidt-head-font-size: var(--sp-table-heading-size);
    --uidt-row-min-height: 2.875rem;

    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    isolation: isolate;
    border-radius: var(--uidt-radius);
    color: var(--uidt-ink);
}

/* A frame that owns workspace chrome becomes one enclosed card: the toolbar,
   the scroll port and the paging footer share a single border and radius, and
   whatever border the caller's scroll wrapper brought is stood down so the
   two never double up. */
.ui-table-frame:has(> .enhanced-table-toolbar),
.ui-table-frame:has(> .ui-table-lead) {
    overflow: clip;
    border: 1px solid var(--uidt-border);
    background: var(--uidt-surface);
    box-shadow: var(--sp-shadow-sm);
}

.ui-table-frame:has(> .enhanced-table-toolbar) > .ui-table-scroll,
.ui-table-frame:has(> .ui-table-lead) > .ui-table-scroll {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* A directory scroll port isolates sticky cells during ordinary table use.
   A nested fixed modal must share the frame's stacking context with the toolbar
   instead, including its exit animation, so the toolbar cannot cover its header. */
/* Fixed dialogs nested in a detail row must escape BOTH table stacking
   contexts; retain ordinary sticky-cell isolation once the dialog exits. */
.ui-table-frame:has(.ui-modal-backdrop),
.ui-table-frame .ui-data-table-directory-wrap:has(.ui-modal-backdrop) {
    isolation: auto;
}

/* ---------------------------------------------------------------- density */

.ui-table-frame.ui-table-density-comfortable,
.ui-table-frame:has(.enhanced-table-comfortable) {
    --uidt-cell-pad-y: 0.625rem;
    --uidt-cell-pad-x: 0.75rem;
    --uidt-font-size: 0.8438rem;
    --uidt-line-height: 1.45;
    --uidt-row-min-height: 2.75rem;
}

.ui-table-frame.ui-table-density-compact,
.ui-table-frame:has(.enhanced-table-compact) {
    --uidt-cell-pad-y: 0.375rem;
    --uidt-cell-pad-x: 0.625rem;
    --uidt-font-size: 0.8125rem;
    --uidt-line-height: 1.35;
    --uidt-row-min-height: 2.875rem;
}

/* ------------------------------------------------------------ caller slot */

.ui-table-lead {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-2) var(--sp-space-3);
    padding: var(--sp-space-2) var(--sp-space-3);
    border-bottom: 1px solid var(--uidt-border-soft);
    background: var(--uidt-surface-sunken);
}

/* Directory toolbars already own their surface and spacing. */
.ui-table-lead:has(> .ui-toolbar-directory) {
    padding: 0;
    border-bottom: 0;
    background: transparent;
}

.ui-table-lead-title {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    margin-right: auto;
    min-width: 0;
}

.ui-table-lead-title > strong {
    font-size: 0.875rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.2;
}

.ui-table-lead-title > span {
    color: var(--uidt-ink-muted);
    font-size: var(--sp-font-size-xs);
}

.ui-table-lead-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-2);
}

/* --------------------------------------------------------- the scroll port

   The port is the only element that scrolls. Edge fades are painted on it so
   a table wider than the viewport advertises the hidden columns instead of
   ending in a hard, ambiguous cut. They are switched on by tableWorkspace.js
   (.is-overflow-start / .is-overflow-end) so a table that fits shows none. */

.ui-table-scroll {
    position: relative;
    overflow: auto;
    max-width: 100%;
    background: var(--uidt-surface);
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--sp-color-ink-faint) 70%, transparent) transparent;
    overscroll-behavior-x: contain;
}

/* MaxHeight bounds a long table inside its card. */
.ui-table-scroll-limited {
    max-height: var(--ui-table-max-height);
    overflow: auto;
}

/* Phone cards flow in the page: a bounded inner scroller of stacked cards
   competes with page scrolling and hides the actions below it. */
@media (max-width: 767.98px) {
    .ui-table-scroll-limited.ui-table-scroll-cards {
        max-height: none;
    }
}

.ui-table-scroll::-webkit-scrollbar {
    width: 0.625rem;
    height: 0.625rem;
}

.ui-table-scroll::-webkit-scrollbar-thumb {
    border: 3px solid transparent;
    border-radius: var(--sp-radius-pill);
    background: color-mix(in srgb, var(--sp-color-ink-faint) 70%, transparent);
    background-clip: padding-box;
}

.ui-table-scroll::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--sp-color-ink-subtle) 85%, transparent);
    background-clip: padding-box;
}

.ui-table-edge {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 12;
    width: 1.5rem;
    opacity: 0;
    pointer-events: none;
}

.ui-table-edge-start {
    left: 0;
    background: linear-gradient(to right, rgb(var(--sp-rgb-ink) / 0.14), transparent);
}

.ui-table-edge-end {
    right: 0;
    background: linear-gradient(to left, rgb(var(--sp-rgb-ink) / 0.14), transparent);
}

.ui-table-frame.is-overflow-start .ui-table-edge-start,
.ui-table-frame.is-overflow-end .ui-table-edge-end {
    opacity: 1;
}

/* ------------------------------------------------------------- the table */

table.ui-table {
    width: 100%;
    margin: 0;
    border-collapse: separate;
    border-spacing: 0;
    background: transparent;
    color: var(--uidt-ink);
    font-size: var(--uidt-font-size);
    line-height: var(--uidt-line-height);
}

/* Bootstrap paints rows through --bs-table-* shadows and background vars.
   The frame owns row surfaces instead, so neutralise them once here rather
   than fighting them per state. */
table.ui-table,
table.ui-table > thead > tr,
table.ui-table > tbody > tr,
table.ui-table > tfoot > tr {
    --bs-table-bg: transparent;
    --bs-table-accent-bg: transparent;
    --bs-table-striped-bg: transparent;
    --bs-table-hover-bg: transparent;
    --bs-table-active-bg: transparent;
    --bs-table-color: inherit;
    --bs-table-striped-color: inherit;
    --bs-table-hover-color: inherit;
}

table.ui-table > caption {
    padding: var(--sp-space-2) var(--uidt-cell-pad-x);
    color: var(--uidt-ink-muted);
    caption-side: top;
    font-size: var(--sp-font-size-xs);
    letter-spacing: 0.02em;
    text-align: start;
}

/* ------------------------------------------------------------ the header */

table.ui-table > thead > tr > th {
    position: relative;
    z-index: 4;
    padding: var(--uidt-head-pad-y) var(--uidt-cell-pad-x);
    border: 0;
    border-bottom: 1.5px solid var(--uidt-ink);
    background: var(--uidt-surface-head);
    color: var(--uidt-ink-muted);
    font-size: var(--uidt-head-font-size);
    font-weight: 700;
    letter-spacing: var(--sp-table-heading-tracking);
    line-height: 1.25;
    text-align: start;
    text-transform: uppercase;
    vertical-align: middle;
    white-space: nowrap;
}

table.ui-table.ui-table-sticky-head > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 8;
}

/* The header only casts a shadow once rows have scrolled beneath it, so a
   table at rest keeps a flat, quiet top edge. */
.ui-table-frame.is-scrolled .ui-table-sticky-head > thead > tr > th {
    box-shadow: 0 0.375rem 0.625rem -0.5rem rgb(var(--sp-rgb-ink) / 0.35);
}

/* ------------------------------------------------------------- the cells */

table.ui-table > tbody > tr > td,
table.ui-table > tbody > tr > th,
table.ui-table > tfoot > tr > td,
table.ui-table > tfoot > tr > th {
    padding: var(--uidt-cell-pad-y) var(--uidt-cell-pad-x);
    border: 0;
    border-bottom: 1px solid var(--uidt-border-soft);
    background: transparent;
    color: inherit;
    vertical-align: middle;
}

table.ui-table > tbody > tr:last-child > td,
table.ui-table > tbody > tr:last-child > th {
    border-bottom: 0;
}

table.ui-table > tfoot > tr > td,
table.ui-table > tfoot > tr > th {
    position: sticky;
    bottom: 0;
    z-index: 6;
    border-top: 1px solid var(--uidt-border);
    border-bottom: 0;
    background: var(--uidt-surface-head);
    font-weight: 650;
}

/* Alignment is declared once on the header (UiDataTableColumn Align/Numeric)
   and mirrored onto the body cells by tableWorkspace.js, so a numeric column
   lines up on its digits without every call site repeating a class. */
table.ui-table [data-align="end"],
table.ui-table th[data-align="end"] {
    text-align: end;
}

table.ui-table [data-align="center"],
table.ui-table th[data-align="center"] {
    text-align: center;
}

table.ui-table [data-numeric="true"] {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
}

.ui-table-truncate {
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------------------------------------------------------------- rows */

table.ui-table > tbody > tr {
    background: transparent;
}

.ui-table-zebra > tbody > tr.ui-data-table-row:nth-of-type(even) {
    background: var(--uidt-surface-zebra);
}

table.ui-table > tbody > tr.ui-data-table-row:hover {
    background: var(--uidt-surface-hover);
}

/* Selected rows read from the accent rail first and the tint second, so the
   state survives a greyscale print and a colour-vision deficiency. */
table.ui-table > tbody > tr.ui-data-table-row.is-selected,
table.ui-table > tbody > tr.ui-data-table-row[aria-selected="true"] {
    background: var(--uidt-surface-selected);
}

table.ui-table > tbody > tr.ui-data-table-row.is-selected > :first-child,
table.ui-table > tbody > tr.ui-data-table-row[aria-selected="true"] > :first-child {
    box-shadow: inset 0.1875rem 0 0 var(--uidt-accent);
}

.ui-data-table-row-clickable {
    cursor: pointer;
}

.ui-data-table-row-clickable:focus-visible {
    outline: 2px solid var(--uidt-accent);
    outline-offset: -2px;
}

/* Background only: rows never animate geometry, opacity or position. */
table.ui-table > tbody > tr.ui-data-table-row {
    transition: var(--sp-motion-transition-surface);
}

/* ------------------------------------------------- sticky first column */

/* The first column only pins while columns are actually out of view. Every
   pinned cell is its own compositing layer; hundreds of them on a table that
   fits made fast vertical scrolling fall behind and paint blank rows.
   Spanning detail cells remain in normal flow so their overlays are not
   trapped inside a pinned-cell stacking or clipping context. */
.ui-table-frame:is(.is-overflow-start, .is-overflow-end) .ui-table-sticky-column > thead > tr > th:first-child,
.ui-table-frame:is(.is-overflow-start, .is-overflow-end)
    .ui-table-sticky-column
    > tbody
    > tr:not([data-table-detail="true"])
    > td:first-child,
.ui-table-frame:is(.is-overflow-start, .is-overflow-end)
    .ui-table-sticky-column
    > tbody
    > tr:not([data-table-detail="true"])
    > th:first-child {
    position: sticky;
    left: 0;
    z-index: 5;
    /* Directory rows publish an opaque surface for their state; without it a transparent base row
       would let the columns scrolling underneath show through the pinned cell. */
    background: var(--ui-directory-sticky-cell-background, inherit);
}

.ui-table-frame:is(.is-overflow-start, .is-overflow-end) .ui-table-sticky-column > thead > tr > th:first-child {
    z-index: 9;
    background: var(--uidt-surface-head);
}

/* Rows stream under the pointer while scrolling; without this each one in
   turn animates its hover background and the table blinks. */
.ui-table-frame.is-scrolling > .ui-table-scroll > table.ui-table > tbody > tr.ui-data-table-row:hover {
    background: transparent;
}
.enhanced-table-scroll.is-scrolling > table.enhanced-data-table:not(.ui-table) > tbody > tr:hover > :is(td, th) {
    background: transparent;
}

/* The divider only becomes a shadow once the table is actually scrolled
   sideways — otherwise every table carries a permanent seam. */
.ui-table-frame.is-overflow-start .ui-table-sticky-column > thead > tr > th:first-child,
.ui-table-frame.is-overflow-start .ui-table-sticky-column > tbody > tr:not([data-table-detail="true"]) > td:first-child,
.ui-table-frame.is-overflow-start
    .ui-table-sticky-column
    > tbody
    > tr:not([data-table-detail="true"])
    > th:first-child {
    box-shadow: 0.375rem 0 0.5rem -0.375rem rgb(var(--sp-rgb-ink) / 0.35);
    clip-path: inset(0 -1rem 0 0);
}

@media (max-width: 767.98px) {
    .ui-table-sticky-column > thead > tr > th:first-child,
    .ui-table-sticky-column > tbody > tr:not([data-table-detail="true"]) > td:first-child,
    .ui-table-sticky-column > tbody > tr:not([data-table-detail="true"]) > th:first-child {
        position: static;
        box-shadow: none;
    }
}

/* ------------------------------------------------------- sortable headers */

.ui-data-table-sort-button {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    width: 100%;
    margin: calc(var(--uidt-head-pad-y) * -1) calc(var(--uidt-cell-pad-x) * -1);
    padding: var(--uidt-head-pad-y) var(--uidt-cell-pad-x);
    border: 0;
    border-radius: var(--sp-radius-sm);
    background: transparent;
    color: inherit;
    cursor: pointer;
    font: inherit;
    letter-spacing: inherit;
    text-align: inherit;
    text-transform: inherit;
}

th[data-align="end"] > .ui-data-table-sort-button {
    justify-content: flex-end;
}

th[data-align="center"] > .ui-data-table-sort-button {
    justify-content: center;
}

.ui-data-table-sort-button:hover {
    background: color-mix(in srgb, var(--sp-color-ink-faint) 18%, transparent);
    color: var(--uidt-ink);
}

.ui-data-table-sort-button:focus-visible {
    outline: 2px solid var(--uidt-accent);
    outline-offset: -2px;
}

/* A sortable column that is not the active sort still shows its affordance,
   at low contrast: discoverability without noise. The glyph is drawn rather
   than typed so it never lands in an exported cell or an accessible name. */
.ui-data-table-sort-indicator {
    flex: none;
    width: 0.75rem;
    height: 0.75rem;
    background: currentcolor;
    opacity: 0.32;
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1.4 8.6 4.6H3.4zM6 10.6 3.4 7.4h5.2z'/%3E%3C/svg%3E");
    mask-repeat: no-repeat;
    mask-size: contain;
}

.ui-data-table-sort-button:hover .ui-data-table-sort-indicator {
    opacity: 0.6;
}

th[aria-sort] .ui-data-table-sort-button {
    color: var(--uidt-ink);
}

th[aria-sort="ascending"] .ui-data-table-sort-indicator {
    opacity: 1;
    color: var(--uidt-accent);
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 2 9.2 6H2.8z'/%3E%3C/svg%3E");
}

th[aria-sort="descending"] .ui-data-table-sort-indicator {
    opacity: 1;
    color: var(--uidt-accent);
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 10 2.8 6h6.4z'/%3E%3C/svg%3E");
}

th[aria-sort] {
    background: color-mix(in srgb, var(--sp-color-brand) 8%, var(--uidt-surface-head));
    color: var(--uidt-ink);
}

/* -------------------------------------------------------- row selection */

.ui-table-select-cell {
    width: 2.5rem;
    padding-inline: var(--sp-space-2) !important;
    text-align: center;
    vertical-align: middle;
}

/* The label wrapping each selection control is its hit area. */
.ui-table-select-target {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    vertical-align: middle;
    cursor: pointer;
}

.ui-table-select-cell input[type="checkbox"] {
    width: 0.9375rem;
    height: 0.9375rem;
    margin: 0;
    accent-color: var(--uidt-accent);
    cursor: pointer;
    vertical-align: middle;
}

.ui-table-select-cell input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--uidt-accent);
    outline-offset: 2px;
}

/* The selection bar takes over the toolbar row rather than stacking below it,
   so acting on a selection never pushes the rows the user just picked. */
.ui-table-selection-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-2) var(--sp-space-3);
    padding: var(--sp-space-2) var(--sp-space-3);
    border-bottom: 1px solid var(--uidt-border);
    background: color-mix(in srgb, var(--sp-color-brand) 12%, var(--sp-color-surface));
    color: var(--uidt-ink);
    font-size: var(--sp-font-size-sm);
}

.ui-table-selection-count {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.ui-table-selection-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-2);
    margin-left: auto;
}

/* ------------------------------------------------- empty and loading frame

   Both states keep the frame's own border and padding so swapping rows for an
   empty state does not change the card's height by a border width. */

.ui-table-state {
    display: grid;
    place-items: center;
    padding: var(--sp-space-6) var(--sp-space-4);
    min-height: 9rem;
    background: var(--uidt-surface);
}

/* First load: the real header over placeholder rows of the real row height,
   filling the frame edge to edge like the table it stands in for. */
.ui-table-state-loading {
    display: block;
    padding: 0;
    min-height: 0;
}

.ui-data-table-skeleton {
    width: 100%;
    overflow: hidden;
}

.ui-data-table-skeleton-table > thead > tr > th {
    color: var(--uidt-ink-muted);
}

.ui-data-table-skeleton-table > tbody > tr > td {
    height: var(--uidt-row-min-height);
    padding: var(--uidt-cell-pad-y) var(--uidt-cell-pad-x);
    border-top: 1px solid var(--uidt-border-soft);
}

.ui-data-table-skeleton-row {
    display: grid;
    grid-template-columns: 2fr repeat(3, minmax(0, 1fr));
    align-items: center;
    gap: var(--sp-space-4);
}

/* Refresh with rows on screen: rows stay readable and usable; past the
   loading delay they dim slightly under a progress line on the frame edge. */
.ui-table-scroll {
    transition: var(--sp-motion-transition-control);
}

.ui-table-scroll.is-refreshing {
    opacity: var(--sp-opacity-refreshing);
    transition: var(--sp-motion-transition-pending);
}

.ui-table-refresh-progress {
    position: absolute;
    inset: 0 0 auto;
    z-index: 4;
    height: 2px;
    animation: var(--sp-motion-loading-in);
    pointer-events: none;
}

/* ============================================================================
   WORKSPACE CHROME — injected after render by wwwroot/js/tableWorkspace.js.

   It has no Blazor owner, so the contract lives here: the class names below
   are the ones that file creates and toggles.
   ========================================================================= */

.enhanced-table-scroll {
    overflow: auto;
    max-width: 100%;
}

.enhanced-table-scroll.is-horizontal-fit {
    overflow-x: hidden;
}

table.enhanced-data-table {
    /* Fit the viewport and let wrappable content share the available width.
       Intrinsic minimums and explicit column widths still permit real overflow. */
    width: 100%;
    min-width: 100%;
    max-width: none;
    table-layout: auto;
    border-collapse: separate;
    border-spacing: 0;
    margin: 0;
}

/* A table the workspace enhanced that is NOT a UiDataTable — a page-owned
   <table> such as a weather archive or a spreadsheet panel — still gets the
   header, row and density treatment, keyed off the classes the script adds.
   UiDataTable opts out of this block: it is already styled from the token
   layer above, and two sets of padding rules on one table would fight. */
.enhanced-data-table:not(.ui-table) > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 8;
    border-bottom: 1px solid var(--sp-color-border);
    background: color-mix(in srgb, var(--sp-color-surface-muted) 82%, var(--sp-color-surface));
    color: var(--sp-color-ink-muted);
    font-size: var(--sp-font-size-xs);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
}

.enhanced-data-table:not(.ui-table) > tbody > tr > :is(td, th) {
    border-bottom: 1px solid color-mix(in srgb, var(--sp-color-border) 55%, transparent);
    background: transparent;
}

.enhanced-data-table:not(.ui-table) > tbody > tr:hover > :is(td, th) {
    background: color-mix(in srgb, var(--sp-color-brand) 7%, var(--sp-color-surface));
}

.enhanced-data-table:not(.ui-table).enhanced-table-compact > * > tr > :is(th, td) {
    padding: 0.4375rem 0.625rem;
    font-size: 0.8125rem;
    line-height: 1.35;
}

.enhanced-data-table:not(.ui-table).enhanced-table-comfortable > * > tr > :is(th, td) {
    padding: 0.625rem 0.75rem;
    font-size: 0.8438rem;
    line-height: 1.45;
}

:is(.is-overflow-start, .is-overflow-end)
    .enhanced-data-table:not(.ui-table).enhanced-sticky-first-column
    > thead
    > tr
    > th:first-child,
:is(.is-overflow-start, .is-overflow-end)
    .enhanced-data-table:not(.ui-table).enhanced-sticky-first-column
    > tbody
    > tr
    > :is(td, th):first-child {
    position: sticky;
    left: 0;
    z-index: 5;
    background: var(--sp-color-surface);
}

:is(.is-overflow-start, .is-overflow-end)
    .enhanced-data-table:not(.ui-table).enhanced-sticky-first-column
    > thead
    > tr
    > th:first-child {
    z-index: 9;
    background: color-mix(in srgb, var(--sp-color-surface-muted) 82%, var(--sp-color-surface));
}

@media (max-width: 767.98px) {
    .enhanced-data-table:not(.ui-table).enhanced-sticky-first-column > thead > tr > th:first-child,
    .enhanced-data-table:not(.ui-table).enhanced-sticky-first-column > tbody > tr > :is(td, th):first-child {
        position: static;
    }
}

.enhanced-column-hidden,
.enhanced-page-hidden {
    display: none;
}

/* A directory's minimum width is sized for its full column set. Once columns are hidden (by default on narrower
   desktops, or from the Columns menu) the table falls back to a feature's compact minimum, so the remaining columns
   fit beside the pinned Actions column instead of sliding under it. */
table.ui-data-table-directory:has(> thead > tr > th.enhanced-column-hidden) {
    min-width: var(--directory-table-compact-min-width, 0);
}

.enhanced-cell-focus:focus-visible {
    outline: 2px solid var(--sp-color-brand);
    outline-offset: -2px;
}

.enhanced-cell-focus:not(:focus-visible) {
    outline: none;
}

/* ---------------------------------------------------------------- toolbar */

.enhanced-table-toolbar {
    position: relative;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-2);
    min-height: 2.25rem;
    padding: 0.25rem var(--sp-space-2) 0.25rem var(--sp-space-3);
    border-bottom: 1px solid var(--sp-color-border);
    background: color-mix(in srgb, var(--sp-color-surface-muted) 45%, var(--sp-color-surface));
    color: var(--sp-color-ink-strong);
}

.enhanced-table-toolbar-left,
.enhanced-table-toolbar-right,
.enhanced-table-paging {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-space-1);
}

.enhanced-table-toolbar-left {
    gap: var(--sp-space-2);
    min-width: 0;
}

.enhanced-table-toolbar-right {
    margin-left: auto;
    gap: 0.375rem;
}

.enhanced-row-count {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--sp-color-ink-muted);
    font-size: var(--sp-font-size-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
    white-space: nowrap;
}

.enhanced-row-count::before {
    content: "";
    width: 0.375rem;
    height: 0.375rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--sp-color-brand) 70%, transparent);
}

.enhanced-export-manifest {
    flex-basis: 100%;
    order: 9;
    color: var(--sp-color-success-text);
    font-size: var(--sp-font-size-xs);
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* ----------------------------------------------------- workspace buttons */

.enhanced-table-button,
.enhanced-page-size {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-height: 1.75rem;
    height: 1.75rem;
    padding: 0 0.5rem;
    border: 1px solid transparent;
    border-radius: var(--sp-radius-sm);
    background: transparent;
    color: var(--sp-color-ink-secondary);
    cursor: pointer;
    font-size: var(--sp-font-size-xs);
    font-weight: 700;
    letter-spacing: 0.01em;
    white-space: nowrap;
}

.enhanced-page-size {
    padding-inline: 0.375rem;
    border-color: var(--sp-color-border);
    background: var(--sp-color-surface);
    width: auto;
}

.enhanced-table-button:hover,
.enhanced-page-size:hover {
    border-color: var(--sp-color-border);
    background: var(--sp-color-surface);
    color: var(--sp-color-ink-strong);
}

.enhanced-table-button:focus-visible,
.enhanced-page-size:focus-visible {
    outline: none;
    border-color: var(--sp-color-brand);
    box-shadow: var(--sp-focus-ring);
}

.enhanced-table-button[aria-expanded="true"] {
    border-color: var(--sp-color-brand-border);
    background: var(--sp-color-brand-soft);
    color: var(--sp-color-brand-text);
}

.enhanced-table-button:disabled {
    opacity: 0.4;
    cursor: default;
}

.enhanced-table-button[aria-busy="true"] {
    border-color: var(--sp-color-border);
    background: var(--sp-color-surface);
}

/* Icons are drawn from masks on a pseudo-element so they contribute nothing
   to textContent or to the accessible name the toolbar buttons expose. */
.enhanced-table-button[data-table-action]::before {
    content: "";
    flex: none;
    width: 0.875rem;
    height: 0.875rem;
    background: currentcolor;
    mask-repeat: no-repeat;
    mask-position: center;
    mask-size: contain;
}

.enhanced-table-button[data-table-action="columns"]::before {
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2h3.2v12H2zm4.4 0h3.2v12H6.4zm4.4 0H14v12h-3.2z'/%3E%3C/svg%3E");
}

.enhanced-table-button[data-table-action="density"]::before {
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2.4h12v1.6H2zm0 3.8h12v1.6H2zm0 3.8h12v1.6H2zm0 3.6h12V15H2z'/%3E%3C/svg%3E");
}

.enhanced-table-button[data-table-action="export"]::before {
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.2 1.6h1.6v6.5l2.1-2.1 1.1 1.1L8 11.2 4 7.1l1.1-1.1 2.1 2.1zM2.4 11.2H4v2.4h8v-2.4h1.6v4H2.4z'/%3E%3C/svg%3E");
}

.enhanced-table-button[data-table-action="reset"]::before {
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2.4a5.6 5.6 0 1 1-5.28 7.44l1.5-.53A4 4 0 1 0 8 4v2.4L4.4 3.6 8 .8z'/%3E%3C/svg%3E");
}

.enhanced-table-button[data-table-action="options"]::before {
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 3.2h6.6a2 2 0 0 1 3.8 0H14v1.6h-1.6a2 2 0 0 1-3.8 0H2zm0 4h1.6a2 2 0 0 1 3.8 0H14v1.6H7.4a2 2 0 0 1-3.8 0H2zm0 4h6.6a2 2 0 0 1 3.8 0H14v1.6h-1.6a2 2 0 0 1-3.8 0H2z'/%3E%3C/svg%3E");
}

.enhanced-table-button[aria-busy="true"]::before {
    display: none;
}

/* Table options gathers the tools on phones only; wider screens show the tools themselves. */
.enhanced-table-toolbar-right > .enhanced-table-button[data-table-action="options"] {
    display: none;
}

.enhanced-options-menu {
    display: grid;
    gap: var(--sp-space-1);
    min-width: 12rem;
}

.enhanced-options-menu .enhanced-table-button {
    justify-content: flex-start;
    margin-top: 0;
}

.enhanced-table-reset {
    color: var(--sp-color-ink-subtle);
}

.enhanced-export-spinner {
    flex: none;
    width: 0.75rem;
    height: 0.75rem;
    border: 2px solid currentcolor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: var(--sp-motion-spin);
}

/* ------------------------------------------------------------ paging bar

   Paging sits under the rows it pages, not above them: the control is next to
   the last row the reader saw rather than at the far end of a long scroll. */

.enhanced-table-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sp-space-2);
    padding: 0.375rem var(--sp-space-2);
    border-top: 1px solid var(--sp-color-border);
    background: color-mix(in srgb, var(--sp-color-surface-muted) 45%, var(--sp-color-surface));
}

.enhanced-table-paging {
    gap: 0.25rem;
    color: var(--sp-color-ink-muted);
    font-size: var(--sp-font-size-xs);
    font-variant-numeric: tabular-nums;
}

.enhanced-table-paging > span {
    padding-inline: var(--sp-space-2);
    font-weight: 650;
    white-space: nowrap;
}

.enhanced-table-paging .enhanced-table-button {
    justify-content: center;
    min-width: 1.75rem;
    padding-inline: 0.25rem;
    border-color: var(--sp-color-border);
    background: var(--sp-color-surface);
    font-size: 0.875rem;
    line-height: 1;
}

/* ------------------------------------------------------- column resizers */

.enhanced-column-resizer {
    position: absolute;
    top: 0;
    /* Keep the last column's hit area inside the table so it cannot create
       a few pixels of horizontal overflow on an otherwise fitted table. */
    right: 0;
    z-index: 20;
    width: 0.4375rem;
    height: 100%;
    cursor: col-resize;
    touch-action: none;
}

.enhanced-column-resizer::after {
    content: "";
    position: absolute;
    top: 20%;
    left: 50%;
    width: 2px;
    height: 60%;
    border-radius: 1px;
    background: var(--sp-color-brand);
    opacity: 0;
    transform: translateX(-50%);
}

.enhanced-column-resizer:hover::after,
.enhanced-column-resizer.is-resizing::after {
    opacity: 1;
}

/* --------------------------------------------------------- column chooser */

/* Shown as a manual popover in the top layer and placed by tableWorkspace from
   its Columns button, so a short or clipped table frame cannot hide it. */
.enhanced-column-menu {
    position: fixed;
    inset: auto;
    margin: 0;
    z-index: 1090;
    display: grid;
    gap: 0.125rem;
    width: min(19rem, calc(100vw - 2rem));
    max-height: 24rem;
    overflow: auto;
    padding: var(--sp-space-2);
    border: 1px solid var(--sp-color-border);
    border-radius: var(--sp-radius-md);
    background: var(--sp-color-surface);
    color: var(--sp-color-ink);
    box-shadow: var(--sp-shadow-md);
}

.enhanced-column-menu > strong {
    padding: 0.125rem var(--sp-space-2) var(--sp-space-2);
    color: var(--sp-color-ink-subtle);
    font-size: var(--sp-font-size-xs);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.enhanced-column-menu label {
    display: flex;
    align-items: center;
    gap: var(--sp-space-2);
    margin: 0;
    padding: 0.3125rem var(--sp-space-2);
    border-radius: var(--sp-radius-sm);
    cursor: pointer;
    font-size: var(--sp-font-size-sm);
}

.enhanced-column-menu label:hover {
    background: var(--sp-color-surface-muted);
}

.enhanced-column-menu label:has(input:focus-visible) {
    outline: 2px solid var(--sp-color-brand);
    outline-offset: -2px;
}

.enhanced-column-menu label > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.enhanced-column-menu input {
    flex: none;
    width: 0.9375rem;
    height: 0.9375rem;
    min-height: 0;
    margin: 0;
    accent-color: var(--sp-color-brand);
}

/* The column search shares the menu's input selector with the checkboxes but keeps
   the global search glyph, so it needs full width and the glyph's start inset. The
   workspace density input reset (ui-workspace-density.css) has a higher specificity
   and would put the placeholder under the glyph, hence !important on the inset. */
.enhanced-column-menu > input.enhanced-column-menu-search {
    width: 100%;
    margin-bottom: var(--sp-space-1);
    padding-inline: 2.25rem 0.5rem !important;
    font-weight: 400;
}

.enhanced-column-menu .enhanced-table-button {
    justify-content: center;
    margin-top: var(--sp-space-2);
    border-color: var(--sp-color-border);
    background: var(--sp-color-surface);
}

/* ============================================================================
   RESPONSIVE — below the medium breakpoint a wide operational table stops
   being a table. Each row becomes a labelled card, which is why every call
   site is asked for data-label on its body cells.
   ========================================================================= */

@media (max-width: 767.98px) {
    .ui-data-table-skeleton.is-cards thead {
        display: none;
    }

    .ui-data-table-skeleton.is-cards .ui-data-table-skeleton-row {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--sp-space-2);
        padding-block: var(--sp-space-2);
    }

    .ui-table-cards > thead {
        display: none;
    }

    /* Column headings are gone, but sorting and select-all must not be: the
       header row collapses to a wrapping strip of just the sortable headings
       and the selection cell, shown as chips above the cards. */
    .ui-table-cards > thead:has(.ui-data-table-sort-button, .ui-table-select-cell input[type="checkbox"]) {
        display: block;
    }

    .ui-table-cards > thead > tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--sp-space-2);
        padding: var(--sp-space-2) var(--sp-space-2) 0;
    }

    .ui-table-cards > thead > tr > th {
        display: none;
    }

    .ui-table-cards
        > thead
        > tr
        > th:is(
            :has(> .ui-data-table-sort-button),
            :has(> .ui-column-controls),
            .ui-table-select-cell:has(input[type="checkbox"])
        ):not(.enhanced-column-hidden) {
        position: static;
        display: inline-flex;
        align-items: center;
        min-height: 2.25rem;
        padding: 0 0.625rem;
        border: 1px solid var(--uidt-border);
        border-radius: 999px;
        background: var(--uidt-surface);
        box-shadow: none;
    }

    .ui-table-cards > thead > tr > th > .ui-data-table-sort-button {
        width: auto;
        min-height: 2.25rem;
        margin: 0;
        padding: 0;
    }

    .ui-table-cards > thead > tr > th.ui-table-select-cell {
        gap: 0.5rem;
        width: auto;
        padding-inline: 0.625rem !important;
    }

    .ui-table-cards > thead > tr > th.ui-table-select-cell::after {
        content: "All";
    }

    .ui-table-cards > tbody > tr > td.ui-table-select-cell {
        padding-inline: var(--uidt-select-pad-inline, var(--sp-space-3)) !important;
    }

    .ui-table-cards .ui-table-select-cell input:is([type="checkbox"], [type="radio"]) {
        width: 1.25rem;
        height: 1.25rem;
    }

    /* The shared table owns its mobile reset: a feature's desktop minimum
       width (--directory-table-min-width or a scoped min-width) must not
       turn a card list back into a sideways-scrolling sheet. */
    .ui-table-cards,
    .ui-table-cards > tbody {
        display: block;
        width: 100% !important;
        min-width: 0 !important;
    }

    /* A table-caption box shrinks to its longest word once the table is a block. */
    .ui-table-cards > caption {
        display: block;
        width: 100%;
    }

    .ui-table-cards > tbody {
        display: grid;
        gap: var(--sp-space-2);
        padding: var(--sp-space-2);
    }

    /* Clip, not hidden: a card is not a scroll container, so an inline editor's sticky action bar
       (ui-sticky-action-bar) inside it stays pinned to the screen. */
    .ui-table-cards > tbody > tr {
        display: block;
        width: 100%;
        overflow: clip;
        border: 1px solid var(--uidt-border);
        border-radius: var(--sp-radius-sm);
        background: var(--uidt-surface);
        box-shadow: var(--sp-shadow-sm);
    }

    /* Virtualize spacers carry an inline height. Card borders and grid gaps
       must not change that measured geometry or appear as empty records. */
    .ui-table-cards[data-virtualized="true"] > tbody {
        display: block;
    }

    /* Page scrolling owns mobile virtualization; a clipped frame or an
       unbounded overflow:auto wrapper would become a false scroll root. */
    .ui-table-frame:has(> .ui-table-scroll > .ui-table-cards[data-virtualized="true"]),
    .ui-table-scroll:has(> .ui-table-cards[data-virtualized="true"]) {
        overflow: visible;
    }

    /* An inline editor's sticky action bar sticks to the page, so the card list's scroll port stands down
       while a card holds one; cards fit the width, so nothing needs the port meanwhile. */
    .ui-table-scroll:has(> .ui-table-cards .ui-sticky-action-bar) {
        overflow: visible;
    }

    .ui-table-cards[data-virtualized="true"] > tbody > tr.ui-data-table-row {
        margin-bottom: var(--sp-space-2);
    }

    .ui-table-cards[data-virtualized="true"] > tbody > tr:not(.ui-data-table-row) {
        /* Blazor supplies inline table-row display; mobile cards need block
           spacers with measurable width for IntersectionObserver. */
        display: block !important;
        overflow: visible;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
    }

    .ui-table-cards > tbody > tr > td,
    .ui-table-cards > tbody > tr > th {
        display: grid;
        grid-template-columns: minmax(6rem, 34%) minmax(0, 1fr);
        gap: var(--sp-space-2);
        width: 100%;
        padding: 0.5rem var(--sp-space-3);
        border-bottom: 1px solid var(--uidt-border-soft);
        text-align: start;
    }

    .ui-table-cards > tbody > tr > td:last-child,
    .ui-table-cards > tbody > tr > th:last-child {
        border-bottom: 0;
    }

    /* The desktop last-row reset above outranks the card divider, so the
       last card restates its field dividers. */
    table.ui-table.ui-table-cards > tbody > tr:last-child > td:not(:last-child),
    table.ui-table.ui-table-cards > tbody > tr:last-child > th:not(:last-child) {
        border-bottom: 1px solid var(--uidt-border-soft);
    }

    /* Column alignment (data-align) is a desktop column rule; a stacked card
       pairs label and value at the start, so it must not inherit end/center. */
    .ui-table-cards > tbody > tr > :is(td, th)[data-align] {
        text-align: start;
    }

    /* Grid cells stretch their items; a status or count pill keeps its pill size at the start of the value column. */
    .ui-table-cards > tbody > tr > :is(td, th) > :is(.badge, .ui-badge) {
        justify-self: start;
    }

    /* The card display rules outrank the plain paging and column-preference
       classes, so restate them: a paged-out row or a hidden column stays
       hidden when the table becomes cards. */
    .ui-table-cards > tbody > tr.enhanced-page-hidden,
    .ui-table-cards > tbody > tr > .enhanced-column-hidden {
        display: none;
    }

    /* Desktop column widths are stored per table and written inline by
       tableWorkspace; a card cell always spans the card instead. */
    .ui-table-cards > tbody > tr > :is(td, th) {
        width: 100% !important;
        min-width: 0 !important;
        max-width: none !important;
    }

    /* A cell with no label would silently lose its meaning once the header is
       gone, so unlabelled cells span the card instead of pretending to pair. */
    .ui-table-cards > tbody > tr > td:not([data-label]) {
        grid-template-columns: minmax(0, 1fr);
    }

    .ui-table-cards > tbody > tr > td[data-label]::before {
        content: attr(data-label);
        color: var(--uidt-ink-faint);
        font-size: var(--sp-font-size-xs);
        font-weight: 700;
        letter-spacing: 0.04em;
        line-height: 1.3;
        text-transform: uppercase;
    }

    /* An empty data-label opts a cell out of the column label (the header
       label is not borrowed for it): its content names itself, such as an
       inline edit field with its own label, so it spans the card, and an
       empty opted-out cell is not shown at all. */
    .ui-table-cards > tbody > tr > td[data-label=""] {
        grid-template-columns: minmax(0, 1fr);
    }

    .ui-table-cards > tbody > tr > td[data-label=""]::before {
        content: none;
    }

    .ui-table-cards > tbody > tr > td[data-label=""]:empty {
        display: none;
    }

    /* A totals footer follows the cards as one summary strip: its cells wrap
       side by side, label first, so a column-spanning TOTAL keeps its value. */
    .ui-table-cards > tfoot {
        display: block;
        padding: 0 var(--sp-space-2) var(--sp-space-2);
    }

    .ui-table-cards > tfoot > tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        justify-content: space-between;
        gap: 0.25rem var(--sp-space-3);
        padding: 0.5rem var(--sp-space-3);
        border: 1px solid var(--uidt-border);
        border-radius: var(--sp-radius-sm);
        background: var(--uidt-surface-head);
    }

    table.ui-table.ui-table-cards > tfoot > tr > :is(td, th) {
        position: static;
        display: block;
        padding: 0;
        border: 0;
        background: transparent;
        text-align: start;
    }

    .ui-table-cards > tfoot > tr > :is(td, th)[data-label]::before {
        content: attr(data-label) " ";
        color: var(--uidt-ink-faint);
        font-size: var(--sp-font-size-xs);
        font-weight: 700;
        letter-spacing: 0.04em;
        text-transform: uppercase;
    }

    .ui-table-cards > tbody > tr.ui-data-table-row.is-selected > :first-child {
        box-shadow: none;
    }

    .ui-table-cards > tbody > tr.ui-data-table-row.is-selected {
        border-color: var(--uidt-accent);
        box-shadow: 0 0 0 1px var(--uidt-accent);
    }

    .enhanced-table-toolbar {
        align-items: stretch;
        flex-direction: column;
    }

    .enhanced-table-toolbar-right {
        margin-left: 0;
    }

    .enhanced-table-footer {
        justify-content: center;
    }

    .ui-table-edge {
        display: none;
    }
}

/* Table tools follow the pointer, not the width: a landscape phone or touch tablet keeps 44px tools. */
@media (pointer: coarse) {
    .enhanced-table-button,
    .enhanced-page-size {
        min-height: 2.75rem;
        height: 2.75rem;
        padding-inline: 0.75rem;
    }

    .enhanced-column-menu {
        max-height: min(24rem, 60dvh);
    }

    .enhanced-column-menu label {
        min-height: 2.75rem;
    }

    .enhanced-column-menu > input.enhanced-column-menu-search {
        padding-inline: 2.25rem 0.75rem !important;
    }
}

@media (max-width: 767.98px) and (pointer: coarse) {
    .ui-table-cards
        > thead
        > tr
        > th:is(
            :has(> .ui-data-table-sort-button),
            :has(> .ui-column-controls),
            .ui-table-select-cell:has(input[type="checkbox"])
        ),
    .ui-table-cards > thead > tr > th > .ui-data-table-sort-button {
        min-height: 2.75rem;
    }
}

@media (max-width: 399.98px) {
    .ui-table-cards > tbody > tr > td,
    .ui-table-cards > tbody > tr > th {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.125rem;
    }
}

/* ---------------------------------------------------------------- print */

@media print {
    .ui-table-frame {
        border: 0;
        box-shadow: none;
    }

    .enhanced-table-toolbar,
    .enhanced-table-footer,
    .ui-table-edge,
    .enhanced-column-resizer {
        display: none;
    }

    .ui-table-scroll,
    .enhanced-table-scroll {
        overflow: visible;
        max-height: none;
    }

    .enhanced-column-hidden {
        display: table-cell;
    }

    table.ui-table > thead > tr > th {
        position: static;
        border-bottom: 1px solid #000;
    }
}

.enhanced-table-paging .enhanced-page-size-field {
    width: 7.5rem;
    min-width: 0;
}

/* Card tables on phones page 20 cards at a time (tableWorkspace registerPaging), so the rows-per-page choice
   is left to wider screens; the previous/next pager stays. */
@media (max-width: 767.98px) {
    .ui-table-frame:has(.ui-table-cards) .enhanced-table-paging .enhanced-page-size-field {
        display: none;
    }
}

/* The rows select shares the paging buttons' control box: the compact height on desktop and the
   touch height wherever the select itself grows to it, so one paging row never mixes heights. */
.enhanced-table-paging .enhanced-page-size-field .ui-select {
    --uis-height: var(--sp-control-height-sm);
}

.enhanced-table-paging .enhanced-page-size-field .ui-select-trigger {
    padding-block: 0;
}

@media (max-width: 767.98px), (pointer: coarse) {
    .enhanced-table-paging .enhanced-page-size-field .ui-select {
        --uis-height: var(--sp-control-height-touch);
    }

    .enhanced-table-paging .enhanced-table-button {
        min-width: var(--sp-control-height-touch);
        min-height: var(--sp-control-height-touch);
        height: var(--sp-control-height-touch);
    }

    /* Table tools keep at least a square touch target. */
    .enhanced-table-button {
        min-width: var(--sp-control-height-touch);
        justify-content: center;
    }
}

/* Counted directory filters share compact rounded chrome; ordinary table
   commands retain the shared button shape. Touch targets expand independently. */
.ui-toolbar-directory .ui-table-filter-chip.btn {
    min-height: 2rem;
    padding: 0.125rem 0.625rem;
    font-size: 0.78125rem;
    font-weight: 600;
    white-space: nowrap;
}
.ui-table-filter-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    padding: 0 0.3125rem;
    border-radius: 999px;
    background: var(--sp-color-brand-soft);
    color: var(--sp-color-ink-muted);
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}
/* A darkening pill keeps the white count readable on the pressed chip in both
   themes; a lightening one fell below 4.5:1 on the dark-theme brand fill. */
.ui-table-filter-chip[aria-pressed="true"] .ui-table-filter-count {
    background: rgb(0 0 0 / 18%);
    color: inherit;
}
@media (pointer: coarse) {
    .ui-toolbar-directory .ui-table-filter-chip.btn {
        min-height: 2.75rem;
    }
}

/* Every phone table keeps its toolbar to one row: the row count and one Table options button whose menu holds
   Columns, Export View and Reset. Compact has no effect on cards and is not offered. */
@media (max-width: 767.98px) {
    .ui-table-frame > .enhanced-table-toolbar {
        flex-direction: row;
        align-items: center;
        min-height: 2rem;
    }
    .ui-table-frame .enhanced-table-toolbar-right {
        margin-left: auto;
    }
    .ui-table-frame
        .enhanced-table-toolbar-right
        > .enhanced-table-button:is(
            [data-table-action="columns"],
            [data-table-action="density"],
            [data-table-action="export"],
            [data-table-action="reset"]
        ) {
        display: none;
    }
    .ui-table-frame .enhanced-table-toolbar-right > .enhanced-table-button[data-table-action="options"] {
        display: inline-flex;
    }
}

/* Directory filters size to their content instead of filling the remaining
   screen. A search/chip/select band can then wrap as individual controls. */
.ui-toolbar-directory .ui-table-filter-group {
    display: contents;
}
.ui-toolbar-directory .ui-toolbar-left > .ui-field {
    flex: 0 1 auto;
    width: auto;
}
.ui-toolbar-directory .ui-toolbar-left > .ui-field:has(input[type="search"]:not(.ui-select-search)) {
    flex: 0 1 18.125rem;
    min-width: 12.5rem;
    max-width: 18.125rem;
}
.ui-toolbar-directory .ui-toolbar-left > .ui-field:has(.ui-select) {
    flex: 0 1 auto;
    width: auto;
    min-width: 8rem;
    max-width: 12rem;
}
@media (max-width: 575.98px) {
    .ui-toolbar-directory .ui-toolbar-left > .ui-field:has(input[type="search"]:not(.ui-select-search)) {
        flex: 1 1 100%;
        max-width: none;
    }
}

/* Directory filters share one wrapping band with actions rather than reserving
   a tall filter-only column beside a separate action rail. The band packs from
   the start: with flattened chip groups, space-between would spread the chips
   of every wrapped line apart. The action rail keeps its own auto margin. */
.ui-toolbar.ui-toolbar-directory {
    justify-content: flex-start;
}
.ui-toolbar-directory .ui-toolbar-left {
    display: contents;
}
.ui-toolbar-directory .ui-toolbar-right {
    margin-inline-start: auto;
}
.ui-toolbar-directory > .ui-field {
    flex: 0 1 10rem;
    min-width: 8rem;
    margin: 0;
}
.ui-toolbar-directory > .ui-field:has(input[type="search"]:not(.ui-select-search)) {
    flex: 0 1 18.125rem;
    min-width: 12.5rem;
}
.ui-toolbar-directory .ui-toolbar-left > .ui-field:has(select) {
    flex: 0 1 10rem;
    min-width: 8rem;
    max-width: 12rem;
}
.ui-toolbar-directory .ui-toolbar-left > .ui-field > .form-label,
.ui-toolbar-directory .ui-toolbar-more-filters > .ui-field > .form-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
/* Phones with a folded Filters toggle: the search shares its row with the
   toggle (UiToolbar MoreFilters), so More and the primary action fit the next. */
@media (max-width: 700px) {
    .ui-toolbar-phone-overflow.ui-toolbar-directory
        .ui-toolbar-left
        > .ui-field:has(input[type="search"]:not(.ui-select-search)):has(~ .ui-phone-filters) {
        flex: 1 1 10rem;
        min-width: 0;
        max-width: none;
    }
}
/* Folded secondary filters (UiToolbar MoreFilters) size like the band's own
   selects wherever the phone panel does not own their layout. */
@media (min-width: 701px) {
    .ui-toolbar-directory .ui-toolbar-more-filters > .ui-field {
        flex: 0 1 10rem;
        width: auto;
        min-width: 8rem;
        max-width: 12rem;
        margin: 0;
    }
}
@media (max-width: 575.98px) {
    .ui-toolbar-directory .ui-toolbar-left > .ui-field:has(input[type="search"]:not(.ui-select-search)) {
        flex-basis: 100%;
        min-width: 0;
        max-width: none;
    }
    /* Below the full-width search, filters form an even two-column grid instead
       of content-sized controls that leave ragged gaps on each row. */
    .ui-toolbar-directory .ui-toolbar-left > .ui-field:not(:has(input[type="search"]:not(.ui-select-search))) {
        flex: 0 1 calc(50% - 4px);
        min-width: 0;
        max-width: none;
    }
    .ui-toolbar-directory .ui-toolbar-right {
        margin-inline-start: 0;
    }
}

.ui-data-table-directory thead .ui-column-controls .btn.ui-table-column-sort {
    min-height: 1.5rem;
    padding-block: 0;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* Touch screens: each row's selection label and the directory table tools
   are 44px targets. The selection target overhangs its cell padding so the
   card layout does not move. */
@media (max-width: 767.98px), (pointer: coarse) {
    .ui-table-select-target {
        min-width: var(--sp-control-height-touch);
        min-height: var(--sp-control-height-touch);
        margin: calc((1.25rem - var(--sp-control-height-touch)) / 2);
    }

    /* In the phone select-all chip the label sets the chip height instead of overhanging it, so the box stays centred. */
    .ui-table-cards > thead .ui-table-select-target {
        margin: 0;
    }
}

@media (max-width: 767.98px) and (pointer: coarse) {
    .ui-table-frame .enhanced-table-toolbar-right,
    .ui-table-frame:has(.ui-data-table-directory) .enhanced-table-toolbar-right {
        gap: 0.5rem;
    }
}

/* Wider touch screens (a phone in landscape) keep their 44px table tools 8px apart too. */
@media (pointer: coarse) {
    .ui-table-frame .enhanced-table-toolbar-right {
        gap: 0.5rem;
    }
}

/* Phone cards: phone and email links get a 44px hit area. Padding with an
   equal negative margin keeps the card layout where it was. */
@media (max-width: 767.98px) {
    .ui-table-cards a:is([href^="tel:"], [href^="mailto:"]) {
        display: inline-block;
        padding-block: 0.9375rem;
        margin-block: -0.9375rem;
    }
}
