body {
    background-color: #f4f6f9;
    /* Bootstrap 5's default light-mode text color is #212529 -- a dark
       gray, not true black, so headings/labels/body copy read as slightly
       "washed out" rather than crisp black. Overriding the variable (not
       just `color` on body) means every component that reads text color
       from it -- cards, tables, form labels, and so on -- picks up true
       black too, not just the <body> element itself. */
    --bs-body-color: #000;
    color: #000;
}
[data-bs-theme="dark"] body {
    background-color: #12161c;
}
.login-body {
    position: relative;
    min-height: 100vh;
    background: linear-gradient(135deg, #1b2838, #2c3e50);
}
.card {
    border: none;
    border-radius: 10px;
}
.stat-card {
    border-radius: 10px;
    color: #fff;
    padding: 1.25rem;
}
.stat-card h3 {
    margin: 0;
    font-size: 1.75rem;
    font-weight: 700;
}
.stat-card p {
    margin: 0;
    opacity: .9;
    font-weight: 600;
}
/* ==========================================================================
   SUMMARY BOXES -- the row of figures at the top of a screen
   ==========================================================================

   The complaint this answers: on the Adjustment and Loan screens four boxes
   filled the whole width of a desktop monitor to show four short numbers,
   and the figure people had actually come to read -- what is still owed --
   was pushed below the fold.

   Adding `stat-row` to the row that holds them fits SIX across instead of
   four and shrinks the padding and type to match. It works by overriding
   whatever col-* classes the boxes already carry, so a screen opts in with
   one class on one line and nothing inside the row has to be touched.

   Deliberately NOT applied to every .card.p-3: the Reports landing page and
   the Investors action panel are built from the same markup but are
   navigation tiles, not figures, and shrinking those would make them harder
   to hit rather than easier to read.

   Specificity, not !important (except on padding, where Bootstrap's own
   .p-3 is what has to lose): `.stat-row .card` is two classes and beats
   Bootstrap's one-class utilities whatever order the stylesheets load in. */

.stat-row > [class*="col-"] {
    flex: 0 0 auto;
    width: 16.666667%;          /* six across */
}
.stat-row .card,
.stat-row .stat-card {
    padding: .55rem .7rem !important;
    height: 100%;
}
.stat-row .card > p,
.stat-row .stat-card > p,
.stat-row .card .stat-label {
    font-size: .72rem;
    line-height: 1.2;
    margin-bottom: .1rem !important;
}
.stat-row .card h3,
.stat-row .card h5,
.stat-row .card h6,
.stat-row .stat-card h3,
.stat-row .card .stat-value,
.stat-row .stat-card .kpi-value {
    font-size: 1.02rem;
    font-weight: 700;
    line-height: 1.25;
    margin-bottom: 0;
    /* A rupee figure has no space to break at, so without this one long
       number sets the width of its box and squeezes the five beside it. */
    overflow-wrap: anywhere;
}
/* The footnote under a figure ("Given 10-Sep-2026", "Amount + Interest +
   Doc Charges"). Small enough to read as a note rather than a second value. */
.stat-row .card .small,
.stat-row .card .stat-note {
    font-size: .66rem;
    line-height: 1.25;
}
.stat-row .card .stat-note { display: block; }
/* A badge beside a figure was inheriting fs-6 (1rem) and making its box
   taller than the five next to it. */
.stat-row .card .badge {
    font-size: .62rem !important;
    vertical-align: middle;
}

/* Tablets and small laptops: six 16%-wide boxes would be about 110px each,
   which is narrower than "₹1,00,000.00". Three across instead. */
@media (min-width: 576px) and (max-width: 991.98px) {
    .stat-row > [class*="col-"] { width: 33.333333%; }
}
/* Phones: two across, which is the widest a rupee figure reads at on a
   360px screen. */
@media (max-width: 575.98px) {
    .stat-row > [class*="col-"] { width: 50%; }
    .stat-row .card h3,
    .stat-row .card h5,
    .stat-row .card h6,
    .stat-row .stat-card h3,
    .stat-row .card .stat-value,
    .stat-row .stat-card .kpi-value { font-size: .95rem; }
}

/* ==========================================================================
   FORM SECTIONS -- grouped field groups on longer forms (Add/Edit Client)
   ==========================================================================
   The complaint this answers: Add/Edit Client mixed personal, contact,
   document and address fields into one undifferentiated grid with only two
   bare <h6> dividers ("Contact", "Address") and no divider at all before
   Photo/Documents/Aadhar -- reading it top to bottom gave no sense of where
   one group of facts ended and the next began, and Client Photo landed
   squeezed into the Contact row with nothing to do with contact details.

   A .form-section is a lightly-tinted, rounded block with its own heading
   bar (icon + title), so the eye can chunk the form into "Basic Details",
   "Contact Details", "Identity & Documents" and "Address" at a glance --
   the same way .stat-row (above) chunks a report's figures into cards. */
.form-section {
    background: var(--bs-tertiary-bg, #f8f9fa);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 10px;
    padding: 1rem 1rem .75rem;
}
[data-bs-theme="dark"] .form-section {
    background: rgba(255, 255, 255, .035);
}
.form-section-title {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-weight: 700;
    color: var(--bs-primary, #0d6efd);
    margin-bottom: .9rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

.bg-navy { background-color: #2c3e50; }
.bg-teal { background-color: #16a085; }
.bg-orange { background-color: #e67e22; }
.bg-red { background-color: #c0392b; }
table.table thead th {
    white-space: nowrap;
}

/* --- Dashboard KPI tiles --- */
.kpi-tile,
.kpi-tile:link,
.kpi-tile:visited,
.kpi-tile:hover,
.kpi-tile:focus,
.kpi-tile * {
    text-decoration: none !important;
}
.kpi-tile {
    display: block;
    color: inherit;
    height: 100%;
    transition: transform .12s ease, box-shadow .12s ease;
}
a.kpi-tile:hover, a.kpi-tile:focus {
    transform: translateY(-2px);
    box-shadow: 0 .5rem 1rem rgba(0,0,0,.1);
    color: inherit;
}
.kpi-tile.kpi-static {
    cursor: default;
}
.kpi-tile .card, .kpi-tile.stat-card {
    height: 100%;
}
.kpi-tile h5, .kpi-tile h3 {
    word-break: break-word;
}
.kpi-label {
    font-size: .72rem;
    font-weight: 600;
    opacity: .85;
    line-height: 1.2;
}
/* Sized here rather than relying on whichever heading tag a tile happens to
   use -- the tiles mix h5 and h3, which rendered the same figures at two
   different sizes across one dashboard. One rule keeps every tile
   consistent and fits long rupee values without wrapping. */
.kpi-value {
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1.25;
}
/* The four accent tiles (Backlogged Loans, Total Outstanding, Bad Debt
   Total, Cash Balance) are built on .stat-card, and its own `.stat-card h3`
   rule near the top of this file sets 1.75rem. That selector is MORE
   SPECIFIC than the plain `.kpi-value` above -- two parts against one -- so
   it kept winning, which is why those four still printed much larger than
   every other tile and why "Rs. 2,633,560.00" wrapped onto a second line
   inside its own card. Matching that specificity here settles it, and the
   whole dashboard prints at one size. */
.stat-card .kpi-value,
.kpi-tile .kpi-value {
    font-size: 1.05rem;
}

/* The figures printed under each dashboard chart. Small, dense, and
   readable without hovering -- and unlike a canvas tooltip they survive
   printing the page. */
.chart-figures {
    font-size: .78rem;
    margin: .6rem 0 0;
}
.chart-figures li {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    padding: .15rem 0;
    border-bottom: 1px solid rgba(128,128,128,.15);
}
.chart-figures li:last-child { border-bottom: 0; }
.chart-figures .swatch {
    width: .65rem;
    height: .65rem;
    border-radius: 2px;
    flex: 0 0 auto;
}
.chart-figures .name { flex: 1 1 auto; opacity: .9; }
.chart-figures .val { font-weight: 700; white-space: nowrap; }
.chart-figures .pct { opacity: .6; white-space: nowrap; }

/* Light pastel tones for the everyday KPI tiles (the four "accent" tiles —
   Backlogged Loans, Total Outstanding, Bad Debt Total, Cash Balance — keep
   their stronger bg-navy/bg-teal/bg-orange/bg-red treatment instead). */
.kpi-blue   { background-color: #e7f1fd; color: #1c4c86; }
.kpi-purple { background-color: #f0ebfb; color: #5b3a9e; }
.kpi-amber  { background-color: #fff4e0; color: #8a5a00; }
.kpi-green  { background-color: #e8f8ee; color: #1e7a41; }
.kpi-pink   { background-color: #fdeaec; color: #a13350; }

/* Dark-mode equivalents -- same hues, darkened background / lightened text
   so they stay legible on a dark page instead of glowing pastel-white. */
[data-bs-theme="dark"] .kpi-blue   { background-color: #1c3a5e; color: #a9cdfb; }
[data-bs-theme="dark"] .kpi-purple { background-color: #3a2c5e; color: #cbb8f5; }
[data-bs-theme="dark"] .kpi-amber  { background-color: #5c4419; color: #ffd98a; }
[data-bs-theme="dark"] .kpi-green  { background-color: #1f4a30; color: #94e0ae; }
[data-bs-theme="dark"] .kpi-pink   { background-color: #5c2430; color: #f5a8ba; }

/* --- Reports landing page (icon tiles, grouped by section) --- */
.report-section-title {
    text-transform: uppercase;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: #8a94a6;
    border-bottom: 1px solid #e5e9f0;
    padding-bottom: .5rem;
    margin-bottom: 1rem;
}
[data-bs-theme="dark"] .report-section-title {
    border-bottom-color: #2a2f3a;
}
.report-tile {
    border: 1px solid #eef0f4;
}
[data-bs-theme="dark"] .report-tile {
    border-color: #2a2f3a;
}
.report-icon {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    margin-bottom: .65rem;
}
.report-tile h6 {
    font-weight: 600;
}
.report-tile small {
    display: block;
    line-height: 1.35;
}

/* --- Dark-mode safety net for a handful of Bootstrap utility classes this
   app leans on heavily (bg-light card headers/filter bars, bg-white, and
   text-dark on card-style link tiles across ~150 call sites app-wide) that
   Bootstrap 5.3 does NOT re-color per theme the way it does .card,
   .text-muted, .table-striped, form controls, dropdowns, modals, etc. --
   those already adapt automatically via data-bs-theme, no fix needed.
   Rather than hunting down and reclassing every individual call site, these
   overrides keep the ones that don't adapt legible wherever they're used. */
[data-bs-theme="dark"] .bg-light {
    background-color: #1e232c !important;
}
[data-bs-theme="dark"] .bg-white {
    background-color: #1a1f28 !important;
}
[data-bs-theme="dark"] .text-dark {
    color: var(--bs-body-color) !important;
}
[data-bs-theme="dark"] .table-light {
    --bs-table-bg: #1e232c;
    --bs-table-color: var(--bs-body-color);
}
/* .table-dark (Collection Run Sheet / Bulk Post / Upload Run Sheet header
   rows, the Investment Report totals row) is the same story in reverse --
   already dark, but a FIXED dark regardless of theme, so it doesn't stand
   out any differently against a dark page than a light one. Dark mode gets
   its own, slightly lighter shade so it still reads as "the header row" /
   "the totals row" instead of blending into the page around it. */
[data-bs-theme="dark"] .table-dark {
    --bs-table-bg: #1a1f28;
    --bs-table-color: #f1f3f5;
    --bs-table-border-color: #2a2f3a;
}

/* --- Navbar: was Bootstrap's fixed bg-dark utility, so toggling Light/Dark
   had literally no visible effect on it -- the one piece of chrome on
   every page that never changed. Now genuinely theme-reactive: a light
   gray bar with black text in Light mode (header.php switches its class
   from navbar-dark to navbar-light and swaps in a dark-text logo variant
   to match), a deeper near-black bar in Dark mode -- so the navbar
   visibly follows the toggle instead of staying frozen. */
.app-navbar {
    background-color: #e9ecef;
}
[data-bs-theme="dark"] .app-navbar {
    background-color: #10141b;
}

/* --- Table toolbar (search / export / import / print) --- */
.table-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}
.table-toolbar .form-control, .table-toolbar .form-select {
    min-width: 10rem;
}

/* DataTables: fold its own search box into our toolbar row instead of a
   separate line, and keep spacing consistent with Bootstrap. */
.dataTables_wrapper .dataTables_filter input {
    margin-left: .5rem;
}
.dataTables_wrapper .dataTables_length select {
    margin: 0 .35rem;
}
/* Breathing room above the "Show N entries" control so it doesn't sit
   flush against the toolbar/table above it. */
.dataTables_wrapper .dataTables_length {
    margin-top: 1rem;
    margin-bottom: .5rem;
}
/* Same breathing room for the "Quick search this table..." box on the
   opposite side of that row, so it doesn't sit flush against the card/table
   below it either. */
.dataTables_wrapper .dataTables_filter {
    margin-top: 1rem;
    margin-bottom: .5rem;
}

/* --- App footer (every internal page) --- */
.app-footer {
    background-color: #fff;
}
[data-bs-theme="dark"] .app-footer {
    background-color: #1a1f28;
}

/* --- Login page footer (sits on the dark gradient background) --- */
.login-footer {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: .75rem 1rem;
    color: rgba(255, 255, 255, .65);
}

/* --- Mobile / small-screen support --- */

/* Every data table on the site lives in a <div class="card-body p-0">
   wrapper with nothing else in it, so this one rule makes every table on
   the site horizontally scrollable on narrow screens without needing a
   .table-responsive wrapper added to two dozen individual page templates.
   Wide tables (Loans, Trial Balance, Investor Capital, etc.) scroll inside
   their card instead of squishing columns or forcing the whole page to
   scroll sideways. */
.card-body.p-0 {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Page header row (title + action button, e.g. "Loans" + "New Loan") —
   drop to a second line with a gap instead of squishing on narrow phones. */
.d-flex.justify-content-between.align-items-center {
    flex-wrap: wrap;
    gap: .5rem;
}

@media (max-width: 575.98px) {
    /* KPI tiles: keep the accent "stat card" figures readable but compact
       once two of them share a row edge-to-edge on a phone. */
    .stat-card h3 {
        font-size: 1.35rem;
    }
    .kpi-tile .card.p-3 {
        padding: .85rem !important;
    }
    .kpi-value {
        font-size: .95rem;
    }
    /* Same specificity story as above: on a phone the accent tiles would
       otherwise fall back to the 1.35rem .stat-card h3 rule. */
    .stat-card .kpi-value,
    .kpi-tile .kpi-value {
        font-size: .95rem;
    }
    .navbar-brand img {
        height: 32px;
    }
    /* Bootstrap's default container-fluid gutter feels wide once the
       screen itself is only ~320-375px; give content a bit more room. */
    main.container-fluid {
        padding-left: .75rem;
        padding-right: .75rem;
    }
}

/* --- Print: show just the data, not chrome around it --- */
@media print {
    nav.navbar, .table-toolbar, form.filter-form, .btn, .no-print, .card-header .btn,
    .dataTables_filter, .dataTables_length, .dataTables_paginate, .dataTables_info {
        display: none !important;
    }
    body {
        background: #fff !important;
    }
    .card {
        box-shadow: none !important;
    }
}

/* ==========================================================================
   PHONE LAYOUT
   ==========================================================================

   The complaint this answers: on a phone the toolbar buttons, the filter
   boxes and DataTables' own search and paging controls filled the screen,
   and the actual data was a nine-column table you had to drag sideways to
   read. You scrolled past a wall of controls to reach a table you then had
   to scroll again, in the other direction.

   So below 768px:

     * every data table turns into a stack of cards, one per record, with
       each value labelled. No sideways scrolling anywhere.
     * the filter form shows the search box and hides the rest behind a
       Filters button (includes/footer.php does the moving; this styles it).
     * DataTables' duplicate search box, "show N entries" menu and result
       count are hidden -- the page's own search already searches the whole
       database, and two search boxes that behave differently is exactly the
       confusion being fixed here.
     * buttons get finger-sized instead of mouse-sized.

   All of it is driven off the `table.data-table` class and the
   `form.filter-form` class that every list screen in this app already
   uses, so no individual page template needed editing -- and a screen
   added later gets the phone layout for free.
   ========================================================================== */

@media (max-width: 767.98px) {

    /* ---- Data tables become cards ------------------------------------- */

    /* The header row is where the column names live; on a card layout each
       value carries its own label instead (the ::before below), so the
       header itself has nothing left to do. Kept in the DOM rather than
       removed: DataTables reads it for sorting, and the footer.php script
       reads it to build the labels. */
    table.data-table thead {
        position: absolute;
        width: 1px; height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    table.data-table,
    table.data-table tbody,
    table.data-table tr,
    table.data-table td {
        display: block;
        width: 100%;
    }

    /* !important, reluctantly, and for two specific reasons.
       DataTables measures each column's natural width when it starts up and
       writes the total onto the table as an inline style -- about 830px for
       the Loans list. Its own stylesheet then adds max-width: none
       !important. On a desktop that is invisible, because a table only
       renders as wide as its container anyway; but these are display: block
       now, and a block element with an inline width of 830px inside a 360px
       phone simply sticks out of it. That is what put every value past the
       right-hand edge of the card.
       Nothing here is wider than the screen, so nothing needs to measure. */
    table.data-table,
    table.data-table.dataTable {
        width: 100% !important;
        max-width: 100% !important;
        margin: 0 !important;
    }
    table.data-table tr,
    table.data-table td { max-width: 100%; }

    table.data-table tr {
        border: 1px solid var(--bs-border-color, #dee2e6);
        border-radius: .6rem;
        margin-bottom: .6rem;
        padding: .3rem .1rem;
        background: var(--bs-body-bg, #fff);
    }

    /* Striping reads as noise once every row is its own bordered card, and
       Bootstrap applies it per-cell, which on a card puts a grey band behind
       half the values and makes every other card look disabled. The card
       border does the separating now.

       Bootstrap 5.3 paints stripes with an INSET BOX-SHADOW, not a
       background -- `box-shadow: inset 0 0 0 9999px var(--bs-table-bg-type)`
       on every cell. Overriding background-color alone (as this first did)
       changes nothing at all, which is why the cards were still coming out
       alternately grey. Both the variable and the shadow are cleared. */
    table.data-table > tbody > tr > td,
    table.data-table > tfoot > tr > td,
    table.data-table.table-striped > tbody > tr:nth-of-type(odd) > td {
        --bs-table-accent-bg: transparent;
        --bs-table-bg-type: transparent;
        --bs-table-color-type: inherit;
        background-color: transparent;
        box-shadow: none;
    }

    /* Row-level status colours (bad debt, deleted, money out) still mean
       something, so they move to the whole card rather than disappearing
       with the striping above. */
    table.data-table > tbody > tr.table-danger { border-color: var(--bs-danger); border-left-width: 4px; }
    table.data-table > tbody > tr.table-warning { border-color: var(--bs-warning); border-left-width: 4px; }
    table.data-table > tbody > tr.table-success { border-color: var(--bs-success); border-left-width: 4px; }
    table.data-table > tbody > tr.table-secondary { opacity: .72; }

    /* box-sizing, spelled out.
       Bootstrap's reset is supposed to put every element on border-box, and
       on a desktop these cells behave as though it had. On the phone they do
       not: measuring the live screen, each value sat 9.7px past its card's
       right border, and 9.7px is exactly what falls out if the cell's .8rem
       padding is being added OUTSIDE its width: 100% rather than inside it
       (the arithmetic matches to a tenth of a pixel). A cell 25.6px wider
       than the card it lives in is what pushed the amounts, the dates and
       the status badges over the edge. Stating it here settles it whatever
       the cause, and costs nothing where it was already right. */
    table.data-table,
    table.data-table tbody,
    table.data-table tr,
    table.data-table td,
    table.data-table td > .cell-value { box-sizing: border-box !important; }

    /* Grid rather than flex, for one property that matters here: a track
       declared minmax(0, x) is allowed to shrink all the way to nothing, so
       the two columns of a cell can ALWAYS be made to fit the card, whatever
       is in them. The flex version could not: the label had flex-shrink: 0
       and a percentage width, so its size depended on the cell's, and a cell
       measured wrongly took the label out with it. This layout cannot
       overflow its cell even if something upstream gets the width wrong. */
    table.data-table td {
        display: grid;
        grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr);
        align-items: baseline;
        column-gap: .75rem;
        border: 0;
        padding: .3rem .8rem;
        min-width: 0;
        max-width: 100%;
        word-break: break-word;
    }

    /* The column name, taken from the table header by footer.php. */
    table.data-table td::before {
        content: attr(data-label);
        min-width: 0;
        text-align: left;
        font-weight: 600;
        font-size: .82rem;
        color: var(--bs-secondary-color, #6c757d);
    }

    /* The value, wrapped by footer.php into a single element.
       Without that wrapper every child of the cell became a grid item of
       its own -- which is how a Paid/Unpaid pair of badges ended up as two
       squeezed columns reading one letter per line. One wrapper means the
       cell is always exactly two items: the label and the value. */
    table.data-table td > .cell-value {
        min-width: 0;
        max-width: 100%;
        text-align: right;
        /* A client name like "TAMIRI KRANTHIKUMAR@AMLET" has no space to
           break at, so without this one long name sets the width of the
           whole card and pushes everything else out with it. */
        overflow-wrap: anywhere;
    }

    /* The Call/WhatsApp/e-mail icon buttons (Clients, Loans, Adjustments'
       Contact columns) sit inline right after the plain-text phone number,
       e.g. "9000274159 [call] [whatsapp]". An inline-block <a class="btn">
       defaults to vertical-align: baseline, which lines up its OWN text
       baseline with the number's -- but the button's padding then puffs its
       box up above that shared baseline, so on screen the icon reads as
       sitting noticeably higher than the digits beside it. Middle-aligning
       it against the number's line box (not its baseline) fixes that. */
    table.data-table td > .cell-value a.btn {
        vertical-align: middle;
    }

    /* A cell holding more than a single value -- a name with the business
       and area under it, a pair of status badges, a photo -- has no room to
       sit beside its label on a 360px screen. Those stack instead: label on
       one line, the whole cell under it, full width. footer.php tags them.
       A cell whose label is blank (a totals heading, a message spanning the
       row) stacks too, and drops the empty label box entirely rather than
       leaving a 7.5rem indent where a word used to be. */
    table.data-table td.cell-block,
    table.data-table td[data-label=""] {
        grid-template-columns: minmax(0, 1fr);
        row-gap: .15rem;
    }
    table.data-table td.cell-block > .cell-value { text-align: left; }
    table.data-table td[data-label=""]::before { content: none; }

    /* First cell of each card is its title -- the Loan ID, the client's
       name, the date. Bigger, no label, full width. */
    table.data-table td:first-child {
        font-size: 1.05rem;
        font-weight: 700;
        padding: .5rem .8rem .35rem;
        text-align: left;
        border-bottom: 1px solid var(--bs-border-color, #dee2e6);
        margin-bottom: .2rem;
    }
    table.data-table td:first-child::before { content: none; }
    /* No label, so no label column: one full-width track, or the title would
       sit indented in the value column with 7.5rem of nothing beside it. */
    table.data-table td:first-child { grid-template-columns: minmax(0, 1fr); }
    /* The title is the one value that reads left, under the label-less
       heading -- the generic right-alignment above is for values sitting
       opposite their label, which the title has none of. */
    table.data-table td:first-child > .cell-value { text-align: left; }

    /* A cell the page left empty would otherwise show as a lonely label. */
    table.data-table td:empty { display: none; }

    /* Actions: full width, wrapping, finger-sized, no label. */
    table.data-table td.actions-cell {
        display: flex;
        flex-wrap: wrap;
        gap: .4rem;
        justify-content: flex-start;
        padding: .5rem .8rem .6rem;
        border-top: 1px solid var(--bs-border-color, #dee2e6);
        margin-top: .2rem;
        text-align: left;
    }
    table.data-table td.actions-cell::before { content: none; }
    table.data-table td.actions-cell .btn {
        min-height: 40px;
        min-width: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* The delete/restore buttons are one-button forms, so the form itself
       has to lay out like a button for the gap above to apply evenly. */
    table.data-table td.actions-cell form.d-inline { display: inline-flex; }

    /* A totals row is not a record -- it reads as one more card otherwise. */
    table.data-table tfoot { display: block; }
    table.data-table tfoot tr {
        border: 0;
        border-top: 2px solid var(--bs-border-color, #dee2e6);
        background: transparent;
        font-weight: 700;
    }
    table.data-table tfoot td { text-align: right; }
    table.data-table tfoot td:first-child { border-bottom: 0; font-size: 1rem; }
    table.data-table tfoot td:empty { display: none; }

    /* The card wrapper no longer needs to scroll sideways, and its own
       padding would double up with the cards' own. */
    .card-body.p-0 { overflow-x: visible; }
    .card-body.p-0 > table.data-table,
    .card-body.p-0 > .dataTables_wrapper { padding: .6rem; }
    .table-responsive { overflow-x: visible; }

    /* ---- DataTables' own controls ------------------------------------- */

    /* Hidden on phones: its search box only narrows the rows already on the
       page, while the page's own search box below queries the whole
       database. Two boxes, two behaviours, no way to tell which is which --
       that is the confusion, so only the real one survives here. */
    .dataTables_filter,
    .dataTables_length,
    .dataTables_info { display: none !important; }

    .dataTables_paginate { float: none !important; text-align: center; padding-top: .5rem; }
    .dataTables_paginate .pagination { justify-content: center; flex-wrap: wrap; margin-bottom: 0; }
    .dataTables_paginate .page-link { min-width: 40px; min-height: 40px; display: flex; align-items: center; justify-content: center; }

    /* ---- Filters ------------------------------------------------------- */

    /* The search box and its Search button stay on screen; the dropdowns, the
       date range, the city filter and "show deleted" fold away until Filters
       is tapped. includes/footer.php decides which column is which and adds
       the toggle -- it adds classes only, it does not move anything existing,
       so the desktop layout is byte-for-byte what it was.
       The toggle's own width comes from the col-6/col-12 footer.php gives it,
       so it can sit beside the Search button or take a line of its own. */
    form.filter-form .filter-extra { display: none; }
    form.filter-form.filters-open .filter-extra { display: block; }

    /* Closed, the bar is two short rows at most, so the margin under it would
       otherwise be doubled by the toggle's own. */
    form.filter-form:not(.filters-open) { margin-bottom: .5rem !important; }

    form.filter-form .filter-toggle {
        min-height: 44px;
        white-space: nowrap;
        width: 100%;
        /* Reads as "tap me to reveal something", which a flat outline button
           sitting alone above a list does not. */
        display: flex;
        align-items: center;
        justify-content: center;
        gap: .4rem;
    }
    form.filter-form .filter-toggle .badge { margin-left: .1rem; }
    /* The chevron turns over when the panel is open, so the button says
       which way it is about to go. */
    form.filter-form .filter-toggle .filter-caret { transition: transform .15s ease; }
    form.filter-form.filters-open .filter-toggle .filter-caret { transform: rotate(180deg); }

    /* A row of buttons held on one line by .text-nowrap is fine on a
       desktop and 393px wide on a 360px phone -- which is exactly how the
       Transactions screen's investor shortcuts pushed the whole page
       sideways. Where this app uses .text-nowrap for a GROUP OF BUTTONS
       (inside an alert or a card header) rather than to stop a date or an
       amount breaking mid-value, let it wrap. Table cells keep their
       no-wrap, which is what it is really for. */
    .alert .text-nowrap,
    .card-header .text-nowrap { white-space: normal; }
    .alert .text-nowrap .btn { margin-bottom: .25rem; }

    /* ---- Toolbar and buttons ------------------------------------------ */

    .table-toolbar { gap: .4rem; }
    .table-toolbar .btn { font-size: .82rem; padding: .35rem .6rem; min-height: 38px; }
    /* Nobody prints from a phone, and it was a third of the toolbar.
       Matched on the handler rather than a class, so no page template
       needed a change and a screen added later loses it too. */
    .table-toolbar .btn[onclick^="printDataTable"] { display: none; }

    /* Anything a finger has to hit gets a real target. Bootstrap's btn-sm
       is 31px tall, which is under every platform's 44px guidance. */
    .btn { min-height: 40px; }
    .btn-sm { min-height: 38px; }
    .form-control, .form-select { min-height: 44px; font-size: 1rem; }

    /* iOS zooms the whole page in when a focused input's text is under
       16px. 1rem above is 16px, which is what stops it. */

    /* Page header: title on its own line, actions below it.
       A GRID rather than a row of stretched buttons. The old rule gave every
       action `flex: 1 1 auto`, which on a 360px screen meant one button per
       line -- the Loans screen's four actions were four full-width bars and
       most of the first screenful before a single loan appeared.
       auto-fit with a 8.5rem floor puts two short actions side by side, lets a
       long label ("Collection Run Sheet") take a full row rather than wrapping
       mid-word, and leaves a lone button full width as it was. */
    main.container-fluid > .d-flex.justify-content-between.align-items-center > div:last-child {
        width: 100%;
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
        gap: .4rem;
    }
    main.container-fluid > .d-flex.justify-content-between.align-items-center > div:last-child > .btn,
    main.container-fluid > .d-flex.justify-content-between.align-items-center > div:last-child > form {
        width: 100%;
        margin: 0;
        min-width: 0;
        font-size: .82rem;
        padding-left: .4rem;
        padding-right: .4rem;
    }

    /* KPI / summary tiles two-up instead of four-up. */
    .row.g-3 > [class*="col-md-3"] { flex: 0 0 50%; max-width: 50%; }

    /* Room for the bottom bar (see includes/footer.php) so the last row of
       a list is never stuck underneath it. */
    body.has-bottom-nav { padding-bottom: 4.5rem; }
}

/* ==========================================================================
   BOTTOM BAR -- phones only, rendered by includes/footer.php
   ========================================================================== */

.bottom-nav { display: none; }

@media (max-width: 767.98px) {
    .bottom-nav {
        display: flex;
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 1030;
        background: var(--bs-body-bg, #fff);
        border-top: 1px solid var(--bs-border-color, #dee2e6);
        /* Clears the iPhone home indicator, and is simply 0 everywhere else. */
        padding-bottom: env(safe-area-inset-bottom, 0px);
        box-shadow: 0 -2px 10px rgba(0, 0, 0, .06);
    }
    .bottom-nav a {
        flex: 1 1 0;
        min-width: 0;
        padding: .45rem .2rem .5rem;
        text-align: center;
        text-decoration: none;
        color: var(--bs-secondary-color, #6c757d);
        font-size: .68rem;
        line-height: 1.15;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: .15rem;
    }
    .bottom-nav a i { font-size: 1.25rem; }
    .bottom-nav a.active { color: var(--bs-primary, #0d6efd); font-weight: 600; }
    .bottom-nav a span {
        display: block;
        width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* The install hint shown to iPhone users, who have no install button --
   Safari only offers Add to Home Screen from its own Share menu. */
.ios-install-hint { display: none; }

@media print {
    .bottom-nav, .ios-install-hint { display: none !important; }
}

/* The Filters toggle is a phone affordance only -- on a desktop every filter
   is already on screen, so there is nothing for it to reveal. */
@media (min-width: 768px) {
    form.filter-form .filter-toggle-col { display: none !important; }
}
