/* SheerBooks theme layer (doc-34 pass, 34a; design doc 31.3).
   The RULES are static; the VALUES arrive as --sb-* CSS variables in a
   server-rendered style block in Site.master (BL_Theme resolves
   user -> company -> base). No JavaScript anywhere in theming. */

body {
    background-color: var(--sb-bg);
    color: var(--sb-text);
    font-family: var(--sb-font), system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: var(--sb-font-size);

    /* THE GENERAL RULE (the Slate label fix): Bootstrap 5.3 colors
       its components through :root variables that default to the
       LIGHT palette - .text-muted/.form-text via
       --bs-secondary-color, cards/list-groups via --bs-body-*, and
       rgba composites via the -rgb triplets. Aliasing them HERE, at
       body level, re-points every such component at the theme in
       one place - never patch a component that merely references
       these; patch only components carrying FIXED colors (below). */
    --bs-body-color: var(--sb-text);
    --bs-body-bg: var(--sb-bg);
    --bs-body-color-rgb: var(--sb-text-rgb);
    --bs-body-bg-rgb: var(--sb-bg-rgb);
    --bs-border-color: var(--sb-border);
    --bs-emphasis-color: var(--sb-text);
    --bs-secondary-bg: var(--sb-row2);
    --bs-tertiary-bg: var(--sb-hover);
    --bs-secondary-color: color-mix(in srgb, var(--sb-text) 80%, var(--sb-bg));
    --bs-tertiary-color: color-mix(in srgb, var(--sb-text) 70%, var(--sb-bg));
    /* 039: links are their OWN token - the Bootswatch sources
       diverge $link-color from $primary (Slate white, Darkly green)
       and primary-as-link failed both dark themes. */
    --bs-link-color: var(--sb-link);
}

a { color: var(--sb-link); }

/* Labels: belt over the inheritance chain - a future container that
   sets its own color cannot dim them. */
label, .form-label, .form-check-label { color: var(--sb-text); }

/* Muted text derives from the THEME, not Bootstrap's fixed gray
   (the owner-confirmed contrast fix). 80% text, NOT 60%: Slate's
   base text is itself gray (#AAA at 6.1:1), so a 60% mix landed at
   3.2:1 - the Slate-labels bug. 80% clears 4.5 on all four themes
   (the UxTests drift net proves it against the live rows).
   color-mix tracks any palette; browsers without it keep the
   Bootstrap gray (a fallback, not the behavior of record). */
.text-muted, .form-text {
    color: color-mix(in srgb, var(--sb-text) 80%, var(--sb-bg)) !important;
}

/* Components carrying FIXED colors (not variable references) - the
   only legitimate per-component patches under the general rule. */
/* 34y: hover consistency (HOVER_PLAN - all button types darken via
   the same filter, matching .btn-primary/.btn-secondary/.sb-linkbtn,
   Delete/outline-danger the one deliberate exception). Bootstrap's
   own fill-swap hover (--bs-btn-hover-bg flips solid) is CANCELED by
   pinning every hover/active custom property to the SAME value as
   resting state, then filter: brightness(0.9) does the actual
   darkening - the exact two-part pattern already used below, just
   via custom properties instead of direct declarations (outline
   buttons render color/background/border through --bs-btn-* vars,
   solid buttons through this file's own direct properties). */
