﻿/* =========================================================
   Data Grid (GridView) — Standard
   - Vertical scroll is owned by .card-content (card-scroll pattern)
   - HORIZONTAL scroll is ALSO owned by .card-content, NOT by .grid-wrapper.
       theme.css:536   .card.card-scroll .card-content   -> overflow-y: auto
       layout.css:925  body.grid-wide ... .card-content  -> overflow-x: auto
     This header used to say ".grid-wrapper owns horizontal scroll" while the rule below
     sets .grid-wrapper { overflow: visible }. IT OWNS NOTHING, AND THAT SENTENCE HAS NOW
     MISLED TWICE - once into looking for a bug in the wrong element, and once into
     reporting a defect that did not exist (CBS-57, 2026-09-22). Corrected rather than
     softened: a comment describing an intention the code does not implement is worse than
     no comment, because it is trusted.
   - GridView header is rendered in the FIRST <tr> (inside <tbody>)
   ========================================================= */

.grid-wrapper {
    width: 100%;
    margin-top: var(--grid-wrapper-margin-top);
    overflow: visible;
    border-radius: var(--grid-radius);
    border: 0;
    box-sizing: border-box;
    background: var(--surface);
    position: relative;
}

    .grid-wrapper::after {
        content: "";
        position: absolute;
        inset: 0;
        border: var(--grid-border-width) solid var(--grid-border-color);
        border-radius: var(--grid-radius);
        pointer-events: none;
        z-index: var(--grid-overlay-z);
    }

    .grid-wrapper::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        height: var(--grid-header-height);
        background: var(--grid-header-bg);
        border-top-left-radius: var(--grid-radius);
        border-top-right-radius: var(--grid-radius);
        pointer-events: none;
        z-index: 0;
    }

.table-modern tbody > tr:last-child > td.col-action {
    border-bottom-left-radius: var(--grid-radius);
}

.table-modern tbody > tr:last-child > td:last-child {
    border-bottom-right-radius: var(--grid-radius);
}

.grid-wrapper .table-modern {
    width: 100%;
    max-width: 100%;
    min-width: 100%;
    border-spacing: 0;
    border-collapse: collapse;
    background: var(--surface);
    border: 0;
    border-radius: 0;
    overflow: visible;
    font-size: var(--grid-font-size);
    position: relative;
    z-index: 1;
    table-layout: fixed;
}

.table-modern .col-select-hidden {
    display: none !important;
}

/* Sticky header (paging-safe once THEAD exists) */
.table-modern thead th {
    position: sticky;
    top: 0;
    z-index: var(--grid-header-z);
    background: var(--grid-header-bg);
    color: var(--grid-header-text);
    font-weight: var(--grid-header-font-weight);
    padding: var(--grid-cell-padding-y) var(--grid-cell-padding-x);
    box-shadow: inset 0 -1px 0 var(--grid-border-color);
    white-space: nowrap;
}

    /* Header corner rounding (now that header lives in THEAD) */
    .table-modern thead th:first-child {
        border-top-left-radius: var(--grid-radius);
    }

    .table-modern thead th:last-child {
        border-top-right-radius: var(--grid-radius);
    }

    /* Header link styling */
    .table-modern thead th a {
        text-decoration: none;
        color: var(--brand-ink);
        font-weight: var(--grid-header-font-weight);
        opacity: 0.85;
    }

        .table-modern thead th a:hover {
            opacity: 1;
        }

/* Body cells */
.table-modern tbody > tr > td {
    padding: var(--grid-cell-padding-y) var(--grid-cell-padding-x);
    border-bottom: var(--grid-border-width) solid var(--grid-border-color);
}

    .table-modern tbody > tr > td.col-action {
        padding-left: var(--grid-icon-btn-padding-x);
    }

.table-modern tbody > tr:last-child > td {
    border-bottom: none;
}

