﻿.cas-logo {
    height: 36px;
    width: auto;
    max-width: 260px;
    object-fit: contain;
    display: block;
}

@media (max-width: 576px) {
    .cas-logo {
        height: 30px;
        max-width: 180px;
    }
}

.cas-header-audit {
    background: #fff9db;
    border: 1px solid #ffe8a1;
    border-radius: 6px;
    padding: 12px 16px;
    min-width: 220px;
    box-shadow: 0 1px 2px rgba(0,0,0,.06);
    color: #212529;
}

    .cas-header-audit > div {
        line-height: 1.4;
        white-space: nowrap;
    }

.cas-grid-overlay {
    position: absolute;
    inset: 0;
    background: rgba(255,255,255,.65);
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
}

.cas-grid-overlay-card {
    background: #fff;
    border: 1px solid rgba(0,0,0,.12);
    border-radius: 12px;
    padding: 14px 16px;
    box-shadow: 0 8px 24px rgba(0,0,0,.12);
    min-width: 280px;
}

/* Full-viewport busy scrim (BusyOverlay.razor). Unlike .cas-grid-overlay
   above, which is absolutely positioned inside a card, this covers the whole
   window and blurs the page behind it — used for long operations such as a
   full-result-set CSV export, where the entire page must be inert.

   This value alone is not enough to clear the sidebar — the scrim renders
   inside `.cas-layout__main`, which is its own stacking context. See the
   `:has()` rule in the MODAL STACKING section below, which lifts the wrapper
   while a scrim is present. Kept under the modal overrides (1079/1080) so a
   modal opened over a running export still sits on top. */
.cas-busy-overlay {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255,255,255,.45);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

/* Safari on iOS and older Chromium builds ignore backdrop-filter. Without a
   fallback the scrim there is a barely-visible 45% white wash with no blur,
   which reads as a rendering glitch rather than a busy state. Deepen the
   plain background when the filter is unavailable. */
@supports not ((backdrop-filter: blur(3px)) or (-webkit-backdrop-filter: blur(3px))) {
    .cas-busy-overlay {
        background: rgba(255,255,255,.78);
    }
}

.cas-busy-overlay-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
    min-width: 180px;
    padding: 26px 34px;
    background: #fff;
    border: 1px solid rgba(0,0,0,.08);
    border-radius: 14px;
    box-shadow: 0 10px 30px rgba(0,0,0,.14);
}

.cas-busy-overlay-spinner {
    width: 2.25rem;
    height: 2.25rem;
    border-width: .2em;
    color: #4f46a3; /* app primary — same family as the toolbar buttons */
}

.cas-busy-overlay-text {
    font-size: 15px;
    font-weight: 500;
    color: #212529;
}

/******************************
STICKY GRID CONFIGURATIONS    
*******************************/

.lightgrid-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    max-width: 100%;
    scrollbar-gutter: stable;
    scrollbar-color: rgb(30, 62, 175);
    scrollbar-width: thin;
}

    .lightgrid-scroll::-webkit-scrollbar-track,
    .lightgrid-scroll::-webkit-scrollbar-track-piece {
        background-color: #27277C;
        border-radius: 5px;
    }

    .lightgrid-scroll::-webkit-scrollbar-thumb {
        background-color: rgb(30, 62, 175) !important;
        border: 0 !important;
        border-radius: 5px;
        box-shadow: none !important;
    }

        .lightgrid-scroll::-webkit-scrollbar-thumb:hover,
        .lightgrid-scroll::-webkit-scrollbar-thumb:active,
        .lightgrid-scroll::-webkit-scrollbar-thumb:window-inactive {
            background-color: rgb(30, 62, 175) !important;
        }

.lightgrid-table {
    width: 100%;
    min-width: max-content;
    border-collapse: separate;
    border-spacing: 0;
    font-size:0.75rem;
}

.lg-sticky-right {
    position: sticky;
    right: 0;
    z-index: 3;
    background: #fff;
    box-shadow: -8px 0 10px -10px rgba(0,0,0,.35);
}

.lg-sticky-right-header {
    position: sticky;
    right: 0;
    z-index: 4;
    background: #f8f9fa;
}


.lightgrid-loading-overlay {
    position: absolute;
    inset: 0;
    z-index: 5;
    background: rgba(255, 255, 255, 0.65);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: all;
}


/***************************************************************************
LightGrid column resizing  (LightGrid ResizableColumns="true")

A thin drag handle straddles each header cell's right border; the drag
itself lives in wwwroot/js/gridColumnResize.js. Only tables carrying
.lg-resizable are affected, so every other grid is untouched.

Header cells become the handle's positioning context — except sticky ones,
which already are (position:sticky establishes one) and must stay sticky.
*****************************************************************************/
.lg-resizable > thead > tr > th:not(.lg-sticky-right-header) {
    position: relative;
}

.lg-col-resizer {
    position: absolute;
    top: 0;
    right: -4px;
    width: 8px;
    height: 100%;
    z-index: 2;
    cursor: col-resize;
    user-select: none;
    touch-action: none;
}

    .lg-col-resizer::after {
        content: "";
        position: absolute;
        top: 20%;
        bottom: 20%;
        left: 3px;
        width: 2px;
        border-radius: 1px;
        background: transparent;
        transition: background-color .15s ease-in-out;
    }

    .lg-col-resizer:hover::after,
    .lg-col-resizer.is-active::after {
        background: rgb(30, 62, 175);
    }

/* The last column has no neighbour to its right; keep its handle inside
   the table so it does not hang over the card edge. */
.lg-resizable > thead > tr > th:last-child > .lg-col-resizer {
    right: 0;
}

/* Same for the column just before a sticky right-hand action column. That
   header (.lg-sticky-right-header, z-index 4) paints over anything crossing
   into it, which hid the right half of this handle and its hover line —
   leaving a 4px sliver to grab, so the column looked impossible to resize.
   Kept wholly inside its own cell, the full 8px handle is reachable. */
.lg-resizable > thead > tr > th:has(+ th.lg-sticky-right-header) > .lg-col-resizer {
    right: 0;
}

/* While a drag is in progress: col-resize everywhere, and no accidental
   text selection as the pointer crosses the grid. */
body.lg-col-resizing,
body.lg-col-resizing * {
    cursor: col-resize !important;
    user-select: none !important;
}


.fe-spin {
    animation: fe-spin 1.5s linear infinite;
}

@keyframes fe-spin {
    to {
        transform: rotate(360deg);
    }
}


/***************************************************************************
Sticky-right columns for BatchItemDetailCandidatesPanel    
*****************************************************************************/

.cas-sticky-right {
    position: sticky;
    background: #fff;
    z-index: 3;
}

/* Keep header cells above body cells */
.cas-sticky-right-header {
    position: sticky;
    background: #f8f9fa;
    z-index: 5;
}

/* Rightmost: Actions */
.cas-sticky-actions {
    right: 0;
    width: 120px;
    min-width: 120px;
}

/* Middle: Selected checkbox (to the left of Actions) */
.cas-sticky-select {
    right: 120px;
    width: 44px;
    min-width: 44px;
    text-align: center;
}

/* Leftmost sticky: Amount (to the left of Select checkbox) */
.cas-sticky-amount {
    right: 164px;
    width: 160px;
    min-width: 160px;
}

/* Optional: add a subtle divider line where sticky region starts */
.cas-sticky-divider {
    box-shadow: 2px 0 0 rgba(0,0,0,.08);
}

/* Sticky-region variant for grids WITHOUT a trailing Actions column
   (e.g. the receipt/commitment allocation panels): the Select checkbox
   hugs the right edge and Amount sits directly beside it — removes the
   120px strip reserved for an Actions column that isn't there. */
.cas-sticky-no-actions .cas-sticky-select {
    right: 0;
}

.cas-sticky-no-actions .cas-sticky-amount {
    right: 44px;
}

/* Recommended: sticky inside tables behaves better with this */
.lightgrid-table {
    border-collapse: separate;
    border-spacing: 0;
}

/*********************************************************************
Horizontal row rules for EVERY LightGrid

Why this rule exists: LightGrid renders `table table-bordered lightgrid-table`,
and `.lightgrid-table` switches the table to `border-collapse: separate` so the
sticky columns/header behave. Bootstrap's `.table-bordered` declares the
HORIZONTAL borders on the `<tr>` (`> :not(caption) > * { border-width: 1px 0 }`)
and only the VERTICAL borders on the cells. In the separated-borders model a
border on a `<tr>` is not rendered at all — so every grid in the app kept its
column dividers and silently lost its row dividers.

Fix: re-declare the horizontal rule on the CELLS, which render in both models.
Direct-child selectors only, so a LightGrid nested inside another grid's cell
styles its own rows and doesn't inherit the parent's.
**********************************************************************/