/* HOVER_WASH (found 2026-08-20 investigating a Light/Medium-only bug
   report on Receive Payment/Edit): filter: brightness(0.9) alone is
   background-dependent, not just theme-dependent - it darkens the
   BORDER/TEXT PIXELS ONLY (outline buttons keep a transparent fill),
   so its visibility rides on how much luminance headroom the resting
   color already has above the page background. Computed per theme:
   the swatch's own relative luminance drop is a uniform ~20%
   everywhere (the mechanism was never broken), but the ABSOLUTE
   luminance delta ranged from 0.032 on Light to 0.080 on Dark for
   Edit alone - a 2.5x spread, because Light/Medium's accent colors
   already sit close to their (bright) page background while
   Slate/Dark's sit far from theirs (dark). No single brightness
   factor closes that gap without either staying invisible on
   Light/Medium or overshooting Slate/Dark - it already pushes
   Receive Payment's hover contrast below 4.5:1 on Slate/Dark at 0.9
   (see UX_BACKLOG), so going steeper is not an option.
   The fix: --bs-btn-hover-bg is no longer pinned to plain transparent
   - it is a low-alpha (~12%) wash of the class's OWN accent token
   composited toward transparent via color-mix (the existing
   .text-muted/--bs-secondary-color pattern, not a new mechanism).
   An alpha blend's relative visibility scales with the percentage,
   not with the theme's own luminance range - computed contrast of
   the washed background against the page background came out
   nearly identical across all four themes at every alpha tested
   (e.g. 12%: 1.17/1.18/1.20/1.20), unlike the filter alone. filter:
   brightness(0.9) stays in place underneath it - this ADDS
   visibility, it does not replace the existing darkening. */
.btn-outline-secondary {
    --bs-btn-color: var(--sb-text);
    --bs-btn-border-color: var(--sb-secondary);
    --bs-btn-hover-color: var(--sb-text);
    --bs-btn-hover-bg: color-mix(in srgb, var(--sb-secondary) 12%, transparent);
    --bs-btn-hover-border-color: var(--sb-secondary);
    --bs-btn-active-color: var(--sb-text);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--sb-secondary);
}
.btn-outline-secondary:hover, .btn-outline-secondary:focus, .btn-outline-secondary:active {
    filter: brightness(0.9);
}
/* Outline-primary RESTS on the LINK token, not primary: the resting
   state sits directly on the page background, and dark-theme
   primaries are near-background by design (Slate #3A3F44 on #272B30
   is 1.3:1 - an ENABLED button that reads as disabled; the
   Update-totals bug). link_color is the primary-family color
   guaranteed readable on the background - the UxTests link/bg
   contrast pair enforces it per theme. Light/Medium are unchanged
   (their link IS primary). 34y: hover/active no longer fill with
   primary - pinned to the SAME resting values instead (transparent
   bg, link-colored text/border), then filter: brightness(0.9)
   darkens, matching every other button type now. The "click never
   flashes Bootstrap's stock blue" protection this originally existed
   for is preserved the same way - --bs-btn-active-bg is still
   explicitly pinned, just to transparent instead of a solid fill. */
.btn-outline-primary {
    --bs-btn-color: var(--sb-link);
    --bs-btn-border-color: var(--sb-link);
    --bs-btn-hover-color: var(--sb-link);
    --bs-btn-hover-bg: color-mix(in srgb, var(--sb-link) 12%, transparent);
    --bs-btn-hover-border-color: var(--sb-link);
    --bs-btn-active-color: var(--sb-link);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--sb-link);
}
.btn-outline-primary:hover, .btn-outline-primary:focus, .btn-outline-primary:active {
    filter: brightness(0.9);
}
/* 046: outline-warning (bill/expense/payment/refund/credit-memo void
   links, tax authority deactivate, reconciliation undo) rested on
   Bootstrap's fixed --bs-warning (#ffc107) since 34y - fine against
   Light/Medium's bright pages, glaring (8-10:1 contrast) against
   Slate/Dark's near-black ones, the same complaint Void's SOLID
   btn-warning had. FIRST ATTEMPT (reverted, found by the live
   contrast walk): text on --sb-warning directly, mirroring
   outline-primary - broke because warning_color's Light/Medium value
   is unchanged stock #FFC107, kept bright deliberately for the SOLID
   fill (paired with black text ON it), and #FFC107 as TEXT sitting
   directly on Light/Medium's own bright page background is only
   1.6:1 - a real, disruptive failure (every Light-base theme save
   tripped the readability gate, not just this button). CORRECTED to
   mirror outline-SECONDARY's shape instead: text on --sb-text (the
   token already guaranteed legible against the page - that is its
   whole job), border on --sb-btn-warning-border (the warning
   identity, same token the solid button's own border uses), hover
   wash on --sb-warning. outline-success (Receive payment) stays on
   the OLD fixed --bs-success pattern below - its own Slate/Dark
   hover-contrast gap was reviewed and accepted as-is (UX_BACKLOG.md),
   out of scope here; Delete (.btn-outline-danger) remains the one
   deliberate exception with no theme.css presence at all. */
