/* =====================================================================
   appTable — macOS "Activity Monitor" list restyle
   ---------------------------------------------------------------------
   A skin-only layer for every table rendered by $.fn.appTable
   (assets/js/app.js -> $.fn.appTable). Scoped to `.dt-container`, the
   wrapper DataTables 2.x creates, so it can only ever touch appTable
   tables — never a plain <table class="table"> elsewhere in the app.

   The model is macOS Sonoma/Sequoia Activity Monitor:
     * rounded alternating row stripes, inset from the table edge
     * NO horizontal rules between rows — the stripes do the separating
     * sentence-case header (not uppercase), thin vertical column dividers
     * a single chevron on the sorted column only
     * tabular figures so numeric columns line up
     * frosted segmented control for the export buttons

   Which tables get the stripe treatment
   -------------------------------------
   Only "list" tables: `.dt-container table.dataTable` that is NOT
   `.no-thead` and NOT `.table-bordered`. The `no-thead b-t b-b-only`
   variants are embedded detail/summary tables (invoice line items,
   totals) that own their own borders — they keep the app's base look and
   only inherit typography. `.no-hover` opts a table out of row hover,
   matching the existing app convention.

   Cascade notes
   -------------
   * Loaded last in includes/head.php, after app.all.css + custom-style.css.
   * The dark theme (assets/css/color/1E202D.css) is injected into <head>
     at RUNTIME by setThemeColor() in general_helper.js, so it always wins
     ties against this file. Every dark rule below therefore needs either
     !important or extra specificity — that is deliberate, not cargo cult.
   * Pure CSS. No change to app.js / app.min.js / app.all.js, so the
     bundle does not need regenerating. Remove the one line in head.php
     to revert the whole skin.

   Known limitation
   ----------------
   Stripes come from :nth-child, because DataTables 2.x adds no odd/even
   classes in this build. In responsive (mobile) mode an expanded
   `tr.child` detail row counts as a child and offsets the alternation
   below it until collapsed. Cosmetic, mobile-only, accepted.
   ===================================================================== */

.dt-container {
    /* geometry */
    --dtg-radius: 12px;
    --dtg-radius-sm: 9px;
    --dtg-stripe-radius: 7px;

    /* ink — macOS label / secondary label */
    --dtg-ink: #1d1d1f;
    --dtg-ink-2: #35353a;
    --dtg-ink-dim: #6e6e73;

    /* accent — focus rings, sorted column, selected row */
    --dtg-accent: #007aff;
    --dtg-accent-soft: rgba(0, 122, 255, 0.10);
    --dtg-accent-ring: rgba(0, 122, 255, 0.22);

    /* surfaces */
    --dtg-hairline: rgba(0, 0, 0, 0.075);
    --dtg-hairline-strong: rgba(0, 0, 0, 0.11);
    --dtg-divider: rgba(0, 0, 0, 0.09);
    --dtg-field-solid: #ffffff;
    --dtg-field: rgba(255, 255, 255, 0.72);
    --dtg-stripe: rgba(0, 0, 0, 0.032);
    --dtg-hover: rgba(0, 0, 0, 0.062);

    /* OPAQUE equivalents — sticky/frozen cells scroll over other cells, so
       they cannot use the translucent stripe/hover values above or the
       content underneath shows through. Keep these in sync with them. */
    --dtg-surface: #ffffff;
    --dtg-stripe-solid: #f7f7f8;
    --dtg-hover-solid: #f0f0f2;
    --dtg-selected-solid: #e6f0fe;
    --dtg-freeze-shadow: -10px 0 14px -10px rgba(0, 0, 0, 0.28);
    --dtg-sticky-shadow: 0 1px 0 rgba(0, 0, 0, 0.10);

    /* iOS loading HUD + activity-indicator grey */
    --dtg-hud: rgba(255, 255, 255, 0.97);
    --dtg-spinner: #8a8a8e;

    /* CSS FALLBACK ONLY — apptable-sticky.js measures the real offset at
       runtime and sets an inline max-height, because no fixed number can
       know how much chrome sits above a given table. This value is a
       conservative backstop for when that JS does not run: the scroll box
       now starts ~197px down (the filter bar moved above it), plus room for
       the pagination bar below. Too small a value here makes the box
       overhang the viewport and the sticky header slide under the navbar. */
    --dtg-viewport-offset: 250px;
    --dtg-blur: saturate(170%) blur(18px);

    /* elevation */
    --dtg-shadow-field: 0 1px 2px rgba(16, 24, 40, 0.05);
    --dtg-shadow-pop: 0 8px 22px -14px rgba(16, 24, 40, 0.35);

    /* rhythm — cell-x is deliberately modest: with single-line (nowrap)
       rows the inline padding is paid on every column, so 13px pushed the
       invoices list 32px past its container and clipped the action button
       behind the horizontal scroller. 11px keeps it inside. */
    --dtg-cell-y: 8px;
    --dtg-cell-x: 11px;
}

/* ============================================================ TOOLBAR */
/* The row above the table: export buttons, search, filter chips.
   Built by the custom sDom in app.js (~line 4365). */

.dt-container .filter-section-container {
    padding: 2px 0 10px;
}

.dt-container .filter-section-flex-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.dt-container .filter-section-right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

.dt-container .filter-item-box {
    margin: 0;
}

/* ------------------------------------------------------------- SEARCH */

.dt-container .dt-search {
    margin: 0;
}