.lightgrid-table > thead > tr > th,
.lightgrid-table > thead > tr > td,
.lightgrid-table > tbody > tr > th,
.lightgrid-table > tbody > tr > td,
.lightgrid-table > tfoot > tr > th,
.lightgrid-table > tfoot > tr > td {
    border-bottom: var(--bs-border-width, 1px) solid var(--bs-table-border-color, #dee2e6);
}

/* No trailing rule under the final body row — the card/panel edge already
   closes the grid, and a last line reads as a stray divider above the pager.
   `tbody:last-child` guards this: when a grid has a totals `<tfoot>`, the last
   body row keeps its divider so the totals stay visually separated. */
.lightgrid-table > tbody:last-child > tr:last-child > th,
.lightgrid-table > tbody:last-child > tr:last-child > td {
    border-bottom: 0;
}



/*********************************
Hide native horizontal scrollbar but keep scrolling enabled
*****************************************/

.cas-hide-native-hscroll {
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE/Edge legacy */
}

    .cas-hide-native-hscroll::-webkit-scrollbar {
        height: 0; /* Chrome/Safari */
    }

/* Sticky bottom horizontal scrollbar (proxy) */
.cas-hscroll-sticky {
    position: sticky;
    bottom: 0;
    z-index: 1;
    background: #fff;
    border-top: 1px solid #dee2e6;
    height: 8px;
    overflow-x: auto;
    overflow-y: hidden;
}

/* The inner element controls the scrollable width */
.cas-hscroll-inner {
    height: 1px;
}

/* Optional: keep the proxy scrollbar visually aligned with your lightgrid scrollbars */
.cas-hscroll-sticky::-webkit-scrollbar,
.lg-footer-hscroll::-webkit-scrollbar {
    height: 12px;
}

.cas-hscroll-sticky::-webkit-scrollbar-track,
.lg-footer-hscroll::-webkit-scrollbar-track {
    background: #ddd;
    border-radius: 5px;
}

.cas-hscroll-sticky::-webkit-scrollbar-thumb,
.lg-footer-hscroll::-webkit-scrollbar-thumb {
    background: #222;
    border-color: #dee2e6;
    border-radius: 5px;
}

    .cas-hscroll-sticky::-webkit-scrollbar-thumb:hover,
    .lg-footer-hscroll::-webkit-scrollbar-thumb:hover {
        background: rgb(30, 62, 175);
    }

    .cas-hscroll-sticky::-webkit-scrollbar-thumb:active,
    .lg-footer-hscroll::-webkit-scrollbar-thumb:active {
        background: rgb(30, 62, 175);
    }


/* =============================================================================
   STICKY GRID FOOTER  (LightGrid StickyFooter="true")
   -----------------------------------------------------------------------------
   Opt-in, off for every grid that does not ask for it.

   On a long grid the footer strip -- horizontal scrollbar, the "Showing x-y of
   n" text and the pager -- sits below the fold, so paging or scrolling
   sideways means scrolling to the end of the table first. `position: sticky`
   pins the strip to the bottom of the viewport while the card still extends
   past it, and releases it into its natural place at the card's bottom edge
   the moment that edge scrolls into view. That release is the browser's own
   behavior, not a scroll listener: there is no JS here and nothing to jump.

   Sticky works because nothing between .card-footer and the document clips
   overflow -- neither .card nor .custom-card sets one, and .cas-layout__main
   is a flex column rather than a scroller -- so the page scrolls on the
   window and the footer's containing block is the card itself.

   The horizontal scrollbar is the reason this needs markup and not just CSS.
   A native one is painted at the bottom edge of .lightgrid-scroll, which is
   exactly the off-screen edge being worked around, and it cannot be lifted
   out of its container. So the grid hides it with .cas-hide-native-hscroll
   and renders the proxy bar below, driven by casStickyHScroll.
   ============================================================================= */
.card .card-footer.lg-sticky-footer,
.lg-sticky-footer {
    position: sticky;
    bottom: 0;

    /* Above the sticky right-hand action column (.lg-sticky-right, 3) and its
       header (4) so the pinned strip is not scrolled through by a row. Far
       below .cas-busy-overlay (1050) and .modal (1080), so an export scrim or
       a dialog still covers it. */
    z-index: 5;

    /* The theme sets `.card .card-footer { background-color: transparent
       !important }`. Pinned over the table that means rows show straight
       through the pager, so the !important has to be answered in kind — AND
       out-specified, which is why this rule is written as
       `.card .card-footer.lg-sticky-footer` (0,3,0) rather than
       `.lg-sticky-footer` (0,1,0). Between two !important declarations
       specificity still decides, and the theme's selector carries two
       classes; a single-class override loses and the footer stays see-through.

       --custom-white is the card's own surface in BOTH themes (#fff in light,
       the dark body color in dark), so this one declaration covers dark mode
       as well and cannot drift from the card it sits on. */
    background-color: var(--custom-white) !important;
}

/* The proxy bar sits above the "Showing ..." / pager row, which is where the
   native scrollbar was, so the strip reads identically pinned or released.
   The negative top margin pulls it up into the footer's 1rem padding to close
   the gap the native bar never had; casStickyHScroll sets display:none on it
   whenever the table fits, so a narrow grid shows no empty strip.
   Chrome (track, thumb, height) is shared with .cas-hscroll-sticky above. */
.lg-footer-hscroll {
    overflow-x: auto;
    overflow-y: hidden;
    height: 8px;
    margin: -0.5rem 0 0.75rem;
}

    /* The element casStickyHScroll stretches to the table's scrollWidth. It
       exists only to give the bar something to scroll. */
    .lg-footer-hscroll .cas-hscroll-inner {
        height: 1px;
    }



/* =============================================================================
   MODAL SIZING — applies to EVERY modal in the app
   =============================================================================

   Ten components hand-roll Bootstrap's modal markup
   (`.modal.fade.show.d-block` > `.modal-dialog` > `.modal-content`) and none of
   them opt into Bootstrap's `.modal-dialog-scrollable`. Without it a tall body
   grows the dialog past the viewport and `.modal` — not the body — is what
   scrolls, so the header scrolls out of view and the footer ends up below the
   fold. That is exactly what the 31-field Communities upsert modal did.

   The goal is "cap the tall ones, leave the short ones alone":

     - the dialog is capped at the viewport minus Bootstrap's own 1.75rem
       `.modal-dialog` margin, giving matching breathing room top and bottom
     - a modal shorter than that cap keeps its natural height — the 90
       ConfirmModal call sites must NOT grow into full-screen boxes
     - only `.modal-body` scrolls; header and footer stay pinned

   This is done with FLEXBOX, not with `max-height` percentages, and that is not
   a style preference — it is the only thing that works:

     A percentage `max-height` resolves against the containing block's HEIGHT.
     `.modal-dialog` computes `height: auto`, and a `max-height` on a box does
     not make its height definite. So `.modal-content { max-height: 100% }`
     computes to `none`, the content keeps growing, the body never overflows and
     `overflow-y: auto` never produces a scrollbar. Bootstrap sidesteps this in
     `.modal-dialog-scrollable` by using `height:` — but `height` FORCES every
     dialog to full screen, which is exactly what we do not want here.

     Making the dialog a column flex container avoids the percentage entirely:
     a flex item shrinks to its container's constrained size on its own, so
     `.modal-content` fits inside the capped dialog with no percentage to
     resolve. `min-height: 0` is what permits that shrink — a flex item's
     default `min-height: auto` refuses to go below its content height.

   `.modal` itself becomes the centering flex container and stops scrolling;
   the body is now the only scroller.
   ========================================================================== */

/* `.d-block` is `display: block !important`, so beating it needs both the extra
   specificity and the !important. Every modal shell in the app renders
   `modal fade show d-block`. */
.modal.show.d-block {
    display: flex !important;
    align-items: flex-start;
    justify-content: center;
    overflow: hidden;
}

    .modal.show.d-block > .modal-dialog {
        max-height: calc(100% - 3.5rem);
        display: flex;
        flex-direction: column;
        min-height: 0;
        /* Without this the dialog is a shrink-to-fit flex item and collapses
           narrower than its `modal-lg`/`modal-xl` max-width. `flex-grow` rather
           than `width: 100%` so Bootstrap's own 1.75rem side margins keep their
           gap instead of being added on top of a full-width box. */
        flex: 1 1 auto;
        min-width: 0;
    }

    /* Bootstrap centres these with `min-height: calc(100% - 3.5rem)`, which would
       force every centered dialog — including all 90 ConfirmModals — to full
       height. Auto margins centre it in the flex container instead, and only
       when there is spare room. */
    .modal.show.d-block > .modal-dialog.modal-dialog-centered {
        min-height: 0;
        margin-top: auto;
        margin-bottom: auto;
    }

        .modal.show.d-block > .modal-dialog > .modal-content {
            min-height: 0;
            /* Stops a scrolled body from painting over the rounded corners. */
            overflow: hidden;
        }

            .modal.show.d-block > .modal-dialog > .modal-content > .modal-header,
            .modal.show.d-block > .modal-dialog > .modal-content > .modal-footer {
                flex: 0 0 auto;
            }

            .modal.show.d-block > .modal-dialog > .modal-content > .modal-body {
                overflow-y: auto;
                min-height: 0;
                /* Stop a scroll that has hit the end of the body from chaining
                   to the page behind the modal. */
                overscroll-behavior: contain;
            }

/* -----------------------------------------------------------------------------
   Modal body scrollbar

   The theme kills every scrollbar in the app with

       body ::-webkit-scrollbar { width: 0; ... }        (styles.css:9706)

   so a scrolling modal body scrolls INVISIBLY — no track, no thumb, nothing to
   tell the user there is more below the fold. That descendant selector beats a
   bare `.modal-body::-webkit-scrollbar` on specificity, hence the explicit
   re-declaration here.

   Values are deliberately identical to `.cas-typeahead-menu` (app.css, SHARED
   TYPEAHEAD section) so the bar in a modal looks like the bar in a dropdown.
   Unlike that one this uses `overflow-y: auto`, not `scroll`, and no
   `scrollbar-gutter`: a short modal — every ConfirmModal — must not show a dead
   10px gutter down its side.
   -------------------------------------------------------------------------- */

.modal .modal-body {
    scrollbar-width: thin;
    scrollbar-color: rgb(30, 62, 175) #e9e9f5;
}

    .modal .modal-body::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }

    .modal .modal-body::-webkit-scrollbar-track,
    .modal .modal-body::-webkit-scrollbar-track-piece {
        background-color: #e9e9f5;
        border-radius: 5px;
    }

    .modal .modal-body::-webkit-scrollbar-thumb {
        background-color: rgb(30, 62, 175) !important;
        border: 0 !important;
        border-radius: 5px;
        box-shadow: none !important;
    }

        /* `body :hover::-webkit-scrollbar-thumb` (styles.css:9713) repaints the
           thumb grey the moment the pointer is anywhere over the modal. */
        .modal .modal-body::-webkit-scrollbar-thumb:hover,
        .modal .modal-body::-webkit-scrollbar-thumb:active,
        .modal .modal-body::-webkit-scrollbar-thumb:window-inactive {
            background-color: rgb(30, 62, 175) !important;
        }

[data-theme-mode=dark] .modal .modal-body {
    scrollbar-color: rgb(30, 62, 175) #2a2f42;
}

    [data-theme-mode=dark] .modal .modal-body::-webkit-scrollbar-track,
    [data-theme-mode=dark] .modal .modal-body::-webkit-scrollbar-track-piece {
        background-color: #2a2f42;
    }

/* Below 576px Bootstrap drops `.modal-dialog` margin from 1.75rem to 0.5rem, so
   the cap has to follow or the dialog loses its bottom gap. */
@media (max-width: 575.98px) {
    .modal.show.d-block > .modal-dialog {
        max-height: calc(100% - 1rem);
    }
}

/***************************
Keep modal edge to edge on the screen
***********************************/

/* Was `height: calc(75vh - 5rem)` with `.cas-modal-content { height: 75% }` —
   a fixed 75%-of-75vh box that both under-used the screen and fought the global
   cap above. These now only opt a dialog into filling the available height;
   the cap, the pinned header/footer and the body scroll all come from the
   global rules. Used by TransactionDetailsUpsertModal. */
.cas-modal-dialog {
    height: calc(100% - 3.5rem);
}

@media (max-width: 575.98px) {
    .cas-modal-dialog {
        height: calc(100% - 1rem);
    }
}

.cas-modal-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    background-color: #fff;
}

/* Retained because several shells put this class on the body directly. The
   global `.modal-body` rule already covers it; this keeps those call sites
   working if the element is ever nested deeper than a direct child. */
.cas-modal-body {
    overflow-y: auto;
    min-height: 0;
}

/* Was `position: sticky; bottom: 0` while the block still lived inside the
   scrolling `.cas-modal-body` — that made the banner hover over whatever field
   was mid-scroll. The block is now a flex sibling between the body and the
   footer, so it is naturally pinned to the bottom of the form and needs no
   sticky positioning. `flex-shrink: 0` stops the body's scroll from squeezing it. */
.cas-modal-error-sticky {
    flex-shrink: 0;
    background: #fff;
    border-top: 1px solid #e5e5e5;
    padding: .75rem 1rem 0;
}

.cas-modal-content .form-status.slide-display {
    max-width: none;
    width: 100%;
    margin: 0;
}

    .cas-modal-content select,
    .cas-modal-content input,
    .cas-modal-content textarea {
        border-color: #333;
    }