.btn-outline-warning {
    --bs-btn-color: var(--sb-text);
    --bs-btn-border-color: var(--sb-btn-warning-border);
    --bs-btn-hover-color: var(--sb-text);
    --bs-btn-hover-bg: color-mix(in srgb, var(--sb-warning) 12%, transparent);
    --bs-btn-hover-border-color: var(--sb-btn-warning-border);
    --bs-btn-active-color: var(--sb-text);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--sb-btn-warning-border);
}
.btn-outline-warning:hover, .btn-outline-warning:focus, .btn-outline-warning:active {
    filter: brightness(0.9);
}
.btn-outline-success {
    --bs-btn-hover-color: var(--bs-success);
    --bs-btn-hover-bg: color-mix(in srgb, var(--bs-success) 12%, transparent);
    --bs-btn-hover-border-color: var(--bs-success);
    --bs-btn-active-color: var(--bs-success);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-border-color: var(--bs-success);
}
.btn-outline-success:hover, .btn-outline-success:focus, .btn-outline-success:active {
    filter: brightness(0.9);
}
.bg-light { background-color: var(--sb-row2) !important; }
.pagination {
    --bs-pagination-bg: var(--sb-row1);
    --bs-pagination-color: var(--sb-link);
    --bs-pagination-border-color: var(--sb-border);
    --bs-pagination-hover-bg: var(--sb-hover);
    --bs-pagination-hover-color: var(--sb-link);
    --bs-pagination-focus-bg: var(--sb-hover);
    --bs-pagination-focus-color: var(--sb-link);
    --bs-pagination-active-bg: var(--sb-primary);
    --bs-pagination-disabled-bg: var(--sb-row2);
    --bs-pagination-disabled-color: color-mix(in srgb, var(--sb-text) 50%, var(--sb-bg));
}
.list-group-item-action:hover, .list-group-item-action:focus {
    background-color: var(--sb-hover);
    color: var(--sb-text);
}

.btn-primary {
    background-color: var(--sb-primary);
    border-color: var(--sb-btn-primary-border);
    color: var(--sb-btn-primary-text);
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
    background-color: var(--sb-primary);
    border-color: var(--sb-btn-primary-border);
    color: var(--sb-btn-primary-text);
    filter: brightness(0.9);
}
.btn-secondary {
    background-color: var(--sb-secondary);
    border-color: var(--sb-btn-secondary-border);
    color: var(--sb-btn-secondary-text);
}
/* 042: without this, .btn-secondary:hover fell through to
   Bootstrap's own hardcoded --bs-btn-hover-bg (a class+pseudo-class
   selector beats theme.css's plain class rule on specificity alone,
   regardless of load order) - a stock gray totally disconnected from
   the theme, found while building the link-button style below and
   fixed here rather than deferred (the 041 precedent). Same
   filter-darken mechanism as .btn-primary, not a second mechanism. */
.btn-secondary:hover, .btn-secondary:focus, .btn-secondary:active {
    background-color: var(--sb-secondary);
    border-color: var(--sb-btn-secondary-border);
    color: var(--sb-btn-secondary-text);
    filter: brightness(0.9);
}
/* 046: Void's solid warning button, same shape as .btn-primary/
   .btn-secondary (direct properties, not --bs-btn-* custom
   properties - solid buttons never went through those). The hover
   rule is added in the SAME pass as the color theming - leaving it
   on Bootstrap's stock #ffca2c fill-swap would mean the button looks
   right at rest but flashes an unrelated hardcoded color on hover,
   the exact gap the .btn-secondary comment above already caught
   once. */