.dt-container .dt-search input,
.datatable-search,
.custom-filter-search {
    height: 32px;
    padding: 5px 32px 5px 12px;
    border: 1px solid var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11));
    border-radius: var(--dtg-radius-sm, 9px);
    background-color: var(--dtg-field-solid, #fff);
    /* crisp vector magnifier replaces the low-res search.png */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236e6e73' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='6.9' cy='6.9' r='4.6'/%3E%3Cpath d='M10.4 10.4 14 14'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 15px 15px;
    box-shadow: var(--dtg-shadow-field, 0 1px 2px rgba(16, 24, 40, 0.05));
    font-size: 13px;
    color: var(--dtg-ink, #1d1d1f);
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.dt-container .dt-search input::placeholder,
.datatable-search::placeholder,
.custom-filter-search::placeholder {
    color: var(--dtg-ink-dim, #6e6e73);
}

.dt-container .dt-search input:focus,
.datatable-search:focus,
.custom-filter-search:focus {
    border-color: var(--dtg-accent, #007aff);
    background-color: var(--dtg-field-solid, #fff);
    box-shadow: 0 0 0 3px var(--dtg-accent-ring, rgba(0, 122, 255, 0.22));
    outline: none;
}

/* ---------------------------------------------- EXPORT / PRINT BUTTONS */
/* Rendered as a segmented control, like the CPU/Memory/Energy picker. */

.dt-container .dt-buttons {
    display: inline-flex;
    align-items: center;
    padding: 0;
    border-radius: var(--dtg-radius-sm, 9px);
    background: var(--dtg-field, rgba(255, 255, 255, 0.72));
    -webkit-backdrop-filter: var(--dtg-blur);
    backdrop-filter: var(--dtg-blur);
    box-shadow:
        inset 0 0 0 1px var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11)),
        var(--dtg-shadow-field, 0 1px 2px rgba(16, 24, 40, 0.05));
    overflow: hidden;
}

.dt-container .dt-buttons .btn,
.dt-container .datatable-export a {
    height: 32px;
    display: inline-flex;
    align-items: center;
    margin: 0;
    padding: 0 12px;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none !important;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--dtg-ink-2, #35353a);
    transition: background-color 0.16s ease, color 0.16s ease;
}

/* hairline divider between segments */
.dt-container .dt-buttons .btn + .btn,
.dt-container .datatable-export a + a {
    box-shadow: inset 1px 0 0 var(--dtg-hairline, rgba(0, 0, 0, 0.075)) !important;
}

.dt-container .dt-buttons .btn:hover,
.dt-container .datatable-export a:hover {
    background: rgba(0, 0, 0, 0.045);
    color: var(--dtg-ink, #1d1d1f);
}

/* ------------------------------------------------- FILTER CHIPS (select2) */

.dt-container .filter-section-container .select2-container .select2-choice,
.dt-container .datatable-tools .select2-container .select2-choice {
    height: 32px;
    display: flex;
    align-items: center;
    border: 1px solid var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11)) !important;
    border-radius: var(--dtg-radius-sm, 9px);
    background: var(--dtg-field-solid, #fff) !important;
    box-shadow: var(--dtg-shadow-field, 0 1px 2px rgba(16, 24, 40, 0.05));
    font-size: 13px;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.dt-container .filter-section-container .select2-container.select2-container-active .select2-choice {
    border-color: var(--dtg-accent, #007aff) !important;
    box-shadow: 0 0 0 3px var(--dtg-accent-ring, rgba(0, 122, 255, 0.22));
}

/* ============================================================== TABLE */

.dt-container table.dataTable {
    border-bottom: 0;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    font-size: 13px;
}

/* Rounded stripes need separate borders — collapse ignores cell radii.
   Confined to list tables so the bordered line-item tables are untouched. */
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) {
    border-collapse: separate;
    border-spacing: 0;
}

/* ---------------------------------------------------------------- HEAD */
/* Sentence case, thin vertical dividers, one hairline underneath. */

.dt-container table.dataTable thead th,
.dt-container table.dataTable thead td {
    /* `!important` required: _plugin.scss sets
       `table.dataTable thead th { padding: 15px 20px 15px 10px !important }`
       (plus a 15px first-child variant), which otherwise forces a 51px-tall
       header. Matching the body's 13px inline padding keeps the header
       labels aligned with their column's cell text. */
    padding: 7px var(--dtg-cell-x, 13px) !important;
    border-top: 0 !important;
    border-bottom: 1px solid var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11)) !important;
    background: var(--dtg-surface, #fff);
    font-size: 13px;
    /* macOS header labels always read heavier than the row text (400) */
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: var(--dtg-ink-2, #35353a);
    white-space: nowrap;
}

/* vertical column dividers, as in the Activity Monitor header */
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) thead th:not(:last-child) {
    border-right: 1px solid var(--dtg-divider, rgba(0, 0, 0, 0.09));
}

/* Restore the gutter the sort chevron sits in. DataTables' own
   `padding-right: 30px` for sortable headers is non-important, so the
   `!important` padding above would otherwise wipe it out and let the
   chevron collide with the label. */
.dt-container table.dataTable thead th.dt-orderable-asc,
.dt-container table.dataTable thead th.dt-orderable-desc,
.dt-container table.dataTable thead th.dt-ordering-asc,
.dt-container table.dataTable thead th.dt-ordering-desc {
    padding-right: 26px !important;
}

.dt-container table.dataTable thead th.dt-orderable-asc:hover,
.dt-container table.dataTable thead th.dt-orderable-desc:hover {
    color: var(--dtg-ink, #1d1d1f);
}

/* ------------------------------------------------- SORT INDICATOR */
/* DataTables 2.x renders <span class="dt-column-order"> with ▲ / ▼
   glyphs. macOS shows ONE chevron, on the sorted column only, vertically
   centred — so both pseudo-elements are stacked in the middle and only
   the one matching the sort direction is faded in.

   Drawn with mask-image + currentColor rather than CSS borders: a
   border-drawn chevron reads as a diagonal arrow at this size, and
   Chrome rounds fractional border-widths down to 1px at DPR 1. The mask
   keeps the true macOS chevron shape and inherits the header's colour in
   both themes. */

.dt-container table.dataTable thead th .dt-column-order::before,
.dt-container table.dataTable thead th .dt-column-order::after {
    content: "" !important;
    position: absolute;
    display: block;
    /* `!important` throughout: _plugin.scss already masks the app's own
       STEMMED arrow-up.svg / arrow-down.svg onto these same pseudo-elements
       at specificity (0,4,3), which out-ranks this rule's (0,3,3). Without
       it you get a Feather-style ↓ arrow instead of a macOS chevron. */
    top: 50% !important;
    bottom: auto !important;
    left: 50% !important;
    width: 11px !important;
    height: 11px !important;
    border: 0;
    transform: translate(-50%, -50%) !important;
    background-color: currentColor !important;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: 11px 11px !important;
    mask-size: 11px 11px !important;
    /* `!important` required: DataTables' own `span.dt-column-order:after
       { opacity: .125 }` out-specifies this, which would leave a ghost
       chevron of the opposite direction stacked under the active one. */
    opacity: 0 !important;
    transition: opacity 0.16s ease;
}

/* chevron up = ascending */
.dt-container table.dataTable thead th .dt-column-order::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.75 7.25 6 4l3.25 3.25' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.75 7.25 6 4l3.25 3.25' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
}

/* chevron down = descending */
.dt-container table.dataTable thead th .dt-column-order::after {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.75 4.75 6 8l3.25-3.25' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.75 4.75 6 8l3.25-3.25' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
}

/* Faint affordance when hovering an UNSORTED sortable column.
   The :not() guards matter — without them this rule (higher specificity)
   would drag an actively-sorted column's own chevron down to 0.35 while
   the pointer rests on it. Verified in-browser. */
.dt-container table.dataTable thead th.dt-orderable-asc:not(.dt-ordering-asc):not(.dt-ordering-desc):hover .dt-column-order::after,
.dt-container table.dataTable thead th.dt-orderable-desc:not(.dt-ordering-asc):not(.dt-ordering-desc):hover .dt-column-order::after {
    opacity: 0.35 !important;
}

.dt-container table.dataTable thead th.dt-ordering-asc .dt-column-order::before,
.dt-container table.dataTable thead th.dt-ordering-desc .dt-column-order::after {
    opacity: 1 !important;
    color: var(--dtg-ink, #1d1d1f);
}

/* the sorted ("selected") column's label is heavier and darker still —
   in macOS it is the strongest text in the header row */
.dt-container table.dataTable thead th.dt-ordering-asc,
.dt-container table.dataTable thead th.dt-ordering-desc {
    color: var(--dtg-ink, #1d1d1f);
    font-weight: 700;
}

/* ---------------------------------------------------------------- BODY */

.dt-container table.dataTable tbody th,
.dt-container table.dataTable tbody td {
    vertical-align: middle;
    color: var(--dtg-ink, #1d1d1f);
    line-height: 1.35;
}

/* --- single-line rows (the Activity Monitor row rhythm) ------------ */
/* Cells stay on one line, so every row is one text-line tall instead of
   growing to two or three. Columns size to their content and any excess
   width is taken by the existing `.table-responsive` horizontal scroller
   the list views already wrap the table in.

   NOTE: intentionally no `overflow:hidden` + `text-overflow:ellipsis`.
   True macOS truncation needs those, but `overflow:hidden` on a <td>
   CLIPS the Bootstrap dropdown in the action column — a functional
   regression. Scrolling instead of truncating also means no data is ever
   hidden from the user.
   Escape hatch: add `dt-wrap-cells` to a table to opt back into wrapping. */
.dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.dt-wrap-cells) tbody > tr:not(.child) > td,
.dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.dt-wrap-cells) tbody > tr:not(.child) > th {
    white-space: nowrap;
}

/* responsive detail rows stack their own label/value list — never nowrap */
.dt-container table.dataTable tbody > tr.child td,
.dt-container table.dataTable tbody > tr.child ul.dtr-details,
.dt-container table.dataTable tbody > tr.child ul.dtr-details > li {
    white-space: normal;
}

/* Compact Activity-Monitor rhythm — skipped for the line-item editors.
   `!important` is REQUIRED, not decorative: app.all.css ships
   `table.dataTable tbody th, table.dataTable tbody td { padding: 12px 10px !important }`,
   which beats this rule on cascade order alone. Verified in-browser. */
.dt-container table.dataTable:not(.no-padding) tbody td,
.dt-container table.dataTable:not(.no-padding) tbody th {
    padding: var(--dtg-cell-y, 9px) var(--dtg-cell-x, 13px) !important;
}

/* no rules between rows on list tables — the stripes separate them */
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody th,
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody td {
    border-top: 0 !important;
    border-bottom: 0 !important;
    transition: background-color 0.12s ease;
}

/* Keep icon-only action cells on one line. DataTables gives this column
   ~64px, which is too narrow for the row's two inline `.action-option`
   controls, so they wrapped onto a second line and set the height of the
   WHOLE row (measured: 65px rows driven by a 49px-tall action cell).
   Safe to nowrap because these cells contain icons, never prose.
   `:has()` degrades silently to no-op on browsers that lack it. */
.dt-container table.dataTable tbody td:has(> :is(.action-option, a.edit, a.delete)),
.dt-container table.dataTable tbody td:has(> .dropdown > .action-option) {
    white-space: nowrap;
}

/* --- the stripes ------------------------------------------------- */

.dt-container:not(.dtg-virtual) table.dataTable:not(.no-thead):not(.table-bordered) tbody tr:nth-child(even) > td,
.dt-container:not(.dtg-virtual) table.dataTable:not(.no-thead):not(.table-bordered) tbody tr:nth-child(even) > th {
    background-color: var(--dtg-stripe, rgba(0, 0, 0, 0.032));
}

/* rounded ends, inset from the table edge */
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody tr > td:first-child,
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody tr > th:first-child {
    border-top-left-radius: var(--dtg-stripe-radius, 7px);
    border-bottom-left-radius: var(--dtg-stripe-radius, 7px);
}

.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody tr > td:last-child,
.dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody tr > th:last-child {
    border-top-right-radius: var(--dtg-stripe-radius, 7px);
    border-bottom-right-radius: var(--dtg-stripe-radius, 7px);
}

/* --- hover / selection ------------------------------------------- */
/* `.no-hover` is the app's existing opt-out; `.js-selection-mode` owns
   its own row backgrounds during bulk select. */

.dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.no-hover):not(.js-selection-mode) tbody tr:hover > td,
.dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.no-hover):not(.js-selection-mode) tbody tr:hover > th {
    background-color: var(--dtg-hover, rgba(0, 0, 0, 0.062));
}

.dt-container table.dataTable:not(.js-selection-mode) tbody tr.active > td,
.dt-container table.dataTable:not(.js-selection-mode) tbody tr.active:hover > td {
    background-color: var(--dtg-accent-soft, rgba(0, 122, 255, 0.10));
}

/* ---------------------------------------------------------------- FOOT */

.dt-container table.dataTable tfoot th,
.dt-container table.dataTable tfoot td {
    padding: 10px var(--dtg-cell-x, 13px);
    border-top: 1px solid var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11)) !important;
    border-bottom: 0 !important;
    background: transparent;
    font-weight: 600;
    color: var(--dtg-ink, #1d1d1f);
}

/* empty state */
.dt-container table.dataTable td.dt-empty {
    padding: 34px var(--dtg-cell-x, 13px) !important;
    background: transparent !important;
    color: var(--dtg-ink-dim, #6e6e73);
    font-size: 13px;
}

/* ============================ STICKY HEADER / FOOTER + FROZEN COLUMN */
/* Only the ROWS scroll: the header pins to the top of the table box, the
   summation footer pins to the bottom, and an action-button column freezes
   to the right with a shadow so sideways-scrolling cells pass underneath.

   Why the wrapper needs a max-height
   ----------------------------------
   `body` is `overflow: hidden` in this app — the document never scrolls,
   an inner content area does. And `.table-responsive` sits between the
   table and that area as its OWN scroll box (`overflow: auto` on both
   axes, because `overflow-x: auto` forces `overflow-y` to compute to
   auto). A sticky <thead> therefore sticks relative to `.table-responsive`
   — which, being content-height, never scrolls, so sticky was a no-op.
   Bounding that wrapper makes it the real scroll container, which is also
   exactly what the frozen right column needs. One container, both effects.

   Matched on `.table-responsive` that actually contains a `.dt-container`,
   so no non-appTable responsive wrapper is affected. NOT scoped to
   `.card >` — /expenses puts its wrapper outside the card and would
   silently miss out on a sticky header.
   `position: sticky` on table cells requires `border-collapse: separate`,
   already set on list tables above. */

/* Fallback for any appTable whose table the JS did not wrap (e.g. it ran
   before apptable-sticky.js loaded): the outer wrapper still scrolls, so
   the sticky header keeps working — the toolbars just move with it. */
.table-responsive:has(> .dt-container):not(.dtg-hscroll-host) {
    max-height: calc(100vh - var(--dtg-viewport-offset, 172px));
    overflow: auto;
}

/* The compact view sets its own height on this same wrapper; the rule
   above out-specifies it, so re-assert it here (later + equal specificity). */
.compact-view-left-panel .scrollable-table:has(> .dt-container),
.compact-view-left-panel .dtg-hscroll {
    height: calc(100vh - 185px);
    max-height: none;
}

/* --- the scroll container --------------------------------------- */
/* `.dtg-hscroll` is created by assets/js/apptable-sticky.js, which wraps
   ONLY the <table> — leaving the filter bar and the pagination bar outside
   it so they never move when you scroll sideways. Scrolling is handed over
   from `.table-responsive` (marked `.dtg-hscroll-host`) to this element.

   A CSS-only version is impossible here: the toolbars are siblings of the
   table inside `.dt-container`, and `position: sticky; left: 0` on them is
   inert because their containing block is only the scrollport's width, so
   it scrolls away wholesale and takes them with it. Measured, not assumed. */

.table-responsive.dtg-hscroll-host {
    overflow: visible;
    max-height: none;
}

.dtg-hscroll {
    max-height: calc(100vh - var(--dtg-viewport-offset, 172px));
    overflow: auto;
    /* Keep the wheel inside this box. Without it the gesture can chain out to
       the page scroller (PerfectScrollbar is attached to an ancestor), which
       is what makes the wheel feel dead over the rows until you drag the
       scrollbar once. Pairs with the `ps__child--consume` class that
       apptable-sticky.js puts on this element. */
    overscroll-behavior: contain;
}

/* --- header pinned to the top ------------------------------------- */
.dtg-hscroll table.dataTable:not(.no-thead) > thead > tr > th,
.dtg-hscroll table.dataTable:not(.no-thead) > thead > tr > td,
.table-responsive:has(> .dt-container) table.dataTable:not(.no-thead) > thead > tr > th,
.table-responsive:has(> .dt-container) table.dataTable:not(.no-thead) > thead > tr > td {
    position: sticky;
    top: 0;
    z-index: 3;
    /* must be opaque — rows scroll underneath it */
    background: var(--dtg-surface, #fff);
    box-shadow: var(--dtg-sticky-shadow, 0 1px 0 rgba(0, 0, 0, 0.10));
}

/* --- summation footer pinned to the bottom ------------------------- */
.dtg-hscroll table.dataTable > tfoot > tr > th,
.dtg-hscroll table.dataTable > tfoot > tr > td,
.table-responsive:has(> .dt-container) table.dataTable > tfoot > tr > th,
.table-responsive:has(> .dt-container) table.dataTable > tfoot > tr > td {
    position: sticky;
    bottom: 0;
    z-index: 3;
    background: var(--dtg-surface, #fff);
    box-shadow: 0 -1px 0 var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11));
}

/* --- action column frozen to the right ----------------------------- */
/* Applied only to tables whose LAST body cell actually holds action
   controls, so ordinary trailing data columns are never frozen.

   The guard must match ALL THREE action-cell conventions in this codebase
   — matching only `.action-option` silently skipped /tasks/all_tasks and
   /expenses, which was the bug that surfaced this:
     `a.action-option` + dropdown  → invoices, tickets, estimates
     `a.edit` + `a.delete`         → tasks, expenses
     `a.delete` only               → team_members
   The frozen cells need solid backgrounds for the same reason as the
   header: translucent stripes would let scrolled cells show through. */

.dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > thead > tr > th:last-child {
    position: sticky;
    right: 0;
    z-index: 4;
    background: var(--dtg-surface, #fff);
    box-shadow: var(--dtg-freeze-shadow, -10px 0 14px -10px rgba(0, 0, 0, 0.28));
}

.dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr > td:last-child {
    position: sticky;
    right: 0;
    z-index: 2;
    background: var(--dtg-surface, #fff);
    box-shadow: var(--dtg-freeze-shadow, -10px 0 14px -10px rgba(0, 0, 0, 0.28));
}

/* keep the frozen cell in step with the stripe / hover / selected states */
.dt-container:not(.dtg-virtual) table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr:nth-child(even) > td:last-child {
    background: var(--dtg-stripe-solid, #f7f7f8);
}

.dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)):not(.no-hover):not(.js-selection-mode) > tbody > tr:hover > td:last-child {
    background: var(--dtg-hover-solid, #f0f0f2);
}

/* A SELECTED row's frozen cell must follow the row, not stay white.
   Without this the last column keeps its opaque surface/stripe colour
   while the rest of the row highlights — the frozen cell needs an opaque
   equivalent of the translucent `--dtg-accent-soft` used on `tr.active`. */
.dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr.active > td:last-child,
.dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr.active:hover > td:last-child {
    background: var(--dtg-selected-solid, #e6f0fe);
}

/* --- an OPEN row menu must win the stacking contest ---------------- */
/* `position: sticky` + `z-index` makes every frozen cell its own stacking
   context, so a dropdown opened inside one is trapped in that context. The
   frozen cells of the rows BELOW carry the same z-index and come later in
   the DOM, so they paint over the open menu and swallow its text.
   Lifting the cell only while its menu is open puts the menu above the
   other frozen cells (2), the sticky header (3) and its frozen corner (5),
   without disturbing the normal stacking order the rest of the time.
   `.show` is Bootstrap's own open-state class, so this needs no JS.

   `!important` is load-bearing here: the frozen-cell rule's own
   `:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete))`
   argument carries MORE specificity than `:has(.dropdown-menu.show)`, so
   without it `z-index: 2` wins and the lift silently does nothing
   (measured: the cell stayed at 2). */
.dt-container table.dataTable > tbody > tr > td:has(.dropdown-menu.show),
.dt-container table.dataTable > tbody > tr > td:has(> .dropdown.show) {
    z-index: 40 !important;
}

/* and the menu itself above anything inside that cell */
.dt-container table.dataTable > tbody > tr > td .dropdown-menu.show {
    z-index: 41;
}

/* the header's frozen corner sits above both sticky axes */
.dtg-hscroll table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > thead > tr > th:last-child,
.table-responsive:has(> .dt-container) table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > thead > tr > th:last-child {
    z-index: 5;
    box-shadow:
        var(--dtg-freeze-shadow, -10px 0 14px -10px rgba(0, 0, 0, 0.28)),
        var(--dtg-sticky-shadow, 0 1px 0 rgba(0, 0, 0, 0.10));
}

/* ====================================================== BOTTOM TOOLBAR */

.dt-container .datatable-tools {
    align-items: center;
    padding-top: 4px;
}

.dt-container .dt-info {
    padding-top: 8px;
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    color: var(--dtg-ink-dim, #6e6e73) !important;
    opacity: 1;
}

.dt-container .summation-section {
    font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------- PAGINATION */

.dt-container .dt-paging .pagination {
    gap: 3px;
}

.dt-container .dt-paging .dt-paging-button {
    background: transparent !important;
    border-radius: var(--dtg-radius-sm, 9px);
}

.dt-container .dt-paging .dt-paging-button .page-link {
    min-width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 9px;
    border: 1px solid transparent;
    border-radius: var(--dtg-radius-sm, 9px);
    background: transparent;
    font-size: 12.5px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--dtg-ink-2, #35353a) !important;
    transition: background-color 0.16s ease, color 0.16s ease;
}

.dt-container .dt-paging .dt-paging-button .page-link:hover {
    border-color: transparent;
    background: rgba(0, 0, 0, 0.05);
}

.dt-container .dt-paging .dt-paging-button.active .page-link,
.dt-container .dt-paging .dt-paging-button.active .page-link:hover {
    border-color: transparent;
    background: var(--dtg-accent-soft, rgba(0, 122, 255, 0.10));
    color: var(--dtg-accent, #007aff) !important;
    font-weight: 600;
}

.dt-container .dt-paging .dt-paging-button.disabled .page-link {
    opacity: 0.35;
}

/* ---------------------------------------------- PROCESSING (iOS spinner) */
/* Replaces DataTables' bouncing 3-dot ellipsis with the iOS activity
   indicator: 12 tapered grey spokes of decreasing opacity, rotated in
   discrete steps.

   Live markup is:
     <div class="dt-processing card" role="status">
       <div><div></div><div></div><div></div><div></div></div>
     </div>
   — one wrapper holding the 4 dot divs, and NO text node.

   The spokes are one SVG used as a MASK over `currentColor`, so the fade
   is carried by the mask's alpha and the colour still follows the theme
   (one asset instead of a light and a dark PNG).
   Centred with absolute offsets rather than flex on purpose: DataTables
   toggles `style="display:block"` inline on the container, and that inline
   value would beat any `display:flex` set here. */

.dt-container .dt-processing {
    width: 54px !important;
    height: 54px !important;
    margin-left: -27px !important;
    margin-top: -27px !important;
    padding: 0 !important;
    border-radius: 14px;
    /* near-opaque on purpose: at 0.72 the row text read straight through
       the HUD and the spinner lost contrast */
    background: var(--dtg-hud, rgba(255, 255, 255, 0.94)) !important;
    -webkit-backdrop-filter: var(--dtg-blur);
    backdrop-filter: var(--dtg-blur);
    box-shadow: var(--dtg-shadow-pop, 0 8px 22px -14px rgba(16, 24, 40, 0.35));
    color: var(--dtg-spinner, #8a8a8e);
}

/* the dot container becomes the spinner */
.dt-container .dt-processing > div:last-child {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 24px !important;
    height: 24px !important;
    margin: -12px 0 0 -12px !important;
    background-color: currentColor;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='1.0' transform='rotate(0 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.85' transform='rotate(30 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.72' transform='rotate(60 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.62' transform='rotate(90 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.53' transform='rotate(120 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.45' transform='rotate(150 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.38' transform='rotate(180 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.32' transform='rotate(210 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.27' transform='rotate(240 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.22' transform='rotate(270 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.18' transform='rotate(300 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.14' transform='rotate(330 12 12)'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='1.0' transform='rotate(0 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.85' transform='rotate(30 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.72' transform='rotate(60 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.62' transform='rotate(90 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.53' transform='rotate(120 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.45' transform='rotate(150 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.38' transform='rotate(180 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.32' transform='rotate(210 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.27' transform='rotate(240 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.22' transform='rotate(270 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.18' transform='rotate(300 12 12)'/%3E%3Crect x='10.9' y='1.7' width='2.2' height='6.1' rx='1.1' fill='%23000' fill-opacity='0.14' transform='rotate(330 12 12)'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 24px 24px;
    mask-size: 24px 24px;
    animation: dtg-ios-spin 1s steps(12, end) infinite;
}

/* the four dots themselves are no longer drawn */
.dt-container .dt-processing > div:last-child > div {
    display: none !important;
}

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

/* ------------------------------------------------- RESPONSIVE CHILD ROWS */

.dt-container table.dataTable > tbody > tr.child ul.dtr-details {
    width: 100%;
}

.dt-container table.dataTable > tbody > tr.child ul.dtr-details > li {
    padding: 8px 2px;
    border-bottom: 1px solid var(--dtg-hairline, rgba(0, 0, 0, 0.075)) !important;
}

.dt-container table.dataTable > tbody > tr.child ul.dtr-details > li .dtr-title {
    font-weight: 600;
    color: var(--dtg-ink-dim, #6e6e73);
}

/* ================================================================= RTL */
/* Mirrors assets/css/rtl.css, which loads BEFORE this file. */

html[dir="rtl"] .dt-container .dt-search input,
html[dir="rtl"] .datatable-search,
html[dir="rtl"] .custom-filter-search,
body.rtl .dt-container .dt-search input,
body.rtl .datatable-search,
body.rtl .custom-filter-search {
    padding: 5px 12px 5px 32px;
    background-position: left 10px center;
}

html[dir="rtl"] .dt-container .filter-section-right,
body.rtl .dt-container .filter-section-right {
    margin-left: 0;
    margin-right: auto;
}

html[dir="rtl"] .dt-container table.dataTable thead th .dt-column-order,
body.rtl .dt-container table.dataTable thead th .dt-column-order {
    right: auto;
    left: 5px;
}

html[dir="rtl"] .dt-container table.dataTable:not(.no-thead):not(.table-bordered) thead th:not(:last-child),
body.rtl .dt-container table.dataTable:not(.no-thead):not(.table-bordered) thead th:not(:last-child) {
    border-right: 0;
    border-left: 1px solid var(--dtg-divider, rgba(0, 0, 0, 0.09));
}

/* ========================================================== DARK THEME */
/* body.color-1E202D — the app's only true dark theme. Its stylesheet is
   appended to <head> at runtime, so it wins ties: !important below is
   required, not decorative. */

body.color-1E202D .dt-container {
    --dtg-ink: #ccd0dd;
    --dtg-ink-2: #b3b9cc;
    --dtg-ink-dim: #7c8296;

    --dtg-accent: #5b8cff;
    --dtg-accent-soft: rgba(91, 140, 255, 0.18);
    --dtg-accent-ring: rgba(91, 140, 255, 0.28);

    --dtg-hairline: rgba(255, 255, 255, 0.07);
    --dtg-hairline-strong: rgba(255, 255, 255, 0.13);
    --dtg-divider: rgba(255, 255, 255, 0.10);
    --dtg-field: rgba(255, 255, 255, 0.05);
    --dtg-field-solid: #2b303b;
    --dtg-stripe: rgba(255, 255, 255, 0.042);
    --dtg-hover: rgba(255, 255, 255, 0.085);

    /* opaque equivalents for the sticky header/footer and frozen column.
       #252932 is the dark theme's own table cell background. */
    --dtg-surface: #252932;
    --dtg-stripe-solid: #2a2e38;
    --dtg-hover-solid: #2f3541;
    --dtg-selected-solid: #2b3550;
    --dtg-freeze-shadow: -10px 0 14px -10px rgba(0, 0, 0, 0.65);
    --dtg-sticky-shadow: 0 1px 0 rgba(255, 255, 255, 0.10);

    --dtg-hud: rgba(30, 34, 43, 0.97);
    --dtg-spinner: #9aa0b4;

    --dtg-shadow-field: 0 1px 2px rgba(0, 0, 0, 0.35);
    --dtg-shadow-pop: 0 10px 26px -14px rgba(0, 0, 0, 0.7);
}

body.color-1E202D .dt-container .dt-search input,
body.color-1E202D .datatable-search,
body.color-1E202D .custom-filter-search {
    background-color: #2b303b !important;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237c8296' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='6.9' cy='6.9' r='4.6'/%3E%3Cpath d='M10.4 10.4 14 14'/%3E%3C/svg%3E") !important;
    background-repeat: no-repeat !important;
    background-position: right 10px center !important;
    background-size: 15px 15px !important;
    border-color: rgba(255, 255, 255, 0.13) !important;
    color: #ccd0dd !important;
}

body.color-1E202D .dt-container .dt-search input:focus,
body.color-1E202D .datatable-search:focus,
body.color-1E202D .custom-filter-search:focus {
    border-color: #5b8cff !important;
    box-shadow: 0 0 0 3px rgba(91, 140, 255, 0.28) !important;
}

/* opaque, not transparent: these cells are sticky and rows pass under them */
body.color-1E202D .dt-container table.dataTable thead th,
body.color-1E202D .dt-container table.dataTable thead td {
    background: #252932 !important;
    border-bottom-color: rgba(255, 255, 255, 0.13) !important;
    color: #b3b9cc !important;
}

body.color-1E202D .dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr > td:last-child {
    background: #252932 !important;
}

body.color-1E202D .dt-container:not(.dtg-virtual) table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr:nth-child(even) > td:last-child {
    background: #2a2e38 !important;
}

body.color-1E202D .dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)):not(.no-hover):not(.js-selection-mode) > tbody > tr:hover > td:last-child {
    background: #2f3541 !important;
}

body.color-1E202D .dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr.active > td:last-child,
body.color-1E202D .dt-container table.dataTable:has(tbody > tr > td:last-child :is(.action-option, a.edit, a.delete)) > tbody > tr.active:hover > td:last-child {
    background: #2b3550 !important;
}

body.color-1E202D .dt-container table.dataTable thead th.dt-ordering-asc,
body.color-1E202D .dt-container table.dataTable thead th.dt-ordering-desc {
    color: #e3e6ef !important;
}

/* stripes — must beat `table.dataTable.display tbody td{background-color:#252932}` */
body.color-1E202D .dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody th,
body.color-1E202D .dt-container table.dataTable:not(.no-thead):not(.table-bordered) tbody td {
    background-color: transparent !important;
    border-top: 0 !important;
    border-bottom: 0 !important;
    color: #ccd0dd;
}

body.color-1E202D .dt-container:not(.dtg-virtual) table.dataTable:not(.no-thead):not(.table-bordered) tbody tr:nth-child(even) > td,
body.color-1E202D .dt-container:not(.dtg-virtual) table.dataTable:not(.no-thead):not(.table-bordered) tbody tr:nth-child(even) > th {
    background-color: rgba(255, 255, 255, 0.042) !important;
}

body.color-1E202D .dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.no-hover):not(.js-selection-mode) tbody tr:hover > td,
body.color-1E202D .dt-container table.dataTable:not(.no-thead):not(.table-bordered):not(.no-hover):not(.js-selection-mode) tbody tr:hover > th {
    background-color: #2f3541 !important;
}

body.color-1E202D .dt-container table.dataTable:not(.js-selection-mode) tbody tr.active > td,
body.color-1E202D .dt-container table.dataTable:not(.js-selection-mode) tbody tr.active:hover > td {
    background-color: rgba(91, 140, 255, 0.18) !important;
    color: #ccd0dd !important;
}

body.color-1E202D .dt-container table.dataTable td.dt-empty {
    background: transparent !important;
    color: #7c8296 !important;
}

body.color-1E202D .dt-container table.dataTable.display tfoot th,
body.color-1E202D .dt-container table.dataTable.display tfoot td,
body.color-1E202D .dt-container table.dataTable tfoot th,
body.color-1E202D .dt-container table.dataTable tfoot td {
    background: #252932 !important;
    border-top-color: rgba(255, 255, 255, 0.13) !important;
    color: #ccd0dd !important;
}

body.color-1E202D .dt-container .dt-buttons {
    background: rgba(255, 255, 255, 0.05);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.13);
}

body.color-1E202D .dt-container .dt-buttons .btn,
body.color-1E202D .dt-container .dt-buttons .buttons-excel,
body.color-1E202D .dt-container .dt-buttons .buttons-print,
body.color-1E202D .dt-container .datatable-export a {
    background-color: transparent !important;
    border-color: transparent !important;
    color: #b3b9cc !important;
}

body.color-1E202D .dt-container .dt-buttons .btn:hover,
body.color-1E202D .dt-container .dt-buttons .buttons-excel:hover,
body.color-1E202D .dt-container .dt-buttons .buttons-excel:active,
body.color-1E202D .dt-container .dt-buttons .buttons-excel:focus,
body.color-1E202D .dt-container .dt-buttons .buttons-print:hover,
body.color-1E202D .dt-container .dt-buttons .buttons-print:active,
body.color-1E202D .dt-container .dt-buttons .buttons-print:focus,
body.color-1E202D .dt-container .datatable-export a:hover {
    background-color: rgba(255, 255, 255, 0.08) !important;
    border-color: transparent !important;
    color: #ccd0dd !important;
}

body.color-1E202D .dt-container .dt-paging .dt-paging-button {
    background: transparent !important;
}

body.color-1E202D .dt-container .dt-paging .dt-paging-button a,
body.color-1E202D .dt-container .dt-paging .dt-paging-button .page-link {
    background: transparent !important;
    color: #b3b9cc !important;
}

body.color-1E202D .dt-container .dt-paging .dt-paging-button .page-link:hover {
    background-color: rgba(255, 255, 255, 0.08) !important;
    color: #ccd0dd !important;
}

body.color-1E202D .dt-container .dt-paging .dt-paging-button.active .page-link,
body.color-1E202D .dt-container .dt-paging .dt-paging-button.active .page-link:hover {
    background: rgba(91, 140, 255, 0.18) !important;
    color: #5b8cff !important;
}

body.color-1E202D .dt-container .filter-section-container .select2-container .select2-choice,
body.color-1E202D .dt-container .datatable-tools .select2-container .select2-choice {
    background: #2b303b !important;
    border-color: rgba(255, 255, 255, 0.13) !important;
    color: #ccd0dd !important;
}

/* near-opaque so the rows underneath don't ghost through the loading HUD */
body.color-1E202D .dt-container .dt-processing {
    background: var(--dtg-hud, rgba(30, 34, 43, 0.97)) !important;
    color: var(--dtg-spinner, #9aa0b4);
}

body.color-1E202D .dt-container table.dataTable > tbody > tr.child ul.dtr-details > li {
    border-bottom-color: rgba(255, 255, 255, 0.07) !important;
}

/* ============================================================== PRINT */
/* The bounded scroll container is a screen-only device. Left in place when
   printing it clips the table to one viewport — measured: a 50-row invoice
   list printed only ~18 rows. Sticky cells are also unreliable in paged
   media (headers can repeat or land mid-page), so everything goes static
   and the table is allowed its full height to flow across pages.

   This is about the BROWSER's own print (Ctrl+P). DataTables' Print button
   opens a separate document that never loads this stylesheet — the app
   styles that via `.dt-print-view`. */

@media print {

    .dtg-hscroll,
    .table-responsive.dtg-hscroll-host,
    .table-responsive:has(> .dt-container) {
        max-height: none !important;
        overflow: visible !important;
    }

    .dtg-hscroll table.dataTable > thead > tr > th,
    .dtg-hscroll table.dataTable > thead > tr > td,
    .dtg-hscroll table.dataTable > tfoot > tr > th,
    .dtg-hscroll table.dataTable > tfoot > tr > td,
    .dt-container table.dataTable > thead > tr > th,
    .dt-container table.dataTable > tfoot > tr > th,
    .dt-container table.dataTable > tbody > tr > td:last-child,
    .dt-container table.dataTable > thead > tr > th:last-child {
        position: static !important;
        box-shadow: none !important;
    }

    /* let the header repeat properly on each printed page instead */
    .dt-container table.dataTable > thead {
        display: table-header-group;
    }

    .dt-container .dt-processing {
        display: none !important;
    }
}

/* ================================================== MOTION PREFERENCES */

@media (prefers-reduced-motion: reduce) {

    .dt-container .dt-search input,
    .dt-container .dt-buttons .btn,
    .dt-container .dt-paging .dt-paging-button .page-link,
    .dt-container table.dataTable tbody td,
    .dt-container table.dataTable thead th .dt-column-order::before,
    .dt-container table.dataTable thead th .dt-column-order::after {
        transition: none;
    }
}

/* ========================================================= SMALL SCREENS */

@media (max-width: 800px) {

    .dt-container {
        --dtg-cell-x: 11px;
        --dtg-cell-y: 8px;
    }

    .dt-container .filter-section-flex-row {
        gap: 6px;
    }

    .dt-container .dt-search input,
    .datatable-search,
    .custom-filter-search {
        width: 100% !important;
    }
}

/* =========================================== VIRTUAL SCROLLING */
/* `.dtg-virtual` is set by assets/js/apptable-infinite.js on lists that
   scroll virtually: the scrollbar spans the whole result set, only the
   visible window is in the DOM, and the gap above/below is held open by
   two spacer rows. The pager is meaningless there. */

.dt-container.dtg-virtual .dt-paging {
    display: none;
}

/* Spacers exist purely to hold height — they must contribute nothing else,
   or the skin's cell padding/borders would add to the measured row pitch
   and the scrollbar would drift out of step with the data.

   `position: static` is the important one. A spacer has a SINGLE <td>, which
   makes it `td:last-child` — so the frozen-action-column rule promoted it to
   `position: sticky; right: 0`. A spacer is tens of thousands of pixels tall,
   so that produced a giant sticky cell pinned over the action column, hiding
   the real row buttons behind it and swallowing their clicks. */
.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-spacer,
.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-spacer > td {
    position: static !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
    pointer-events: none;
}

/* Striping in a virtual table CANNOT use :nth-child — spacer rows share the
   tbody, so the count shifts with scroll position and the stripes flip. The
   parity is assigned in JS from each row's ABSOLUTE index instead, and every
   nth-child rule above now excludes `.dtg-virtual` rather than being fought
   with !important (which would also have beaten the hover state). */
.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > td,
.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > th {
    background-color: var(--dtg-stripe, rgba(0, 0, 0, 0.032));
}

/* the frozen action cell follows the same parity, but opaque */
.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > td:last-child {
    background: var(--dtg-stripe-solid, #f7f7f8);
}

/* the rows-per-page picker is meaningless when the window is driven by the
   scrollbar — the chunk size is an implementation detail here */
.dt-container.dtg-virtual .dt-length {
    display: none;
}

/* --- dim placeholders for rows not yet fetched --------------------- */

.dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-placeholder > td {
    vertical-align: middle;
}

.dtg-vs-bar {
    display: block;
    height: 9px;
    max-width: 78%;
    border-radius: 4px;
    background: var(--dtg-hairline-strong, rgba(0, 0, 0, 0.11));
    opacity: 0.55;
    animation: dtg-vs-pulse 1.4s ease-in-out infinite;
}

/* stagger the columns slightly so it reads as "loading", not "broken" */
.dtg-vs-placeholder td:nth-child(3n) .dtg-vs-bar { animation-delay: 0.15s; max-width: 55%; }
.dtg-vs-placeholder td:nth-child(3n+1) .dtg-vs-bar { animation-delay: 0.30s; max-width: 66%; }

@keyframes dtg-vs-pulse {
    0%, 100% { opacity: 0.30; }
    50%      { opacity: 0.70; }
}

@media (prefers-reduced-motion: reduce) {
    .dtg-vs-bar { animation: none; opacity: 0.45; }
}

body.color-1E202D .dtg-vs-bar {
    background: rgba(255, 255, 255, 0.16);
}

body.color-1E202D .dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > td,
body.color-1E202D .dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > th {
    background-color: rgba(255, 255, 255, 0.042) !important;
}

body.color-1E202D .dt-container.dtg-virtual table.dataTable > tbody > tr.dtg-vs-even > td:last-child {
    background: #2a2e38 !important;
}

/* ------------------------------------------- resizable, fixed columns */
/* Virtual tables run `table-layout: fixed` with explicit widths (set by
   assets/js/apptable-infinite.js) so columns stop re-measuring on every
   chunk. Fixed layout means content no longer widens a cell, so text that
   does not fit has to be trimmed rather than allowed to bleed into the
   next column.

   The LAST column is excluded on purpose: it holds the row-action buttons
   and a Bootstrap dropdown, and `overflow: hidden` there would clip the
   open menu — the same trap that hid the action buttons earlier. */

.dt-container.dtg-virtual table.dataTable > tbody > tr > td:not(:last-child) {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* the drag grip lives on the header's right edge */
.dt-container.dtg-virtual table.dataTable > thead > tr > th {
    position: relative;
}

.dtg-col-grip {
    position: absolute;
    top: 0;
    right: -3px;
    width: 7px;
    height: 100%;
    cursor: col-resize;
    z-index: 6;
    /* invisible until hovered — a permanent handle on every column would be
       visual noise on a list this dense */
    background: transparent;
    transition: background-color 0.15s ease;
}

.dtg-col-grip:hover,
.dt-container.dtg-resizing .dtg-col-grip:active {
    background: var(--dtg-accent, #007aff);
    opacity: 0.55;
}

/* while dragging, stop the pointer selecting header text across the table */
.dt-container.dtg-resizing,
.dt-container.dtg-resizing table.dataTable {
    user-select: none;
    -webkit-user-select: none;
    cursor: col-resize;
}

body.color-1E202D .dtg-col-grip:hover,
body.color-1E202D .dt-container.dtg-resizing .dtg-col-grip:active {
    background: #5b8cff;
}

/* -------------------------------------- quiet background loading */
/* `.dtg-quiet` is set by assets/js/apptable-infinite.js only while an IDLE
   prefetch is in flight — work the user did not ask for. DataTables shows
   its processing indicator on every draw, so without this the spinner would
   pop up every two seconds while they sit still, which reads as the page
   being busy rather than smooth.

   `!important` is required: DataTables sets `display: block` INLINE on that
   element, and an inline style beats a plain class rule.

   A fetch the user DID trigger (jumping to an unloaded range) keeps its
   spinner — that one is feedback, not noise. */

.dt-container.dtg-quiet .dt-processing {
    display: none !important;
}