.cas-modal-open {
    overflow: hidden;
}


/* =============================================================================
   DARK MODE OVERRIDES
   Scoped to [data-theme-mode=dark] — the attribute the Spruko theme sets on
   <html> when dark mode is active. Nothing here leaks into light mode.
   ============================================================================= */

/* 1. System ID (and other .text-info) links stay a clear blue in dark mode.
      Default theme overrides .text-info to a pale/yellow-ish tone that ends up
      matching the .cas-header-audit card — forcing an explicit info blue here
      so the ID links stay visually distinct as actionable links. */
[data-theme-mode=dark] a .text-info,
[data-theme-mode=dark] a.text-info,
[data-theme-mode=dark] .text-info {
    color: #49b6f5 !important;
}

[data-theme-mode=dark] a:hover .text-info,
[data-theme-mode=dark] a.text-info:hover {
    color: #80caf8 !important;
}

/* 2. Grid header cells — the .bg-light / sticky-right-header backgrounds flip
      to a dark grey in dark mode (per the theme) but the header text inherits
      a near-black color from the Spruko `.table thead tr th { color:#000 !important }`
      rule and becomes unreadable. Beat that rule on both the <th> itself AND
      any inner wrapper (the LightGrid sort <a>/<span>/<button> and the plain
      text content). */
[data-theme-mode=dark] .lightgrid-table thead th,
[data-theme-mode=dark] .lightgrid-table thead th.bg-light,
[data-theme-mode=dark] .lightgrid-table thead .bg-light,
[data-theme-mode=dark] .lightgrid-table > thead > tr > th,
[data-theme-mode=dark] table.table thead tr th,
[data-theme-mode=dark] table.table thead tr th.bg-light,
[data-theme-mode=dark] table thead th.bg-light,
[data-theme-mode=dark] .lg-sticky-right-header,
[data-theme-mode=dark] .cas-sticky-right-header {
    color: #ffffff !important;
    background-color: #1a2236 !important;
}

/* Descendants of the header cell — the column title is often wrapped in
   <a>/<span>/<button> by the sort affordance, and those descendants inherit
   their own color rules. Force white on every child element of a dark-mode
   header cell. */
[data-theme-mode=dark] .lightgrid-table thead th a,
[data-theme-mode=dark] .lightgrid-table thead th span,
[data-theme-mode=dark] .lightgrid-table thead th button,
[data-theme-mode=dark] .lightgrid-table thead th i,
[data-theme-mode=dark] .lightgrid-table thead th * ,
[data-theme-mode=dark] table.table thead tr th a,
[data-theme-mode=dark] table.table thead tr th span,
[data-theme-mode=dark] table.table thead tr th button,
[data-theme-mode=dark] table.table thead tr th i,
[data-theme-mode=dark] table.table thead tr th * {
    color: #ffffff !important;
}

/* Sticky body cells in dark mode — keep the sticky columns opaque against the
   dark backdrop so the shadow/divider still reads. */
[data-theme-mode=dark] .cas-sticky-right,
[data-theme-mode=dark] .lg-sticky-right {
    background-color: #242e47 !important;
}

[data-theme-mode=dark] .cas-hscroll-sticky {
    background: #1a2236;
    border-top-color: #2d3a5a;
}

/* 3. Typeahead / dropdown hover + active option — default theme flips the
      background to a light color on hover, leaving the text also near-white
      which makes the option unreadable. Force white text on a visible blue
      background so the hovered item stands out. */
[data-theme-mode=dark] .dropdown-menu .dropdown-item:hover,
[data-theme-mode=dark] .dropdown-menu .dropdown-item:focus,
[data-theme-mode=dark] .dropdown-menu .dropdown-item.active,
[data-theme-mode=dark] .dropdown-menu .dropdown-item:active {
    color: #ffffff !important;
    background-color: #1e3eaf !important;
}

[data-theme-mode=dark] .dropdown-menu .dropdown-item {
    color: #dfe3eb;
}

/* 4. Disabled form controls — the theme's default sets `color` to a very dark
      value on disabled inputs/selects/textareas, rendering them near-invisible
      against the dark background. Force a light-grey readable color. */
[data-theme-mode=dark] .form-control:disabled,
[data-theme-mode=dark] .form-control[readonly],
[data-theme-mode=dark] .form-select:disabled,
[data-theme-mode=dark] .form-select[readonly],
[data-theme-mode=dark] textarea.form-control:disabled,
[data-theme-mode=dark] textarea.form-control[readonly],
[data-theme-mode=dark] input.form-control:disabled,
[data-theme-mode=dark] input.form-control[readonly],
[data-theme-mode=dark] input[type="text"]:disabled,
[data-theme-mode=dark] input[type="number"]:disabled,
[data-theme-mode=dark] input[type="date"]:disabled {
    color: #e6e9f0 !important;
    -webkit-text-fill-color: #e6e9f0 !important; /* Safari: disabled inputs need this */
    opacity: 1 !important;
}

/* 5. .btn-outline-dark affordances — including the Clear (✕) and Toggle (▾)
      buttons inside every typeahead input-group — use a near-black border and
      icon color which vanish against the dark backdrop. Flip border + icon
      text color to white; leave the transparent background so it keeps the
      "outline" look. Covers both the enabled and disabled state (the disabled
      rule wins via the --bs-btn-disabled-* CSS variables the theme reads). */
[data-theme-mode=dark] .btn-outline-dark,
[data-theme-mode=dark] .btn.btn-outline-dark,
[data-theme-mode=dark] .btn-outline-dark:disabled,
[data-theme-mode=dark] .btn-outline-dark.disabled,
[data-theme-mode=dark] fieldset:disabled .btn-outline-dark {
    color: #ffffff !important;
    border-color: #ffffff !important;
    --bs-btn-color: #ffffff;
    --bs-btn-border-color: #ffffff;
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-border-color: #ffffff;
    --bs-btn-disabled-color: #ffffff;
    --bs-btn-disabled-border-color: #ffffff;
}

/* On hover/focus/active Bootstrap fills `.btn-outline-dark` with its "dark"
   swatch, which the dark theme remaps to white — so the toggle (▾) and clear
   (✕) glyphs, forced white above, disappear into a white pill. Pin an explicit
   slate fill instead: lighter than the panel so the hover is still obvious,
   dark enough that the white glyph and border stay legible. Applies to every
   typeahead (BaseTypeahead / BaseMultiSelectTypeahead) and the smart-date
   picker, since they all share `.btn.btn-outline-dark`. */
[data-theme-mode=dark] .btn-outline-dark:hover,
[data-theme-mode=dark] .btn.btn-outline-dark:hover,
[data-theme-mode=dark] .btn-outline-dark:focus,
[data-theme-mode=dark] .btn.btn-outline-dark:focus,
[data-theme-mode=dark] .btn-outline-dark:focus-visible,
[data-theme-mode=dark] .btn.btn-outline-dark:focus-visible {
    color: #ffffff !important;
    border-color: #ffffff !important;
    background-color: #3a4260 !important;
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: #3a4260;
    --bs-btn-hover-border-color: #ffffff;
    --bs-btn-focus-shadow-rgb: 108, 117, 168;
}

/* Pressed state — one step darker than hover so the click reads as a press. */
[data-theme-mode=dark] .btn-outline-dark:active,
[data-theme-mode=dark] .btn.btn-outline-dark:active,
[data-theme-mode=dark] .btn-check:checked + .btn-outline-dark,
[data-theme-mode=dark] .btn-outline-dark.active,
[data-theme-mode=dark] .btn-outline-dark.show {
    color: #ffffff !important;
    border-color: #ffffff !important;
    background-color: #2c3350 !important;
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: #2c3350;
    --bs-btn-active-border-color: #ffffff;
}

/* 6. Placeholder text — typeahead inputs (`.form-control` with a placeholder
      attribute like "-SELECT/TYPE COMMUNITY-") render with a dim/near-black
      placeholder color that's hard to read on the dark background. Native
      date/time inputs get their own browser-level white placeholder handling,
      so the typeaheads look inconsistent. Force placeholder text to white
      across all form fields in dark mode to match. Each browser needs its
      own pseudo — ::placeholder is the modern standard but we include
      vendor prefixes so older Chromium/Gecko engines pick it up too. */
[data-theme-mode=dark] .form-control::placeholder,
[data-theme-mode=dark] .form-select::placeholder,
[data-theme-mode=dark] input::placeholder,
[data-theme-mode=dark] textarea::placeholder {
    color: #ffffff !important;
    opacity: 1 !important; /* Firefox lowers placeholder opacity by default */
}

[data-theme-mode=dark] .form-control::-webkit-input-placeholder,
[data-theme-mode=dark] input::-webkit-input-placeholder,
[data-theme-mode=dark] textarea::-webkit-input-placeholder {
    color: #ffffff !important;
    opacity: 1 !important;
}

[data-theme-mode=dark] .form-control::-moz-placeholder,
[data-theme-mode=dark] input::-moz-placeholder,
[data-theme-mode=dark] textarea::-moz-placeholder {
    color: #ffffff !important;
    opacity: 1 !important;
}

[data-theme-mode=dark] .form-control:-ms-input-placeholder,
[data-theme-mode=dark] input:-ms-input-placeholder,
[data-theme-mode=dark] textarea:-ms-input-placeholder {
    color: #ffffff !important;
    opacity: 1 !important;
}

/* 7. Native date / datetime / time / month / week inputs — the text value
      (e.g. "01/29/2026", "01/01/1900") is rendered by browser-native
      pseudo-elements (::-webkit-datetime-edit-*) that ignore the outer
      `color` cascade. Setting `color-scheme: dark` tells the UA to paint
      the native calendar picker + the textual value in dark-mode colors;
      the WebKit pseudo overrides below force white specifically for
      Chromium/Safari where needed. */
[data-theme-mode=dark] input[type="date"],
[data-theme-mode=dark] input[type="datetime-local"],
[data-theme-mode=dark] input[type="time"],
[data-theme-mode=dark] input[type="month"],
[data-theme-mode=dark] input[type="week"] {
    color: #ffffff !important;
    color-scheme: dark;
}

/* Chromium/Safari render date values through ::-webkit-datetime-edit-*
   sub-components — these inherit from their host but not from .form-control,
   so an explicit override is required. Applies to the month/day/year segment
   digits and the separator "/" glyphs. */
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit,
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit-fields-wrapper,
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit-text,
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit-month-field,
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit-day-field,
[data-theme-mode=dark] input[type="date"]::-webkit-datetime-edit-year-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-fields-wrapper,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-text,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-month-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-day-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-year-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-hour-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-minute-field,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-datetime-edit-ampm-field,
[data-theme-mode=dark] input[type="time"]::-webkit-datetime-edit,
[data-theme-mode=dark] input[type="time"]::-webkit-datetime-edit-fields-wrapper,
[data-theme-mode=dark] input[type="time"]::-webkit-datetime-edit-hour-field,
[data-theme-mode=dark] input[type="time"]::-webkit-datetime-edit-minute-field,
[data-theme-mode=dark] input[type="time"]::-webkit-datetime-edit-ampm-field {
    color: #ffffff !important;
}