/* =================================================================================================
   TL-170: THE ZEBRA EXCLUDES A HEADER ROW BY WHAT IT IS, NOT BY WHERE IT SITS - AND HOVER NOW WINS.

   TWO REPORTED SYMPTOMS ON STORE INFO, ONE CAUSE EACH, BOTH IN THESE FEW LINES.

   SYMPTOM 1: "first two rows white, then coloured, then white."
   The rule was :nth-child(odd):not(:first-child). The :not was there to skip a HEADER row, back
   when the header was rendered inside tbody - which is what a GridView does by default. Store Info
   moves its header into <thead> (StoreInfo.aspx.cs GridView1_PreRender sets
   TableSection = TableRowSection.TableHeader), so tbody holds data rows only and the exclusion
   started skipping the first DATA row instead:
       data row 1 = nth-child(1), odd, but excluded  -> white
       data row 2 = nth-child(2), even               -> white
       data row 3 = nth-child(3), odd                -> striped
   Exactly the pattern reported.

   SYMPTOM 2: "hover does not work on all rows, no visible pattern."
   It was a SPECIFICITY LOSS, not a missing rule. The zebra selector carried more weight than the
   hover selector, so on a STRIPED row the stripe won and the hover colour never painted; on an
   unstriped row it did. Hover therefore worked on alternate rows - and because the striping itself
   was offset by symptom 1, the alternation did not line up with anything the eye could predict.

   WHY :not(:has(th)) AND NOT JUST REMOVING THE EXCLUSION: six pages use .table-modern and only FOUR
   move their header into thead - CBS Invoices, Store Info, Delete Users and Edit Users. Commission
   Plans and Store Groups still render the header inside tbody, so dropping the exclusion outright
   would have striped their header row. Testing for a th is true of a header row wherever it sits and
   false of every data row, so the same rule is now correct in both arrangements.

   HOVER IS GIVEN THE SAME SHAPE PLUS :hover so it outranks the stripe rather than relying on source
   order, which an equal-specificity rule would, and which the next edit to this file could undo
   without anyone noticing. */
.table-modern tbody > tr:nth-child(odd):not(:has(th)) {
    background: var(--grid-row-alt-bg);
}

.table-modern tbody > tr {
    transition: background 0.15s ease;
}

    .table-modern tbody > tr:hover,
    .table-modern tbody > tr:nth-child(odd):not(:has(th)):hover {
        background: var(--grid-row-hover-bg);
    }

/* Wrapping */
.table-modern td,
.table-modern th {
    white-space: nowrap;
}

    .table-modern td.col-comment {
        white-space: normal;
        word-break: var(--grid-wrap-break);
        overflow-wrap: anywhere;
    }

    .table-modern td.col-username,
    .table-modern td.col-email,
    .table-modern td.col-first,
    .table-modern td.col-last {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

/* Column sizing */
.col-action {
    min-width: 24px;
    width: var(--grid-col-action-width);
    white-space: nowrap;
}

.col-username {
    min-width: 11rem; /* U2-008: the card scrolls before a name or address is elided */
    max-width: 340px;
}

.col-email {
    min-width: 16rem;
    max-width: 360px;
}

.col-first,
.col-last {
    min-width: 7rem;
    max-width: 140px;
}

/* Non-sortable headers */
/* TL-202 2026-10-02: KEPT, BECAUSE THIS ONE IS NOT DEAD - it is the one the sweep flagged that
   turned out to be mostly working, and that distinction is why it was not removed with the others.
   This file is loaded by Site.master so the rule reaches every page, and it MATCHES wherever the
   grid's id survives to the browser: CBS/CBSInvoices.aspx and Settings/StoreMgmt/StoreInfo.aspx
   both set ClientIDMode="Static" on their GridView1.
   IT MISSES ONE PAGE: Settings/StoreMgmt/StoreGoals.aspx, which does not, so under Web.config's
   clientIDMode="AutoID" its grid renders as ctl00_..._GridView1. That page is DEFERRED (TL-203) -
   it has not been refactored for this release - so the miss is knowingly accepted rather than
   outstanding.
   INTENT: mark a NON-SORTABLE column header as not clickable - normal weight, muted colour,
   default cursor, no wrap. */
body #GridView1 th.muted {
    font-weight: normal;
    cursor: default;
    color: var(--muted);
    white-space: nowrap;
}

/* TL-21: the "content: none !important" override that used to sit here blanked every CSS-drawn sort
   glyph in the app, so only the sorted column showed a caret injected in C#. The shared glyph in
   components.css is the standard now: dimmed on every sortable column, caret when sorted, brighter on
   hover (the same three states as table.app.css). */

/* Sortable headers */
th.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

    th.sortable .sort-icon {
        opacity: var(--grid-sort-icon-opacity-idle);
        margin-left: 0.25rem;
        transition: opacity 0.2s ease;
    }

    th.sortable.sorted-asc .sort-icon,
    th.sortable.sorted-desc .sort-icon {
        opacity: var(--grid-sort-icon-opacity-sorted);
    }

    th.sortable:hover .sort-icon {
        opacity: var(--grid-sort-icon-opacity-hover);
    }

