/* ── Grid tokens ─────────────────────────────────────────
   One brand-green "data line" colour drives the header rule, the zebra
   tint, the current page, and the item count. It defaults to the shared
   --capstone-rule-color hairline token (capstone-bootstrap.css), which is
   already split per theme like .text-brand — #007055 light, #5cbc94
   dark — so the grid rule and the section rule always match. Override
   --capstone-grid-rule-color on a grid to give one table a different
   accent without touching the section rules. */
.capstone-grid {
    /* ReSharper disable once CssNotResolved */
    --capstone-grid-rule-color: var(--capstone-rule-color, var(--bs-primary, #007055));
}

/* Rows carry a whisper of brand green instead of Bootstrap's grey wash, so
   the zebra belongs to the same family as the header rule. Hover is the
   same hue, twice as strong, so it still reads over a striped row. Both are
   custom-property overrides, so the sticky-column layering rules below
   (which re-read --bs-table-striped-bg / --bs-table-hover-bg) follow. */
.capstone-grid .table {
    /* ReSharper disable InvalidValue */
    --bs-table-striped-bg: color-mix(in srgb, var(--capstone-grid-rule-color) 5%, transparent);
    --bs-table-hover-bg: color-mix(in srgb, var(--capstone-grid-rule-color) 11%, transparent);
    /* ReSharper restore InvalidValue */
}

/* ── Table column headers ────────────────────────────────── */

.capstone-grid thead th,
.datagrid-sticky-header th,
.datagrid-toolbar + .table-responsive th,
.datagrid-toolbar + div + .table-responsive th {
    white-space: nowrap;
}

/* ── Data cells (no-wrap — keeps columns at their declared width and enables horizontal scroll) ── */

.capstone-grid tbody td,
.datagrid-toolbar + .table-responsive td,
.datagrid-toolbar + div + .table-responsive td {
    white-space: nowrap;
    cursor: default;
}

/* Copyable cells — stamped with td-copyable at render time (capstone-grid) or
   via tbody.tbody-copyable for standalone enableClipboardCopy usage. */
.capstone-grid tbody td.td-copyable,
tbody.tbody-copyable td {
    cursor: pointer;
}

/* ── Column headers ──────────────────────────────────────── */

.capstone-grid thead th {
    cursor: default;
    user-select: none;
    /* The header sits on the page surface, not a grey slab — the brand-green
       rule beneath it is what says "data starts here" (the same solid-green
       language as .capstone-section-rule, at 2px so it holds against the
       zebra). An inset shadow rather than border-bottom because a collapsed
       table border doesn't travel with a position:sticky header. */
    /* ReSharper disable once CssNotResolved */
    background-color: var(--bs-body-bg);
    box-shadow: inset 0 -2px 0 var(--capstone-grid-rule-color);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
}

.capstone-grid thead th.sortable {
    cursor: pointer;
}

/* Sortable header trigger — focusable button with no visible chrome.
   Lives inside the .d-flex; expands to fill so the filter button (when present) sits on the right. */
.capstone-grid-sort-btn {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    background: none;
    border: 0;
    padding: 0;
    color: inherit;
    font: inherit;
    text-align: inherit;
    cursor: pointer;
}

.capstone-grid-sort-btn > span {
    flex-grow: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

.capstone-grid-sort-btn:focus-visible,
button.filter-icon:focus-visible {
    /* ReSharper disable once CssNotResolved */
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Filter trigger button reset — keep the bi pseudo-glyph rendering identical to the old <i>. */
button.filter-icon {
    background: none;
    border: 0;
    padding: 0;
    line-height: 1;
}

/* ── Sort icon in table headers ─────────────────────────── */

.sort-icon {
    flex-shrink: 0;
    margin-left: 0.35rem;
    font-size: 0.75em;
    /* ReSharper disable once CssNotResolved */
    color: var(--bs-secondary-color);
    /* No opacity dimming — secondary-color alone meets WCAG 1.4.11 (3:1 for UI components).
       Layering opacity on top dropped contrast under the bar; the active state is still
       distinct via the color-token swap (emphasis-color) and the icon glyph swap. */
    /* Hover affordance — transform-origin centers the scale, transition keeps
       it short so the icon feels responsive rather than animated for animation's
       sake. inline-block guarantees the transform applies (Bootstrap Icons
       render as inline <i> by default, which transforms can't reach). */
    display: inline-block;
    transform-origin: center;
    transition: color 150ms ease-out, transform 150ms ease-out;
}

    .sort-icon.sort-icon-active {
        /* ReSharper disable once CssNotResolved */
        color: var(--bs-emphasis-color);
    }

/* Hovering anywhere on the clickable sort button (whole header area) brightens
   and slightly enlarges the sort icon — surfaces the column as sortable to
   mouse users without changing the existing focus-visible outline path. */
.capstone-grid-sort-btn:hover .sort-icon {
    /* ReSharper disable once CssNotResolved */
    color: var(--bs-emphasis-color);
    transform: scale(1.2);
}

/* ── Filter icon in table headers ───────────────────────── */

.filter-icon {
    cursor: pointer;
    margin-left: 0.5rem;
    font-size: 0.75em;
    /* ReSharper disable once CssNotResolved */
    color: var(--bs-secondary-color);
    /* See .sort-icon — no opacity dimming so we stay above WCAG 1.4.11. */
    /* See .sort-icon — inline-block + transition for the hover affordance. */
    display: inline-block;
    transform-origin: center;
    transition: color 150ms ease-out, transform 150ms ease-out;
}

    .filter-icon.active {
        /* ReSharper disable once CssNotResolved */
        color: var(--bs-emphasis-color);
    }

/* Filter icon is its own button — scope the hover to that button only so
   hovering the column name doesn't pulse the filter icon (would imply the
   header text triggers filtering, which it doesn't). */
button.filter-icon:hover {
    /* ReSharper disable once CssNotResolved */
    color: var(--bs-emphasis-color);
    transform: scale(1.2);
}

/* Honour the user's OS-level motion preference — keep the color cue (purely
   visual, no motion) but drop the scale so vestibular-sensitive users don't
   see a pulse on every header hover. */
@media (prefers-reduced-motion: reduce) {
    .sort-icon,
    .filter-icon {
        transition: color 150ms ease-out;
    }

    .capstone-grid-sort-btn:hover .sort-icon,
    button.filter-icon:hover {
        transform: none;
    }
}

/* ── DataGrid toolbar (top) ─────────────────────────────── */

.datagrid-toolbar {
    display: flex;
    /* Loose content clusters at the start (leading edge) — the standard toolbar default
       (cf. Bootstrap .btn-toolbar). Source order, left to right, tight; adding a button
       just appends rather than rearranging the others. For a two-sided "filters left /
       actions right" split, use the explicit <grid-toolbar-start>/<grid-toolbar-end> zones. */
    justify-content: flex-start;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--bs-border-color);
    /* ReSharper disable CssNotResolved */
    border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
    /* ReSharper restore CssNotResolved */
    /* One step lighter than the old secondary-bg slab: the frame recedes so
       the header rule leads. .btn-outline-secondary (restyled per theme in
       capstone-bootstrap.css) clears AA here too — 7.6:1 light, 7.4:1 dark. */
    /* ReSharper disable once CssNotResolved */
    background-color: var(--bs-tertiary-bg);
    min-height: 42px;
}

/* Zoned toolbar: explicit <grid-toolbar-start/center/end>. A 3-column grid (1fr auto 1fr)
   so the center zone sits at the true center of the toolbar regardless of how wide the
   start/end groups grow — flex `space-between` can't do that (it only centers the middle
   child when the two sides happen to be equal width). */
.datagrid-toolbar-zoned {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
}

/* Each zone is its own tight flex cluster so related buttons stay adjacent. */
.datagrid-toolbar-start,
.datagrid-toolbar-center,
.datagrid-toolbar-end {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.datagrid-toolbar-start { justify-self: start; }
.datagrid-toolbar-center { justify-self: center; }
.datagrid-toolbar-end { justify-self: end; }

/* ── DataGrid footer (bottom) ───────────────────────────── */

/* Remove border-radius from tables between toolbar and footer */
.datagrid-toolbar + .table-responsive .table,
.datagrid-toolbar + div + .table-responsive .table {
    border-radius: 0;
}

/* Vertical side lines on the table wrapper so the toolbar and footer borders
   visually connect through the middle of the grid. */
.capstone-grid .table-responsive,
.datagrid-toolbar + .table-responsive,
.datagrid-toolbar + div + .table-responsive {
    border-left: 1px solid var(--bs-border-color);
    border-right: 1px solid var(--bs-border-color);
}

/* Top border and rounded top corners when the grid has no toolbar. */
.capstone-grid:not(:has(> .datagrid-toolbar)) .table-responsive {
    border-top: 1px solid var(--bs-border-color);
    /* ReSharper disable CssNotResolved */
    border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
    /* ReSharper restore CssNotResolved */
}

/* When the inner table uses Bootstrap's .table-bordered, the outer cell borders
   already form the side line — skip the wrapper borders to avoid doubling up. */
.capstone-grid .table-responsive:has(> .table-bordered),
.datagrid-toolbar + .table-responsive:has(> .table-bordered),
.datagrid-toolbar + div + .table-responsive:has(> .table-bordered) {
    border-left: none;
    border-right: none;
}

.datagrid-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--bs-border-color);
    border-top: none;
    /* ReSharper disable CssNotResolved */
    border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
    background-color: var(--bs-tertiary-bg);
    min-height: 42px;
    /* Counts and page numbers line up as they tick over. */
    font-variant-numeric: tabular-nums;
}

/* Pagination: quiet text buttons with the current page as a solid brand-green
   tile. Bootstrap's compiled pagination is link-blue with a blue active tile,
   which fought the green count beside it. White on #007055 is 6.09:1; the
   hover/focus tint is the same hue as the zebra so the footer reads as one
   family. Disabled arrows drop to the secondary text tone (exempt from the
   contrast bar, but still legible). */
.datagrid-footer .pagination {
    margin-bottom: 0;
    gap: 0.125rem;
    /* ReSharper disable CssNotResolved */
    --bs-pagination-color: var(--bs-body-color);
    --bs-pagination-bg: transparent;
    --bs-pagination-border-color: transparent;
    --bs-pagination-border-radius: var(--bs-border-radius-sm);
    --bs-pagination-hover-color: var(--bs-emphasis-color);
    --bs-pagination-hover-bg: color-mix(in srgb, var(--capstone-grid-rule-color) 11%, transparent);
    --bs-pagination-hover-border-color: transparent;
    --bs-pagination-focus-color: var(--bs-emphasis-color);
    --bs-pagination-focus-bg: color-mix(in srgb, var(--capstone-grid-rule-color) 11%, transparent);
    --bs-pagination-focus-box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--capstone-grid-rule-color) 35%, transparent);
    --bs-pagination-active-color: #fff;
    --bs-pagination-active-bg: var(--bs-primary);
    --bs-pagination-active-border-color: var(--bs-primary);
    --bs-pagination-disabled-color: var(--bs-secondary-color);
    --bs-pagination-disabled-bg: transparent;
    --bs-pagination-disabled-border-color: transparent;
    /* ReSharper restore CssNotResolved */
}