.btn-warning {
    background-color: var(--sb-warning);
    border-color: var(--sb-btn-warning-border);
    color: var(--sb-btn-warning-text);
}
.btn-warning:hover, .btn-warning:focus, .btn-warning:active {
    background-color: var(--sb-warning);
    border-color: var(--sb-btn-warning-border);
    color: var(--sb-btn-warning-text);
    filter: brightness(0.9);
}
/* 042: the Links category's opt-in button style
   (BUTTON_AND_LINK_STYLE_PLAN.md) - its own independent color set,
   never button_secondary_* or outline-secondary. Bootstrap's .btn
   base rule supplies padding/border-radius/etc; only the three
   colors and hover need declaring here, exactly like
   .btn-primary/.btn-secondary above. */
.sb-linkbtn {
    background-color: var(--sb-linkbtn-bg);
    border-color: var(--sb-linkbtn-border);
    color: var(--sb-linkbtn-text);
}
.sb-linkbtn:hover, .sb-linkbtn:focus, .sb-linkbtn:active {
    background-color: var(--sb-linkbtn-bg);
    border-color: var(--sb-linkbtn-border);
    color: var(--sb-linkbtn-text);
    filter: brightness(0.9);
}

.btn, .card, .form-control, .form-select, .alert, .badge {
    border-radius: var(--sb-radius);
}

.card, .form-control, .form-select, .table, .border-top, .border, hr {
    border-color: var(--sb-border) !important;
}
.card { background-color: var(--sb-row1); }
.form-control, .form-select {
    background-color: var(--sb-row1);
    color: var(--sb-text);
}

/* Checkbox/radio double-outline fix (found 2026-09-09 investigating a
   "faint shadow/border line above the box" report on checkboxes
   app-wide). ROOT CAUSE: <asp:CheckBox CssClass="form-check-input">
   (used on 29+ pages) renders as
   <span class="form-check-input"><input type="checkbox"></span> - a
   WebForms rendering quirk where CssClass always lands on a wrapping
   <span>, never on the actual <input>. bootstrap.min.css's
   .form-check-input rule (unmodified, matches by class name exactly
   as written - not too broad) therefore already sizes, borders, and
   backgrounds the SPAN correctly (including this file's own --sb-*
   aliasing), while the real <input> stays fully native/unstyled
   inside it. A <span> can never be :checked, :focus, or :disabled,
   so the input's own tiny native border peeks out from behind the
   span's - most visible, as reported, above and left of the box in
   the resting (unchecked) state, because a solid :checked fill
   mostly hides the mismatch. Plain HTML
   <input class="form-check-input"> elsewhere (e.g. the disabled
   radio buttons in settings_security.aspx) already carry the class
   themselves and are untouched by this.
   FIRST ATTEMPT (reverted): display:contents on the span to drop its
   box and re-declare the full ruleset on the input directly. Broke
   position: Bootstrap's .form-check-input{float:left;margin-left:
   -1.5em} depends on the CLASSED element being an actual floated
   box - display:contents drops the float entirely per spec, so the
   checkbox rendered ~1.5em too far right, indented under the
   .form-check padding instead of pulled out to the label's margin.
   CORRECTED approach: leave the span's existing float/margin/size/
   border/background completely alone (already correct, never the
   problem) and only (1) add the [type]-dependent border-radius
   Bootstrap normally keys off the classed element's own type
   attribute - the span has none, so this was ALSO silently missing
   before either attempt (a second, smaller pre-existing gap found
   while fixing the first); (2) turn the real <input> into a fully
   transparent, exactly-overlaid hit target (position:absolute;
   inset:0 within the now-relatively-positioned span) so it stays the
   genuine interactive/native control - still tabbable, checkable,
   the thing screen readers see - without ever painting a competing
   border of its own; (3) forward :checked/:focus/:disabled/:active
   from that real input onto the visible span via :has(), reusing
   bootstrap.min.css's own literal stock colors and checkmark/dot
   (not --sb-* values - those are unchanged) since the span itself
   structurally cannot carry those pseudo-classes. */
