/* GENERATED — do not edit. Concatenated from Styles/*.css at build time. */
/* =============================================================================
   Theming contract
   -----------------------------------------------------------------------------
   Public tokens are named --stingray-dt-*. This package NEVER declares them — it
   only reads them, so a consumer can set them anywhere up the tree and win.

   Three selectors, each earning its place:

     :root                 puts the resolved tier on the document, so content
                           rendered OUTSIDE a table — the column chooser, which
                           lives in the host's popup — still resolves its tokens.
     .stingray-data-table  re-resolves per instance, so wrapping one table in an
                           element that sets a public token themes only that table.
                           A :root-only resolver would compute the private tier
                           once and per-instance theming would silently stop working.
     .sdt-chooser           covers the panel when it is rendered detached.

   RULE FOR REVIEWERS: outside this file, no colour, size, radius or weight
   literal. Anything needing a value gets a token first.
   ============================================================================= */

:root,
.stingray-data-table,
.sdt-chooser {
    /* Surface */
    --_sdt-bg: var(--stingray-dt-bg, #ffffff);
    --_sdt-border-color: var(--stingray-dt-border-color, #eceef4);
    --_sdt-radius: var(--stingray-dt-radius, .75rem);
    /* Block-only by default. Inline padding here would hold the row hover tint away from the
       card's edges; the cells' own padding already insets the content. Block padding stays so the
       first and last rows never collide with the rounded corners — which is what lets this avoid
       `overflow: hidden`, and that matters because it would create a scroll container and break
       the frozen columns' stickiness. */
    --_sdt-padding: var(--stingray-dt-padding, .25rem 0);
    --_sdt-shadow: var(--stingray-dt-shadow, 0 1px 2px rgba(16, 24, 40, .04));

    /* Size. Both default to the component's natural behaviour, so setting neither leaves the
       table exactly as it was: auto height, and a max-height that only bites inside a container
       with a definite one.

       The Height and MaxHeight parameters write the PRIVATE property directly on the root
       element, which is what layers them: a public token set at :root, on a section, or on a
       CssClass is the default for every table it covers, and a table's own parameter beats it
       because an inline style outranks any selector. A table opts back out with Height="auto"
       or MaxHeight="none" — the parameters pass their string through, so the CSS keyword is the
       escape hatch.

       Re-resolving here is also the only thing stopping a table in a RowDetailTemplate from
       inheriting its parent's height. It is a DESCENDANT of the outer table, so a custom property
       set on the outer root reaches it — the same hazard the resize properties carry an instance
       id for. Measured before this line existed: an outer table at Height="20rem" dragged its
       inner table from its natural 245px to 320px. */
    --_sdt-height: var(--stingray-dt-height, auto);
    --_sdt-max-height: var(--stingray-dt-max-height, 100%);

    /* Type */
    --_sdt-font-family: var(--stingray-dt-font-family, inherit);
    --_sdt-font-size: var(--stingray-dt-font-size, .875rem);
    --_sdt-text-color: var(--stingray-dt-text-color, #1f2a4e);
    /* Secondary TEXT — the footer range, the empty state, the search placeholder. 4.68:1 on the
       default surface, because WCAG 1.4.3 asks 4.5:1 of text regardless of how quiet it looks.
       The previous #8a93a8 measured 3.08:1 and now lives on as --_sdt-icon-muted-color below. */
    --_sdt-muted-color: var(--stingray-dt-muted-color, #6b7391);
    /* The same grey one tier lighter, for glyphs rather than text: 1.4.11 asks 3:1 of a non-text
       element that carries meaning, not 4.5:1, so the icons keep the lighter tone the text cannot. */
    --_sdt-icon-muted-color: var(--stingray-dt-icon-muted-color, #8a93a8);

    /* Accent */
    --_sdt-accent: var(--stingray-dt-accent, #1b2f7e);
    --_sdt-accent-soft: var(--stingray-dt-accent-soft, #e8edfb);

    /* Header */
    /* Opaque, not transparent: the header is sticky, so it has to paint over the rows scrolling
       beneath it. Setting this to a transparent colour will let them show through. */
    --_sdt-header-bg: var(--stingray-dt-header-bg, var(--_sdt-bg));
    --_sdt-header-color: var(--stingray-dt-header-color, #6b7391);
    --_sdt-header-font-size: var(--stingray-dt-header-font-size, .8125rem);
    --_sdt-header-font-weight: var(--stingray-dt-header-font-weight, 500);
    --_sdt-header-padding-y: var(--stingray-dt-header-padding-y, .875rem);
    --_sdt-header-padding-x: var(--stingray-dt-header-padding-x, .75rem);
    --_sdt-header-border-color: var(--stingray-dt-header-border-color, #eceef4);
    --_sdt-header-hover-bg: var(--stingray-dt-header-hover-bg, #f5f7fc);

    /* Table. min-width defaults to auto, so the table scrolls once its own content will
       not fit. Set an explicit value to stop narrow columns squashing before that. */
    --_sdt-table-min-width: var(--stingray-dt-table-min-width, auto);
    /* Keeps a control tabbed to from landing UNDER the sticky header — WCAG 2.2 2.4.11. The header
       is its two paddings plus one line, so this tracks it rather than hardcoding a height. */
    --_sdt-sticky-scroll-padding: var(--stingray-dt-sticky-scroll-padding, calc(var(--_sdt-header-padding-y) * 2 + 1.25rem));

    /* Sort indicator */
    /* The unsorted glyph is the only thing saying a column CAN be sorted, so it is a meaningful
       non-text element and 1.4.11 applies. #b6bccd measured 1.90:1. */
    --_sdt-sort-indicator-color: var(--stingray-dt-sort-indicator-color, var(--_sdt-icon-muted-color));
    --_sdt-sort-indicator-active-color: var(--stingray-dt-sort-indicator-active-color, var(--_sdt-accent));
    --_sdt-sort-indicator-width: var(--stingray-dt-sort-indicator-width, .625rem);
    --_sdt-sort-indicator-height: var(--stingray-dt-sort-indicator-height, .875rem);

    /* Expansion */
    --_sdt-indent: var(--stingray-dt-indent, 1.5rem);
    /* The toggle's box, and a token rather than a literal in 06-expansion.css because the expand
       column's width under AllowColumnResize is computed from it — a size living in two files
       would drift into a clipped chevron. The opposite number of --stingray-dt-check-size in the
       other chrome column. */
    --_sdt-expand-toggle-size: var(--stingray-dt-expand-toggle-size, 1.5rem);
    /* Each chrome column holds one control and nothing else, so its width is that control plus the
       row padding either side. Auto layout works this out on its own — `width: 1px` and
       shrink-to-fit — but a fixed-layout column cannot size to content, so 18-resize.css states
       both from here. Private only, and deliberately: this is arithmetic the stylesheet owes
       itself, not a theming choice, and a consumer who wants either column roomier has the two
       tokens it is built from. */
    --_sdt-expand-cell-width: calc(var(--_sdt-expand-toggle-size) + 2 * var(--_sdt-row-padding-x));
    --_sdt-detail-bg: var(--stingray-dt-detail-bg, #fbfcfe);
    --_sdt-expand-icon-color: var(--stingray-dt-expand-icon-color, var(--_sdt-icon-muted-color));
    --_sdt-expand-hover-bg: var(--stingray-dt-expand-hover-bg, #e9edf7);
    /* Tinted, not transparent. A child row that paints nothing of its own is distinguishable from
       its parent only by the indent — which is a weak signal in a table and no signal at all in a
       card, where nesting has to carry the meaning. Set it back to transparent for the flat look. */
    --_sdt-child-row-bg: var(--stingray-dt-child-row-bg, #f7f9fd);

    /* Selection */
    --_sdt-check-size: var(--stingray-dt-check-size, 1rem);
    /* The selection column's half of the arithmetic above. Same shape, its own control. */
    --_sdt-select-cell-width: calc(var(--_sdt-check-size) + 2 * var(--_sdt-row-padding-x));
    --_sdt-check-radius: var(--stingray-dt-check-radius, .25rem);
    /* The unchecked box's only visible edge, which is the boundary of a UI component — 3:1 under
       1.4.11. #c3c9d9 measured 1.66:1 and read as an absent control on a bright screen. */
    --_sdt-check-border-color: var(--stingray-dt-check-border-color, var(--_sdt-icon-muted-color));
    --_sdt-check-bg: var(--stingray-dt-check-bg, #ffffff);
    --_sdt-check-checked-bg: var(--stingray-dt-check-checked-bg, var(--_sdt-accent));
    --_sdt-check-checked-color: var(--stingray-dt-check-checked-color, #ffffff);
    /* Deliberately NOT --_sdt-accent-soft. That put selected at #e8edfb against a #eef1f8 hover —
       1.04:1 apart, which is to say identical — so the row tint conveyed nothing and the checkbox
       was doing all the work. These three are set together and must stay ordered:
       hover < selected < selected-and-hovered. */
    --_sdt-row-selected-bg: var(--stingray-dt-row-selected-bg, #d7e1fa);
    --_sdt-row-selected-hover-bg: var(--stingray-dt-row-selected-hover-bg, #c9d6f7);
    /* On by default, unlike the hover bar below. A tint alone is a weak signal for a state the user
       chose, and it is the one signal a consumer's own row background can wipe out. */
    --_sdt-row-selected-accent-width: var(--stingray-dt-row-selected-accent-width, 3px);

    /* Frozen columns */
    --_sdt-frozen-bg: var(--stingray-dt-frozen-bg, var(--_sdt-bg));
    --_sdt-frozen-hover-bg: var(--stingray-dt-frozen-hover-bg, var(--_sdt-row-hover-bg));
    /* A pinned cell repaints every row state, because its own opaque background hides the row's. */
    --_sdt-frozen-selected-bg: var(--stingray-dt-frozen-selected-bg, var(--_sdt-row-selected-bg));
    --_sdt-frozen-selected-hover-bg: var(--stingray-dt-frozen-selected-hover-bg, var(--_sdt-row-selected-hover-bg));
    --_sdt-frozen-header-bg: var(--stingray-dt-frozen-header-bg, var(--_sdt-bg));
    --_sdt-frozen-edge-color: var(--stingray-dt-frozen-edge-color, #e3e7f0);
    --_sdt-frozen-edge-width: var(--stingray-dt-frozen-edge-width, 1px);

    /* Rows */
    --_sdt-row-padding-y: var(--stingray-dt-row-padding-y, .8125rem);
    --_sdt-row-padding-x: var(--stingray-dt-row-padding-x, .75rem);
    --_sdt-row-border-color: var(--stingray-dt-row-border-color, #f2f4f9);
    --_sdt-row-hover-bg: var(--stingray-dt-row-hover-bg, #eef1f8);
    --_sdt-row-accent-color: var(--stingray-dt-row-accent-color, var(--_sdt-accent));
    /* Off by default — the hover reads as a tint alone. The rule below is kept rather than
       deleted so setting a width brings the leading-edge bar back. */
    --_sdt-row-accent-width: var(--stingray-dt-row-accent-width, 0);

    /* Card view. The grid is auto-fill, so min-width is the only knob — cards per row follow from
       the width available and there is no breakpoint to configure. The CardMinWidth parameter
       writes the private property directly on the root element. */
    --_sdt-card-min-width: var(--stingray-dt-card-min-width, 17rem);
    --_sdt-card-gap: var(--stingray-dt-card-gap, .75rem);
    /* Most cards allowed on one row. 999 is a sentinel meaning "no cap", not a real limit: at that
       N the 1/N share the grid rule computes is negative at every realistic width, so the max()
       there always resolves to the minimum width above and the cap disappears. That keeps the
       uncapped path byte-identical to having no cap at all, with no second rule and no modifier
       class — and keeps the arithmetic in the stylesheet rather than composed in C#. */
    --_sdt-card-max-per-row: var(--stingray-dt-card-max-per-row, 999);
    --_sdt-card-padding-y: var(--stingray-dt-card-padding-y, .875rem);
    --_sdt-card-padding-x: var(--stingray-dt-card-padding-x, 1rem);
    --_sdt-card-radius: var(--stingray-dt-card-radius, .625rem);
    --_sdt-card-bg: var(--stingray-dt-card-bg, var(--_sdt-bg));
    --_sdt-card-border-color: var(--stingray-dt-card-border-color, var(--_sdt-border-color));
    --_sdt-card-shadow: var(--stingray-dt-card-shadow, var(--_sdt-shadow));
    --_sdt-card-hover-bg: var(--stingray-dt-card-hover-bg, var(--_sdt-row-hover-bg));
    --_sdt-card-selected-bg: var(--stingray-dt-card-selected-bg, var(--_sdt-row-selected-bg));
    /* Nested children. The indent is per level and accumulates through nesting, so it is
       deliberately smaller than the table's --stingray-dt-indent: a card has a card's width to
       spend, not a page's, and depth is unbounded. */
    --_sdt-card-nest-indent: var(--stingray-dt-card-nest-indent, .625rem);
    --_sdt-card-nested-bg: var(--stingray-dt-card-nested-bg, #f7f9fd);
    --_sdt-card-nested-border-color: var(--stingray-dt-card-nested-border-color, #e6eaf4);
    --_sdt-card-rail-color: var(--stingray-dt-card-rail-color, #dde3f0);
    --_sdt-card-field-gap: var(--stingray-dt-card-field-gap, .5rem);
    --_sdt-card-label-color: var(--stingray-dt-card-label-color, var(--_sdt-header-color));
    --_sdt-card-label-font-size: var(--stingray-dt-card-label-font-size, var(--_sdt-header-font-size));
    --_sdt-card-label-font-weight: var(--stingray-dt-card-label-font-weight, var(--_sdt-header-font-weight));
    --_sdt-card-label-width: var(--stingray-dt-card-label-width, 40%);
    --_sdt-card-divider-color: var(--stingray-dt-card-divider-color, var(--_sdt-row-border-color));

    /* Toolbar controls — the view toggle and the card-mode sort picker. */
    --_sdt-control-height: var(--stingray-dt-control-height, 2.5rem);
    --_sdt-control-bg: var(--stingray-dt-control-bg, var(--_sdt-select-bg));
    --_sdt-control-border-color: var(--stingray-dt-control-border-color, var(--_sdt-select-border-color));
    --_sdt-control-radius: var(--stingray-dt-control-radius, var(--_sdt-select-radius));
    --_sdt-control-color: var(--stingray-dt-control-color, var(--_sdt-page-color));
    --_sdt-control-hover-bg: var(--stingray-dt-control-hover-bg, var(--_sdt-page-hover-bg));
    --_sdt-control-active-bg: var(--stingray-dt-control-active-bg, var(--_sdt-accent-soft));
    --_sdt-control-active-color: var(--stingray-dt-control-active-color, var(--_sdt-accent));

    /* Search */
    --_sdt-search-bg: var(--stingray-dt-search-bg, #ffffff);
    /* A text box's border is its boundary — 3:1 under 1.4.11. #e3e7f0 measured 1.24:1. */
    --_sdt-search-border-color: var(--stingray-dt-search-border-color, var(--_sdt-icon-muted-color));
    --_sdt-search-radius: var(--stingray-dt-search-radius, 999px);
    --_sdt-search-height: var(--stingray-dt-search-height, 2.5rem);
    --_sdt-search-color: var(--stingray-dt-search-color, var(--_sdt-text-color));
    --_sdt-search-placeholder-color: var(--stingray-dt-search-placeholder-color, var(--_sdt-muted-color));
    --_sdt-search-icon-color: var(--stingray-dt-search-icon-color, var(--_sdt-muted-color));
    --_sdt-search-min-width: var(--stingray-dt-search-min-width, 15rem);

    /* Pager */
    --_sdt-page-size: var(--stingray-dt-page-size, 2rem);
    --_sdt-page-radius: var(--stingray-dt-page-radius, .5rem);
    --_sdt-page-gap: var(--stingray-dt-page-gap, .25rem);
    --_sdt-page-color: var(--stingray-dt-page-color, #5a6486);
    --_sdt-page-hover-bg: var(--stingray-dt-page-hover-bg, #f2f4fb);
    --_sdt-page-active-bg: var(--stingray-dt-page-active-bg, var(--_sdt-accent-soft));
    --_sdt-page-active-color: var(--stingray-dt-page-active-color, var(--_sdt-accent));
    --_sdt-page-disabled-color: var(--stingray-dt-page-disabled-color, #c3c9d9);

    /* Page-size select */
    --_sdt-select-bg: var(--stingray-dt-select-bg, #ffffff);
    --_sdt-select-border-color: var(--stingray-dt-select-border-color, var(--_sdt-icon-muted-color));
    --_sdt-select-radius: var(--stingray-dt-select-radius, .5rem);
    --_sdt-select-color: var(--stingray-dt-select-color, var(--_sdt-text-color));
    --_sdt-select-caret-color: var(--stingray-dt-select-caret-color, var(--_sdt-muted-color));

    /* Column chooser. Like the drag visuals below, the panel renders outside every table — inside
       the host's popup — so these have to resolve on :root, which the block's first selector
       guarantees, and again on .sdt-chooser for a panel themed on its own. */
    --_sdt-chooser-row-gap: var(--stingray-dt-chooser-row-gap, .5rem);
    --_sdt-chooser-gap: var(--stingray-dt-chooser-gap, .5rem);
    --_sdt-chooser-card-bg: var(--stingray-dt-chooser-card-bg, var(--_sdt-bg));
    --_sdt-chooser-card-border-color: var(--stingray-dt-chooser-card-border-color, #e3e7f0);
    --_sdt-chooser-card-radius: var(--stingray-dt-chooser-card-radius, .5rem);
    --_sdt-chooser-card-padding-y: var(--stingray-dt-chooser-card-padding-y, .75rem);
    --_sdt-chooser-card-padding-x: var(--stingray-dt-chooser-card-padding-x, .875rem);
    --_sdt-chooser-card-hover-bg: var(--stingray-dt-chooser-card-hover-bg, var(--_sdt-page-hover-bg));
    --_sdt-chooser-actions-width: var(--stingray-dt-chooser-actions-width, 2.25rem);
    --_sdt-chooser-move-color: var(--stingray-dt-chooser-move-color, var(--_sdt-text-color));
    --_sdt-chooser-move-size: var(--stingray-dt-chooser-move-size, .875rem);
    --_sdt-chooser-move-disabled-color: var(--stingray-dt-chooser-move-disabled-color, var(--_sdt-page-disabled-color));

    /* The chooser's visibility toggle. A switch rather than a tick box, but still the same
       <input type="checkbox"> — .sdt-check's appearance reset is what makes it paintable. The
       thumb's diameter is the height less twice the inset, so those three move together. */
    --_sdt-switch-width: var(--stingray-dt-switch-width, 2.5rem);
    --_sdt-switch-height: var(--stingray-dt-switch-height, 1.375rem);
    --_sdt-switch-inset: var(--stingray-dt-switch-inset, .1875rem);
    /* The off state IS the control here, so its track has to clear 3:1 too. #d8dce7 was 1.37:1. */
    --_sdt-switch-off-bg: var(--stingray-dt-switch-off-bg, var(--_sdt-icon-muted-color));
    --_sdt-switch-on-bg: var(--stingray-dt-switch-on-bg, var(--_sdt-accent));
    --_sdt-switch-thumb-color: var(--stingray-dt-switch-thumb-color, #ffffff);
    --_sdt-switch-thumb-shadow: var(--stingray-dt-switch-thumb-shadow, 0 1px 2px rgba(16, 24, 40, .2));

    /* Drag-to-reorder. The ghost and the drop indicator render on <body>, outside every table, so
       these have to resolve on :root — which the block's first selector already guarantees. */
    --_sdt-drop-indicator-color: var(--stingray-dt-drop-indicator-color, var(--_sdt-accent));
    --_sdt-drop-indicator-width: var(--stingray-dt-drop-indicator-width, 2px);
    --_sdt-drag-ghost-bg: var(--stingray-dt-drag-ghost-bg, var(--_sdt-header-bg));
    --_sdt-drag-ghost-color: var(--stingray-dt-drag-ghost-color, var(--_sdt-text-color));
    --_sdt-drag-ghost-border-color: var(--stingray-dt-drag-ghost-border-color, var(--_sdt-border-color));
    --_sdt-drag-ghost-shadow: var(--stingray-dt-drag-ghost-shadow, 0 6px 16px rgba(16, 24, 40, .18));
    --_sdt-drag-ghost-opacity: var(--stingray-dt-drag-ghost-opacity, .9);

    /* Cell popup. The panel stays where Blazor rendered it — inside the table — and escapes the
       wrapper's clip through `position: fixed` alone, so unlike the drag visuals these do resolve
       against .stingray-data-table. They are kept in this block anyway: the panel is equally usable
       outside a table, where only :root can reach it. */
    --_sdt-popup-bg: var(--stingray-dt-popup-bg, var(--_sdt-bg));
    --_sdt-popup-border-color: var(--stingray-dt-popup-border-color, var(--_sdt-border-color));
    --_sdt-popup-radius: var(--stingray-dt-popup-radius, var(--_sdt-page-radius));
    --_sdt-popup-shadow: var(--stingray-dt-popup-shadow, 0 6px 16px rgba(16, 24, 40, .18));
    --_sdt-popup-min-width: var(--stingray-dt-popup-min-width, 10rem);
    /* A ceiling, so a long menu scrolls inside itself rather than running off the viewport. The
       module measures the panel to place it, and an unbounded one measured taller than the screen
       could only be clamped to an edge. */
    --_sdt-popup-max-height: var(--stingray-dt-popup-max-height, min(20rem, 60vh));

    /* Column resize. The handle is wider than the line it draws, because a 1px grab target on a
       trailing edge is a target most pointers miss. */
    --_sdt-resize-handle-width: var(--stingray-dt-resize-handle-width, .5rem);
    --_sdt-resize-line-width: var(--stingray-dt-resize-line-width, 2px);
    --_sdt-resize-line-color: var(--stingray-dt-resize-line-color, var(--_sdt-accent));
    --_sdt-resize-line-inset: var(--stingray-dt-resize-line-inset, .35rem);

    /* State */
    --_sdt-overlay-bg: var(--stingray-dt-overlay-bg, rgba(255, 255, 255, .6));
    --_sdt-busy-opacity: var(--stingray-dt-busy-opacity, .55);
    --_sdt-spinner-color: var(--stingray-dt-spinner-color, var(--_sdt-accent));
    --_sdt-spinner-size: var(--stingray-dt-spinner-size, 1rem);
    /* Two rings, not a glow. The old `0 0 0 3px rgba(27, 47, 126, .15)` resolved to #dde0ec on the
       default surface — 1.32:1, which is the low-contrast outline WCAG 2.4.13 names by example. The
       inner ring is the surface colour, so the outer one reads against the control as well as
       against the page whatever either is painted. Every :focus-visible in this stylesheet sets
       `outline: none` and then this, so it is the only focus indicator the component has. */
    --_sdt-focus-ring-color: var(--stingray-dt-focus-ring-color, var(--_sdt-accent));
    --_sdt-focus-ring: var(--stingray-dt-focus-ring, 0 0 0 2px var(--_sdt-bg), 0 0 0 4px var(--_sdt-focus-ring-color));
    /* For controls whose ring would be clipped: anything inside .sdt-view-toggle's `overflow:
       hidden`, and every header cell once AllowColumnResize turns on `overflow: hidden` to
       ellipsise. An outward shadow simply does not survive those, so those controls draw inward. */
    --_sdt-focus-ring-inset: var(--stingray-dt-focus-ring-inset, inset 0 0 0 2px var(--_sdt-focus-ring-color));
    --_sdt-transition: var(--stingray-dt-transition, .15s ease-in-out);
    --_sdt-danger-color: var(--stingray-dt-danger-color, #b42318);
}

.stingray-data-table {
    color: var(--_sdt-text-color);
    font-family: var(--_sdt-font-family);
    font-size: var(--_sdt-font-size);

    /* One grid column that is allowed to shrink below its content's width.
       This is what makes the horizontal scrolling below actually work. A wide table's
       min-content size otherwise propagates up through every ancestor, and any host
       layout with a flex or grid shell — where an item's min-width defaults to auto —
       is forced wider than the viewport, so the whole PAGE scrolls sideways instead of
       the table. Verified in a browser: min-width/max-width on the inner elements has
       no effect at all; the containment has to sit here on the root. */
    display: grid;
    grid-template-columns: minmax(0, 1fr);

    /* Toolbar / card / footer. The middle track takes whatever height is left over, and
       minmax(0, …) lets it shrink below its content — without that the row area would refuse to
       get smaller than the full table and the whole component would overflow instead of scrolling.
       So giving this component a height from outside makes the ROWS scroll while the toolbar and
       pager stay put, rather than the container scrolling everything away. */
    grid-template-rows: auto minmax(0, 1fr) auto;

    /* Both resolve in 01-tokens.css — height to auto and max-height to 100%, which is the natural
       size this had before either was a token. A Height makes the middle track absorb whatever the
       toolbar and pager leave, so the footprint is the value given and does NOT move with the row
       count. A MaxHeight leaves the table its natural size until the content would exceed the cap,
       and the same track scrolls from there. The 100% default is what makes a container with a
       definite height constrain the table without either parameter being set. */
    height: var(--_sdt-height);
    max-height: var(--_sdt-max-height);
}

/* Placed explicitly, and it has to be. The toolbar is conditional while the card and footer always
   render, so under auto-placement a table with no toolbar shifts everything up a track and the
   FOOTER lands in the 1fr row — measured at 241px of slack on a 25rem table, with the rows left at
   their natural height. Naming the rows makes a missing toolbar collapse row 1 to zero instead. */
/* Transparent by default, which is what every existing table already renders. It exists because
   only .sdt-card paints a surface: the toolbar and footer sit outside it and inherit whatever the
   host has behind them. That is fine when the host and the table share a theme, and it breaks when
   they do not — `.sdt-theme-dark` on a wrapper inside a light page gives the footer light text on a
   light ground, measured at 1.21:1 for "Page Size". A consumer theming ONE table sets this and the
   whole component sits on a matching surface. */
.stingray-data-table {
    background-color: var(--stingray-dt-root-bg, transparent);
}

.sdt-toolbar {
    grid-row: 1;
}

.sdt-card {
    grid-row: 2;
}

.sdt-footer {
    grid-row: 3;
}

/* Column components render nothing, but the region must stay in the tree so they
   are not disposed and re-created on every render. */
.sdt-column-definitions {
    display: none;
}


/* ---------- Toolbar ---------- */

.sdt-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-block-end: .75rem;
}

.sdt-toolbar-start {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.sdt-search {
    position: relative;
    display: flex;
    align-items: center;
    min-width: var(--_sdt-search-min-width);
}

.sdt-search-icon {
    position: absolute;
    inset-inline-start: .875rem;
    width: 1rem;
    height: 1rem;
    color: var(--_sdt-search-icon-color);
    pointer-events: none;
}

.sdt-search-input {
    width: 100%;
    height: var(--_sdt-search-height);
    padding-inline: 2.5rem 1rem;
    color: var(--_sdt-search-color);
    background-color: var(--_sdt-search-bg);
    border: 1px solid var(--_sdt-search-border-color);
    border-radius: var(--_sdt-search-radius);
    /* 16px keeps iOS WebKit from auto-zooming when the box takes focus. */
    font-size: 16px;
    font-family: inherit;
    outline: none;
    transition: box-shadow var(--_sdt-transition), border-color var(--_sdt-transition);
}

.sdt-search-input::placeholder {
    color: var(--_sdt-search-placeholder-color);
}

.sdt-search-input:focus-visible {
    border-color: var(--_sdt-accent);
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-search-input:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}


/* ---------- View toggle and card-mode sort picker ---------- */

/* Sized to the search box's height so the strip reads as one row of controls. */
.sdt-view-toggle {
    display: inline-flex;
    align-items: stretch;
    height: var(--_sdt-control-height);
    background-color: var(--_sdt-control-bg);
    border: 1px solid var(--_sdt-control-border-color);
    border-radius: var(--_sdt-control-radius);
    /* Clips the buttons' own corners to the group's, so the pair reads as one segmented control. */
    overflow: hidden;
}

.sdt-view-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    padding: 0;
    color: var(--_sdt-control-color);
    background-color: transparent;
    border: 0;
    outline: none;
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

.sdt-view-button svg {
    width: 1rem;
    height: 1rem;
}

.sdt-view-button:hover:not(:disabled):not(.sdt-active) {
    background-color: var(--_sdt-control-hover-bg);
}

.sdt-view-button.sdt-active {
    color: var(--_sdt-control-active-color);
    background-color: var(--_sdt-control-active-bg);
}

/* Inset: .sdt-view-toggle clips to its own radius so the pair reads as one segmented control, and
   an outward ring is the thing that clipping removes. */
.sdt-view-button:focus-visible {
    box-shadow: var(--_sdt-focus-ring-inset);
}

.sdt-view-button:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}

/* ---------- Column chooser button ---------- */

/* Named for what it opens, not for where it sits: .sdt-chooser-move is a button INSIDE the panel,
   and a shared -button suffix would have put the two a character apart. Standing alone rather than
   in a segmented group, so unlike .sdt-view-button it carries its own border and radius. */
.sdt-chooser-open,
.sdt-reset-columns {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: var(--_sdt-control-height);
    padding: 0;
    color: var(--_sdt-control-color);
    background-color: var(--_sdt-control-bg);
    border: 1px solid var(--_sdt-control-border-color);
    border-radius: var(--_sdt-control-radius);
    outline: none;
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

.sdt-chooser-open svg,
.sdt-reset-columns svg {
    width: 1rem;
    height: 1rem;
}

.sdt-chooser-open:hover:not(:disabled),
.sdt-reset-columns:hover:not(:disabled) {
    background-color: var(--_sdt-control-hover-bg);
}

.sdt-chooser-open:focus-visible,
.sdt-reset-columns:focus-visible {
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-chooser-open:disabled,
.sdt-reset-columns:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}

.sdt-sort-picker {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
}

/* The chrome comes from .sdt-select-wrap select in 12-footer.css; only the toolbar's taller
   sizing is set here. */
.sdt-sort-picker select {
    height: var(--_sdt-control-height);
    /* 16px keeps iOS WebKit from auto-zooming when the control takes focus, same as the search
       box. The footer's select is out of thumb's reach in practice; this one is not. */
    font-size: 16px;
}

.sdt-sort-direction {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--_sdt-control-height);
    height: var(--_sdt-control-height);
    padding: 0;
    color: var(--_sdt-control-color);
    background-color: var(--_sdt-control-bg);
    border: 1px solid var(--_sdt-control-border-color);
    border-radius: var(--_sdt-control-radius);
    outline: none;
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

/* Vertical glyph — it must NOT mirror in RTL, so there is deliberately no dir rule here. */
.sdt-sort-direction svg {
    width: var(--_sdt-sort-indicator-width);
    height: var(--_sdt-sort-indicator-height);
    color: var(--_sdt-sort-indicator-active-color);
}

.sdt-sort-direction:hover:not(:disabled) {
    background-color: var(--_sdt-control-hover-bg);
}

.sdt-sort-direction:focus-visible {
    border-color: var(--_sdt-accent);
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-sort-direction:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}

/* The card-mode controls travel as one unit, so they need a container of their own: dropped
   straight into a ToolbarTemplate they would be direct children of .sdt-toolbar, whose
   space-between would push the checkbox and the sort picker to opposite ends of the strip. */
.sdt-card-tools {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
}

/* Boxed to the same square as the direction button beside it, so the strip's controls line up
   whichever of them a given table shows. Only the frame lives here — the checkbox inside keeps
   the one appearance every .sdt-check has, from 07-selection.css. */
.sdt-select-all {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--_sdt-control-height);
    height: var(--_sdt-control-height);
    background-color: var(--_sdt-control-bg);
    border: 1px solid var(--_sdt-control-border-color);
    border-radius: var(--_sdt-control-radius);
}

/* ---------- Card ---------- */

.sdt-card {
    /* Positioning context for the loading overlay. It deliberately sits here and not on
       the scrolling wrapper, so the spinner stays put while a wide table scrolls. */
    position: relative;
    /* Lets the card, and the scroller inside it, shrink to the grid track rather than
       forcing the component taller than its container. */
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: var(--_sdt-padding);
    background-color: var(--_sdt-bg);
    border: 1px solid var(--_sdt-border-color);
    border-radius: var(--_sdt-radius);
    box-shadow: var(--_sdt-shadow);
}

/* One rule for both views: a fixed Height scrolls a card grid exactly as it scrolls rows.
   No max-height of its own — the middle grid track is what bounds this, and min-height: 0 plus the
   default flex-shrink is what makes it scroll rather than push the component taller. */
.sdt-table-wrapper,
.sdt-cards-wrapper {
    overflow: auto;
    min-height: 0;
    /* Grow into the whole of the card, rather than stopping at the rows' own height. Without this a
       two-row table inside a 25rem card left 200px of card with no scroll area in it at all, and
       the frozen columns' dividers — which only a cell can draw — had nowhere to run to. */
    flex: 1 1 auto;
}

/* WCAG 2.2 2.4.11. Tab to a control just above the fold and the browser scrolls it flush to the top
   of this box — which is exactly where the sticky header sits, so the thing that just took focus
   ends up behind it. Only the table wrapper needs this; the card grid has no sticky anything. */
.sdt-table-wrapper {
    scroll-padding-block-start: var(--_sdt-sticky-scroll-padding);
}

/* In card view the cards carry their own borders and shadows, so the container's would read as
   heavy nesting — a bordered box of bordered boxes. It stays in the tree rather than being
   replaced: it is the loading overlay's positioning context, which is load-bearing. */
.sdt-view-cards .sdt-card {
    padding: 0;
    background-color: transparent;
    border-color: transparent;
    box-shadow: none;
}


/* ---------- Table ---------- */

.sdt-table {
    width: 100%;
    min-width: var(--_sdt-table-min-width);
    /* Fill the scroller, so the frozen dividers reach its bottom instead of stopping at the last
       row. Resolves to auto — and so changes nothing — unless the wrapper has a definite height,
       which it only gets from a fixed Height or a constrained container. The slack this creates is
       taken entirely by .sdt-filler; see 10-rows.css for why that row exists. */
    height: 100%;
    margin: 0;
    border-collapse: collapse;
    background-color: transparent;
}

/* Sticky so the columns stay readable once the rows scroll. The header needs its own opaque
   background for the same reason a pinned column does — rows would otherwise show through it. */
.sdt-table > thead > tr > th {
    position: sticky;
    inset-block-start: 0;
    z-index: 2;
    padding: var(--_sdt-header-padding-y) var(--_sdt-header-padding-x);
    text-align: start;
    vertical-align: middle;
    white-space: nowrap;
    color: var(--_sdt-header-color);
    background-color: var(--_sdt-header-bg);
    font-size: var(--_sdt-header-font-size);
    font-weight: var(--_sdt-header-font-weight);
    border-block-end: 1px solid var(--_sdt-header-border-color);
}

/* The wrapper carries tabindex="0" so the scroll region can be reached from the keyboard, which
   makes it a control like any other and means it owes the user an indicator. Inset rather than the
   outer ring: this box is the full width of the table, and a ring drawn outside it reads as a
   border around the whole component rather than as focus. */
.sdt-table-wrapper:focus-visible {
    outline: none;
    box-shadow: var(--_sdt-focus-ring-inset);
}

.sdt-table > tbody > tr > td {
    position: relative;
    /* A single unbroken token -- a URL, an email, a file path, a base64 id, a German compound --
       otherwise sets the column's min-content width and forces it open: measured at 1705px for a
       180-character string in a column that was 93px, taking the table from 425px to 2033px.
       `anywhere` rather than `break-word`: the latter does not enter the min-content calculation,
       so it leaves the table exactly as wide -- verified, both values, same cell. The resizable
       path never had this, being table-layout: fixed with an ellipsis. */
    overflow-wrap: anywhere;
    padding: var(--_sdt-row-padding-y) var(--_sdt-row-padding-x);
    vertical-align: middle;
    border-block-end: 1px solid var(--_sdt-row-border-color);
}

.sdt-table > tbody > tr:last-child > td {
    border-block-end: 0;
}

.sdt-table > tbody > tr {
    transition: background-color var(--_sdt-transition);
}

.sdt-table > tbody > tr:hover {
    background-color: var(--_sdt-row-hover-bg);
}

/* Accent bar on the hovered row. A pseudo-element with inset-inline-start rather
   than an inset box-shadow: a shadow offset has no logical form, so it would sit
   on the wrong edge in RTL. */
.sdt-table > tbody > tr:hover > td:first-child::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--_sdt-row-accent-width);
    background-color: var(--_sdt-row-accent-color);
}


/* ---------- Expansion ---------- */

/* Shrink-to-fit: an auto-layout table gives this column no more than the toggle needs, at whatever
   depth the deepest open row happens to be. That is a property of auto layout alone, and
   AllowColumnResize switches the same table to `table-layout: fixed` under a public parameter --
   where `width` stops being a hint and 1px becomes an instruction. 18-resize.css states the width
   outright for that case; the two declarations are a pair. */
.sdt-expand-cell {
    width: 1px;
    white-space: nowrap;
}

.sdt-expand-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--_sdt-expand-toggle-size);
    height: var(--_sdt-expand-toggle-size);
    padding: 0;
    color: var(--_sdt-expand-icon-color);
    background-color: transparent;
    border: 0;
    border-radius: var(--_sdt-page-radius);
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

.sdt-expand-toggle:hover:not(:disabled) {
    background-color: var(--_sdt-expand-hover-bg);
}

.sdt-expand-toggle:focus-visible {
    outline: none;
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-expand-toggle:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}

.sdt-expand-icon {
    width: .875rem;
    height: .875rem;
    transition: transform var(--_sdt-transition);
}

/* Closed points along the inline axis, so like the pager chevrons it mirrors in RTL. Open points
   down, which is direction-neutral and needs no mirroring. */
html[dir="rtl"] .sdt-expand-icon {
    transform: scaleX(-1);
}

.sdt-expanded .sdt-expand-icon,
html[dir="rtl"] .sdt-expanded .sdt-expand-icon {
    transform: rotate(90deg);
}

.sdt-detail-row > td {
    padding: 0;
    background-color: var(--_sdt-detail-bg);
}

/* The detail panel is content, not a row: it should not light up on hover. */
.sdt-table > tbody > tr.sdt-detail-row:hover {
    background-color: var(--_sdt-detail-bg);
}

.sdt-table > tbody > tr.sdt-child-row {
    background-color: var(--_sdt-child-row-bg);
}

/* ---------- Children fetched on demand ---------- */

/* Where a ChildrenLoader row reports its own fetch, standing in for children that have not arrived.
   Painted and indented as a child row because that is exactly what it is a placeholder for. */
.sdt-child-status > td {
    padding-block: .75rem;
    padding-inline-start: var(--_sdt-indent);
    background-color: var(--_sdt-child-row-bg);
}

/* Status, not a row: no hover response, the same rule the detail row follows. */
.sdt-table > tbody > tr.sdt-child-status:hover {
    background-color: var(--_sdt-child-row-bg);
}

.sdt-cards-child-status {
    padding-block: .5rem;
}

/* Secondary text, so the muted TEXT tier at 4.5:1 — not the icon tier. */
.sdt-child-loading {
    color: var(--_sdt-muted-color);
}

.sdt-child-error {
    color: var(--_sdt-danger-color);
}

/* Message and control on one line. Wraps rather than overflowing, because a narrow table or a
   long translation would otherwise push the button out of the row. */
.sdt-child-status-row {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    flex-wrap: wrap;
}


/* ---------- Selection ---------- */

/* Shrink-to-fit: an auto-layout table gives this column no more than the checkbox needs. Auto
   layout only — see 18-resize.css for the fixed-layout half, and 06-expansion.css for the same
   note on the other chrome column. */

/* No text-align: centred and start are the same thing in a column shrink-wrapped to one checkbox,
   so this only ever did something in the case where it was wrong. A fixed-layout column takes a
   share of whatever width the table has spare — there is no opting out, min-width and max-width are
   both ignored by that algorithm — and centring in the wider box floated the checkbox into the
   middle of the gutter, away from both the row edge and the column beside it. Left where a snug
   column would have put it, the spare width falls after it instead, which is what every other cell
   in the table does with slack. */
.sdt-select-cell {
    width: 1px;
    white-space: nowrap;
}

.sdt-table > tbody > tr.sdt-row-selected {
    background-color: var(--_sdt-row-selected-bg);
}

/* Without this a selected row swallowed its own hover: the two rules land on the same specificity
   and this file loads after 05-table.css, so pointing at a selected row gave no feedback at all. */
.sdt-table > tbody > tr.sdt-row-selected:hover {
    background-color: var(--_sdt-row-selected-hover-bg);
}

/* The second signal. Selection is a state the user chose and a tint is a weak way to say so —
   weaker still once a consumer themes the row background. Same pseudo-element and the same logical
   inset as the hover bar in 05-table.css, so the two can never draw at once, and this file loads
   later so selection wins the cell when a row is both. */
.sdt-table > tbody > tr.sdt-row-selected > td:first-child::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--_sdt-row-selected-accent-width);
    background-color: var(--_sdt-row-accent-color);
}

/* Drawn rather than native, for two reasons: the browser default cannot be themed, and the
   half-selected state has no attribute form — `indeterminate` is a DOM property only, so a
   native control would need JS to show the dash. Painting it here keeps the package script-free. */
.sdt-check {
    appearance: none;
    flex: none;
    width: var(--_sdt-check-size);
    height: var(--_sdt-check-size);
    margin: 0;
    vertical-align: middle;
    background-color: var(--_sdt-check-bg);
    border: 1px solid var(--_sdt-check-border-color);
    border-radius: var(--_sdt-check-radius);
    cursor: pointer;
    transition: background-color var(--_sdt-transition), border-color var(--_sdt-transition);

    /* The tick and the dash are backgrounds, masked so their colour stays tokenised. */
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
}

.sdt-check:checked,
.sdt-check-mixed {
    background-color: var(--_sdt-check-checked-bg);
    border-color: var(--_sdt-check-checked-bg);
}

.sdt-check:checked::after,
.sdt-check-mixed::after {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    background-color: var(--_sdt-check-checked-color);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

.sdt-check:checked::after {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2 4.8 8.5 9.5 3.8' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2 4.8 8.5 9.5 3.8' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Wins over :checked — the half state must show a dash even though the box is unchecked. */
.sdt-check-mixed::after,
.sdt-check-mixed:checked::after {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 6h6' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 6h6' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}

.sdt-check:focus-visible {
    outline: none;
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-check:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}


/* ---------- Frozen columns ---------- */

/* A pinned cell floats over the scrolled content, so it has to paint an opaque background of
   its own — the row's background belongs to the <tr> and would let content show through.
   Written at full selector depth on purpose: the `position: relative` on every cell above is
   (0,1,3), so a bare `.sdt-frozen` at (0,1,0) loses and the column never becomes sticky. */
.sdt-table > thead > tr > th.sdt-frozen,
.sdt-table > tbody > tr > td.sdt-frozen {
    position: sticky;
    z-index: 1;
    background-color: var(--_sdt-frozen-bg);
}

/* The corner cell: pinned horizontally AND stuck vertically, so it has to outrank both the
   sticky header row (z 2) and the pinned body cells (z 1) or it is crossed by one of them. */
.sdt-table > thead > tr > th.sdt-frozen {
    z-index: 3;
    background-color: var(--_sdt-frozen-header-bg);
}

.sdt-table > tbody > tr:hover > .sdt-frozen {
    background-color: var(--_sdt-frozen-hover-bg);
}

/* A pinned cell paints its own opaque background, so it does NOT inherit the row's — which meant a
   selected row went tinted everywhere except its frozen columns, and those stayed white. Invisible
   while selected and hover were 1.04:1 apart; obvious the moment selection got a colour of its own.
   Ordered after the hover rule above, which it ties with on specificity. */
.sdt-table > tbody > tr.sdt-row-selected > .sdt-frozen {
    background-color: var(--_sdt-frozen-selected-bg);
}

.sdt-table > tbody > tr.sdt-row-selected:hover > .sdt-frozen {
    background-color: var(--_sdt-frozen-selected-hover-bg);
}

/* Separator on the innermost pinned column of each edge. A pseudo-element rather than a
   border or box-shadow: only this can be placed with logical properties, so it lands on the
   correct side in RTL without a second rule. Collapsed borders also belong to the table
   rather than the cell, so a border here would not track the pinned column. */
.sdt-frozen-edge-start::after,
.sdt-frozen-edge-end::after {
    content: "";
    position: absolute;
    inset-block: 0;
    width: var(--_sdt-frozen-edge-width);
    background-color: var(--_sdt-frozen-edge-color);
    pointer-events: none;
}

.sdt-frozen-edge-start::after {
    inset-inline-end: 0;
}

.sdt-frozen-edge-end::after {
    inset-inline-start: 0;
}


/* ---------- Sortable headers ---------- */

.sdt-sortable {
    cursor: pointer;
    user-select: none;
}

.sdt-sortable:hover {
    background-color: var(--_sdt-header-hover-bg);
}

/* The control is a real <button> INSIDE the <th>, never a role="button" on it. Putting the role on
   the cell replaced its implicit `columnheader`, and that cost two things at once: every cell in
   the column stopped being associated with its header in a screen reader's table mode, and
   `aria-sort` — which ARIA supports on columnheader/rowheader and nowhere else — became inert
   markup on an element that could not carry it. The cell keeps scope and aria-sort; the button
   takes the click, and with it Enter and Space handled by the browser rather than by a keydown
   handler that had no way to preventDefault Space's page scroll.

   The negative margin is what keeps the whole cell clickable: the <th> owns the padding, so a
   button sized to the content box alone would leave a 14px dead border around every header. */
.sdt-header-button {
    display: flex;
    align-items: center;
    /* Stated rather than left at the initial value, and that is the whole fix: text-align does not
       position flex items — justify-content does — so the start alignment below held only for as
       long as nothing else declared one. MudBlazor ships a bare
       `button { display: inline-flex; justify-content: center }` reset, and against it this rule
       declared NOTHING: an element selector at (0,0,1) applied unopposed over .sdt-header-button at
       (0,1,0), centring every sortable header over the start-aligned cells beneath it. An absence
       rather than a specificity loss, which is why it reproduces only with a host's vendor
       stylesheets loaded. flex-start follows the writing direction, so RTL is unaffected. */
    justify-content: flex-start;
    width: calc(100% + var(--_sdt-header-padding-x) * 2);
    margin: calc(var(--_sdt-header-padding-y) * -1) calc(var(--_sdt-header-padding-x) * -1);
    padding: var(--_sdt-header-padding-y) var(--_sdt-header-padding-x);
    color: inherit;
    background-color: transparent;
    border: 0;
    /* Not what positions the label — justify-content above does that, and under `display: flex`
       this is inert. It earns its place one layer down: the UA stylesheet sets
       `button { text-align: center }`, so this is what undoes that if a host pushes `display` the
       other way and the button becomes a block container again. The two are insurance against the
       same class of reset, one layer apart. Inherited rather than set to a value, so a consumer's
       HeaderCssClass that changes the alignment still reaches the label. */
    text-align: inherit;
    font: inherit;
    cursor: pointer;
    outline: none;
}

/* Inset, and it has to be: AllowColumnResize puts `overflow: hidden` on every <th> to ellipsise
   long titles, and an outward ring does not survive that. */
.sdt-header-button:focus-visible {
    box-shadow: var(--_sdt-focus-ring-inset);
}

.sdt-header-content {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}

.sdt-sort-indicator {
    width: var(--_sdt-sort-indicator-width);
    height: var(--_sdt-sort-indicator-height);
    flex: none;
    color: var(--_sdt-sort-indicator-color);
    transition: color var(--_sdt-transition);
}

/* The unsorted glyph draws both directions; the sorted ones draw a single centred arrow, so
   there is nothing to rotate and the box never changes size. */
.sdt-sort-ascending,
.sdt-sort-descending {
    color: var(--_sdt-sort-indicator-active-color);
}


/* ---------- Rows ---------- */

.sdt-clickable {
    cursor: pointer;
}

.sdt-message {
    padding-block: 2rem;
    text-align: center;
    color: var(--_sdt-muted-color);
}

/* Same specificity trap the frozen columns carry a comment about: the cell rule is
   `.sdt-table > tbody > tr > td` at (0,1,3), so the bare class above at (0,1,0) loses its padding
   and every empty, no-results and error state renders at row height. Source order does not save it
   — this file already loads after 05-table.css. Measured before the fix: 47px tall in the table
   against 85px for the identical state in card view, where the message is a <p> and no cell rule
   applies. The two views are meant to be two drawings of one state. */
.sdt-table > tbody > tr > td.sdt-message {
    padding-block: 2rem;
}

.sdt-message-error {
    color: var(--_sdt-danger-color);
}

/* The stack inside a state message. It is an inner element on purpose: .sdt-message is a <td> in
   table view and a <p> in card view, and flexing the cell itself would drop it out of the table
   layout. Tight between the mark and its line, generous before the control — the rhythm says the
   first two belong together and the button is a separate decision. */
.sdt-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .625rem;
}

.sdt-state .sdt-retry {
    margin-block-start: .25rem;
}

/* Sized well above the 1rem control glyphs so it reads as an illustration rather than a stray icon,
   and held on the icon tier, which clears 3:1 for a non-text mark. The error state's own colour
   cascades into its mark through currentColor, so the three states never disagree. */
.sdt-state-icon {
    width: 2.5rem;
    height: 2.5rem;
    color: var(--_sdt-icon-muted-color);
    flex: none;
}

.sdt-message-error .sdt-state-icon {
    color: currentColor;
}

/* The component's one bordered text button, shared by the error state's Retry and the chooser
   panel's Reset. They were two identical definitions in two files, and the drift that hides in that
   arrangement had already happened: the chooser's copy carried no :focus-visible rule and did not
   set `outline: none`, so it was the single control in the package still wearing the BROWSER's
   focus ring — measured as `outline: auto 0.8px rgb(229,151,0)` against the two-ring token every
   other control uses. One definition, so it cannot happen again. The chooser file points here. */
.sdt-retry,
.sdt-chooser-reset {
    padding: .375rem .75rem;
    color: var(--_sdt-accent);
    background-color: transparent;
    border: 1px solid var(--_sdt-border-color);
    border-radius: var(--_sdt-page-radius);
    font-family: inherit;
    font-size: inherit;
    outline: none;
    cursor: pointer;
    transition: background-color var(--_sdt-transition);
}

.sdt-retry:hover:not(:disabled),
.sdt-chooser-reset:hover:not(:disabled) {
    background-color: var(--_sdt-page-hover-bg);
}

.sdt-retry:focus-visible,
.sdt-chooser-reset:focus-visible {
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-retry:disabled,
.sdt-chooser-reset:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}

/* ---------- Filler row ---------- */

/* The last row of every table, empty and aria-hidden. It exists because `height: 100%` on the table
   alone would share the slack out across the REAL rows — two rows in a 400px table would render
   150px tall each. A row that is itself `height: 100%` absorbs the lot instead, leaving the real
   rows at their natural height, and its cells carry the frozen classes so each pinned column's
   ::after divider continues to the bottom of the scroll area. With no slack to take it collapses to
   zero and costs nothing, which is every table that was not given a Height.

   Written at full depth to out-specify the (0,1,3) padding and border on every cell above. */
.sdt-table > tbody > tr.sdt-filler {
    height: 100%;
    /* Decorative: hovering blank space must not light the row up or draw the accent bar. */
    pointer-events: none;
}

.sdt-table > tbody > tr.sdt-filler > td {
    padding: 0;
    border: 0;
}

/* The filler is what :last-child now finds, so the "no rule under the final row" rule above has to
   look past it — otherwise every table grows a border it never used to have. */
.sdt-table > tbody > tr:has(+ .sdt-filler) > td {
    border-block-end: 0;
}

/* An empty table draws no divider in its body. The message row above the filler is one colspan
   cell, pinned to nothing, so it cannot carry an ::after — the line broke there and resumed below,
   leaving a gap the height of the message and then a stripe down blank space. Measured on a 25rem
   table: drawn 825→873, absent 873→920, drawn again 920→1166.

   Suppressed rather than made continuous, because with no rows there are no cells for the divider
   to separate — and this is already how an empty table with no Height looks, its filler having
   collapsed to zero. The header keeps its own: that cell is real. Covers the error message too,
   which is the same colspan row under a second class. */
.sdt-table > tbody > tr:has(> td.sdt-message) + tr.sdt-filler > td::after {
    display: none;
}

/* ---------- Loading ---------- */

/* Non-destructive: rows dim but stay on screen while the next page loads.
   Replacing the table body on every keystroke-driven refetch would be worse. */
.sdt-busy {
    opacity: var(--_sdt-busy-opacity);
    pointer-events: none;
    transition: opacity var(--_sdt-transition);
}

.sdt-loading-overlay {
    position: absolute;
    /* Inset from the card's border, which the card no longer provides via padding. */
    inset-block-start: .25rem;
    inset-inline-end: .5rem;
    padding: .5rem .75rem;
    border-radius: var(--_sdt-page-radius);
    background-color: var(--_sdt-overlay-bg);
    pointer-events: none;
}

.sdt-spinner {
    display: inline-block;
    width: var(--_sdt-spinner-size);
    height: var(--_sdt-spinner-size);
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
    color: var(--_sdt-spinner-color);
    animation: sdt-spin .75s linear infinite;
}

@keyframes sdt-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .sdt-spinner {
        animation-duration: 2s;
    }
}

.sdt-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}


/* ---------- Footer ---------- */

.sdt-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin-block-start: .875rem;
}

.sdt-footer-end {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.sdt-info {
    color: var(--_sdt-muted-color);
    white-space: nowrap;
}

.sdt-page-size {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0;
    white-space: nowrap;
}

.sdt-select-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.sdt-select-caret {
    position: absolute;
    inset-inline-end: .625rem;
    width: .625rem;
    height: .375rem;
    color: var(--_sdt-select-caret-color);
    pointer-events: none;
}

/* Keyed off the wrapper, not off .sdt-page-size: the toolbar's card-mode sort picker wraps its
   select in the same .sdt-select-wrap, so both get the themed caret and chrome from one rule. */
.sdt-select-wrap select {
    padding-block: .375rem;
    padding-inline: .625rem 1.75rem;
    color: var(--_sdt-select-color);
    background-color: var(--_sdt-select-bg);
    border: 1px solid var(--_sdt-select-border-color);
    border-radius: var(--_sdt-select-radius);
    font-family: inherit;
    font-size: inherit;
    outline: none;
    cursor: pointer;
    /* The native caret is suppressed so the one drawn in markup can be themed. */
    appearance: none;
}

.sdt-select-wrap select:focus-visible {
    border-color: var(--_sdt-accent);
    box-shadow: var(--_sdt-focus-ring);
}

.sdt-select-wrap select:disabled {
    opacity: var(--_sdt-busy-opacity);
    cursor: not-allowed;
}


/* ---------- Pager ---------- */

/* Wraps, and has to. Eleven items at the default window — First, Previous, five numbers, an
   ellipsis, the last page, Next, Last — come to 392px, which is wider than a phone. Unwrapped, that
   width has nowhere to go: .sdt-footer wraps around the pager but the pager itself stayed one
   unbreakable line, and the whole PAGE scrolled sideways instead. That is the exact failure the
   component root's minmax(0, 1fr) exists to prevent for the table, so the pager may not reintroduce
   it. A flex line only breaks when it must, so every width that already fit is untouched.

   Not centred: the wrapped line start-aligns, which is conventional, correct under RTL with no
   second rule, and leaves the desktop rendering identical. */
.sdt-pager-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--_sdt-page-gap);
    margin: 0;
    padding: 0;
    list-style: none;
}

.sdt-page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--_sdt-page-size);
    height: var(--_sdt-page-size);
    padding-inline: .375rem;
    color: var(--_sdt-page-color);
    background-color: transparent;
    border: 0;
    border-radius: var(--_sdt-page-radius);
    font-family: inherit;
    font-size: inherit;
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

.sdt-page-link:hover:not(:disabled) {
    background-color: var(--_sdt-page-hover-bg);
}

.sdt-page-link:focus-visible {
    outline: none;
    box-shadow: var(--_sdt-focus-ring);
}

/* The colour is the pager's own, and stays: --stingray-dt-page-disabled-color is public, so it is
   a consumer's to tune against the pager's surface. The CURSOR was simply inconsistent — every other
   disabled control in the component says `not-allowed`, and two disabled buttons giving two
   different pointer answers is drift rather than meaning. */
.sdt-page-link:disabled {
    color: var(--_sdt-page-disabled-color);
    cursor: not-allowed;
}

.sdt-active > .sdt-page-link {
    color: var(--_sdt-page-active-color);
    background-color: var(--_sdt-page-active-bg);
    font-weight: 600;
}

.sdt-page-ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--_sdt-page-size);
    height: var(--_sdt-page-size);
    color: var(--_sdt-muted-color);
}

.sdt-chevron {
    width: 1rem;
    height: 1rem;
}

/* Chevrons are the one directional glyph here, so they mirror in RTL. */
html[dir="rtl"] .sdt-chevron {
    transform: scaleX(-1);
}

/* The panel renders inside the HOST's popup, so it inherits nothing from the table. Its tokens
   come from the shared block in 01-tokens.css, which also targets .sdt-chooser for this reason. */
.sdt-chooser {
    color: var(--_sdt-text-color);
}

/* Shown in place of the list when two columns share a key. It reports a fault in the consumer's
   markup, so it is sized to be read rather than glanced at — the list it replaces would have been
   a lie, since every mutation is refused in that state. */
.sdt-chooser-error {
    margin: 0;
    max-width: 22rem;
    color: var(--_sdt-danger-color);
    font-size: var(--_sdt-font-size);
    line-height: 1.5;
}


.sdt-chooser-list {
    display: flex;
    flex-direction: column;
    gap: var(--_sdt-chooser-row-gap);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The row itself paints nothing: it carries two boxes — the card and the arrow pair — with a gap
   between them, and it is the element the drag module hit-tests and draws its drop line against.
   `stretch` is what makes the arrow box take the card's height rather than its own content's. */
.sdt-chooser-item {
    display: flex;
    align-items: stretch;
    gap: var(--_sdt-chooser-gap);
}

/* The card is the <label>, so its whole area toggles the column. */
.sdt-chooser-label {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin: 0;
    padding: var(--_sdt-chooser-card-padding-y) var(--_sdt-chooser-card-padding-x);
    background-color: var(--_sdt-chooser-card-bg);
    border: 1px solid var(--_sdt-chooser-card-border-color);
    border-radius: var(--_sdt-chooser-card-radius);
    cursor: pointer;
    transition: background-color var(--_sdt-transition), border-color var(--_sdt-transition);
}

.sdt-chooser-label:hover {
    background-color: var(--_sdt-chooser-card-hover-bg);
}

/* One line, clipped: a long title must not push the switch off the card's edge. */
.sdt-chooser-label > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------- the visibility switch ---------- */

/* Still the checkbox 07-selection.css styles — this only repaints it. Everything below out-weighs
   that file by carrying both classes, so the box form's size, radius and tick are all replaced
   rather than fought with. */
.sdt-check.sdt-chooser-switch {
    position: relative;
    width: var(--_sdt-switch-width);
    height: var(--_sdt-switch-height);
    background-color: var(--_sdt-switch-off-bg);
    border: 0;
    /* Half the height would do; the height itself is simply always past it, so the track stays a
       pill whatever the two tokens are set to. */
    border-radius: var(--_sdt-switch-height);
    transition: background-color var(--_sdt-transition);
}

.sdt-check.sdt-chooser-switch:checked {
    background-color: var(--_sdt-switch-on-bg);
}

/* The thumb. Written for both states at once so it beats .sdt-check:checked::after, which paints
   the tick belonging to the box form — the mask is switched off here rather than left to draw a
   tick across the thumb. `inset-inline-start` is what mirrors the travel under RTL: an off switch
   sits at the inline start, whichever edge that is. */
.sdt-check.sdt-chooser-switch::after,
.sdt-check.sdt-chooser-switch:checked::after {
    content: "";
    position: absolute;
    inset-block-start: var(--_sdt-switch-inset);
    inset-inline-start: var(--_sdt-switch-inset);
    width: calc(var(--_sdt-switch-height) - var(--_sdt-switch-inset) * 2);
    height: calc(var(--_sdt-switch-height) - var(--_sdt-switch-inset) * 2);
    background-color: var(--_sdt-switch-thumb-color);
    border-radius: 50%;
    box-shadow: var(--_sdt-switch-thumb-shadow);
    -webkit-mask-image: none;
    mask-image: none;

    /* The travel is a transform, not an offset. `inset-inline-start` is a layout property, so
       animating it ran layout on every frame of the toggle; a transform is compositor-only. The
       static origin stays logical, so an off switch still sits at the inline start whichever edge
       that is — only the distance is physical, and the RTL rule below flips its sign. */
    transition: transform var(--_sdt-transition);
}

/* Track width less the thumb's own travel span: the thumb starts one inset in and stops one inset
   from the far edge, so the distance is width − height regardless of the inset. */
.sdt-check.sdt-chooser-switch::after {
    --_sdt-switch-travel: calc(var(--_sdt-switch-width) - var(--_sdt-switch-height));
}

html[dir="rtl"] .sdt-check.sdt-chooser-switch::after {
    --_sdt-switch-travel: calc((var(--_sdt-switch-width) - var(--_sdt-switch-height)) * -1);
}

.sdt-check.sdt-chooser-switch:checked::after {
    transform: translateX(var(--_sdt-switch-travel));
}

/* ---------- move up / move down ---------- */

/* A box of its own beside the card, not a pair of loose glyphs: the arrows reorder the column and
   the card toggles it, and separating them keeps a mis-aimed click from doing the other thing. */
.sdt-chooser-actions {
    display: inline-flex;
    flex: none;
    flex-direction: column;
    width: var(--_sdt-chooser-actions-width);
    background-color: var(--_sdt-chooser-card-bg);
    border: 1px solid var(--_sdt-chooser-card-border-color);
    border-radius: var(--_sdt-chooser-card-radius);
}

.sdt-chooser-move {
    display: flex;
    /* Equal halves of whatever height the card sets, so the pair always fills the box. */
    flex: 1 1 0;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--_sdt-chooser-move-color);
    background-color: transparent;
    border: 0;
    /* Inside the box's own corner, less its border. */
    border-radius: calc(var(--_sdt-chooser-card-radius) - 1px);
    cursor: pointer;
    transition: background-color var(--_sdt-transition), color var(--_sdt-transition);
}

.sdt-chooser-move:not(:disabled):hover {
    background-color: var(--_sdt-chooser-card-hover-bg);
}

.sdt-chooser-move:focus-visible {
    outline: none;
    box-shadow: var(--_sdt-focus-ring);
}

/* Nowhere legal to go in that direction: the end of the column's freeze group, or a group
   of one. Greyed rather than removed, so the row keeps its shape all the way down the list
   and the arrow still says which way it would have gone. `disabled` also takes it out of
   the tab order, which is the whole point of using the attribute rather than a class. */
.sdt-chooser-move:disabled {
    color: var(--_sdt-chooser-move-disabled-color);
    cursor: default;
}

.sdt-chooser-move svg {
    width: var(--_sdt-chooser-move-size);
    height: var(--_sdt-chooser-move-size);
}

.sdt-chooser-footer {
    display: flex;
    justify-content: flex-end;
    margin-block-start: .75rem;
}

/* .sdt-chooser-reset is styled in 10-rows.css, beside .sdt-retry — the two are the same bordered
   text button and are defined once so their focus and disabled states cannot drift apart again. */

/* ---------- Card view ----------
   Note the class names. `.sdt-card` is already the container SURFACE in 04-card.css — the box the
   rows sit inside — so nothing here reuses it. Everything belonging to the cards VIEW is
   `.sdt-cards-*`, which also reads as "part of the cards view" rather than "a card".

   Numbered last because it out-specifies nothing: it styles its own elements, and the only rule
   reaching outside it lives in 04-card.css next to the rule it neutralises. */

.sdt-cards {
    display: grid;
    /* auto-fill, so the number of cards per row falls out of the width available. A media query
       would have to guess at the container's width, which is not the viewport's whenever the table
       sits in a sidebar or a split pane.

       The track floor is the LARGER of the content minimum and the container's 1/N share, which is
       what turns MaxCardCountPerRow into a cap rather than a count. Wide: the share wins, so
       auto-fill fits exactly N and never N+1. Narrow: the share drops under the content minimum,
       the minimum wins, and the count steps down on its own. A hard repeat(N, 1fr) would instead
       crush N cards into whatever space there was. Uncapped, the share is negative (see the
       sentinel on --_sdt-card-max-per-row) and this reduces to the minimum alone. */
    grid-template-columns: repeat(auto-fill, minmax(max(var(--_sdt-card-min-width),
        calc((100% - (var(--_sdt-card-max-per-row) - 1) * var(--_sdt-card-gap))
             / var(--_sdt-card-max-per-row))), 1fr));
    gap: var(--_sdt-card-gap);
    align-items: start;
}

.sdt-cards-item {
    position: relative;
    padding: var(--_sdt-card-padding-y) var(--_sdt-card-padding-x);
    color: var(--_sdt-text-color);
    background-color: var(--_sdt-card-bg);
    border: 1px solid var(--_sdt-card-border-color);
    border-radius: var(--_sdt-card-radius);
    box-shadow: var(--_sdt-card-shadow);
    transition: background-color var(--_sdt-transition), box-shadow var(--_sdt-transition);
}

/* :hover matches every ancestor of the pointer, so a nested card would light up its whole chain of
   parents at once. The :has() guard keeps the tint on the innermost card under the cursor, which is
   also the one a click would act on. */
.sdt-cards-item:hover:not(:has(.sdt-cards-item:hover)) {
    background-color: var(--_sdt-card-hover-bg);
}

.sdt-cards-item.sdt-cards-selected {
    background-color: var(--_sdt-card-selected-bg);
    border-color: var(--_sdt-accent);
}

/* Children of an expanded card, drawn inside it. The inline padding is the indent, and because
   this container nests inside the parent card the offset accumulates on its own — depth is never
   computed. The rail reads as a tree spine, and being a logical property it mirrors in RTL with
   no dir rule. */
.sdt-cards-children {
    display: flex;
    flex-direction: column;
    gap: var(--_sdt-card-gap);
    margin-block-start: var(--_sdt-card-gap);
    padding-inline-start: var(--_sdt-card-nest-indent);
    border-inline-start: 2px solid var(--_sdt-card-rail-color);
}

/* A nested card is demoted rather than repeated: no shadow, a lighter border and a tint, so depth
   reads by weight as well as by containment. Every level uses the same treatment — stacking
   progressively darker tints would run out of contrast after two. */
.sdt-cards-nested {
    background-color: var(--_sdt-card-nested-bg);
    border-color: var(--_sdt-card-nested-border-color);
    box-shadow: none;
}

/* Selection and expansion controls. Absolutely positioned so they are chrome on the card rather
   than content within it — which is what lets CardTemplate own the body without losing them. */
.sdt-cards-chrome {
    position: absolute;
    inset-block-start: var(--_sdt-card-gap);
    inset-inline-end: var(--_sdt-card-gap);
    display: flex;
    align-items: center;
    gap: .375rem;
    /* Above the body, but the body's own interactive content stays reachable because this box is
       only as wide as its controls. */
    z-index: 1;
}

/* Keeps the first field's value clear of the chrome above it. Applies only when there IS chrome,
   so a card without selection or expansion uses its full width. */
.sdt-cards-chrome ~ .sdt-cards-body > .sdt-cards-field:first-child {
    padding-inline-end: 3.5rem;
}

.sdt-cards-body {
    display: flex;
    flex-direction: column;
}

.sdt-cards-field {
    display: flex;
    align-items: baseline;
    gap: var(--_sdt-card-field-gap);
    padding-block: calc(var(--_sdt-row-padding-y) / 2);
    border-block-end: 1px solid var(--_sdt-card-divider-color);
}

.sdt-cards-field:last-child {
    border-block-end: 0;
    padding-block-end: 0;
}

.sdt-cards-field:first-child {
    padding-block-start: 0;
}

.sdt-cards-label {
    flex: 0 0 var(--_sdt-card-label-width);
    min-width: 0;
    color: var(--_sdt-card-label-color);
    font-size: var(--_sdt-card-label-font-size);
    font-weight: var(--_sdt-card-label-font-weight);
    /* The header's counterpart wraps rather than truncating: a card has vertical room where a
       header row does not, and a clipped label is unreadable at this size. */
    overflow-wrap: anywhere;
}

.sdt-cards-value {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* RowDetailTemplate content, revealed inside the card rather than in a row beneath it. */
.sdt-cards-detail {
    margin-block-start: var(--_sdt-card-gap);
    /* Full-bleed to the card's edges, undoing the padding, so the tint reads as a section of the
       card rather than a floating box inside it. */
    margin-inline: calc(-1 * var(--_sdt-card-padding-x));
    padding: var(--_sdt-card-padding-y) var(--_sdt-card-padding-x);
    background-color: var(--_sdt-detail-bg);
    border-block-start: 1px solid var(--_sdt-card-divider-color);
}

/* Only bleed into the card's bottom edge when nothing follows. With children expanded the detail
   panel is no longer the last thing in the card, and a negative bottom margin would drag the
   nested subtree up over it. */
.sdt-cards-detail:last-child {
    margin-block-end: calc(-1 * var(--_sdt-card-padding-y));
    border-end-start-radius: var(--_sdt-card-radius);
    border-end-end-radius: var(--_sdt-card-radius);
}

/* ---------- Drag to reorder ---------- */

/* The header already carries user-select: none via .sdt-sortable, but a non-sortable column has no
   such rule and a mouse drag across it would select its title instead. */
.sdt-table > thead > tr > th[data-sdt-key] {
    user-select: none;
    -webkit-user-select: none;
}

/* iOS raises the selection callout on a long press, which is exactly the gesture that starts a
   touch drag. */
.sdt-chooser-item[data-sdt-key] {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}

/* Set on <body> for the duration, so the grabbing cursor holds even when the pointer leaves the
   header it started on. */
body.sdt-dragging,
body.sdt-dragging .sdt-table > thead > tr > th[data-sdt-key],
body.sdt-dragging .sdt-chooser-item[data-sdt-key] {
    cursor: grabbing;
}

/* Both of these are created on <body> by the drag module, so they are deliberately NOT scoped to
   .stingray-data-table — there is no table above them in the tree. That is also why their tokens
   have to resolve on :root. */
.sdt-drag-ghost {
    position: fixed;
    z-index: 2147483647;

    /* Physical, not logical. The module writes left/top from clientX/clientY, which are viewport
       coordinates in both directions — inset-inline-start would resolve to `right` under RTL and
       over-constrain the box against that inline `left`. */
    top: 0;
    left: 0;
    padding: var(--_sdt-header-padding-y) var(--_sdt-header-padding-x);
    color: var(--_sdt-drag-ghost-color);
    background-color: var(--_sdt-drag-ghost-bg);
    border: 1px solid var(--_sdt-drag-ghost-border-color);
    border-radius: var(--_sdt-page-radius);
    box-shadow: var(--_sdt-drag-ghost-shadow);
    opacity: var(--_sdt-drag-ghost-opacity);
    font-family: var(--_sdt-font-family);
    font-size: var(--_sdt-header-font-size);
    font-weight: var(--_sdt-header-font-weight);
    white-space: nowrap;

    /* The module positions this by left/top and offsets it here rather than in script, so the
       arithmetic stays with the visual. */
    transform: translate(-50%, -140%);

    /* Never a hit-test target: the module reads element rects, but a stray elementFromPoint or a
       browser's own drag heuristics must not find this instead of the header underneath. */
    pointer-events: none;
}

.sdt-drop-indicator {
    position: fixed;
    z-index: 2147483646;
    top: 0;
    left: 0;
    background-color: var(--_sdt-drop-indicator-color);
    /* Half the thickness, so the line's ends read as rounded rather than cut. */
    border-radius: var(--_sdt-drop-indicator-width);
    pointer-events: none;
}

/* The module sets the length along the drag axis and leaves the thickness to these. */
.sdt-drop-indicator-vertical {
    width: var(--_sdt-drop-indicator-width);
    transform: translateX(-50%);
}

.sdt-drop-indicator-horizontal {
    height: var(--_sdt-drop-indicator-width);
    transform: translateY(-50%);
}

/* ---------- Cell popup ---------- */

.sdt-popup-anchor {
    display: inline-flex;
}

/* Closed, and in its original place in the tree. Never `display: none` on the OPEN state — the
   module measures the panel to position it, and a display:none box measures zero. */
.sdt-popup-panel {
    display: none;
}

/* Open, and still exactly where Blazor rendered it — the module writes coordinates and moves
   nothing, for the reasons its own header comment sets out at length. Fixed rather than absolute is
   the whole point: a fixed box is clipped only by a transformed ancestor, and this package puts
   transforms on leaf glyphs only — never on .sdt-table-wrapper, .sdt-card or the root. So the
   table's `overflow: auto` cannot cut the menu off.

   left/top are physical, and written by the module from getBoundingClientRect, which reports
   viewport coordinates in both writing directions. A logical property here would resolve to the
   wrong edge under RTL and fight that inline `left`. */
.sdt-popup-panel.sdt-popup-open {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    /* Looks redundant next to the rule at the foot of this file, and is not: that one raises
       the containing CELL, which only exists when the popup is used inside a table. Used
       anywhere else, this is the only thing keeping the panel over the host's own content. */
    z-index: 2147483645;
    min-width: var(--_sdt-popup-min-width);
    /* A menu longer than the screen scrolls inside itself rather than off the bottom of it. The
       module measures the panel AFTER this applies, so it places the clamped box, not the full one.
       Scrolling here no longer dismisses the panel — the module ignores a scroll that came from
       inside it. */
    max-height: var(--_sdt-popup-max-height);
    overflow-y: auto;
    background-color: var(--_sdt-popup-bg);
    border: 1px solid var(--_sdt-popup-border-color);
    border-radius: var(--_sdt-popup-radius);
    box-shadow: var(--_sdt-popup-shadow);
}

/* Raise the cell that holds an open panel. `position: fixed` escapes an ancestor's *clipping* —
   which is what the rule above is for — but not its *stacking*. Every frozen cell is
   `position: sticky; z-index: 1`, and sticky with a z-index creates a stacking context, so the
   panel's own z-index is resolved INSIDE its cell and the whole cell competes at 1. A later row's
   frozen cell, also at 1, then wins on DOM order and paints over the menu.

   4 clears the body cells' 1, the header row's 2 and the frozen corner's 3, so a menu flipped
   upwards covers the header as it should. Where :has() is unsupported the result is exactly the
   old behaviour — degradation, not breakage. */
.sdt-table > thead > tr > th:has(.sdt-popup-open),
.sdt-table > tbody > tr > td:has(.sdt-popup-open) {
    z-index: 4;
}

/* ---------- Column resize ---------- */

/* Only under AllowColumnResize, and this is the whole reason the flag exists. The table is
   auto-layout otherwise, where a cell's `width` is a hint: content pushes the column wider
   regardless, so dragging one NARROWER than its content would snap straight back. Fixed layout makes
   the widths authoritative, which is what the gesture needs to mean anything.

   The cost is real and is why this is opt-in rather than always on: columns with no Width share the
   remainder equally instead of sizing to their content, and content that no longer fits is clipped
   rather than widening its column. */
.sdt-table-resizable {
    table-layout: fixed;
}

/* Clipped, because a fixed-layout column that cannot grow would otherwise spill its text across the
   next one. Child combinators, not descendants: a RowDetailTemplate may hold another table, and
   without the `>` this would clip the inner table's cells from the outer table's rule — the same
   reason every row rule in this stylesheet is written this way. */
.sdt-table-resizable > thead > tr > th,
.sdt-table-resizable > tbody > tr > td {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------- The chrome columns under fixed layout ---------- */

/* The selection and expand columns size themselves to their content, which is the one thing a
   fixed-layout column cannot do: 06-expansion.css and 07-selection.css say `width: 1px` and let
   auto layout grow the column to fit, and under `table-layout: fixed` that 1px stops being a hint
   and becomes the answer. Both columns then held a control in a cell too narrow for it, clipped by
   the rule above — a checkbox and a chevron painted at a few pixels wide, and at depth not painted
   at all.

   box-sizing is stated rather than inherited because these two widths are arithmetic and have to
   land on the same number in every host. The package sets it nowhere else on purpose — it is the
   consumer's reset to make — but a value that means "plus the padding" under Bootstrap and
   "before the padding" without it is not a width, it is a guess. Both columns are chrome the
   consumer never sized, so nothing they wrote is being overridden here. */
.sdt-table-resizable > thead > tr > th.sdt-select-cell,
.sdt-table-resizable > tbody > tr > td.sdt-select-cell {
    box-sizing: border-box;
    width: var(--_sdt-select-cell-width);
}

/* The expand column's width is a function of how deep the tree is currently opened, because the
   per-level indent is padding on THIS cell — so the room a chevron needs grows with its row's
   depth, and a constant is either too narrow at depth or a wide empty gutter at the top.
   --sdt-max-depth is written by the component on .sdt-table-wrapper every render; the fallback
   covers the table that has not rendered a row yet.

   The number is the one auto layout would compute for the same tree, so the DECLARED width does
   not move when the flag goes on. The rendered one still can: fixed layout hands any width the
   table has to spare out across every column that declared one, these two included, exactly as it
   does to a consumer's Width. That is the flag's own trade-off arriving here, not a second one.

   The max() is what makes the arithmetic right, and it is not a sum. A depth-0 cell keeps the row
   padding on both sides; a deeper one has its LEADING padding replaced by the indent, so past the
   first level the width is the toggle, the trailing padding and the indent — with the depth-0
   width as the floor under it, never a term in it. Add the two and every nested level sits one
   padding wider than it should, which no assertion that the control is merely unclipped catches.

   No instance id, unlike --sdt-w-<id>-<i>: that one lives on the <table>, which a table in a
   RowDetailTemplate is a descendant of and inherits from. Every table writes this on its own
   wrapper unconditionally, so a nested one always shadows its parent's value. */
.sdt-table-resizable > thead > tr > th.sdt-expand-cell,
.sdt-table-resizable > tbody > tr > td.sdt-expand-cell {
    box-sizing: border-box;
    width: max(var(--_sdt-expand-cell-width),
               calc(var(--_sdt-expand-toggle-size) + var(--_sdt-row-padding-x)
                    + var(--sdt-max-depth, 0) * var(--_sdt-indent)));
}

/* Exempt from the clipping above, and not only as insurance for the arithmetic. These two cells
   hold a control and no text, so there is no overflowing sentence to ellipsise and nothing that
   clipping degrades gracefully — it removes the control. It also un-clips the toggle's focus ring,
   which is the outer two-ring token and draws outside the button's own box: the reason every <th>
   in a resizable table uses --_sdt-focus-ring-inset is this same `overflow: hidden`, and the
   chevron sits in a cell that no longer needs to impose it. */
.sdt-table-resizable > thead > tr > th.sdt-select-cell,
.sdt-table-resizable > tbody > tr > td.sdt-select-cell,
.sdt-table-resizable > thead > tr > th.sdt-expand-cell,
.sdt-table-resizable > tbody > tr > td.sdt-expand-cell {
    overflow: visible;
}

/* The grab target. Absolute against the <th>, which is already `position: sticky` and therefore a
   positioned ancestor. Logical inset so it lands on the trailing edge in both writing directions —
   the module mirrors the drag delta to match. */
.sdt-resize-handle {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: var(--_sdt-resize-handle-width);
    cursor: col-resize;

    /* Above the header content, so the last few pixels of a long title cannot steal the press. */
    z-index: 1;

    /* Never inherits the header's clipping: the handle is the one thing in a resizable cell that
       must stay reachable at the very edge. */
    touch-action: none;
}

/* The visible line, drawn only on hover or during the drag. A permanent divider on every column
   would read as a table with vertical rules, which this component deliberately does not have. */
.sdt-resize-handle::after {
    content: "";
    position: absolute;
    inset-block: var(--_sdt-resize-line-inset);
    inset-inline-end: 0;
    width: var(--_sdt-resize-line-width);
    background-color: var(--_sdt-resize-line-color);
    opacity: 0;
    transition: opacity var(--_sdt-transition);
}

.sdt-resize-handle:hover::after,
.sdt-resize-handle:focus-visible::after,
body.sdt-resizing .sdt-resize-handle::after {
    opacity: 1;
}

/* Focused rather than hovered, so the bar has to be findable without a pointer sitting on it: full
   height, the handle's own width rather than the hairline, and the focus colour rather than the
   resize one. The ring itself is deliberately absent — a box-shadow on a strip this narrow reads as
   a smudge, and the <th> clips it under AllowColumnResize anyway. */
.sdt-resize-handle:focus-visible {
    outline: none;
}

.sdt-resize-handle:focus-visible::after {
    inset-block: 0;
    width: var(--_sdt-resize-handle-width);
    background-color: var(--_sdt-focus-ring-color);
}

/* Set on <body> for the duration, so the cursor holds when the pointer leaves the handle it started
   on — the same reason body.sdt-dragging exists. user-select here rather than on the header: a
   resize can travel far past the table, and selecting the page's text under it is worse than
   selecting a column title. */
body.sdt-resizing {
    cursor: col-resize;
    user-select: none;
    -webkit-user-select: none;
}

/* =============================================================================
   Dark theme — OPT-IN, and deliberately so.

   A component package that flipped itself dark on `prefers-color-scheme` alone would turn a light
   host application's table dark the moment the user's OS was set that way, on a page whose every
   other element stayed white. That is not a theme, it is one dark rectangle. So the host says when:

     class="sdt-theme-dark"   always dark
     class="sdt-theme-auto"   dark only while the OS asks for it

   Either goes on <html>, <body>, or any ancestor — including one that only wraps a single table.
   Put it high enough to cover the popup host as well, or the column chooser stays light: it renders
   OUTSIDE the table, which is the same reason 01-tokens.css resolves on :root at all.

   WHY THE DESCENDANT SELECTORS: 01-tokens.css re-resolves the whole private tier on
   .stingray-data-table and .sdt-chooser. Inheriting a dark value from <html> would lose to those,
   so each has to be re-declared at a deeper point than the light one.

   WHAT IS NOT HERE: anything that derives. --_sdt-header-bg already falls back to --_sdt-bg,
   --_sdt-check-border-color to --_sdt-icon-muted-color, --_sdt-focus-ring-color to --_sdt-accent,
   and so on down the file — so overriding roots is enough and the rest follows. Public
   --stingray-dt-* tokens still win over every line below, exactly as they do in the light tier.

   THE TWO BLOCKS ARE ONE LIST. They differ only in selector; a value changed in one must be
   changed in the other. Measured against #151a26: body text 14.33:1, muted 6.91:1, icons 4.67:1,
   accent 7.71:1, focus ring 5.33:1 at its worst (over a selected row).
   ============================================================================= */

.sdt-theme-dark,
.sdt-theme-dark .stingray-data-table,
.sdt-theme-dark .sdt-chooser {
    /* Tells the browser to paint its own furniture dark too — the <select>'s dropdown,
       the scrollbars on .sdt-table-wrapper, and any native control a consumer nests. */
    color-scheme: dark;

    /* Surface */
    --_sdt-bg: var(--stingray-dt-bg, #151a26);
    --_sdt-border-color: var(--stingray-dt-border-color, #2a3247);
    --_sdt-shadow: var(--stingray-dt-shadow, 0 1px 2px rgba(0, 0, 0, .5));

    /* Type */
    --_sdt-text-color: var(--stingray-dt-text-color, #e6e9f2);
    --_sdt-muted-color: var(--stingray-dt-muted-color, #9aa3bd);
    --_sdt-icon-muted-color: var(--stingray-dt-icon-muted-color, #7b849f);

    /* Accent. Light enough to be readable ON the surface, which the light theme's #1b2f7e is not:
       it is the focus ring, the checked box and the active sort arrow as well as a text colour. */
    --_sdt-accent: var(--stingray-dt-accent, #93aaf5);
    --_sdt-accent-soft: var(--stingray-dt-accent-soft, #26355c);

    /* Header */
    --_sdt-header-color: var(--stingray-dt-header-color, #a8b1c9);
    --_sdt-header-border-color: var(--stingray-dt-header-border-color, #2a3247);
    --_sdt-header-hover-bg: var(--stingray-dt-header-hover-bg, #1e2432);

    /* Rows */
    --_sdt-row-border-color: var(--stingray-dt-row-border-color, #232a3b);
    --_sdt-row-hover-bg: var(--stingray-dt-row-hover-bg, #1d2331);
    --_sdt-row-selected-bg: var(--stingray-dt-row-selected-bg, #26355c);
    --_sdt-row-selected-hover-bg: var(--stingray-dt-row-selected-hover-bg, #2f4070);
    --_sdt-child-row-bg: var(--stingray-dt-child-row-bg, #1a2030);
    --_sdt-detail-bg: var(--stingray-dt-detail-bg, #181e2b);

    /* Selection. The tick inverts: white on a light accent is the one pairing that gets worse in dark. */
    --_sdt-check-bg: var(--stingray-dt-check-bg, #1b2231);
    --_sdt-check-checked-color: var(--stingray-dt-check-checked-color, #101623);

    /* Frozen columns */
    --_sdt-frozen-edge-color: var(--stingray-dt-frozen-edge-color, #3d4763);

    /* Controls */
    --_sdt-page-color: var(--stingray-dt-page-color, #b3bcd4);
    --_sdt-page-hover-bg: var(--stingray-dt-page-hover-bg, #1e2432);
    --_sdt-page-disabled-color: var(--stingray-dt-page-disabled-color, #59627a);
    --_sdt-select-bg: var(--stingray-dt-select-bg, #1b2231);
    --_sdt-select-border-color: var(--stingray-dt-select-border-color, #3d4763);
    --_sdt-search-bg: var(--stingray-dt-search-bg, #1b2231);
    --_sdt-search-border-color: var(--stingray-dt-search-border-color, #3d4763);
    --_sdt-switch-thumb-color: var(--stingray-dt-switch-thumb-color, #101623);

    /* Cards and chooser */
    --_sdt-card-nested-bg: var(--stingray-dt-card-nested-bg, #1a2030);
    --_sdt-card-nested-border-color: var(--stingray-dt-card-nested-border-color, #2a3247);
    --_sdt-card-rail-color: var(--stingray-dt-card-rail-color, #3d4763);
    --_sdt-chooser-card-border-color: var(--stingray-dt-chooser-card-border-color, #2a3247);

    /* State */
    --_sdt-overlay-bg: var(--stingray-dt-overlay-bg, rgba(21, 26, 38, .6));
    --_sdt-danger-color: var(--stingray-dt-danger-color, #ff9d94);
    --_sdt-popup-shadow: var(--stingray-dt-popup-shadow, 0 6px 16px rgba(0, 0, 0, .55));
    --_sdt-drag-ghost-shadow: var(--stingray-dt-drag-ghost-shadow, 0 6px 16px rgba(0, 0, 0, .55));
}

@media (prefers-color-scheme: dark) {
    .sdt-theme-auto,
    .sdt-theme-auto .stingray-data-table,
    .sdt-theme-auto .sdt-chooser {
        /* Tells the browser to paint its own furniture dark too — the <select>'s dropdown,
           the scrollbars on .sdt-table-wrapper, and any native control a consumer nests. */
        color-scheme: dark;

        /* Surface */
        --_sdt-bg: var(--stingray-dt-bg, #151a26);
        --_sdt-border-color: var(--stingray-dt-border-color, #2a3247);
        --_sdt-shadow: var(--stingray-dt-shadow, 0 1px 2px rgba(0, 0, 0, .5));

        /* Type */
        --_sdt-text-color: var(--stingray-dt-text-color, #e6e9f2);
        --_sdt-muted-color: var(--stingray-dt-muted-color, #9aa3bd);
        --_sdt-icon-muted-color: var(--stingray-dt-icon-muted-color, #7b849f);

        /* Accent. Light enough to be readable ON the surface, which the light theme's #1b2f7e is not:
           it is the focus ring, the checked box and the active sort arrow as well as a text colour. */
        --_sdt-accent: var(--stingray-dt-accent, #93aaf5);
        --_sdt-accent-soft: var(--stingray-dt-accent-soft, #26355c);

        /* Header */
        --_sdt-header-color: var(--stingray-dt-header-color, #a8b1c9);
        --_sdt-header-border-color: var(--stingray-dt-header-border-color, #2a3247);
        --_sdt-header-hover-bg: var(--stingray-dt-header-hover-bg, #1e2432);

        /* Rows */
        --_sdt-row-border-color: var(--stingray-dt-row-border-color, #232a3b);
        --_sdt-row-hover-bg: var(--stingray-dt-row-hover-bg, #1d2331);
        --_sdt-row-selected-bg: var(--stingray-dt-row-selected-bg, #26355c);
        --_sdt-row-selected-hover-bg: var(--stingray-dt-row-selected-hover-bg, #2f4070);
        --_sdt-child-row-bg: var(--stingray-dt-child-row-bg, #1a2030);
        --_sdt-detail-bg: var(--stingray-dt-detail-bg, #181e2b);

        /* Selection. The tick inverts: white on a light accent is the one pairing that gets worse in dark. */
        --_sdt-check-bg: var(--stingray-dt-check-bg, #1b2231);
        --_sdt-check-checked-color: var(--stingray-dt-check-checked-color, #101623);

        /* Frozen columns */
        --_sdt-frozen-edge-color: var(--stingray-dt-frozen-edge-color, #3d4763);

        /* Controls */
        --_sdt-page-color: var(--stingray-dt-page-color, #b3bcd4);
        --_sdt-page-hover-bg: var(--stingray-dt-page-hover-bg, #1e2432);
        --_sdt-page-disabled-color: var(--stingray-dt-page-disabled-color, #59627a);
        --_sdt-select-bg: var(--stingray-dt-select-bg, #1b2231);
        --_sdt-select-border-color: var(--stingray-dt-select-border-color, #3d4763);
        --_sdt-search-bg: var(--stingray-dt-search-bg, #1b2231);
        --_sdt-search-border-color: var(--stingray-dt-search-border-color, #3d4763);
        --_sdt-switch-thumb-color: var(--stingray-dt-switch-thumb-color, #101623);

        /* Cards and chooser */
        --_sdt-card-nested-bg: var(--stingray-dt-card-nested-bg, #1a2030);
        --_sdt-card-nested-border-color: var(--stingray-dt-card-nested-border-color, #2a3247);
        --_sdt-card-rail-color: var(--stingray-dt-card-rail-color, #3d4763);
        --_sdt-chooser-card-border-color: var(--stingray-dt-chooser-card-border-color, #2a3247);

        /* State */
        --_sdt-overlay-bg: var(--stingray-dt-overlay-bg, rgba(21, 26, 38, .6));
        --_sdt-danger-color: var(--stingray-dt-danger-color, #ff9d94);
        --_sdt-popup-shadow: var(--stingray-dt-popup-shadow, 0 6px 16px rgba(0, 0, 0, .55));
        --_sdt-drag-ghost-shadow: var(--stingray-dt-drag-ghost-shadow, 0 6px 16px rgba(0, 0, 0, .55));
    }
}

/* =============================================================================
   Windows High Contrast / forced-colors

   In this mode the OS discards author colours and substitutes its own. Two things this component
   does are undone by that, and both leave a control that is still there but invisible:

     1. box-shadow is dropped outright. Every :focus-visible in this stylesheet draws its ring with
        box-shadow after setting `outline: none`, so without the rules below a forced-colors user
        has no focus indicator at all.
     2. .sdt-check is `appearance: none` — the native checkbox is gone and what replaces it is a
        background and a border this mode overrides. It becomes an empty square with no state.

   System colour keywords are the fix rather than forced-color-adjust: they are honoured in this
   mode and resolve to whatever pairing the user actually chose, where opting out with
   forced-color-adjust: none would pin our palette against a background we no longer control.
   ============================================================================= */

@media (forced-colors: active) {

    /* One rule for every focusable control the component has, because they all share the same
       box-shadow ring and therefore all lose it together. Highlight is the system's focus colour. */
    .sdt-header-button:focus-visible,
    .sdt-search-input:focus-visible,
    .sdt-view-button:focus-visible,
    .sdt-retry:focus-visible,
    .sdt-chooser-reset:focus-visible,
    .sdt-table-wrapper:focus-visible,
    .sdt-chooser-open:focus-visible,
    .sdt-reset-columns:focus-visible,
    .sdt-sort-direction:focus-visible,
    .sdt-expand-toggle:focus-visible,
    .sdt-check:focus-visible,
    .sdt-select-wrap select:focus-visible,
    .sdt-page-link:focus-visible,
    .sdt-chooser-move:focus-visible,
    .sdt-resize-handle:focus-visible {
        outline: 2px solid Highlight;
        outline-offset: 1px;
    }

    /* The painted checkbox, rebuilt from system colours. ButtonBorder for the edge so an unchecked
       box is a box; Highlight/HighlightText for the checked and mixed states, which is the pairing
       the OS guarantees is legible against itself. */
    .sdt-check {
        background-color: Canvas;
        border-color: ButtonBorder;
    }

    .sdt-check:checked,
    .sdt-check-mixed {
        background-color: Highlight;
        border-color: Highlight;
    }

    .sdt-check:checked::after,
    .sdt-check-mixed::after {
        background-color: HighlightText;
    }

    .sdt-check:disabled {
        border-color: GrayText;
    }

    /* The switch reads as off/on by track colour alone, and both tracks would be forced to the same
       system background. The border is what keeps the off state visible as a control at all. */
    .sdt-check.sdt-chooser-switch {
        background-color: Canvas;
        border: 1px solid ButtonBorder;
    }

    .sdt-check.sdt-chooser-switch:checked {
        background-color: Highlight;
    }

    .sdt-check.sdt-chooser-switch::after {
        background-color: ButtonBorder;
    }

    .sdt-check.sdt-chooser-switch:checked::after {
        background-color: HighlightText;
    }

    /* Selection survives on the accent bar rather than the tint: a forced background is the same
       for every row, so the bar in 07-selection.css is the only signal left. */
    .sdt-table > tbody > tr.sdt-row-selected > td:first-child::before {
        background-color: Highlight;
    }

    /* Structure the mode would otherwise flatten: where the pinned columns end, where a column is
       being resized, and where a dragged column would land. */
    .sdt-frozen-edge-start::after,
    .sdt-frozen-edge-end::after {
        background-color: CanvasText;
    }

    .sdt-resize-handle::after {
        background-color: CanvasText;
    }

    .sdt-sort-indicator {
        color: CanvasText;
    }

    .sdt-drop-indicator {
        background-color: Highlight;
    }

    /* Dimming by opacity alone stops reading as disabled once contrast is forced up. */
    .sdt-page-link:disabled,
    .sdt-chooser-move:disabled,
    .sdt-check:disabled,
    .sdt-view-button:disabled,
    .sdt-sort-direction:disabled,
    .sdt-chooser-open:disabled {
        color: GrayText;
    }
}