.datagrid-footer .page-link {
    padding: 0.25rem 0.5rem;
    min-width: 2rem;
    text-align: center;
    font-size: 0.85rem;
    /* Every tile gets the radius, not just the first/last of the run. */
    /* ReSharper disable once CssNotResolved */
    border-radius: var(--bs-pagination-border-radius);
}

.datagrid-footer .page-item:not(:first-child) .page-link {
    margin-left: 0;
}

.datagrid-footer .page-item.active .page-link {
    font-weight: 700;
}

.datagrid-footer .page-link-jump {
    color: var(--bs-secondary-color);
    letter-spacing: 0.05em;
}

.datagrid-footer-right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    /* Brand green for the item-count indicator. 6.85:1 on the light
       footer bg (--bs-secondary-bg = #e9ecef). The dark override below
       handles the inverse problem. */
    color: var(--bs-primary);
    /* Always pin to the right, even when there's no left-side (pagination) sibling */
    margin-left: auto;
}

[data-bs-theme=dark] .datagrid-footer-right {
    /* --bs-primary (#007055) only gives 1.89:1 on dark footer bg (#343a40).
       Use a lighter brand-aligned green that clears AA. */
    color: #5cbc94;
}

/* The refresh icon takes the footer's theme-split green (above) rather than
   .text-primary, whose !important #007055 vanished on the dark footer. */
