/*
 * Row action menus in list tables move the page when they open.
 *
 * Three bugs, measured on this app's own /admin/users markup (14 columns,
 * 15 rows) under Chrome with a 15px scrollbar - not headless, whose overlay
 * scrollbars are zero-width and report every shift as 0.
 *
 * Own file, not the layouts' inline <style>: every list table in both the
 * admin and the customer area has this, so the rules must not be scoped to one
 * page. Same reasoning as accordion.css - app.css is customer-only and unsafe
 * to load in the admin layout, so both layouts link this directly.
 *
 * A third bug on the same page is NOT fixed here, and had to go first: the
 * open menu landed 87px to the right of its own button, outside the card. Its
 * cause is markup, not CSS - see the comment above each `<span class="dropdown">`
 * in the views. While it was live it grew its own 15px horizontal scrollbar
 * inside .table-responsive, which masked rule 3 completely and made it measure
 * as a no-op twice.
 *
 * Requires :has() (Chrome 105+, Safari 15.4+, Firefox 121+). An older browser
 * ignores these rules and gets today's behaviour back - degraded, not broken.
 *
 * ---------------------------------------------------------------------------
 * Measured dead ends. Do not spend an afternoon re-trying these:
 *   data-bs-popper-config strategy "fixed" ....... does not stop the focus
 *                                                  scroll
 *   save scrollLeft on show, restore on shown .... scroll restored, but Popper
 *                                                  has already placed the menu
 *   scrollbar-gutter / overflow-y: visible|clip .. shift stays 15px
 *   contain: layout / contain: paint ............. no effect
 *   .page-body { flex: 0 0 auto } ................ shift stays 15px
 * ---------------------------------------------------------------------------
 */

/*
 * 1. The table snaps sideways to its far edge.
 *
 * Scoped to .is-scrollable, which assets/js/table-actions.js puts on a
 * .table-responsive whose content is actually wider than it is. Pinning only
 * pays for itself on a table that overflows - that is the only time the Actions
 * can scroll out of reach, and it is also the only time this bug exists. On a
 * table that fits, the pin is invisible but its border-left is not: it draws a
 * stray vertical rule, and since a <table> stretches to its container the spare
 * width all lands in that last column and parks the buttons at the far right
 * behind a band of empty space. /admin/emails and /admin/pages are the pages
 * where that showed; /admin/domains and /admin/users overflow and never did.
 *
 * Bootstrap 5.3's Dropdown.show() calls focus() on the toggle, and the browser
 * then scrolls the nearest scrollable ancestor to bring a focused element fully
 * into view - the toggle sits in the clipped part of the table. Measured on
 * /admin/users: .table-responsive scrollLeft jumped 0 -> 551 at 1200px and
 * 0 -> 351 at 1400px, i.e. the whole table snapped to its right edge on every
 * menu open. Pinning the column leaves nothing to scroll into view, so the
 * focus becomes a no-op (measured after: 0 -> 0 at every width).
 *
 * The :has() is load-bearing, not decoration. A blanket td:last-child would
 * freeze a data column against the right edge; matching on a .btn inside the
 * last body cell scored 52 hits and 0 misclassifications across every view in
 * app/Views (the only last cells without one are the dashboard's stat tables
 * and the email templates, neither of which has an action column).
 *
 * border-left rather than box-shadow: Bootstrap paints .table-hover's row hover
 * as an inset box-shadow, and setting our own would replace it, leaving the
 * pinned cell the one cell that stops responding to hover.
 *
 * No z-index here. A positioned cell already paints above the non-positioned
 * ones it covers, and adding one turns every pinned cell into a stacking
 * context - which breaks rule 2 in a way rule 2 cannot repair.
 */
.table-responsive.is-scrollable table:has(> tbody > tr > td:last-child .btn) > thead > tr > th:last-child,
.table-responsive.is-scrollable table:has(> tbody > tr > td:last-child .btn) > tbody > tr > td:last-child {
    position: sticky;
    right: 0;
    background-color: var(--tblr-bg-surface);
    border-left: 1px solid var(--tblr-border-color);
}

/*
 * 2. The menu is cut off mid-word.
 *
 * Created by rule 1: every last cell is now positioned, so paint order among
 * them is DOM order and the cells of the rows *below* an open menu draw over
 * it. Lifting only the open cell is the narrowest thing that restores the menu
 * - raising all of them re-creates the same conflict one layer up.
 */
.table-responsive.is-scrollable table > tbody > tr > td:last-child:has(.dropdown-menu.show) {
    z-index: 2;
}

/*
 * 3. The page footer, the pagination and the table header drop by one
 *    scrollbar width.
 *
 * An open menu's overflow feeds back into the document height through
 * row-deck's stretching, and whichever box *can* stretch absorbs it, then
 * moves whatever sits below. This one only appears once the table FITS its
 * container - while it overflows, the scrollbar is there anyway. Measured at
 * 1665px with a fitting table, both selectors are needed:
 *
 *      rules applied   page footer  card footer  table header  doc height
 *      neither               +15         +15          +15          +15
 *      column only           +15         +15          +15          +15
 *      card only             +15           0            0          +15
 *      both                    0           0            0            0
 *
 * The card alone stops the header and the pagination moving while leaving the
 * page footer moving, which is what makes this feel like it keeps coming back.
 *
 * Not .card-footer's margin-top: auto, which also stops the shift - that
 * margin is what aligns footers across a row of cards of different heights.
 *
 * Costs nothing at rest: a full-width list card is never actually stretched.
 * It only becomes stretchable once something else grows the document, which is
 * exactly the coupling being removed. Kept to a direct-child match on purpose -
 * a loose descendant match would also unstretch cards in a multi-card row,
 * where equal heights are the point.
 */
.row-deck > [class*="col"]:has(> .card > .table-responsive),
.row-deck > [class*="col"] > .card:has(> .table-responsive) {
    align-self: flex-start;
}