.selected-row {
    background-color: var(--grid-row-selected-bg) !important;
}

/* Icon buttons */
.gv-action-delete {
    color: var(--grid-delete-color) !important;
    font-size: var(--grid-icon-btn-font-size);
    /* padding: var(--grid-icon-btn-padding-y) var(--grid-icon-btn-padding-x); */
    border: 0;
    background-color: transparent;
    text-overflow: clip;
    align-items: center;
    justify-content: center;
    transition: background 0.15s ease, color 0.15s ease;
    opacity: 0.8;
    text-decoration: none;
}

    .gv-action-delete:hover {
        background: var(--grid-delete-hover-bg);
        color: var(--grid-delete-color);
        opacity: 1;
    }

.gv-action-edit {
    color: var(--grid-edit-color);
    font-size: var(--grid-icon-btn-font-size);
    padding: var(--grid-icon-btn-padding-y) var(--grid-icon-btn-padding-x);
    border-radius: var(--grid-icon-btn-radius);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s ease, color 0.15s ease;
    opacity: 0.8;
}

    .gv-action-edit:hover {
        background: var(--grid-edit-hover-bg);
        color: var(--grid-edit-color);
        opacity: 1;
    }

.gv-lock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

    .gv-lock.locked {
        color: var(--grid-lock-locked-color);
        font-size: 18px;
    }

    .gv-lock.unlocked {
        color: var(--grid-lock-unlocked-color);
        font-size: 18px;
        cursor: default;
        pointer-events: none;
    }

    /* TL-229 (response size): the open padlock of an UNLOCKED user is drawn here, once, instead of as ~700 bytes
       of inline SVG in every row of Edit users (Settings/UserMgmt/Edit.aspx renders a bare span for it). The path is
       the same one the row used to carry; the colour is the same token (.icon-success -> --icon-success), and
       a user who is not activated gets the muted colour the row used to give it (.icon-muted). Edit.aspx's
       locked padlock keeps its inline SVG and its postback link. */
    .gv-lock.unlocked::before {
        content: "";
        display: inline-block;
        width: 24px;
        height: 24px;
        background-color: var(--icon-success);
        -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M208 84H92V56a36 36 0 0 1 36-36c17.24 0 32.75 12.38 36.08 28.8a4 4 0 1 0 7.84-1.6C167.78 26.81 149.31 12 128 12a44.05 44.05 0 0 0-44 44v28H48a12 12 0 0 0-12 12v112a12 12 0 0 0 12 12h160a12 12 0 0 0 12-12V96a12 12 0 0 0-12-12m4 124a4 4 0 0 1-4 4H48a4 4 0 0 1-4-4V96a4 4 0 0 1 4-4h160a4 4 0 0 1 4 4Zm-76-56a8 8 0 1 1-8-8a8 8 0 0 1 8 8'/%3E%3C/svg%3E") no-repeat center / contain;
        mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M208 84H92V56a36 36 0 0 1 36-36c17.24 0 32.75 12.38 36.08 28.8a4 4 0 1 0 7.84-1.6C167.78 26.81 149.31 12 128 12a44.05 44.05 0 0 0-44 44v28H48a12 12 0 0 0-12 12v112a12 12 0 0 0 12 12h160a12 12 0 0 0 12-12V96a12 12 0 0 0-12-12m4 124a4 4 0 0 1-4 4H48a4 4 0 0 1-4-4V96a4 4 0 0 1 4-4h160a4 4 0 0 1 4 4Zm-76-56a8 8 0 1 1-8-8a8 8 0 0 1 8 8'/%3E%3C/svg%3E") no-repeat center / contain;
    }

    .gv-lock.unlocked.icon-muted::before {
        background-color: var(--icon-muted);
    }

/* SEC-32: the Manage passkeys row action on Edit users, beside the padlock in .col-action. It is built like the padlock: a bare
   24px glyph (the Unlock padlock's size), 4px from it, no border; brand-ink like every other action glyph (--grid-edit-color) with the
   grid's own hover tint. An anchor, so it navigates and works with Enter. Touch gets the 44px floor. The column is two icons wide
   (.col-action--2, set on the header and cell in Edit.aspx) because the shared 3% width only fits one. */