.datagrid-footer-right .datagrid-refresh {
    --bs-btn-color: currentColor;
    --bs-btn-hover-color: currentColor;
    --bs-btn-active-color: currentColor;
    --bs-btn-disabled-color: currentColor;
    font-size: 1rem;
    line-height: 1;
}

/* ── Scrollable grid with sticky header ────────────────── */

.datagrid-sticky-header thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* ── Sticky columns (sticky="left" / sticky="right" on <grid-column>) ─── */

.capstone-grid th.sticky-left,
.capstone-grid td.sticky-left,
.capstone-grid th.sticky-right,
.capstone-grid td.sticky-right {
    position: sticky;
    z-index: 1;
    background-color: var(--bs-body-bg);
}

.capstone-grid th.sticky-left,
.capstone-grid td.sticky-left {
    left: 0;
    /* ReSharper disable once CssNotResolved */
    box-shadow: 2px 0 6px color-mix(in srgb, var(--bs-body-color) 18%, transparent);
}

.capstone-grid th.sticky-right,
.capstone-grid td.sticky-right {
    right: 0;
    /* ReSharper disable once CssNotResolved */
    box-shadow: -2px 0 6px color-mix(in srgb, var(--bs-body-color) 18%, transparent);
}

/* Header cells need higher z-index so sticky headers (max-height mode) layer above sticky columns.
   Restated so the header rule's body-bg stays explicit alongside the base sticky rule. */
