@scope (table-ui) {
  :where(:scope) {
    /* ── Tokens ── */
    --table-font-size:        var(--a-ui-size);
    --table-font-family:      var(--a-font-family-ui);
    --table-heading-size:     var(--a-ui-xs);
    --table-heading-weight:   var(--a-weight-medium);
    --table-heading-tracking: 0.06em;
    --table-heading-fg:       var(--a-fg-muted);
    --table-heading-bg:       transparent;

    --table-fg:               var(--a-fg);
    --table-fg-hover:         var(--a-fg-strong);
    --table-fg-secondary:     var(--a-fg-muted);
    --table-border:           var(--a-border-subtle);
    --table-border-strong:    var(--a-border);
    --table-py:               var(--a-space-3);
    --table-px:               var(--a-space-4);

    --table-row-bg:           transparent;
    --table-row-bg-hover:     var(--a-bg-hover);
    --table-row-bg-selected:  var(--a-bg-selected);
    --table-row-bg-striped:   var(--a-bg-subtle);

    --table-sort-fg:          var(--a-fg-muted);
    --table-accent:           var(--a-primary);
    --table-fg-disabled:      var(--a-ui-text-disabled);
    --table-bg:               var(--a-bg);
    /* No default rounding — table-ui composes inside card-ui / drawer-ui
       which own the surface rounding; doubly-rounded corners (table inside
       card) look broken. Override per-instance via `--table-radius: …` if
       table-ui sits standalone on the page. */
    --table-radius:           0;

    /* ── Resize + pinned-column intrinsics ── */
    --table-resize-width:     var(--a-space-1);
    --table-pin-shadow-width: var(--a-space-1);
    --table-cell-min-width:   11.25rem;

    /* ── Filter popover ── */
    --table-filter-bg:        var(--a-canvas);
    --table-filter-fg:        var(--a-canvas-text);
    --table-filter-border:    var(--a-border);
    --table-filter-radius:    var(--a-radius-md);
    --table-filter-inset:     var(--a-space-2);
    --table-filter-gap:       var(--a-space-1);
    --table-filter-shadow:    var(--a-shadow-md);
    --table-filter-input-py:  var(--a-space-1);
    --table-filter-input-px:  var(--a-space-2);
    --table-filter-input-radius: var(--a-radius-sm);
    --table-filter-input-size: var(--a-ui-tiny);
    --table-filter-btn-px:    var(--a-space-1);
    --table-filter-clear-fg:  var(--a-accent);

    /* ── Chips / filter bar ── */
    --table-chip-bar-bg:      var(--a-canvas-subtle);
    --table-chip-bar-border:  var(--a-border);
    --table-chip-bar-gap:     var(--a-space-1);
    --table-chip-bar-py:      var(--a-space-1);
    --table-chip-bar-px:      var(--a-space-2);
    --table-chip-dismiss-gap: var(--a-space-1);

    /* ── Detail / expansion row ── */
    --table-detail-bg:        var(--a-canvas-subtle);
    --table-detail-border:    var(--a-border);
    --table-detail-py:        var(--a-space-3);
    --table-detail-px:        var(--a-space-4);

    /* ── Pinning ── */
    --table-pinned-bg:        var(--a-canvas);
    --table-pinned-shadow:    var(--a-chrome-shadow-soft);

    /* ── Aggregation row ── */
    --table-agg-border:       var(--a-border);
    --table-agg-size:         var(--a-ui-tiny);
    --table-agg-py:           var(--a-space-2);
    --table-agg-px:           var(--a-space-3);

    /* ── Sort priority badge ── */
    --table-sort-priority-fg: var(--a-chrome-light);

    /* ── Sort icon gap ── */
    --table-sort-icon-gap:    var(--a-space-1);

    /* ── Footer / pagination ── */
    --table-footer-py:        var(--a-space-3);

    /* ── Overlays ── */
    --table-overlay-gap:      var(--a-space-3);
    --table-overlay-pad:      var(--a-space-8);

    /* ── Transitions ── */
    --table-duration:         var(--a-duration-fast);
    --table-easing:           var(--a-easing);

    /* ── State ── */
    --table-focus-ring:       var(--a-focus-ring);

    /* ── Z-index layers ── */
    --_z-cell:                0;
    --_z-pinned:              1;
    --_z-header:              2;
    --_z-header-pinned:       3;
  }

  /* ═══════ Base ═══════ */

  :scope {
    box-sizing: border-box;
    display: grid;
    /* `overflow: auto` (both axes) — needed so position:sticky on the
       header works relative to table-ui's own scroll viewport. Vertical
       scroll only kicks in when consumer sets max-height; horizontal
       only when grid-template-columns sum exceeds the container width.
       Pre-fix: overflow-x: auto alone made table-ui the sticky-containing
       block but with no vertical clip — wrapping in an outer max-height
       div made the WHOLE table scroll (header included), defeating sticky. */
    overflow: auto;
    /* font-family floor — host anchor; cells + filter inputs inherit it. */
    font-family: var(--table-font-family);
    font-size: var(--table-font-size);
    color: var(--table-fg);
    background: var(--table-bg);
    /* Use inset box-shadow as a faux border so the 1px chrome paints
       inside the content box and doesn't shrink clientWidth — a real
       `border: 1px` made grid content overflow by exactly 2 px on every
       table (scrollbar appeared with nothing to scroll). */
    box-shadow: inset 0 0 0 1px var(--table-border);
    border-radius: var(--table-radius);
    position: relative;
    /* Own stacking context — sticky headers, pinned columns, and filter
       dropdowns use internal z-indices that must not leak to parents. */
    isolation: isolate;
  }

  /* ═══════ Raw (no chrome — for embedding inside card-ui) ═══════ */

  :scope[raw] {
    background: none;
    box-shadow: none;
    border-radius: 0;
  }

  /* ═══════ Subgrid passthrough ═══════ */

  /* Rowgroup wrappers (header, body, footer) must participate in the
     parent grid so their children can use subgrid. */
  [data-header],
  [data-body] {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
  }

  [role="row"] {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    align-items: center;
  }

  /* ═══════ Header row ═══════ */

  [data-header] {
    border-bottom: 1px solid var(--table-border-strong);
    position: sticky;
    top: 0;
    z-index: var(--_z-header);
    background: var(--table-bg);
  }

  /* ═══════ Column headers ═══════ */

  [role="columnheader"] {
    box-sizing: border-box;
    text-align: left;
    padding: var(--table-py) var(--table-px);
    font-size: var(--table-heading-size);
    font-weight: var(--table-heading-weight);
    color: var(--table-heading-fg);
    background: var(--table-heading-bg);
    text-transform: uppercase;
    letter-spacing: var(--table-heading-tracking);
    white-space: nowrap;
    position: relative;
    display: flex;
    align-items: center;
  }

  /* Label span truncates; sort icon + filter button remain visible.
     Cannot put overflow:hidden on the columnheader itself — it would
     clip the position:absolute filter dropdown. */
  [role="columnheader"] > span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ═══════ Sortable headers ═══════ */

  [data-sort-key] {
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    transition: color var(--table-duration) var(--table-easing);
  }

  [data-sort-key]:hover {
    color: var(--table-fg);
  }

  /* ═══════ Sort indicator ═══════ */

  [data-sort-icon] {
    color: var(--table-sort-fg);
    margin-inline-start: var(--table-sort-icon-gap);
    vertical-align: middle;
    --a-icon-size: 0.75em;
  }

  [aria-sort] [data-sort-icon] {
    color: var(--table-fg);
  }

  [data-sort-priority] {
    display: inline-flex;
    font-size: 0.6em;
    background: var(--table-accent);
    color: var(--table-sort-priority-fg);
    width: 1em;
    height: 1em;
    border-radius: 50%;
    align-items: center;
    justify-content: center;
    margin-inline-start: 2px;
  }

  /* ═══════ Body cells ═══════ */

  [data-body] [role="gridcell"] {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    align-self: stretch;
    padding: var(--table-py) var(--table-px);
    border-bottom: 1px solid var(--table-border);
    min-width: 0;
    /* Default: single line with ellipsis. Authors opt in to wrapping
       via [wrap] on the host (whole table) or [data-wrap] on a column /
       individual cell. Matches `<select-ui>` / `<nav-item-ui>` single-line
       row convention; long unbreakable strings (URLs, IDs) clip gracefully
       rather than rewrapping the row. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* text-overflow on a flex container doesn't reach text nodes (anonymous
     flex items). Built-in cell types now wrap text in <span>; link type
     creates <a>; avatar/progress composite types use row-ui > span. */
  [data-body] [role="gridcell"] > span,
  [data-body] [role="gridcell"] > a {
    min-width: 0;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  [data-body] [role="gridcell"] > row-ui {
    min-width: 0;
    overflow: hidden;
  }

  [data-body] [role="gridcell"] > row-ui > span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Wrap opt-in: per-table (all body cells) or per-cell (one column).
     Restores the legacy `overflow-wrap: anywhere` behavior so wrapped
     content can break inside long unbroken tokens (URLs, IDs). Row
     height auto-grows to fit. */
  :scope[wrap] [data-body] [role="gridcell"],
  [data-body] [role="gridcell"][data-wrap] {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: anywhere;
  }

  :scope[wrap] [data-body] [role="gridcell"] > span,
  :scope[wrap] [data-body] [role="gridcell"] > a,
  [data-body] [role="gridcell"][data-wrap] > span,
  [data-body] [role="gridcell"][data-wrap] > a {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: anywhere;
  }

  [data-align="right"] {
    text-align: right;
    justify-content: flex-end;
  }

  [data-align="center"] {
    text-align: center;
    justify-content: center;
  }

  /* ═══════ Row states ═══════ */

  [data-body] > [role="row"] {
    background: var(--table-row-bg);
    transition: background var(--table-duration) var(--table-easing);
  }

  [data-body] > [role="row"]:hover {
    background: var(--table-row-bg-hover);
    color: var(--table-fg-hover);
  }

  /* Selected row — must out-specify the base row rule `[data-body] >
     [role="row"]` (0,2,0) and the hover rule (0,2,1), or the highlight never
     paints. Scoping to `[data-body] > [role="row"][data-selected]` lifts it to
     (0,4,0). (The striped-even override below handles selected+striped, which
     is higher still.) */
  [data-body] > [role="row"][data-selected] {
    background: var(--table-row-bg-selected);
  }

  /* ═══════ Striped ═══════ */

  :scope[striped] [data-body] [role="gridcell"] {
    border-bottom: none;
  }

  :scope[striped] [data-body] > [role="row"]:nth-child(even) {
    background: var(--table-row-bg-striped);
  }

  :scope[striped] [data-body] > [role="row"]:nth-child(even):hover {
    background: var(--table-row-bg-hover);
  }

  /* Selected wins over the stripe — same (0,5,0) specificity as the striped-
     even rule above, placed AFTER it so source order resolves the tie in
     favor of selection (a selected row reads as selected regardless of stripe). */
  :scope[striped] [data-body] > [role="row"][data-selected] {
    background: var(--table-row-bg-selected);
  }

  /* ═══════ Selection checkboxes ═══════ */

  [data-check-col] {
    width: 2.5rem;
    text-align: center;
    flex-shrink: 0;
  }

  /* Selectable: checkbox column width is handled by the parent grid-template-columns.
     Subgrid rows inherit it automatically — no override needed. */

  /* ═══════ Density ═══════ */

  :scope[density="compact"] {
    --table-py: var(--a-space-1);
    --table-px: var(--a-space-2);
  }

  :scope[density="comfortable"] {
    --table-py: var(--a-space-4);
  }

  /* ═══════ Pinned cells ═══════ */

  [data-pinned="left"] {
    position: sticky;
    left: var(--_pin-left, 0);
    z-index: var(--_z-pinned);
    background: var(--table-bg);
  }

  [data-pinned="right"] {
    position: sticky;
    right: var(--_pin-right, 0);
    z-index: var(--_z-pinned);
    background: var(--table-bg);
  }

  [data-header] [data-pinned] {
    z-index: var(--_z-header-pinned);
  }

  /* ═══════ Resize handle ═══════ */

  [data-resize-handle] {
    position: absolute;
    right: calc(-1 * var(--table-resize-width) / 2);
    top: 0;
    bottom: 0;
    width: var(--table-resize-width);
    cursor: col-resize;
    /* Sit above the next column header so pointer events hit the handle
       (the handle is centered on the column boundary; without z-index the
       neighbor's content wins the overlap and the resize never starts). */
    z-index: 2;
  }

  /* Hide the resize handle on the last column — it overflows 2 px past the
     table's right edge (handle centered on the column boundary; the last
     column has no neighbor to resize against) and triggered a 2 px scrollbar
     overhang on every table even with nothing to scroll. */
  [role="row"] > [role="columnheader"]:last-child [data-resize-handle] {
    display: none;
    z-index: 1;
  }

  [data-resize-handle]:hover,
  [data-resize-handle][data-resizing] {
    background: var(--table-accent);
  }

  /* ═══════ Footer / pagination ═══════ */

  [data-footer] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--table-footer-py) var(--table-px);
    font-size: var(--table-heading-size);
    color: var(--table-heading-fg);
    border-top: 1px solid var(--table-border);
  }

  /* ═══════ Overlays (empty / loading / error) ═══════ */

  [data-overlay],
  [data-empty],
  [data-loading] {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--table-overlay-gap);
    padding: var(--table-overlay-pad);
    color: var(--table-heading-fg);
  }

  /* ═══════ Keyboard focus ═══════ */

  [data-focused] {
    outline: 2px solid var(--table-accent);
    outline-offset: -2px;
  }

  /* ═══════ Disabled ═══════ */

  :scope[disabled] {
    pointer-events: none;
    color: var(--table-fg-disabled);
  }

  :scope[disabled] [data-body] > [role="row"]:hover {
    background: var(--table-row-bg);
    color: var(--table-fg-disabled);
  }

  /* ═══════ Loading (skeleton rows) ═══════
     Skeleton rows replace real rows while [loading] is set on the host
     (see table.class.js #renderOverlays). Each row is a [data-skeleton-row]
     containing <skeleton-ui> cells. Inherit body-row layout so column
     widths track the header, then suppress hover/striping/click states
     (no real data to interact with). */

  [data-body] > [data-skeleton-row] {
    pointer-events: none;
  }

  [data-body] > [data-skeleton-row]:hover {
    background: transparent;
  }

  :scope[striped] [data-body] > [data-skeleton-row]:nth-child(even) {
    background: transparent;
  }

  /* No-op the dim-when-loading rule the old overlay relied on: with skeleton
     rows, the body IS the loading affordance — dimming it would just blur
     the shimmer. */

  /* ═══════ Filter UI ═══════ */

  [data-filter-btn] {
    all: unset;
    cursor: pointer;
    opacity: 0.5;
    padding: 0 var(--table-filter-btn-px);
    &:hover { opacity: 1; }
  }

  [role="columnheader"] { position: relative; }

  [data-filter-dropdown] {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    min-width: var(--table-cell-min-width);
    z-index: 10;
    background: var(--table-filter-bg);
    border: 1px solid var(--table-filter-border);
    border-radius: var(--table-filter-radius);
    padding: var(--table-filter-inset);
    box-shadow: var(--table-filter-shadow);
    display: flex;
    flex-direction: column;
    gap: var(--table-filter-gap);
  }

  [data-filter-input] {
    width: 100%;
    padding: var(--table-filter-input-py) var(--table-filter-input-px);
    border: 1px solid var(--table-filter-border);
    border-radius: var(--table-filter-input-radius);
    font: inherit;
    font-size: var(--table-filter-input-size);
    background: var(--table-filter-bg);
    color: var(--table-filter-fg);
  }

  [data-filter-op] {
    padding: var(--table-filter-input-py);
    border: 1px solid var(--table-filter-border);
    border-radius: var(--table-filter-input-radius);
    font: inherit;
    font-size: var(--table-filter-input-size);
    background: var(--table-filter-bg);
    color: var(--table-filter-fg);
  }

  [data-filter-option] {
    display: flex;
    align-items: center;
    gap: var(--table-filter-gap);
    font-size: var(--table-filter-input-size);
    cursor: pointer;
    padding: var(--table-filter-input-py) 0;
  }

  [data-filter-clear] {
    all: unset;
    cursor: pointer;
    font-size: var(--table-filter-input-size);
    color: var(--table-filter-clear-fg);
    text-align: center;
    padding: var(--table-filter-input-py);
    &:hover { text-decoration: underline; }
  }

  /* ═══════ Filter Chips Bar ═══════ */

  [data-filter-bar] {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--table-chip-bar-gap);
    padding: var(--table-chip-bar-py) var(--table-chip-bar-px);
    border-bottom: 1px solid var(--table-chip-bar-border);
    background: var(--table-chip-bar-bg);
  }

  [data-chip-dismiss] {
    all: unset;
    cursor: pointer;
    margin-left: var(--table-chip-dismiss-gap);
    opacity: 0.6;
    &:hover { opacity: 1; }
  }

  [data-filter-clear-all] {
    all: unset;
    cursor: pointer;
    font-size: var(--table-filter-input-size);
    color: var(--table-filter-clear-fg);
    padding: var(--table-chip-bar-py) var(--table-chip-bar-px);
    &:hover { text-decoration: underline; }
  }

  /* ═══════ Row Expansion ═══════ */

  [data-detail-row] {
    grid-column: 1 / -1;
    padding: var(--table-detail-py) var(--table-detail-px);
    background: var(--table-detail-bg);
    border-bottom: 1px solid var(--table-detail-border);
  }

  [data-expand-toggle] {
    all: unset;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform var(--table-duration) var(--table-easing);
  }

  [data-expanded] [data-expand-toggle] {
    transform: rotate(90deg);
  }

  /* ═══════ Column Pinning ═══════ */

  [data-pinned] {
    position: sticky;
    z-index: 1;
    background: var(--table-pinned-bg);
  }

  [data-pinned="left"]::after {
    content: '';
    position: absolute;
    right: calc(-1 * var(--table-pin-shadow-width)); top: 0; bottom: 0;
    width: var(--table-pin-shadow-width);
    pointer-events: none;
    background: linear-gradient(to right, var(--table-pinned-shadow), transparent);
  }

  [data-pinned="right"]::before {
    content: '';
    position: absolute;
    left: calc(-1 * var(--table-pin-shadow-width)); top: 0; bottom: 0;
    width: var(--table-pin-shadow-width);
    pointer-events: none;
    background: linear-gradient(to left, var(--table-pinned-shadow), transparent);
  }

  /* ═══════ Aggregation Footer ═══════ */

  [data-agg-row] {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    border-top: 2px solid var(--table-agg-border);
    font-weight: 600;
    font-size: var(--table-agg-size);
    color: var(--table-fg-secondary);
  }

  [data-agg-row] > [role="gridcell"] {
    padding: var(--table-agg-py) var(--table-agg-px);
  }
}