span.form-check-input {
    position: relative;
}
span.form-check-input:has(> input[type="checkbox"]) { border-radius: .25em; }
span.form-check-input:has(> input[type="radio"]) { border-radius: 50%; }
span.form-check-input > input[type="checkbox"],
span.form-check-input > input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
}
span.form-check-input:has(> input:active) { filter: brightness(90%); }
span.form-check-input:has(> input:focus) {
    border-color: #86b7fe;
    outline: 0;
    box-shadow: 0 0 0 .25rem rgba(13, 110, 253, .25);
}
span.form-check-input:has(> input:checked) {
    background-color: #0d6efd;
    border-color: #0d6efd;
}
span.form-check-input:has(> input[type="checkbox"]:checked) {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
span.form-check-input:has(> input[type="radio"]:checked) {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
}
span.form-check-input:has(> input:disabled) {
    filter: none;
    opacity: .5;
}
span.form-check-input > input:disabled {
    pointer-events: none;
}

/* zone_edit.aspx's cblItems: asp:CheckBoxList renders a bare
   input/label pair with no form-check wrapper, so Bootstrap's own
   spacing never engages (item 4, 2026-10-24). */
.checkbox-list-spaced input[type=checkbox] {
    margin-right: 0.5rem;
}

/* The five table controls (doc 35.4): row_1 / row_2 / hover /
   selected / header. Plain background-color rules with enough
   specificity to beat Bootstrap's table variables. */
.table { color: var(--sb-text); }
.table > :not(caption) > * > * {
    background-color: var(--sb-row1);
    color: var(--sb-text);
    border-color: var(--sb-border);
}
.table-striped > tbody > tr:nth-of-type(odd) > * {
    background-color: var(--sb-row2);
}
/* 2026-10-13: --sb-header (table_header) is already a distinct,
   themeable color, separate from --sb-row1/--sb-row2 - but the
   STORED values are close enough in 3 of 4 themes (Light ~15 RGB
   points from row2, Medium ~17, Slate ~9; only Dark's ~36-point gap
   actually reads as distinct) that the header didn't visually read
   as a header, especially against the odd-row stripe. Rather than
   touch the stored per-theme hex values - table_header is a
   user-customizable field (settings_theme.aspx/settings_my_theme.aspx),
   and re-tuning contrast is its own design decision - this reuses
   register.aspx's own existing convention for marking a real
   boundary: a 2px rule (heavier than the 1px per-cell borders) in
   --sb-border. Works identically regardless of what header/row
   colors a theme or company has picked, and being the shared base
   .table rule, applies to every table on every list screen and the
   register automatically - no per-page change needed. */
.table > thead > tr > th {
    background-color: var(--sb-header);
    border-bottom: 2px solid var(--sb-border);
}
.table-hover > tbody > tr:hover > * {
    background-color: var(--sb-hover);
}
.table > tbody > tr.sb-selected > * {
    background-color: var(--sb-selected);
}

footer.sb-footer {
    background-color: var(--sb-row1);
    border-top: 1px solid var(--sb-border);
}

/* The navigation sidebar (doc 34; 34c). Position via order classes
   inside the flex shell; three states; the size arrives as an inline
   width for vertical positions. Colors are the theme's sidebar pair. */
.sb-topbar { background-color: var(--sb-sidebar); }
.sb-sidebar {
    background-color: var(--sb-sidebar);
    color: var(--sb-sidebar-text);
    flex-shrink: 0;
    padding: 0.5rem 0;
}
.sb-pos-left { order: 0; }
.sb-pos-right { order: 2; }
.sb-pos-top { order: 0; }
.sb-pos-bottom { order: 2; }
.sb-main { order: 1; min-width: 0; }
.sb-pos-top, .sb-pos-bottom { width: 100%; }
.sb-pos-top .sb-nav-link, .sb-pos-bottom .sb-nav-link { display: inline-flex; }
.sb-pos-top .sb-nav-head, .sb-pos-bottom .sb-nav-head { display: inline-flex !important; float: right; }
.sb-nav-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 1rem;
    color: var(--sb-sidebar-text);
    text-decoration: none;
    white-space: nowrap;
}
.sb-nav-link i { flex-shrink: 0; font-size: calc(1em * var(--sb-icon-scale, 1)); }
.sb-nav-label {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.sb-nav-link:hover { color: var(--sb-sidebar-text); filter: brightness(1.15); text-decoration: underline; }
.sb-nav-link.sb-active { background-color: rgba(255, 255, 255, 0.14); font-weight: 600; }
.sb-nav-tool {
    color: var(--sb-sidebar-text);
    padding: 0.25rem 0.6rem;
    text-decoration: none;
    background: none;
    border: none;
    cursor: pointer;
}
.sb-nav-tool:hover { color: var(--sb-sidebar-text); filter: brightness(1.2); }
.sb-collapsed { width: calc(40px + 16px * var(--sb-icon-scale, 1)) !important; }
.sb-collapsed .sb-nav-label { display: none; }
.sb-collapsed .sb-nav-link { justify-content: center; padding: 0.45rem 0.25rem; }

/* User-created nav groups (NAV_GROUPS_PLAN.md): a header row (name +
   collapse toggle) plus, when expanded, an indented members block -
   collapsed groups render NO member markup at all (server-decided,
   not CSS-hidden), so there is deliberately no ".sb-nav-group-collapsed
   .sb-nav-group-members { display: none }" rule to maintain here. */
.sb-nav-group-header { display: flex; }
.sb-nav-group-toggle {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.45rem 1rem;
    color: var(--sb-sidebar-text);
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
    text-align: left;
    white-space: nowrap;
}
.sb-nav-group-toggle i { flex-shrink: 0; font-size: calc(1em * var(--sb-icon-scale, 1)); }
.sb-nav-group-toggle:hover { color: var(--sb-sidebar-text); filter: brightness(1.15); }
.sb-nav-group-label { overflow: hidden; text-overflow: ellipsis; min-width: 0; font-weight: 600; }
.sb-nav-group-members .sb-nav-link { padding-left: 2.1rem; }
.sb-collapsed .sb-nav-group-label { display: none; }
.sb-collapsed .sb-nav-group-toggle { justify-content: center; padding: 0.45rem 0.25rem; }
.sb-collapsed .sb-nav-group-members .sb-nav-link { padding-left: 0.25rem; }
.sb-pos-top .sb-nav-group, .sb-pos-bottom .sb-nav-group,
.sb-pos-top .sb-nav-group-header, .sb-pos-bottom .sb-nav-group-header,
.sb-pos-top .sb-nav-group-members, .sb-pos-bottom .sb-nav-group-members {
    display: inline-flex;
}
.sb-pos-top .sb-nav-group-toggle, .sb-pos-bottom .sb-nav-group-toggle { width: auto; }
.sb-nav-showstrip {
    background-color: var(--sb-sidebar);
    order: 0;
    flex-shrink: 0;
    align-self: stretch;
    display: flex;
    align-items: flex-start;
}

/* Toast-style status messages (doc-34 pass, 34q). Opt-in via the
   sb-toast class LAYERED onto the existing alert markup every page
   already uses (Panel.Visible + Panel.CssClass + Literal.Text) -
   this changes only how the message is positioned/dismissed, not the
   underlying mechanism, so adopting it elsewhere later is a one-line
   CssClass-string change, not a new control. Pure CSS, no JS: the
   message is fully present and readable in the initial HTML exactly
   as before - there is no JS path to fall back FROM here, so the
   no-JS floor is trivially satisfied rather than merely defended.
   animation-fill-mode "forwards" (the trailing keyword below) is what
   makes the auto-dismiss stick without any script: the element keeps
   the last keyframe's opacity:0/visibility:hidden permanently once
   the animation completes. No opacity/visibility is set OUTSIDE the
   animation, so a browser without @keyframes support just shows the
   message at its default (fully opaque) style, positioned correctly,
   staying visible indefinitely - a safe degradation, not a broken
   one. position:fixed takes it out of the normal flow entirely, so it
   cannot push page content by construction. z-index 1100 clears every
   layer already in use anywhere in this app, checked directly rather
   than guessed: Coloris' own popup sits at 1000, and Bootstrap's CSS
   (whose JS this app never loads, so its own layers are inert) tops
   out at --bs-toast-zindex: 1090. */
.sb-toast {
    position: fixed;
    top: 1rem;
    right: 1rem;
    max-width: min(420px, calc(100vw - 2rem));
    z-index: 1100;
    animation: sb-toast-life 3s ease forwards;
}
@keyframes sb-toast-life {
    0%   { opacity: 0; transform: translateY(-0.5rem); }
    8%   { opacity: 1; transform: translateY(0); }
    85%  { opacity: 1; transform: translateY(0); }
    100% { opacity: 0; transform: translateY(-0.5rem); visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) {
    .sb-toast { animation-name: sb-toast-life-reduced; }
    @keyframes sb-toast-life-reduced {
        0%   { opacity: 0; }
        8%   { opacity: 1; }
        85%  { opacity: 1; }
        100% { opacity: 0; visibility: hidden; }
    }
}

/* No-JS fallback controls (found during the register.aspx review,
   2026-10-12): a control whose ONLY job is to duplicate, for users
   without JavaScript, a postback an AutoPostBack control already
   fires automatically - never a control that also carries its own
   independent responsibility (a free-typed date range, a report's
   actual "run" action). Hidden by default; Site.master's own
   <noscript> block in <head> is the only thing that ever re-shows
   them, so a JS user can never tab into one (display:none removes a
   control from the tab order as well as from view - no separate
   tabindex handling is needed). Never apply this class to a control
   that is NOT a byte-for-byte duplicate of an AutoPostBack handler. */
.sb-nojs-fallback { display: none; }

/* sb-js-only is the mirror case (found 2026-10-14, payment_receive's
   "Add new..." dropdown entry - dead with JS off, since selecting it
   fires no postback without JS, yet a no-JS user could still pick it
   and get a confusing refusal at Save): a control that only works via
   AutoPostBack and duplicates a true no-JS control that already
   exists. It carries NO rule here - visible is simply the unstyled
   default - the hiding lives entirely in Site.master's own <noscript>
   block, same one shared place as the rule above. */

/* customer_list.aspx / vendor_list.aspx row actions (2026-10-12):
   the row-action buttons wrapped onto their own line at common
   laptop widths - confirmed in a headless render at 1280/1366px,
   where the long Email column leaves the action cell less room than
   its natural width; both pages stopped wrapping at 1440px+.
   CONTAINER ONLY, no button's own class changes. grid-template-
   columns: repeat(2, auto) sizes each column to its own widest
   member, so same-column buttons line up vertically across rows;
   justify-content:end keeps the block flush with the row's existing
   text-end alignment. customer_list.aspx carries 4 buttons (Edit,
   Prices, Deactivate-or-Reactivate, Delete) - a genuine 2x2, Edit/
   Prices then Deactivate-or-Reactivate/Delete. vendor_list.aspx has
   no Prices (vendors have no rates feature) - its 3 buttons land as
   Edit/Deactivate-or-Reactivate on row one, Delete alone under Edit
   on row two, consistently every render rather than wherever the
   browser happens to break the line. */
.sb-row-actions {
    display: grid;
    grid-template-columns: repeat(2, auto);
    gap: 0.25rem;
    justify-content: end;
}

/* INVOICE_ENTRY_UX_PLAN.md section 2.2 (Finding 7): a control that
   spans both columns, placed FIRST inside .sb-row-actions, so CSS
   Grid auto-placement gives it its own full-width row and the
   existing four-button 2x2 block below it flows completely
   unchanged - no reflow, nothing to re-verify about those four.
   Additive only - .sb-row-actions itself is untouched. */
.sb-row-actions > .sb-row-action-span {
    grid-column: 1 / -1;
}

/* Print (doc 31.4): chrome unprinted; dark themes forced readable.
   d-print-none remains the per-element tool the report screens
   already use. */
@media print {
    nav, .navbar, .sb-sidebar, .btn, footer.sb-footer, .d-print-none {
        display: none !important;
    }
    body {
        background-color: #FFFFFF !important;
        color: #000000 !important;
    }
    .table > :not(caption) > * > * {
        background-color: #FFFFFF !important;
        color: #000000 !important;
    }
}