.capstone-grid thead th.sticky-left,
.capstone-grid thead th.sticky-right {
    z-index: 3;
    background-color: var(--bs-body-bg);
}

/* Preserve table-striped zebra pattern on sticky cells.
   Bootstrap's --bs-table-striped-bg is semi-transparent (e.g. rgba(0,0,0,0.05)), so setting it
   as background-color alone makes the cell see-through. Layer it over the opaque body bg instead. */
.capstone-grid .table-striped > tbody > tr:nth-of-type(odd) > td.sticky-left,
.capstone-grid .table-striped > tbody > tr:nth-of-type(odd) > td.sticky-right {
    background-color: var(--bs-body-bg);
    /* ReSharper disable once UnexpectedValue */
    background-image: linear-gradient(var(--bs-table-striped-bg), var(--bs-table-striped-bg));
}

/* Preserve hover highlight on sticky cells — same layering approach */
.capstone-grid .table-hover > tbody > tr:hover > td.sticky-left,
.capstone-grid .table-hover > tbody > tr:hover > td.sticky-right {
    background-color: var(--bs-body-bg);
    /* ReSharper disable once UnexpectedValue */
    background-image: linear-gradient(var(--bs-table-hover-bg), var(--bs-table-hover-bg));
}

/* ── Loading overlay (shown while read-url fetch / setLoading(true) is active) ── */

/* table-responsive needs to be the positioning ancestor for the overlay. */
.capstone-grid .table-responsive {
    position: relative;
}

.capstone-grid-loading-overlay {
    position: absolute;
    /* Top offset is set by JS (capstone-grid.js setLoading) to the thead's
       offsetHeight so the dots center against the tbody area only, not the
       whole table including the header. */
    top: var(--capstone-grid-thead-h, 0);
    right: 0;
    bottom: 0;
    left: 0;
    display: none;
    align-items: center;
    justify-content: center;
    /* ReSharper disable once InvalidValue */
    background-color: color-mix(in srgb, var(--bs-body-bg) 92%, transparent);
    z-index: 4;
    pointer-events: none;
}