/* The little calendar-picker icon on the right of a date input also inherits
   the text color on Chromium — invert it so the icon stays visible on dark.
   Using !important + a higher specificity `input.form-control[type=...]`
   variant so the theme's own `::-webkit-calendar-picker-indicator` rules
   don't win via source order. */
[data-theme-mode=dark] input[type="date"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input[type="datetime-local"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input[type="time"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input[type="month"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input[type="week"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input.form-control[type="date"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input.form-control[type="datetime-local"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input.form-control[type="time"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input.form-control[type="month"]::-webkit-calendar-picker-indicator,
[data-theme-mode=dark] input.form-control[type="week"]::-webkit-calendar-picker-indicator {
    filter: invert(1) brightness(1.4) !important;
    opacity: 1 !important;
    cursor: pointer;
}

/* 9. Modal content in dark mode — the Bootstrap `.modal-content` ships with
      `background-color: #fff` via the theme and its text `color` comes from
      `--default-text-color` which doesn't always flip white in dark mode.
      Result: the body text inside ConfirmModal / TransactionDetailsUpsertModal
      is invisible (dark-on-dark or empty-looking). Override both the chrome
      (header/footer/backdrop-card) and the body text to dark-mode-safe values.
      Also neutralises my own `.cas-modal-content { background: #fff }` from
      earlier in this file. */
[data-theme-mode=dark] .modal-content,
[data-theme-mode=dark] .cas-modal-content {
    background-color: #1a2236 !important;
    color: #ffffff !important;
    border-color: #2d3a5a !important;
}

[data-theme-mode=dark] .modal-header,
[data-theme-mode=dark] .modal-footer {
    background-color: #1a2236 !important;
    border-color: #2d3a5a !important;
    color: #ffffff !important;
}

[data-theme-mode=dark] .modal-body,
[data-theme-mode=dark] .cas-modal-body {
    color: #ffffff !important;
}

/* Cover descendants that render their own text — headings, labels, the
   text-muted subtitles, and strong emphasis inside the modal all need
   an explicit color override because the theme's rules target them
   by tag/class independently of `.modal-content`. */
[data-theme-mode=dark] .modal-content .modal-title,
[data-theme-mode=dark] .modal-content h1,
[data-theme-mode=dark] .modal-content h2,
[data-theme-mode=dark] .modal-content h3,
[data-theme-mode=dark] .modal-content h4,
[data-theme-mode=dark] .modal-content h5,
[data-theme-mode=dark] .modal-content h6,
[data-theme-mode=dark] .modal-content label,
[data-theme-mode=dark] .modal-content p,
[data-theme-mode=dark] .modal-content strong,
[data-theme-mode=dark] .modal-content span {
    color: #ffffff !important;
}

[data-theme-mode=dark] .modal-content .text-muted {
    color: #9aa5bd !important;
}

/* The little close "×" button at the top-right of a Bootstrap modal uses a
   dark SVG mask — invert it in dark mode so it's visible on the dark modal
   chrome. */
[data-theme-mode=dark] .modal-content .btn-close,
[data-theme-mode=dark] .cas-modal-content .btn-close {
    filter: invert(1) brightness(1.4);
}

/* My earlier `.cas-modal-error-sticky { background: #fff }` override — flip
   to a dark surface in dark mode so the error band blends with the modal
   chrome. Border is now on the top edge (the band sits above the footer). */
[data-theme-mode=dark] .cas-modal-error-sticky {
    background: #1a2236 !important;
    border-top-color: #2d3a5a !important;
    color: #ffffff !important;
}


/* =============================================================================
   MODAL STACKING (light + dark — global fix)
   -----------------------------------------------------------------------------
   Modals render inside <div class="cas-layout__main">, which sets
   `position: relative; z-index: 1;` to carve out its own stacking context.
   Everything inside that context is bounded by z-index 1 at the root level,
   meaning Bootstrap's `.modal { z-index: 1055 }` is only 1055 RELATIVE to
   `.cas-layout__main` (root-level it's still ~1). The sidebar (`.cas-sb`)
   sits at root-level z-index 1040 and therefore covers the modal.

   Using :has() we boost the wrapper's own z-index only when a modal is open,
   which lifts the whole stacking context above the sidebar. Falls back
   gracefully on older browsers (Chrome < 105) — there the modal remains
   below the sidebar, same as today's behavior.
   ============================================================================= */
.cas-layout__main:has(.modal.fade.show),
.cas-layout__main:has(> .modal-backdrop.show) {
    z-index: 1070; /* above .cas-sb (1040) and .cas-sb-backdrop (1025) */
}

/* Belt-and-braces for the modal + backdrop themselves: give them an explicit
   z-index higher than the sidebar just in case the :has() support is absent
   OR the modal gets moved outside `.cas-layout__main`. Also ensures the
   backdrop covers the sidebar area, matching the legacy expectation that
   a modal is truly blocking. */
.modal.fade.show.d-block {
    z-index: 1080 !important;
}

.modal-backdrop.fade.show {
    z-index: 1079 !important;
}

/* Same stacking problem, same fix, for the full-viewport busy scrim
   (.cas-busy-overlay, raised by LightGrid during a CSV export). It also
   renders inside `.cas-layout__main`, so without this its 1050 is 1050
   RELATIVE to a context pinned at root-level z-index 1 — the sidebar paints
   straight over it and stays clickable, letting a user navigate away from a
   page that is mid-export. Kept just below the modal boost (1070) so a modal
   opened over an export still wins.

   The scrim is nested deep inside the page (LightGrid renders it), so this
   matches a DESCENDANT, not a direct child. */
.cas-layout__main:has(.cas-busy-overlay) {
    z-index: 1060;
}


/* =============================================================================
   DOCUMENT PREVIEW (client-side rendering of docx / xlsx / msg / txt)
   -----------------------------------------------------------------------------
   Styling for the content injected into the DocumentPreviewModal by casDocs
   (mammoth for .docx, SheetJS for .xlsx, msgreader for .msg, plain text
   for .txt). Scoped to `.cas-doc-preview` so it doesn't leak elsewhere.
   ============================================================================= */

.cas-doc-preview-wrap {
    height: 100%;
    overflow: auto;
    padding: 16px;
    font-size: 0.9rem;
}

.cas-doc-preview {
    line-height: 1.5;
}

/* .docx — mammoth renders standard HTML with headings, paragraphs, tables.
   Constrain images and tables so they don't overflow the modal. */
.cas-docx img {
    max-width: 100%;
    height: auto;
}

.cas-docx table {
    border-collapse: collapse;
    margin: 8px 0;
}

.cas-docx table td,
.cas-docx table th {
    border: 1px solid #dee2e6;
    padding: 4px 8px;
}

/* .xlsx — one table per sheet. SheetJS emits plain <table> with inline
   border attributes; normalize to match the app's look and make big
   spreadsheets scroll horizontally instead of overflowing the modal. */
.cas-xlsx-sheet-name {
    font-weight: 600;
    color: inherit;
    padding: 4px 8px;
    background: rgba(0,0,0,0.04);
    border-radius: 4px;
    display: inline-block;
}

.cas-xlsx-sheet table {
    width: auto;
    min-width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.cas-xlsx-sheet td,
.cas-xlsx-sheet th {
    border: 1px solid #dee2e6;
    padding: 4px 8px;
    white-space: nowrap;
    max-width: 320px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cas-xlsx-sheet tr:first-child td,
.cas-xlsx-sheet tr:first-child th {
    background: rgba(0,0,0,0.04);
    font-weight: 600;
}

/* .msg — headers table + body section. The body may itself be HTML produced
   by Outlook — we don't constrain its styling aggressively, just keep
   images responsive. */
.cas-msg-headers th {
    text-align: right;
    font-weight: 600;
    white-space: nowrap;
    vertical-align: top;
}

.cas-msg-headers td {
    word-break: break-word;
}

.cas-msg-body img {
    max-width: 100%;
    height: auto;
}

.cas-msg-body table {
    max-width: 100%;
}

.cas-msg-attachments {
    font-size: 0.85rem;
    padding: 8px 12px;
    background: rgba(0,0,0,0.04);
    border-radius: 4px;
}

/* .txt — preformatted. Wrap long lines so reading is comfortable. */
.cas-txt {
    white-space: pre-wrap;
    word-break: break-word;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    background: transparent;
    color: inherit;
}

/* Dark-mode adjustments so the preview content is readable on dark chrome. */
[data-theme-mode=dark] .cas-docx table td,
[data-theme-mode=dark] .cas-docx table th,
[data-theme-mode=dark] .cas-xlsx-sheet td,
[data-theme-mode=dark] .cas-xlsx-sheet th {
    border-color: #2d3a5a !important;
    color: #ffffff !important;
}

[data-theme-mode=dark] .cas-xlsx-sheet-name {
    background: rgba(255,255,255,0.06) !important;
    color: #ffffff !important;
}

[data-theme-mode=dark] .cas-xlsx-sheet tr:first-child td,
[data-theme-mode=dark] .cas-xlsx-sheet tr:first-child th {
    background: rgba(255,255,255,0.06) !important;
    color: #ffffff !important;
}

[data-theme-mode=dark] .cas-msg-attachments {
    background: rgba(255,255,255,0.06) !important;
    color: #ffffff !important;
}

[data-theme-mode=dark] .cas-doc-preview,
[data-theme-mode=dark] .cas-doc-preview * {
    color: #ffffff;
}


/* =============================================================================
   GRID LOADING STATE (dark mode)
   -----------------------------------------------------------------------------
   The LightGrid component shows two loading affordances:
     1. `.lightgrid-loading-overlay` — full-area overlay with spinner over the
        grid while fetching. Default rule uses `background: rgba(255,255,255,.65)`
        which looks awful on dark chrome (big bright square washing out the
        grid rows). Flip to a dark translucent overlay.
     2. A table row `<td class="text-muted">Loading…</td>` inside the grid
        body. `.text-muted` resolves to a very dim grey in dark mode — barely
        visible. Force a readable light-grey for in-grid loading text.
   Also covers `.cas-grid-overlay` / `.cas-grid-overlay-card` (used elsewhere
   for grid-level busy cards) with matching dark styling.
   ============================================================================= */

[data-theme-mode=dark] .lightgrid-loading-overlay {
    background: rgba(26, 34, 54, 0.72) !important; /* app dark surface + translucency */
}

[data-theme-mode=dark] .lightgrid-loading-overlay .spinner-border {
    color: #8aa8f5 !important; /* app accent blue — visible on the dim overlay */
}

[data-theme-mode=dark] .lightgrid-loading-overlay .text-muted {
    color: #dfe3eb !important;
}

/* The in-body "Loading…" / empty-state row uses .text-muted — force a more
   readable colour so users can still see the message on the dark table. */
[data-theme-mode=dark] .lightgrid-table tbody td.text-muted {
    color: #dfe3eb !important;
}

/* Row dividers in dark mode. The global rule in the sticky-grid section colours
   itself from `--bs-table-border-color`, which this app never re-declares for
   dark (the theme switches on `data-theme-mode`, not Bootstrap's own
   `data-bs-theme`), so the light #dee2e6 would glare on the dark surface.
   Point the variable at the app's dark divider tone instead of overriding the
   border shorthand, so the light/dark rules can't drift apart. */
[data-theme-mode=dark] .lightgrid-table {
    --bs-table-border-color: #2a2f42;
}

/* `.cas-grid-overlay` + card — used by a few pages as a busy scrim. */
[data-theme-mode=dark] .cas-grid-overlay {
    background: rgba(26, 34, 54, 0.72) !important;
}

[data-theme-mode=dark] .cas-grid-overlay-card {
    background: #1a2236 !important;
    border-color: #2d3a5a !important;
    color: #ffffff !important;
    box-shadow: 0 8px 24px rgba(0,0,0,.4) !important;
}

[data-theme-mode=dark] .cas-grid-overlay-card .spinner-border {
    color: #8aa8f5 !important;
}

/* `.cas-busy-overlay` — the full-viewport export scrim. Same treatment: the
   default white wash would flash the screen bright on a dark page. */
[data-theme-mode=dark] .cas-busy-overlay {
    background: rgba(15, 20, 34, 0.55) !important;
}

@supports not ((backdrop-filter: blur(3px)) or (-webkit-backdrop-filter: blur(3px))) {
    [data-theme-mode=dark] .cas-busy-overlay {
        background: rgba(15, 20, 34, 0.82) !important;
    }
}

[data-theme-mode=dark] .cas-busy-overlay-card {
    background: #1a2236 !important;
    border-color: #2d3a5a !important;
    box-shadow: 0 10px 30px rgba(0,0,0,.5) !important;
}

[data-theme-mode=dark] .cas-busy-overlay-spinner {
    color: #8aa8f5 !important;
}

[data-theme-mode=dark] .cas-busy-overlay-text {
    color: #ffffff !important;
}

/* Preserve existing inline colors on .msg bodies (Outlook sends styled HTML
   with specific link/signature colors) unless they fight the dark chrome.
   The blanket `*` selector above gives us white as a sane default; Outlook's
   own inline `style="color:..."` still wins at the element level because
   those ship as inline styles with higher specificity than our `*`. */


/* 8. Currency / numeric value inputs (`InputCurrency`, plain number/text
      fields showing "$20,000.00", etc.) sometimes render in black because
      the Spruko theme sets `.form-control { color: var(--default-input-text-color) }`
      but that variable isn't reliably flipped to white for every input type
      in dark mode. Force white text on every enabled `.form-control` and raw
      text/number input in dark mode so the value is always readable. */
[data-theme-mode=dark] .form-control,
[data-theme-mode=dark] input.form-control,
[data-theme-mode=dark] input[type="text"].form-control,
[data-theme-mode=dark] input[type="number"].form-control,
[data-theme-mode=dark] input[type="tel"].form-control,
[data-theme-mode=dark] input[type="email"].form-control,
[data-theme-mode=dark] input[type="search"].form-control,
[data-theme-mode=dark] input[type="url"].form-control,
[data-theme-mode=dark] input[type="password"].form-control,
[data-theme-mode=dark] textarea.form-control,
[data-theme-mode=dark] .form-select {
    color: #ffffff !important;
}

/* =============================================================================
   Commitments tab content
   The UpsertCommitment page puts Notes / Documents / Details inside a Bootstrap
   nav-tabs + tab-content pair. Bootstrap's defaults assume light mode, so the
   pane + the inner table rows come out glaringly white when the Spruko theme is
   in dark mode. Force both the tab pane and the commitment details table to
   inherit the theme's card colors. Light mode is already fine with defaults —
   this block only kicks in under [data-theme-mode=dark].
   ============================================================================= */

/* Light-mode pane has Bootstrap's default white card background; dark mode needs
   an explicit dark surface so the pane doesn't stand out against the card. */
.cas-commitment-tab-content {
    background: #ffffff;
    color: inherit;
}

/* Generic tab pane surface — same treatment as the commitment pane above but
   not tied to one page. Used by the Approval upsert tabs; safe for any future
   tabbed page. Kept as its own rule so changes here can't regress Commitments. */
.cas-tab-content {
    background: #ffffff;
    color: inherit;
}

[data-theme-mode=dark] .cas-tab-content {
    background: #1a1d29;         /* matches Spruko dark card surface */
    border-color: #2a2f42 !important;
    color: #e6e8f1;
}

[data-theme-mode=dark] .cas-tab-content .table > :not(caption) > * > * {
    background-color: transparent;
    color: #e6e8f1;
}

[data-theme-mode=dark] .cas-tab-content table.lightgrid-table {
    color: #e6e8f1;
}

[data-theme-mode=dark] .cas-commitment-tab-content {
    background: #1a1d29;         /* matches Spruko dark card surface */
    border-color: #2a2f42 !important;
    color: #e6e8f1;
}

/* Commitment Details table — force the row bg to the theme surface so
   detail rows stop rendering pure white in dark mode. The campaign band,
   designation sub-band, and yellow assessment-tint rows keep their own
   inline styles and aren't affected by this rule. */
[data-theme-mode=dark] .cas-commitment-tab-content .table > :not(caption) > * > * {
    background-color: transparent;
    color: #e6e8f1;
}

[data-theme-mode=dark] .cas-commitment-tab-content table.lightgrid-table {
    color: #e6e8f1;
}

/* =============================================================================
   Tab bar — generic
   Applies to every `ul.nav.nav-tabs` in the app (Send Statement, One-Click Send,
   Approval upsert, Commitments, Emergency Commitments, Write Offs, End of Year
   Goals, and anything added later). No page markup changes required.

   Bootstrap's stock nav-tabs draw the active tab as a white "folder" tab with a
   hairline border. On the Spruko gray page background (#eef1f7) that reads as
   almost no contrast, and inactive tabs get no chrome at all so the strip looks
   like loose text. This replaces that with a flat strip: muted inactive tabs,
   a tinted + accent-underlined active tab, and a single rule under the strip
   that doubles as the top edge of an attached `.tab-content` pane.

   Colors come from the theme's own variables so light/dark flip automatically.
   The two exceptions are documented inline: --primary-color is a deep indigo
   that fails contrast on the dark surface, and --default-border is too faint to
   read as a strip rule, so both get explicit dark-mode values.
   ============================================================================= */

:root {
    --cas-tab-accent: var(--primary-color);
    --cas-tab-active-bg: var(--primary01);
    --cas-tab-hover-bg: var(--primary005);
    --cas-tab-border: #dde3ec;
    --cas-tab-color: #5c6a85;              /* --text-muted is too low-contrast here */
    --cas-tab-color-hover: var(--default-text-color);
}

[data-theme-mode=dark] {
    --cas-tab-accent: #8aa8f5;             /* lightened primary; deep indigo is unreadable on dark */
    --cas-tab-active-bg: rgba(138, 168, 245, 0.14);
    --cas-tab-hover-bg: rgba(255, 255, 255, 0.06);
    --cas-tab-border: rgba(255, 255, 255, 0.12);
    --cas-tab-color: rgba(255, 255, 255, 0.62);
}

.nav-tabs {
    --bs-nav-tabs-border-width: 0;
    gap: 0.25rem;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    border-bottom: 1px solid var(--cas-tab-border);
    scrollbar-width: none;                 /* tab strips scroll on narrow viewports */
    -ms-overflow-style: none;
}

.nav-tabs::-webkit-scrollbar {
    display: none;
}

.nav-tabs .nav-item {
    flex: 0 0 auto;
}

.nav-tabs .nav-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    white-space: nowrap;
    padding: 0.625rem 1rem;
    border: 0;
    border-radius: 6px 6px 0 0;
    background: transparent;
    color: var(--cas-tab-color);
    font-weight: 500;
    line-height: 1.25;
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out;
}

/* Accent bar sits on top of the strip rule so the active tab reads as attached
   to the pane below it. -1px keeps it flush with the 1px border-bottom. */
.nav-tabs .nav-link::after {
    content: "";
    position: absolute;
    left: 0.5rem;
    right: 0.5rem;
    bottom: -1px;
    height: 2px;
    border-radius: 2px 2px 0 0;
    background: transparent;
    transition: background-color 0.15s ease-in-out;
}

.nav-tabs .nav-link:hover:not(.active):not(:disabled),
.nav-tabs .nav-link:focus-visible:not(.active):not(:disabled) {
    background: var(--cas-tab-hover-bg);
    color: var(--cas-tab-color-hover);
    border-color: transparent;
}

.nav-tabs .nav-link:hover:not(.active):not(:disabled)::after {
    background: var(--cas-tab-border);
}

.nav-tabs .nav-link.active {
    background: var(--cas-tab-active-bg);
    border-color: transparent;
    color: var(--cas-tab-accent);
    font-weight: 600;
}

.nav-tabs .nav-link.active::after {
    background: var(--cas-tab-accent);
}

.nav-tabs .nav-link:focus,
.nav-tabs .nav-link:focus-visible {
    outline: 0;
    box-shadow: none;
}

.nav-tabs .nav-link:focus-visible {
    box-shadow: 0 0 0 2px var(--cas-tab-active-bg), 0 0 0 3px var(--cas-tab-accent);
}

.nav-tabs .nav-link:disabled,
.nav-tabs .nav-link.disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Icons render at the theme's inherited size; nudge them onto the text baseline. */
.nav-tabs .nav-link > i {
    font-size: 0.95em;
    line-height: 1;
}

/* Attached pane — pages that render `.tab-content.border.border-top-0` right
   after the strip. Match the pane border to the strip rule so the two read as
   one surface instead of two slightly different grays. */
.nav-tabs + .tab-content.border {
    border-color: var(--cas-tab-border) !important;
    border-radius: 0 0 6px 6px;
}

/* =============================================================================
   Read-only detail grid — generic
   Label/value pairs for record context that is displayed, never edited (the
   recipient strip on the statement send page, and any future header strip).
   Read-only `.form-control` inputs were being used for this: they imply the
   value is editable, and each one costs ~38px of height plus a border.

   Markup is a <dl> so the label/value pairing is exposed to screen readers:
     <dl class="cas-detail-grid">
       <div class="cas-detail-item">
         <dt class="cas-detail-label">Email</dt>
         <dd class="cas-detail-value">someone@example.org</dd>
       </div>
     </dl>
   Columns auto-fit, so the same grid works for 3 fields or 12.
   ============================================================================= */

.cas-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.85rem 1.5rem;
    margin: 0;
}

/* min-width:0 lets long values wrap instead of stretching their column. */
.cas-detail-item {
    min-width: 0;
}

.cas-detail-label {
    margin: 0 0 1px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.cas-detail-value {
    margin: 0;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.4;
    color: var(--default-text-color);
    overflow-wrap: anywhere;      /* long emails must not blow out the column */
}

/* Optional modifier for values that should read as secondary (codes, IDs). */
.cas-detail-value.cas-detail-value-muted {
    font-weight: 400;
    color: var(--text-muted);
}

/* ------------------------------------------------------------------ */
/* Outlook email import — picker dropdown rows + attachment selection */
/* ------------------------------------------------------------------ */

.cas-outlook-item {
    white-space: normal;
    text-align: left;
}

.cas-outlook-item-subject {
    font-weight: 500;
    font-size: 0.875rem;
    line-height: 1.3;
}

.cas-outlook-item-preview {
    color: #6c757d;
    font-size: 12px;
    margin-top: 2px;
}

.cas-outlook-item-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    color: #6c757d;
    font-size: 12px;
    margin-top: 4px;
}

.cas-outlook-item-attach {
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

.cas-outlook-attachments {
    display: flex;
    flex-direction: column;
    border: 1px solid #dee2e6;
    border-radius: 6px;
    overflow: hidden;
}

.cas-outlook-attachment {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
}

.cas-outlook-attachment + .cas-outlook-attachment {
    border-top: 1px solid #dee2e6;
}

.cas-outlook-attachment-selected {
    background: #eef4ff;
}

.cas-outlook-attachment-icon {
    font-size: 18px;
    color: #6c757d;
    flex-shrink: 0;
}

.cas-outlook-attachment-name {
    flex: 1 1 auto;
    min-width: 0;
}

/* The dropdown-item hover/active fill must not make the muted lines unreadable. */
.dropdown-item.active .cas-outlook-item-preview,
.dropdown-item.active .cas-outlook-item-meta,
.dropdown-item:active .cas-outlook-item-preview,
.dropdown-item:active .cas-outlook-item-meta {
    color: inherit;
    opacity: 0.85;
}

[data-theme-mode=dark] .cas-outlook-item-preview,
[data-theme-mode=dark] .cas-outlook-item-meta,
[data-theme-mode=dark] .cas-outlook-attachment-icon {
    color: #bcc0d1;
}

[data-theme-mode=dark] .cas-outlook-attachments {
    border-color: #2a2f42;
}

[data-theme-mode=dark] .cas-outlook-attachment + .cas-outlook-attachment {
    border-top-color: #2a2f42;
}

[data-theme-mode=dark] .cas-outlook-attachment-selected {
    background: rgba(138, 168, 245, 0.14);
}

/* Outlook email viewer — sandboxed body frame + plain-text body */

.cas-outlook-email-frame {
    width: 100%;
    height: 55vh;
    border: 1px solid #dee2e6;
    border-radius: 6px;
    background: #ffffff;
}

.cas-outlook-email-text {
    max-height: 55vh;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
    font-size: 13px;
    margin-bottom: 0;
    border: 1px solid #dee2e6;
    border-radius: 6px;
    padding: 12px;
}

/* Email HTML is authored for light backgrounds — the frame stays white in dark
   mode on purpose; only the chrome around it adapts. */
[data-theme-mode=dark] .cas-outlook-email-frame,
[data-theme-mode=dark] .cas-outlook-email-text {
    border-color: #2a2f42;
}

[data-theme-mode=dark] .cas-outlook-email-text {
    color: #e6e8f1;
}

/* Outlook picker — attachment filter pinned at the top of the dropdown */

.cas-outlook-filter {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: #ffffff;
    border-bottom: 1px solid #dee2e6;
}

[data-theme-mode=dark] .cas-outlook-filter {
    background: #1a1d29;
    border-bottom-color: #2a2f42;
}

/*********************************
Typeahead dropdown scrollbar (always visible)
*****************************************/

.cas-typeahead-menu {
    overflow-x: hidden;
    overflow-y: scroll; /* scroll (not auto) so the track is always visible */
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: rgb(30, 62, 175) #e9e9f5;
}

    .cas-typeahead-menu::-webkit-scrollbar {
        width: 10px;
    }

    .cas-typeahead-menu::-webkit-scrollbar-track,
    .cas-typeahead-menu::-webkit-scrollbar-track-piece {
        background-color: #e9e9f5;
        border-radius: 5px;
    }

    .cas-typeahead-menu::-webkit-scrollbar-thumb {
        background-color: rgb(30, 62, 175) !important;
        border: 0 !important;
        border-radius: 5px;
        box-shadow: none !important;
    }

        .cas-typeahead-menu::-webkit-scrollbar-thumb:hover,
        .cas-typeahead-menu::-webkit-scrollbar-thumb:active,
        .cas-typeahead-menu::-webkit-scrollbar-thumb:window-inactive {
            background-color: rgb(30, 62, 175) !important;
        }

[data-theme-mode=dark] .cas-typeahead-menu {
    scrollbar-color: rgb(30, 62, 175) #2a2f42;
}

    [data-theme-mode=dark] .cas-typeahead-menu::-webkit-scrollbar-track,
    [data-theme-mode=dark] .cas-typeahead-menu::-webkit-scrollbar-track-piece {
        background-color: #2a2f42;
    }

/* =============================================================================
   SHARED FILTER PANEL LAYOUT  (.cas-filter-grid)
   =============================================================================

   One layout for every "Filter Options" panel in the app. Replaces the
   per-page Bootstrap `.row g-2` + `col-lg-1..6` guesswork, which produced a
   different field width on nearly every page and left System ID and the date
   boxes roughly four times wider than their content ever needs.

   Fields are FIXED WIDTH and simply wrap, so a panel with five filters and a
   panel with twelve filters both lay out correctly with zero per-page tuning.
   That is why this is flexbox and not a CSS grid: a grid would need its column
   count declared per page.

   Widths:
     --cas-ff-w-id     System ID. Matches the Approvals panel's `1fr` track
                       (`.approvals-filter-grid`, tracks 1fr 2.5fr 2.5fr 4fr 4fr
                       with four 0.75rem gaps) at desktop width.
     --cas-ff-w-date   Start Date / End Date. Holds `MM/DD/YYYY` plus the
                       InputCasSmartDate calendar button and nothing more.
     --cas-ff-w        Everything else — exactly half the old col-lg-3.

   Canonical field ORDER (skip what a page does not have):
     System ID, Start Date, End Date, Year, City Size, Community, Campaign,
     Designation, Project, Agency, <remaining fields>, Posted (always last).

   Markup contract:
     <div class="cas-filter-grid">
         <div class="cas-ff cas-ff-id">   <label>System ID</label>  ... </div>
         <div class="cas-ff cas-ff-date"> <label>Start Date</label> ... </div>
         <div class="cas-ff">             <label>Year</label>       ... </div>
     </div>

   Global (not a scoped .razor.css) on purpose: the typeahead and smart-date
   controls render their own markup from other components, so scoped styles
   would need ::deep on every rule.
   ========================================================================== */

:root {
    --cas-ff-w: 200px;
    --cas-ff-w-id: 115px;
    --cas-ff-w-date: 135px;
}

.cas-filter-grid {
    display: flex;
    flex-wrap: wrap;
    column-gap: 0.75rem;
    row-gap: 0.75rem;
    align-items: flex-start;
}

    .cas-filter-grid .cas-ff {
        flex: 0 0 var(--cas-ff-w);
        width: var(--cas-ff-w);
        min-width: 0;
    }

    .cas-filter-grid .cas-ff-id {
        flex-basis: var(--cas-ff-w-id);
        width: var(--cas-ff-w-id);
    }

    .cas-filter-grid .cas-ff-date {
        flex-basis: var(--cas-ff-w-date);
        width: var(--cas-ff-w-date);
    }

    .cas-filter-grid .cas-ff > label {
        display: block;
        margin-bottom: 0.25rem;
        white-space: nowrap;
    }

/* Checkbox filters ("Inactive only", "Do Not Mail", ...). They carry their own
   inline label so they need no fixed width and no label row above; align-self
   pins them to the bottom of their flex line so the box lines up with the
   inputs beside it rather than floating at the top of the row. */
.cas-filter-grid .cas-ff-check {
    flex: 0 0 auto;
    width: auto;
    align-self: flex-end;
    display: flex;
    align-items: center;
    /* Matches the rendered height of a Bootstrap .form-control. */
    min-height: calc(1.5em + 0.75rem + 2px);
}

    .cas-filter-grid .cas-ff-check .form-check {
        margin-bottom: 0;
    }

    .cas-filter-grid .cas-ff-check .form-check-label {
        white-space: nowrap;
    }

    .cas-filter-grid .cas-ff > .form-control,
    .cas-filter-grid .cas-ff > .form-select {
        width: 100%;
    }

/* Typeahead and InputCasSmartDate both render a Bootstrap .input-group with a
   trailing toggle button. Without these the button wraps onto its own line once
   the cell gets this narrow. */
.cas-filter-grid .cas-ff .input-group {
    flex-wrap: nowrap;
}

    .cas-filter-grid .cas-ff .input-group > .form-control {
        flex: 1 1 auto;
        width: 1%;
        min-width: 0;
    }

/* Placeholders such as "-SELECT/TYPE DESIGNATION-" have to fit the narrower
   box instead of being clipped mid-word. */
.cas-filter-grid .cas-ff .form-control::placeholder {
    font-size: 0.75rem;
    letter-spacing: -0.01em;
}

/* The dropdown menu keeps its own width; long option text wraps rather than
   being cut off by the now-narrow input it hangs from. */
.cas-filter-grid .cas-ff .dropdown-menu .dropdown-item,
.cas-filter-grid .cas-ff .dropdown-menu .dropdown-item-text {
    white-space: normal;
    word-break: break-word;
    overflow-wrap: anywhere;
    line-height: 1.25;
}

/* Phones: one field per row, full width. */
@media (max-width: 575.98px) {
    .cas-filter-grid .cas-ff,
    .cas-filter-grid .cas-ff-id,
    .cas-filter-grid .cas-ff-date {
        flex-basis: 100%;
        width: 100%;
    }
}

/* =============================================================================
   LEGACY-STYLE FILTER PANEL MODIFIERS
   (.cas-filter-inline + .cas-filter-1col / .cas-filter-2col)
   =============================================================================

   OPT-IN modifiers that sit on top of `.cas-filter-grid`. None of them changes
   anything for a panel that does not ask for them, so every existing filter
   panel keeps the stacked label-above-field layout above.

   The class names say "filter" because filter panels are where this started,
   but the layout is not filter-specific: it is the app's generic legacy
   label-left form row, and upsert forms that want the same proportions
   (EndOfMonthSummaryUpsert, for one) use it as-is rather than growing a
   parallel set of rules.

   `.cas-filter-inline`
       Puts the label to the LEFT of the control, right-aligned against it, and
       gives every control one uniform width — the layout the legacy ASP.NET MVC
       screens used. Uniform width is the point: `MM/DD/YYYY` and
       `ARKANSAS-MISC - 04999` share a box, so nothing is clipped and the column
       edge stays straight.

       Implemented as a 2-track CSS grid on each `.cas-ff`, which is why it can
       ignore what the second child actually is. `Posted` is a bare
       `<select class="form-select">`, `System ID` is a bare
       `<input class="form-control">`, and the typeaheads and InputCasSmartDate
       each render a `<div class="position-relative">` wrapper around their own
       `.input-group`. As direct grid children all three land in track 2 and
       stretch to it, so no per-control rule is needed.

       The fixed `.cas-ff-id` / `.cas-ff-date` widths from the stacked layout are
       reset here — in inline mode the shared `--cas-ff-inline-input-w` wins.

   `.cas-filter-1col`
       Stacks the fields in a single column. No wrappers — the `.cas-ff` blocks
       are the flex items.

   `.cas-filter-2col`
       Lays the panel out as explicit side-by-side columns instead of one wrapping
       row. Each column is its own `.cas-ff-col` wrapper, so a page controls
       exactly which field goes in which column and how many each holds. That is
       deliberate: legacy splits Commitments 7 / 3, not 5 / 5, and an automatic
       balance would not reproduce it.

   Markup contract — two columns:
       <div class="cas-filter-grid cas-filter-inline cas-filter-2col">
           <div class="cas-ff-col">
               <div class="cas-ff"><label>System ID</label> ... </div>
               ...
           </div>
           <div class="cas-ff-col">
               <div class="cas-ff"><label>Start Date</label> ... </div>
               ...
           </div>
       </div>

   Markup contract — one column:
       <div class="cas-filter-grid cas-filter-inline cas-filter-1col">
           <div class="cas-ff"><label>Start Date</label> ... </div>
           <div class="cas-ff"><label>End Date</label>   ... </div>
       </div>

   Global (not scoped .razor.css) for the same reason `.cas-filter-grid` is: the
   typeahead and smart-date controls render their markup from other components,
   so scoped rules would need ::deep on every line.
   ========================================================================== */

:root {
    /* Wide enough for the common labels ("Designation", "Calendar Year",
       "Organization", "Start Date") without wrapping.

       The track is a FIXED length on purpose: every `.cas-ff` is its own grid,
       so a content-sized track would let each row pick a different label width
       and the input edges would no longer line up. A page whose longest label
       does not fit — "Transaction Type" on /bank-deposits, for instance —
       raises it for that panel only, by setting the custom property inline on
       the `.cas-filter-grid` element:

           <div class="cas-filter-grid cas-filter-inline cas-filter-2col"
                style="--cas-ff-inline-label-w: 135px;">

       Raise it there rather than here: widening the global value pushes every
       other panel's inputs right for a label none of them have. */
    --cas-ff-inline-label-w: 105px;
    /* Fits "ARKANSAS-MISC - 04999" plus the clear and toggle buttons, and
       "MM/DD/YYYY" plus the calendar button, with room to spare. */
    --cas-ff-inline-input-w: 290px;
    --cas-ff-inline-gap: 0.75rem;
    --cas-ff-col-gap: 2.5rem;
}

.cas-filter-grid.cas-filter-inline .cas-ff {
    display: grid;
    grid-template-columns: var(--cas-ff-inline-label-w) var(--cas-ff-inline-input-w);
    column-gap: var(--cas-ff-inline-gap);
    align-items: center;
    /* Cancels the fixed flex sizing the stacked layout applies. */
    flex: 0 0 auto;
    width: auto;
}

/* The stacked layout's per-field widths do not apply in inline mode — every
   control shares --cas-ff-inline-input-w. */
.cas-filter-grid.cas-filter-inline .cas-ff-id,
.cas-filter-grid.cas-filter-inline .cas-ff-date {
    flex-basis: auto;
    width: auto;
}

.cas-filter-grid.cas-filter-inline .cas-ff > label {
    margin-bottom: 0;
    text-align: right;
    justify-self: end;
    white-space: nowrap;
}

/* At 290px the placeholders fit at their normal size, so the tightened
   placeholder type from the stacked layout is not needed here. */
.cas-filter-grid.cas-filter-inline .cas-ff .form-control::placeholder {
    font-size: inherit;
    letter-spacing: normal;
}

/* Checkbox filters carry their own inline label and have no label element of
   their own, so track 1 would swallow them. Push them into track 2 to line up
   with the inputs above and below. */
.cas-filter-grid.cas-filter-inline .cas-ff-check {
    display: flex;
    align-items: center;
    align-self: auto;
    width: auto;
    flex: 0 0 auto;
    margin-left: calc(var(--cas-ff-inline-label-w) + var(--cas-ff-inline-gap));
}

/* --- Fields that carry a validation message ------------------------------- */

/* A `.cas-ff` in inline mode is a 2-track grid, so it must have EXACTLY two
   children — label, then control. A field that also renders a validation
   message would put that message in track 1 of the next implicit row, under
   the label. Wrap the control and its message together instead:

       <div class="cas-ff">
           <label>Calendar Year</label>
           <div class="cas-ff-control">
               <YearTypeahead ... />
               <div class="text-danger mt-1">Calendar Year is required.</div>
           </div>
       </div>

   Rows that carry a wrapper align their label to the TOP rather than the
   vertical centre, so the label sits level with the control instead of
   drifting halfway down beside the message. */
.cas-filter-grid.cas-filter-inline .cas-ff > .cas-ff-control {
    min-width: 0;
}

.cas-filter-grid.cas-filter-inline .cas-ff:has(> .cas-ff-control) {
    align-items: start;
}

    /* Matches the .form-control's own top padding + border so the label text
       lines up with the text inside the input. */
    .cas-filter-grid.cas-filter-inline .cas-ff:has(> .cas-ff-control) > label {
        padding-top: calc(0.375rem + 1px);
    }

/* --- Single-column panel -------------------------------------------------- */

/* For a panel with few enough filters that one column reads better than two.
   No `.cas-ff-col` wrappers needed — the fields are the flex items and simply
   stack. `align-items: flex-start` keeps each row only as wide as its own
   label + control instead of stretching across the card. */
.cas-filter-grid.cas-filter-1col {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: flex-start;
    row-gap: 0.5rem;
}

/* --- Two-column panel ---------------------------------------------------- */

.cas-filter-grid.cas-filter-2col {
    column-gap: var(--cas-ff-col-gap);
}

    .cas-filter-grid.cas-filter-2col > .cas-ff-col {
        display: flex;
        flex-direction: column;
        row-gap: 0.5rem;
        flex: 0 0 auto;
        min-width: 0;
    }

/* A column that holds ONLY checkbox filters (`.cas-ff-col.cas-ff-col-checks`)
   has no labels, so the label-track indent `.cas-ff-check` normally gets would
   just leave a blank gap. The boxes sit flush at the column's left edge
   instead; the column gap already separates them from the inputs. Each
   checkbox row keeps the .form-control min-height, so the rows line up with
   the input rows in the column beside it. */
.cas-filter-grid.cas-filter-inline .cas-ff-col-checks > .cas-ff-check {
    margin-left: 0;
}

/* Tablets and phones: the two columns stack, and each field goes back to
   label-above-control at full width so nothing overflows the card. */
@media (max-width: 767.98px) {
    .cas-filter-grid.cas-filter-inline .cas-ff {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 0.25rem;
    }

        .cas-filter-grid.cas-filter-inline .cas-ff > label {
            text-align: left;
            justify-self: start;
        }

    .cas-filter-grid.cas-filter-inline .cas-ff-check {
        margin-left: 0;
    }

    .cas-filter-grid.cas-filter-2col > .cas-ff-col {
        flex-basis: 100%;
        width: 100%;
    }
}

/* =============================================================================
   MODAL FORM LAYOUT  (.cas-modal-form)
   =============================================================================

   For long upsert modals whose fields are grouped into labelled sections and
   laid out two or three across. Deliberately NOT a new grid: `.cas-modal-form`
   goes ON the existing Bootstrap `row g-3` element, so the columns stay the
   ordinary `col-12 col-md-6 col-lg-4` the rest of the app already uses and only
   the things Bootstrap does not give us are added here.

   Markup contract:
     <div class="row g-3 cas-modal-form">
         <div class="col-12"><h6 class="cas-modal-section">Address</h6></div>
         <div class="col-12 col-md-6"><label>City</label> ... </div>
     </div>

   Global (not scoped .razor.css) for the same reason `.cas-filter-grid` is:
   typeaheads and InputCasSmartDate render their own markup from other
   components, so scoped rules would need ::deep on every line.
   ========================================================================== */

.cas-modal-form > [class*="col-"] > label {
    display: block;
    margin-bottom: 0.25rem;
}

/* Section heading. Sits in its own full-width column and rules off the group
   below it. */
.cas-modal-form .cas-modal-section {
    margin: 0.75rem 0 0;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--default-border);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #6c757d;
}

/* The first section already has the modal-body's own padding above it. */
.cas-modal-form > [class*="col-"]:first-child > .cas-modal-section {
    margin-top: 0;
}

/* Checkboxes carry their own inline label, so they get no label row above.
   This lines their box up with the inputs on the same grid line instead of
   letting them float at the top of a taller cell. */
.cas-modal-form .cas-ff-check {
    display: flex;
    align-items: center;
    /* Matches the rendered height of a Bootstrap .form-control plus its label. */
    min-height: calc(1.5em + 0.75rem + 2px);
}

    .cas-modal-form .cas-ff-check .form-check {
        margin-bottom: 0;
    }

/* Typeahead and InputCasSmartDate both render a Bootstrap .input-group with a
   trailing toggle button. Without these the button wraps onto its own line once
   a cell is only a third of the dialog wide. Same fix as .cas-filter-grid. */
.cas-modal-form .input-group {
    flex-wrap: nowrap;
}

    .cas-modal-form .input-group > .form-control {
        flex: 1 1 auto;
        width: 1%;
        min-width: 0;
    }

/* Long option text wraps instead of being clipped by the narrower input the
   menu hangs from. */
.cas-modal-form .dropdown-menu .dropdown-item,
.cas-modal-form .dropdown-menu .dropdown-item-text {
    white-space: normal;
    word-break: break-word;
    overflow-wrap: anywhere;
    line-height: 1.25;
}

/* Placeholders such as "-SELECT/TYPE CATEGORY CODE-" have to fit the cell. */
.cas-modal-form .input-group > .form-control::placeholder {
    font-size: 0.78rem;
    letter-spacing: -0.01em;
}

/* Dark mode. Specificity here (attribute + two classes) deliberately beats the
   blanket `[data-theme-mode=dark] .modal-content h6 { color: #fff }` rule in the
   DARK MODE OVERRIDES block below, which would otherwise flatten the heading
   into the same white as the field labels and lose the grouping. */
[data-theme-mode=dark] .modal-content .cas-modal-section {
    color: #9aa5bd !important;
    border-bottom-color: #2d3a5a !important;
}

/* =============================================================================
   Help popover — generic (`<CasHelp>`)
   A "?" next to a label that reveals an explanation. Built as markup rather
   than bootstrap.Popover because nothing in this app initializes Bootstrap's
   JS popovers, and doing so on a Server circuit costs an interop call per
   instance plus a disposal path.

   Colors come from the tab variables defined above, so dark mode needs no
   extra rules here.
   ============================================================================= */

.cas-help {
    position: relative;
    display: inline-flex;
}

.cas-help-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    border: 1px solid var(--text-muted);
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
}

.cas-help-btn:hover,
.cas-help-btn[aria-expanded="true"] {
    border-color: var(--cas-tab-accent);
    background: var(--cas-tab-active-bg);
    color: var(--cas-tab-accent);
}

.cas-help-btn:focus-visible {
    outline: 0;
    box-shadow: 0 0 0 3px var(--cas-tab-active-bg);
}

/* Opens upward: the trigger sits in a toolbar at the top of a scrolling pane,
   so a downward panel would be clipped by the pane's own overflow. */
.cas-help-pop {
    position: absolute;
    left: 0;
    bottom: calc(100% + 8px);
    z-index: 20;
    width: 330px;
    max-width: min(330px, calc(100vw - 2rem));
    padding: 12px 14px;
    border: 1px solid var(--default-border);
    border-radius: 8px;
    background: var(--custom-white);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .14);
    color: var(--default-text-color);
    font-size: 12.5px;
    font-weight: 400;
    line-height: 1.5;
    text-align: left;
    white-space: normal;
}

.cas-help-pop--end {
    left: auto;
    right: 0;
}

.cas-help-pop b {
    display: block;
    margin-bottom: 5px;
}

.cas-help-pop ul {
    margin: 6px 0 0;
    padding-left: 16px;
}

.cas-help-pop li {
    margin-bottom: 3px;
}

.cas-help-muted {
    color: var(--text-muted);
}

/* =============================================================================
   Record history timeline — generic (`<RecordHistoryPanel>`)
   The History Audit tab. Not tied to batches: the same markup renders the
   trail for any record type that has a history map.

   Every color is an existing theme variable or derived from --cas-tab-accent,
   so the panel follows light/dark automatically. The three status colors are
   the exception and carry explicit dark values, because the light greens and
   reds fail contrast on the dark surface.
   ============================================================================= */

:root {
    --cas-hist-rail: #dfe5ee;
    --cas-hist-add: #1a7f52;
    --cas-hist-del: #b23c3c;
    --cas-hist-chip-bg: #eef1f7;
}

[data-theme-mode=dark] {
    --cas-hist-rail: rgba(255, 255, 255, .14);
    --cas-hist-add: #5ed4a0;
    --cas-hist-del: #ef8a8a;
    --cas-hist-chip-bg: rgba(255, 255, 255, .07);
}

.cas-hist-toolbar {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.cas-hist-search {
    position: relative;
    flex: 1 1 340px;
    min-width: 240px;
}

.cas-hist-search .form-control {
    padding-left: 34px;
}

.cas-hist-search > i {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    opacity: .5;
    pointer-events: none;
}

.cas-hist-count {
    color: var(--text-muted);
    font-size: 12.5px;
    white-space: nowrap;
}

.cas-hist-filters {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.cas-hist-chip {
    padding: 4px 12px;
    border: 1px solid var(--default-border);
    border-radius: 999px;
    background: transparent;
    color: var(--cas-tab-color);
    font-size: 12.5px;
    cursor: pointer;
}

.cas-hist-chip.active {
    border-color: transparent;
    background: var(--cas-tab-active-bg);
    color: var(--cas-tab-accent);
    font-weight: 600;
}

.cas-hist-chip:focus-visible {
    outline: 0;
    box-shadow: 0 0 0 3px var(--cas-tab-active-bg);
}

.cas-hist-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12.5px;
}

.cas-hist-toggle-label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    cursor: pointer;
}

/* Fixed-height scroll rather than paging: the point of the view is one
   continuous timeline, and a pager breaks that reading. */
.cas-hist-timeline {
    position: relative;
    max-height: 560px;
    overflow-y: auto;
    margin: 0;
    padding: 4px 4px 0 0;
    list-style: none;
}

.cas-hist-timeline::before {
    content: "";
    position: absolute;
    left: 7px;
    top: 14px;
    bottom: 10px;
    width: 2px;
    background: var(--cas-hist-rail);
}

.cas-hist-event {
    position: relative;
    padding: 0 0 22px 30px;
}

.cas-hist-event:last-child {
    padding-bottom: 4px;
}

.cas-hist-dot {
    position: absolute;
    left: 0;
    top: 4px;
    width: 16px;
    height: 16px;
    border: 2px solid var(--cas-hist-rail);
    border-radius: 50%;
    background: var(--custom-white);
}

.cas-hist-op-create .cas-hist-dot { border-color: var(--cas-hist-add); }
.cas-hist-op-delete .cas-hist-dot { border-color: var(--cas-hist-del); }
.cas-hist-op-update .cas-hist-dot { border-color: var(--cas-tab-accent); }

/* System-only events are opt-in and carry no change of their own, so they read
   as muted: a hollow dot on the rail color and dimmed text. They must remain
   legible — an auditor turned them on deliberately — just never compete with
   the events that record a real change. */
.cas-hist-op-system .cas-hist-dot { border-color: var(--cas-hist-rail); }

.cas-hist-op-system .cas-hist-title,
.cas-hist-op-system .cas-hist-scope {
    color: var(--text-muted);
    font-weight: 500;
}

.cas-hist-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.cas-hist-title {
    font-size: 13.5px;
    font-weight: 600;
}

.cas-hist-scope,
.cas-hist-user {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--cas-hist-chip-bg);
    color: var(--cas-tab-color);
    font-size: 11.5px;
    font-weight: 500;
}

.cas-hist-user {
    background: var(--cas-tab-active-bg);
    color: var(--cas-tab-accent);
}

.cas-hist-when {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12px;
    white-space: nowrap;
}

/* Two tracks so the field labels line up down the event, which is what makes
   a multi-field change scannable. */
.cas-hist-changes {
    display: grid;
    grid-template-columns: minmax(120px, 190px) 1fr;
    gap: 4px 16px;
    margin-top: 8px;
}

.cas-hist-field {
    padding-top: 1px;
    color: var(--text-muted);
    font-size: 12.5px;
    font-weight: 600;
}

.cas-hist-values {
    font-size: 13px;
    overflow-wrap: anywhere;
}

.cas-hist-old {
    color: var(--text-muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.cas-hist-arrow {
    margin: 0 6px;
    color: var(--text-muted);
}

.cas-hist-new {
    font-weight: 500;
}

.cas-hist-empty {
    color: var(--text-muted);
    font-style: italic;
    text-decoration: none;
}

.cas-hist-note,
.cas-hist-footer {
    margin-top: 6px;
    color: var(--text-muted);
    font-size: 12.5px;
}

.cas-hist-footer {
    padding-top: 10px;
    border-top: 1px solid var(--default-border);
}

.cas-hist-none {
    padding: 28px 0;
    color: var(--text-muted);
    font-size: 13.5px;
    text-align: center;
}

/* Single column under ~576px: the label track cannot hold a field name and
   leave room for a value at that width. */
@media (max-width: 575.98px) {
    .cas-hist-changes {
        grid-template-columns: 1fr;
        gap: 1px 0;
    }

    .cas-hist-field {
        margin-top: 6px;
    }

    .cas-hist-when {
        margin-left: 0;
        width: 100%;
    }
}

/* ---------------------------------------------------------------------------
   .cas-setup-form — the compact "Save and setup" create form shared by the
   transaction upsert screens (Batch, Transfer, Disbursement, Credit, Reclass).

   A two-column grid: the label track is sized to `max-content`, so it is
   exactly as wide as the longest caption on that form and never pads the row
   with dead space, and the control track is capped so inputs stay compact
   instead of stretching to the card width. Fields stack one per row, flush
   against the card's left edge.
   --------------------------------------------------------------------------- */
.cas-setup-form {
    display: grid;
    grid-template-columns: max-content minmax(0, 22rem);
    column-gap: 0.75rem;
    row-gap: 0.5rem;
    align-items: center;
}

.cas-setup-form > label {
    margin-bottom: 0;
    white-space: nowrap;
}

/* Opt-in for a label paired with a multi-line control (e.g. a Notes
   textarea) — keeps the label pinned to the control's first line instead of
   being centered against its full height. */
.cas-setup-form > label.cas-setup-form-label-top {
    align-self: start;
    padding-top: .375rem;
}

/* Single column under ~576px: the label track and a usable input do not fit
   side by side at that width. */
@media (max-width: 575.98px) {
    .cas-setup-form {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 0.25rem;
    }
}

/* ---------------------------------------------------------------------------
   .cas-doc-link — hyperlink styling for document file-name / action links in
   the document grids.

   The Spruko theme resets every bare anchor in wwwroot/css/styles.css:

       a { text-decoration: none; color: rgba(var(--dark-rgb), ...); }

   which renders the Download column's file name as plain black text with no
   underline — it does not read as clickable. The legacy ASP.NET MVC app showed
   these as conventional blue underlined links, so this class restores that
   appearance. `!important` is required to win over the theme's bare-`a` rule,
   which lands later in the cascade.
   --------------------------------------------------------------------------- */
.cas-doc-link {
    color: #0d6efd !important;
    text-decoration: underline !important;
    text-underline-offset: 0.15em;
    cursor: pointer;
}

.cas-doc-link:hover,
.cas-doc-link:focus {
    color: #0a58ca !important;
    text-decoration: underline !important;
}

/* Dark chrome needs a lighter blue to stay legible against the dark row
   background. */
[data-theme-mode=dark] .cas-doc-link {
    color: #6ea8fe !important;
}

[data-theme-mode=dark] .cas-doc-link:hover,
[data-theme-mode=dark] .cas-doc-link:focus {
    color: #8bb9fe !important;
}

/* ---------------------------------------------------------------------------
   .cas-phone — InputPhoneNumber: country-code selector inside the left of the
   field, masked national number on the right.

   The country button is an .input-group-text so it shares the input's border
   and background exactly like the dial-code label on InputPhone. The
   dropdown and its click-away backdrop sit OUTSIDE the .input-group (siblings
   under .cas-phone) on purpose: Bootstrap's input-group rounding rules key
   off :first-child / :last-child, and extra children inside the group would
   strip the input's right-hand radius.
   --------------------------------------------------------------------------- */
.cas-phone {
    position: relative;
}

.cas-phone .cas-phone-country {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    white-space: nowrap;
    cursor: pointer;
    font-size: 0.8125rem;
    line-height: 1;
}

.cas-phone .cas-phone-country:disabled {
    cursor: default;
    opacity: 0.65;
}

.cas-phone .cas-phone-country.dropdown-toggle::after {
    margin-left: 0.1rem;
    opacity: 0.6;
}

.cas-phone-iso {
    font-weight: 600;
    letter-spacing: 0.3px;
}

.cas-phone-dial {
    color: var(--text-muted);
}

.cas-phone-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1049;
    background: transparent;
}

.cas-phone-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1050;
    min-width: 17rem;
    max-height: 18rem;
    margin-top: 0.125rem;
    padding-top: 0;
}

.cas-phone-search {
    position: sticky;
    top: 0;
    padding: 0.5rem 0.5rem 0.375rem;
    background: inherit;
}

.cas-phone-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
}

.cas-phone-menu .dropdown-item .cas-phone-iso {
    width: 1.75rem;
}

.cas-phone-menu .dropdown-item .cas-phone-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cas-phone-menu .dropdown-item.active .cas-phone-dial {
    color: inherit;
    opacity: 0.85;
}