.gv-passkey {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 4px;
    border-radius: var(--grid-icon-btn-radius);
    color: var(--grid-edit-color);
    text-decoration: none;
    opacity: 0.85;
    transition: background 0.15s ease, opacity 0.15s ease;
}

    .gv-passkey:hover {
        background: var(--grid-edit-hover-bg);
        opacity: 1;
    }

    .gv-passkey:focus-visible {
        outline: 3px solid color-mix(in srgb, var(--brand-primary) 55%, var(--shade-toward));
        outline-offset: 1px;
        opacity: 1;
    }

.col-action.col-action--2 {
    width: 84px;
    min-width: 84px;
}

@media (pointer: coarse) {
    .gv-passkey {
        min-width: 44px;
        min-height: 44px;
    }

    .col-action.col-action--2 {
        width: 104px;
        min-width: 104px;
    }
}

/* Legacy scroll */
.table-scroll {
    overflow-y: auto;
    max-height: calc(100vh - 200px);
}

/* =========================================================
GridView pager (Bottom-only) — sticky + header-matching + one-line layout
Requirements:
- GridView Header promoted to THEAD (PreRender)
- BottomPagerRow.TableSection = TableFooter
- BottomPagerRow has class gv-pager-row
- Pager cell has class gv-pager-cell and ColumnSpan = Columns.Count
========================================================= */

/* Ensure pager area paints solid (prevents 'transparent' look when border-spacing is used) */
.table-modern tfoot {
    background: var(--grid-header-bg);
}

    /* Scrolling sticky pager row — styled like header */


    /* Flex wrapper INSIDE the cell */
    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell .pager-inner {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 12px;
        width: 100%;
    }

    /* Page X of Y: left side, icons right */


    /* GridView pager controls are rendered as a nested table */
    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell > table {
        display: inline-table;
        width: auto;
        margin: 0;
        border-collapse: collapse;
        white-space: nowrap;
    }

        /* Remove nested pager cell padding that increases height */
        .table-modern tfoot tr.gv-pager-row td.gv-pager-cell > table td {
            padding: 0;
            vertical-align: middle;
        }

    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell a,
    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell input,
    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell img {
        vertical-align: middle;
        line-height: 1;
    }


    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell .pager-inner > table {
        margin-right: 24px;
    }

    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell .gv-pageinfo {
        margin-left: 14px;
        margin-right: auto;
        color: var(--grid-header-text);
        opacity: 0.85;
        font-weight: var(--grid-header-font-weight);
        white-space: nowrap;
        line-height: 1;
    }

    .table-modern tfoot tr.gv-pager-row td.gv-pager-cell {
        position: sticky;
        bottom: 0;
        background: var(--grid-header-bg);
        box-shadow: inset 0 1px 0 var(--grid-border-color);
        color: var(--grid-header-text);
        padding: 0px 0px 1px 0px !important;
        margin-bottom: -5px;
        font-weight: var(--grid-header-font-weight);
        white-space: nowrap;
        width: 100%;
        box-sizing: border-box;
    }

/* CBS-04 2026-09-18: totals rows in TFOOT pin above the pager instead of
       scrolling away at the end of the page rows. Direct children only: the pager cell
       holds a nested table whose row would otherwise be caught by this rule. Each row
       supplies its own
       --grid-tfoot-offset (the height of everything already pinned below it);
       with no offset the rule is a no-op for grids that have no totals row. */
.table-modern > tfoot > tr:not(.gv-pager-row) > td {
    position: sticky;
    bottom: var(--grid-tfoot-offset, 0px);
    background: var(--grid-header-bg);
    box-shadow: inset 0 1px 0 var(--grid-border-color);
    z-index: var(--grid-header-z);
}

/* T2 SMALL-SCREEN GRIDS (U2-007/008/009/010/011, 2026-10-15). Under 768 wide a grid is not squeezed into the card (which turned names into "A..." at
   360): table-layout fixed ignores a column's min-width, so a narrow card shrank every column evenly. Here the table takes its content width with a
   floor, and the card (.card-content / .grid-wrapper) scrolls sideways, which it already does. The header and first column stay readable. */
@media (max-width: 767.98px) {
    .grid-wrapper .table-modern {
        table-layout: auto;
        width: max-content;
        min-width: 720px;
        max-width: none;
    }
    .grid-wrapper .table-modern thead th,
    .grid-wrapper .table-modern tbody > tr > td { white-space: nowrap; }
    .grid-wrapper .table-modern tbody > tr > td.col-comment { white-space: normal; min-width: 14rem; }
}