/* Guarantee a visible data area while loading so the dots don't squash into
   a sliver when the tbody is empty (e.g. setData([]) was just called). */
.capstone-grid[data-loading="true"] .table-responsive {
    min-height: calc(var(--capstone-grid-thead-h, 0) + 6rem);
}

/* Hide tbody contents while loading. Covers the in-tbody "No Data Available."
   row that table-utilities renders for empty data, and stops existing rows
   from bleeding through the overlay. visibility (not display) keeps the
   tbody's intrinsic height so layout doesn't jump. */
.capstone-grid[data-loading="true"] tbody {
    visibility: hidden;
}

/* Container's data-loading attribute is set by CapstoneGrid.setLoading() —
   read() flips it around the fetch, consumers can flip it manually for setData() flows. */
.capstone-grid[data-loading="true"] .capstone-grid-loading-overlay {
    display: flex;
}

/* Suppress the empty-state message while loading — the dots already explain
   why the table is empty; showing both is contradictory. */
.capstone-grid[data-loading="true"] > [id$="-no-data"] {
    display: none !important;
}

/* Five-dot pulsating indicator inside the overlay. Layout, dot styling, and
   the brand color-cycle animation live in the shared "Loading dots" block in
   capstone-site.css (loaded by <capstone-head> before this file). */

/* ── Clipboard toast countdown bar ──────────────────────── */

.toast-timer-bar {
    height: 4px;
    background: #ffffff;
    background: rgba(255, 255, 255, 0.7);
    width: 100%;
    animation: toast-timer 2s linear forwards;
}

@keyframes toast-timer {
    from { width: 0; }
    to { width: 100%; }
}

/* ── Responsive grid (small screens) ───────────────────── */

@media (max-width: 575.98px) {
    /* Give wrapped rows breathing room — column gap is already set, add row gap */
    .datagrid-toolbar,
    .datagrid-footer {
        row-gap: 0.5rem;
    }

    /* A 3-column grid is too cramped on phones — collapse zones to a wrapping flex row so
       they stack instead of squeezing. justify-self no-ops once display is flex. */
    .datagrid-toolbar-zoned {
        display: flex;
        flex-wrap: wrap;
    }

    /* Let pagination page buttons wrap within the pagination <ul> instead of overflowing */
    .datagrid-footer .pagination {
        flex-wrap: wrap;
    }

    .datagrid-footer .page-link {
        padding: 0.375rem 0.625rem;
        font-size: 0.9rem;
    }
}

/* ── Inline / batch editing ──────────────────────────────────
   Cells become input controls in edit-mode="inline" (one row) or
   edit-mode="batch" (all rows). Editors are `sm` so they fit the
   table-sm row height. */

.capstone-grid-edit-cell {
    /* Editors can wrap onto the row; keep them vertically centered and let the
       autocomplete (taller, with its floating label) set the row height. */
    vertical-align: middle;
    white-space: normal;
}

/* Keep editors compact and stop them stretching the column unboundedly. The
   autocomplete editor (a .capstone-autocomplete widget, not a bare .form-control-sm)
   gets the same floor — without it the widget's width:100% input has no min-content
   of its own, so under table-layout:auto the column collapses to a sliver narrower
   than every other editor type. A consuming grid can override per-column for a wider
   picker (e.g. `#myGrid td[data-editor-field='X'] .capstone-autocomplete`). */
.capstone-grid-edit-cell .form-control-sm,
.capstone-grid-edit-cell .form-select-sm,
.capstone-grid-edit-cell .capstone-autocomplete {
    min-width: 7rem;
    max-width: 16rem;
}

/* The active inline-edit row gets a subtle tint so it reads as "in edit". */
.capstone-grid tbody tr[data-row-editing="true"] {
    /* ReSharper disable once CssNotResolved */
    background-color: var(--bs-tertiary-bg);
}

