/* ═════════════════════════════════════════════════════════════════════════
   Air list shell — structural CSS for server-side virtualized/paged list
   screens (AirVirtualizedListShell / AirListStatusBar / AirSkeletonRow /
   AirLoadError). Semantic tokens only; no raw colors.
   ═════════════════════════════════════════════════════════════════════════ */

.air-list-shell {
    display: flex;
    flex-direction: column;
    height: calc(100dvh - var(--air-list-offset, 64px));
    min-height: var(--air-list-min-height, 320px);
}

.air-list-toolbar-slot,
.air-list-error-slot,
.air-list-statusbar {
    flex: 0 0 auto;
}

.air-list-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    background: var(--air-surface);
}

.air-list-header {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--air-surface);
    border-bottom: 1px solid var(--air-border);
    font-weight: var(--air-font-weight-semibold);
    font-size: var(--air-font-size-sm, 0.8rem);
}

.air-list-statusbar {
    padding: var(--air-space-xs) var(--air-space-md);
    border-top: 1px solid var(--air-border-soft);
    color: var(--air-text-muted);
    font-size: var(--air-font-size-sm, 0.8rem);
}

.air-list-status { display: flex; gap: var(--air-space-sm); align-items: center; }
.air-list-status-loading { color: var(--air-text-subtle); }
.air-list-status-trailing { margin-left: auto; }

/* Row primitives: fixed height, ellipsis, hover — consumers define the grid
   template columns for their DTO. */
.air-list-row {
    display: grid;
    align-items: center;
    column-gap: var(--air-space-xs);
    border-bottom: 1px solid var(--air-border-soft);
    cursor: pointer;
    overflow: hidden;
}

.air-list-row:hover { background: var(--air-hover); }
.air-list-row > div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.air-list-sortable { cursor: pointer; user-select: none; }

.air-skeleton-row {
    display: flex;
    align-items: center;
    padding: 0 var(--air-space-md);
    border-bottom: 1px solid var(--air-border-soft);
}

.air-skeleton-bar {
    width: 100%;
    height: 1.2em;
    border-radius: var(--air-radius);
    background: var(--air-bg-muted);
    animation: air-skeleton-pulse 1.4s ease-in-out infinite;
}

@keyframes air-skeleton-pulse {
    0%, 100% { opacity: 0.55; }
    50% { opacity: 1; }
}

.air-load-error {
    display: flex;
    align-items: center;
    gap: var(--air-space-sm);
    margin: var(--air-space-sm) var(--air-space-md);
    padding: var(--air-space-sm) var(--air-space-md);
    border: 1px solid var(--air-danger-border);
    border-radius: var(--air-radius);
    background: var(--air-danger-bg);
    color: var(--air-danger-text);
}

/* ─────────────────────────────────────────────────────────────────────────
   AirViewportScrollRegion — a region that fills the height left in the
   viewport and is the only thing that scrolls. The controller
   (js/air-viewport-scroll.js) writes --air-viewport-scroll-height and
   data-air-measured; before it has (prerender, no JS) the region keeps its
   natural height and the page scrolls as usual.
   ───────────────────────────────────────────────────────────────────────── */

.air-viewport-scroll {
    position: relative;
    min-height: 0;
    overflow: auto;
    /* The controller keeps the reader's row still itself, identically in every browser; the
       browser's own anchoring would adjust a second time. */
    overflow-anchor: none;
    /* A scrollbar appearing does not change the content width, so it cannot re-trigger layout. */
    scrollbar-gutter: stable;
}

.air-viewport-scroll[data-air-measured="fill"] {
    height: var(--air-viewport-scroll-height);
    min-height: var(--air-viewport-scroll-min, 0);
}

.air-viewport-scroll[data-air-measured="cap"] {
    max-height: var(--air-viewport-scroll-height);
}

.air-viewport-scroll:focus-visible {
    outline: 2px solid var(--air-accent);
    outline-offset: -2px;
}

/* Sticky column headers inside the region: a table's own header row, or any element marked
   .air-viewport-scroll-sticky. Zero specificity, so a consumer's header styles win. */
:where(.air-viewport-scroll) > table > thead > tr > th,
:where(.air-viewport-scroll) .air-viewport-scroll-sticky {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--air-surface);
}

@media (forced-colors: active) {
    :where(.air-viewport-scroll) > table > thead > tr > th,
    :where(.air-viewport-scroll) .air-viewport-scroll-sticky {
        background: Canvas;
        border-bottom: 1px solid CanvasText;
    }
}