/* Batch dirty cell — a value changed since the last save. NOT colour alone (WCAG 1.4.1):
   a tint PLUS a corner "flag" triangle (a shape) PLUS a title="Unsaved change" tooltip set
   by the JS. The grid-level count is announced via a visually-hidden role=status region. */
.capstone-grid-cell-dirty {
    position: relative;
    /* ReSharper disable once CssNotResolved */
    background-color: rgba(var(--bs-warning-rgb), 0.18);
}

.capstone-grid-cell-dirty::after {
    content: "";
    position: absolute;
    top: 0;
    inset-inline-start: 0;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 0.5rem 0.5rem 0 0;
    /* ReSharper disable once CssNotResolved */
    border-color: var(--bs-warning) transparent transparent transparent;
    pointer-events: none;
}

/* ── Click-to-edit (edit-trigger="click") ────────────────────
   Editable cells render as read-only display text and swap to a live editor only when
   activated. The cell is a focusable role="button"; cursor + hover tint + focus ring
   advertise that it edits, so the affordance doesn't depend on colour alone. */

.capstone-grid-editable-cell {
    cursor: pointer;
    position: relative;
    /* A faint dashed underline hints "editable field" without shouting; it pairs with the
       cursor + hover tint so the affordance survives a colour-blind / high-contrast view. */
    text-decoration: underline dotted color-mix(in srgb, var(--bs-body-color) 28%, transparent);
    text-underline-offset: 3px;
}

.capstone-grid-editable-cell:hover {
    /* ReSharper disable once InvalidValue */
    background-color: color-mix(in srgb, var(--bs-primary) 8%, transparent);
}

.capstone-grid-editable-cell:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
    border-radius: 2px;
}

/* Read-only checkbox indicator carries the underline on the cell, not the box itself. */
.capstone-grid-editable-cell:has(> .capstone-grid-display-check) {
    text-decoration: none;
}

/* The cell currently swapped to its editor — let the autocomplete (taller) set the row height. */
.capstone-grid-cell-editing {
    text-decoration: none;
    cursor: default;
}

/* A required display cell that failed validation (no editor present to show .is-invalid on). */
.capstone-grid-cell-invalid {
    outline: 2px solid var(--bs-danger);
    outline-offset: -2px;
    background-color: rgba(var(--bs-danger-rgb), 0.1);
}

/* Honour reduced-motion: the cell affordance is colour/decoration only (no motion) already,
   so nothing to disable here — the existing global rule covers the sort/filter icons. */

/* NOTE: editable grids intentionally keep .table-responsive's overflow-x:auto so
   wide inline/batch grids scroll horizontally like read-only ones. Per-cell
   autocomplete editor dropdowns are rendered position:fixed (see
   capstone-autocomplete.js _positionList), so they escape the scroll container
   without an overflow:visible override that would suppress horizontal scroll. */

/* ── Detail rows (<grid-detail />) ───────────────────────────
   A compact toggle column of chevron buttons; the expanded detail row is a
   single colspan cell that reads as subordinate to its parent: one step
   quieter than the page (--bs-tertiary-bg, the toolbar/footer surface),
   inset under the parent's cells, with the grid's own rule colour as a
   left accent so it belongs to the same family as the header rule and
   the zebra. Tokens only — every colour flips with the theme. */

.capstone-grid th.capstone-grid-detail-col,
.capstone-grid tbody td.capstone-grid-detail-toggle-cell {
    width: 2.5rem;
    padding-left: 0.25rem;
    padding-right: 0.25rem;
    text-align: center;
}

.capstone-grid tbody td.capstone-grid-detail-toggle-cell {
    vertical-align: middle;
}

/* A bare button (not .btn — a labelled 44px button in every row would dominate the
   table). 1.75rem clears the 24px AA target floor on every pointer. */
.capstone-grid-detail-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    background: none;
    /* ReSharper disable CssNotResolved */
    border-radius: var(--bs-border-radius-sm);
    color: var(--bs-body-color);
    /* ReSharper restore CssNotResolved */
    cursor: pointer;
    line-height: 1;
}

.capstone-grid-detail-toggle:hover {
    /* ReSharper disable once InvalidValue */
    background-color: color-mix(in srgb, var(--capstone-grid-rule-color) 11%, transparent);
}

.capstone-grid-detail-toggle:focus-visible {
    /* ReSharper disable once CssNotResolved */
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* The chevron points right when collapsed and turns to point down when expanded —
   a shape change, so the state doesn't rest on colour (aria-expanded carries it for AT). */
.capstone-grid-detail-toggle > .bi {
    display: inline-block;
    transform-origin: center;
    transition: transform 150ms ease-out;
}

.capstone-grid-detail-toggle[aria-expanded="true"] > .bi {
    transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
    .capstone-grid-detail-toggle > .bi {
        transition: none;
    }
}

/* The parent row hands off to its detail: drop the hairline between them so the pair
   reads as one unit, and keep its hover/zebra tint on the parent only. */
.capstone-grid tbody tr[data-row-expanded="true"] > td {
    border-bottom-width: 0;
}

/* The detail cell. Bootstrap paints zebra + hover through an inset box-shadow on every
   cell (--bs-table-bg-type / --bs-table-bg-state); replacing that shadow with the left
   accent is what keeps hover and striping off the detail row — it is not a data row. */
.capstone-grid tbody tr.capstone-grid-detail-row > td.capstone-grid-detail-content {
    white-space: normal;
    cursor: auto;
    padding: 0.75rem 1rem 0.875rem 2.75rem;
    /* ReSharper disable CssNotResolved */
    --bs-table-bg-type: transparent;
    --bs-table-bg-state: transparent;
    background-color: var(--bs-tertiary-bg);
    box-shadow: inset 3px 0 0 var(--capstone-grid-rule-color);
    color: var(--bs-body-color);
    /* ReSharper restore CssNotResolved */
}

/* Zebra striping must count DATA rows only. Bootstrap's :nth-of-type(odd) would count the
   detail rows too and flip the stripe under every open row, so on a detail-enabled grid the
   stock rule is cancelled and re-stated with `of :not(.capstone-grid-detail-row)`. Split into
   separate rules so an engine without `:nth-child(… of …)` (pre-2023) drops only the re-statement
   and shows plain rows rather than a mis-aligned stripe. */
.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-of-type(odd) > * {
    --bs-table-bg-type: initial;
}

.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-child(odd of :not(.capstone-grid-detail-row)) > * {
    /* ReSharper disable once CssNotResolved */
    --bs-table-bg-type: var(--bs-table-striped-bg);
}

/* Same correction for the sticky-column stripe layering above. */
.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-of-type(odd) > td.sticky-left,
.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-of-type(odd) > td.sticky-right {
    background-image: none;
}

.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-child(odd of :not(.capstone-grid-detail-row)) > td.sticky-left,
.capstone-grid[data-detail-rows] .table-striped > tbody > tr:nth-child(odd of :not(.capstone-grid-detail-row)) > td.sticky-right {
    /* ReSharper disable once UnexpectedValue */
    background-image: linear-gradient(var(--bs-table-striped-bg), var(--bs-table-striped-bg));
}

/* Lazy (Promise) detail: the shared dots, sized down for a cell. */
.capstone-grid-detail-loading > span[aria-hidden="true"] {
    width: 0.625rem;
    height: 0.625rem;
}

/* Consumers usually nest a small table of child records in the detail cell. Keep it
   quiet on the detail surface: transparent (no white slab, no header fill), a hairline
   under its header instead of the grid's 2px brand rule, and wrapping text. */
.capstone-grid-detail-content .table {
    /* ReSharper disable once CssNotResolved */
    --bs-table-bg: transparent;
    margin-bottom: 0;
}

.capstone-grid-detail-content .table > thead > tr > th {
    background-color: transparent;
    /* ReSharper disable once CssNotResolved */
    box-shadow: inset 0 -1px 0 var(--bs-border-color);
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
    font-size: 0.8125rem;
    white-space: nowrap;
}

.capstone-grid-detail-content .table > tbody > tr > td {
    white-space: normal;
    cursor: auto;
}
