/* dd-theme.css -- DigiDrawer dark design tokens.
 *
 * LOAD LAST, after bootstrap.min.css / the DataTables bundle / mb.css / mb1.css
 * / bs5-compat.css. This file overrides Bootstrap *variables*, never Bootstrap
 * rules, so it can never lose a specificity fight and never needs !important.
 *
 * REQUIRES <html lang="en" data-bs-theme="dark" class="dark"> on any page that
 * links it. Both attributes are needed:
 *   - data-bs-theme="dark" switches Bootstrap 5.3.8's own dark token set on, and
 *     is what the ~37 [data-bs-theme=dark] rules already inside
 *     v2/DataTables-bs5/datatables.min.css key off.
 *   - class="dark" is the legacy hook for the 4 DataTables rules that were never
 *     ported to the attribute (lightbox close button, tag-field loading state)
 *     plus :root.dark{--dt-html-background}, which the Editor bubble reads.
 *
 * ROLLOUT STATUS: linked by mb.php only. Do not add the link to mb.css or
 * mb1.css -- those are loaded by 12 and 21 pages respectively, and the grid
 * pages have not been themed yet. Add the <link> per page as each one lands.
 */

/* Tokens live on :root so they resolve even before the theme attribute is read
 * (e.g. inside <style> blocks that run against the same page). */
:root {
    /* ---- Surfaces: four levels. Deliberately NOT #000 -- pure black causes
       halation on OLED and leaves nowhere to go for a recessed surface. ---- */
    --dd-s0: #0d1013;   /* page canvas                            */
    --dd-s0-rgb: 13, 16, 19;   /* the same, for rgba() fades      */
    --dd-s1: #14181d;   /* panel / card / tile                    */
    --dd-s2: #1b2027;   /* raised: navbar, modal header, thead    */
    --dd-s3: #232a33;   /* hover / active row                     */

    /* ---- Borders ---- */
    --dd-line-subtle: #1f262e;
    --dd-line:        #2c343e;
    --dd-line-strong: #3c4652;
    /* Control edges. The module glow (radial-gradient at the panel's top-left,
       peaking around #1D4048) is LIGHTER than every surface token, so darkening
       a border makes it worse there: --dd-line measures 1.13:1 on the glow and
       --dd-line-strong 1.16:1. Only a light edge clears WCAG 1.4.11's 3:1 on
       both the flat panel and the glow. 3.58:1 on --dd-s1, 3.03:1 on the glow. */
    --dd-line-control: rgba(255, 255, 255, .38);

    /* ---- Text ---- */
    --dd-text:  #e8edf2;
    --dd-muted: #a4b0bd;
    --dd-dim:   #7c8894;

    /* ---- Brand. #ff5000 is the existing DigiDrawer orange (717 occurrences
       tree-wide); #ff5001 elsewhere is drift, not a second colour. ---- */
    --dd-accent:       #ff5000;
    --dd-accent-rgb:   255, 80, 0;
    --dd-accent-hover: #ff7a3d;
    --dd-accent-ring:  rgba(255, 122, 61, .35);

    /* ---- Module accents, corrected for a dark surface. The originals in
       mb.css (#17a2b8 #28a745 #ffc107 #dc3545) are Bootstrap mid-tones picked
       against white; on #14181d they read muddy and vibrate. Same hues,
       lightened and desaturated. RGB triplets are supplied alongside so tinted
       fills can use rgba() -- color-mix() needs Chrome 111+ and the appliance's
       browser floor is not yet established.

       BUSINESSES IS THE ONE EXCEPTION -- it is a HUE CHANGE, not a correction,
       and it was made for two measured reasons, so do not "restore" the red:
         1. --dd-businesses was BYTE-IDENTICAL to --dd-err (#f87171 both). On the
            Businesses grid the module identity and the Delete glyph were the
            same colour, i.e. the destructive action had no colour of its own.
         2. Against Properties green under deuteranopia the red measured CIE
            dE 9.2 -- indistinguishable. Blue-violet measures 30.6, and is the
            only candidate tested that is far from BOTH People cyan and the
            error red under deuteranopia, protanopia AND tritanopia.
       #a78bfa runs 7.01 / 6.55 / 6.02 / 5.32 on --dd-s0..--dd-s3, so it clears
       AA on every surface in the set including the hover row. Warm neutrals were
       evaluated and are unavailable here: amber already owns the warm arc and
       deuteranopia collapses green onto it (copper dE 5.3 from --dd-err, sand
       dE 1.5 from Properties) -- all worse than the red being replaced.

       VEHICLES IS THE SECOND EXCEPTION, and the same kind: amber #fbbf24 was
       BYTE-IDENTICAL to --dd-warn, so warning severity had no colour of its own
       on this module. Measured symptoms: the archive .dd-count and the alert
       .dd-bubble rendered pixel-identical (same hue, same 18px circle) despite
       coming from --dd-mod and --dd-warn respectively; and on the detail page
       the ACTIVE TAB and the "!" alert marker were the same amber, so the amber
       severity tier read as decoration while red still carried.

       #dbc2b3 is warm stone, L* 80 / C* 13, and its low chroma is FORCED rather
       than chosen -- above about C*15 the warm arc collapses onto Properties
       green and the error red under deuteranopia (tan dE 5.0, caramel dE 7.1),
       which is the same wall the paragraph above hit. Worst case over every
       module and semantic token, across deuteranopia, protanopia and
       tritanopia: 19.1. It runs 11.25 / 10.51 / 9.65 / 8.54 on --dd-s0..--dd-s3,
       the highest of any accent in the set. Ruled out on the way: plum (the arc
       between violet and "reads as pink" is only 8.3 degrees), blue and indigo
       (dE 0.4 and 2.1 from --dd-businesses under deuteranopia), teal (boxed in
       between People cyan and Properties green; teal-600 fails AA at 3.87), and
       rose (rejected on brand association, not on measurement). ---- */
    --dd-people:         #3ecfe0;  --dd-people-rgb:     62, 207, 224;
    --dd-properties:     #4ade80;  --dd-properties-rgb: 74, 222, 128;
    --dd-vehicles:       #dbc2b3;  --dd-vehicles-rgb:  219, 194, 179;
    --dd-businesses:     #a78bfa;  --dd-businesses-rgb:167, 139, 250;

    /* ---- Semantic ---- */
    --dd-ok:   #4ade80;
    --dd-warn: #fbbf24;
    --dd-err:  #f87171;
    --dd-info: #3ecfe0;

    /* ---- Radius ---- */
    --dd-r-sm: 6px;
    --dd-r-md: 10px;
    --dd-r-lg: 16px;

    /* ---- Elevation: borders + tint, not large blurs. Cheap to paint on the
       Pi's VideoCore. NEVER use backdrop-filter: blur() anywhere -- it forces a
       full-surface readback every frame. ---- */
    --dd-e1: 0 1px 2px rgba(0, 0, 0, .5);
    --dd-e2: 0 4px 12px rgba(0, 0, 0, .55);
    --dd-e3: 0 12px 32px rgba(0, 0, 0, .6);

    /* ---- Spacing, 4px base ---- */
    --dd-1:  4px; --dd-2:  8px; --dd-3: 12px;
    --dd-4: 16px; --dd-5: 24px; --dd-6: 32px; --dd-7: 48px;

    /* ---- Type scale. No webfont: the appliance is offline and Bootstrap's
       system stack already resolves locally with correct Hebrew fallback.
       Keep form inputs at --dd-t-base (16px) or iOS Safari zooms on focus. ---- */
    --dd-t-xs:   .75rem;
    --dd-t-sm:   .8125rem;
    --dd-t-md:   .875rem;
    --dd-t-base: 1rem;
    --dd-t-lg:   1.125rem;
    --dd-t-xl:   1.5rem;
    --dd-t-2xl:  2rem;
}

/* ---- Bind Bootstrap to the tokens ---- */
[data-bs-theme="dark"] {
    --bs-body-bg:          var(--dd-s0);
    --bs-body-color:       var(--dd-text);
    --bs-emphasis-color:   #ffffff;
    --bs-secondary-color:  var(--dd-muted);
    --bs-secondary-bg:     var(--dd-s2);
    --bs-tertiary-bg:      var(--dd-s1);
    --bs-border-color:     var(--dd-line);
    --bs-border-radius:    var(--dd-r-md);
    --bs-border-radius-sm: var(--dd-r-sm);
    --bs-border-radius-lg: var(--dd-r-lg);
    --bs-link-color:       var(--dd-accent-hover);
    --bs-link-hover-color: var(--dd-accent);
    --bs-link-color-rgb:   255, 122, 61;
}

/* mb1.css paints the body pure black on 21 pages; override it here. */
[data-bs-theme="dark"] body {
    background-color: var(--dd-s0);
    color: var(--dd-text);
}

/* ---- The PRINT / EXPORT window, which is a different document ----
   Buttons' print action does window.open("","") and builds a BLANK document: it
   copies every <style> and <link> into the new head, then sets body.innerHTML.
   It does NOT copy the <html> attributes, so the print window has no
   data-bs-theme and no class="dark" -- every dark rule in this file is inert
   there, which is correct and is why print was never black-on-black on paper.

   What DID follow it across is mb1.css's unscoped `body { background-color:
   black }`, against Bootstrap's light default text of #212529. The preview
   window rendered near-black on black. That predates the dark rollout -- mb1.css
   has always been linked on these pages -- but it surfaces the moment anyone
   exercises Export > Print, so it is fixed here.

   Unscoped ON PURPOSE, and no @media print: the popup renders with SCREEN media
   until you actually hit print, so a print-only rule would leave the preview
   unreadable. It cannot leak into the app because [data-bs-theme="dark"] body
   above is 0,1,1 and wins there; and it beats mb1.css at equal specificity
   because this file loads last. */
body {
    background-color: #ffffff;
    color: #212529;
}

/* ---- One focus ring for the whole product, in the brand orange.
   #ff7a3d is 7.36:1 on --dd-s0 and 5.59:1 on --dd-s3, so it stays AA on every
   surface in the set. Bootstrap's default ring is primary-blue at 25% alpha and
   is close to invisible on a dark panel. :focus-visible (not :focus) keeps it
   off mouse clicks. Never set outline: none. ---- */
[data-bs-theme="dark"] :focus-visible {
    outline: 2px solid var(--dd-accent-hover);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--dd-accent-ring);
}

/* ==========================================================================
   GRID PAGES  (mbupload*.php -- 8 of them)

   EVERYTHING below is scoped to [data-bs-theme="dark"]. That is not stylistic:
   mb.css is shared with the four mbuploadsingle* DETAIL pages, which are NOT
   themed yet and must keep their existing light appearance. Scoping here is
   what lets the grids go dark without touching them. Do not lift any of these
   rules out of the scope until the detail pages are converted too.

   Several rules below use !important. That is also deliberate: the pages carry
   hardcoded inline styles inside JS-generated HTML strings, and an author
   !important beats a non-important inline style. It reaches ~6 export-button
   sites per page without editing 8 files' worth of string literals.
   ========================================================================== */

/* Row selection. mb.css sets --dt-row-selected: 0,0,0, so a selected row paints
   BLACK -- on a dark grid the row you just clicked vanishes. :root[...] rather
   than a bare attribute selector so this outranks mb.css's :root whatever the
   load order turns out to be. */
:root[data-bs-theme="dark"] {
    --dt-row-selected: 255, 80, 0;
    --dt-row-selected-text: 255, 255, 255;
}
/* ...but DataTables paints the selected row as a 95%-opaque fill and pairs it
   with WHITE text, and white on #ff5000 is only 3.28:1 -- it fails AA on the one
   row the user is looking at. A 28% tint instead of a fill keeps the row
   unmistakably orange while leaving the text at ~12:1, and sits better in a dark
   UI than a solid bar.

   !important is needed here and specificity alone will not do it: the bundle
   ships table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > *
   -- five classes plus a pseudo-class -- so a striped row that is also selected
   beats any reasonable hand-written selector. Do not "clean this up" by dropping
   the !important; it silently reverts to the unreadable white-on-orange. */
[data-bs-theme="dark"] table.dataTable > tbody > tr.selected > * {
    box-shadow: inset 0 0 0 9999px rgba(var(--dd-accent-rgb), .28) !important;
    color: var(--dd-text) !important;
}

/* Per-module accent, set once on the panel and inherited by everything inside
   it. The converter adds .dd-panel alongside the existing module class, so
   every rule below can say .dd-panel instead of listing all four. The -rgb twin
   is for rgba() tints; color-mix() would avoid it but needs Chrome 111+.

   TRAP: the --dd-mod-rgb triplets below are NOT derived from the --dd-*-rgb
   tokens, they are restated. Changing a module colour at the top of this file
   and stopping there gives you the new hue on the border and title but the OLD
   one on the glow, the pagination, the avatar ring and every hover tint. Change
   both lines, always. (Referencing var(--dd-people-rgb) here would remove the
   duplication and is safe -- left as literals only to match the existing
   style; if you touch this block again, prefer the var.) */
[data-bs-theme="dark"] .personsborders   { --dd-mod: var(--dd-people);     --dd-mod-rgb:  62, 207, 224; }
[data-bs-theme="dark"] .homesborders     { --dd-mod: var(--dd-properties); --dd-mod-rgb:  74, 222, 128; }
[data-bs-theme="dark"] .vehiclesborders  { --dd-mod: var(--dd-vehicles);   --dd-mod-rgb: 219, 194, 179; }
[data-bs-theme="dark"] .companiesborders { --dd-mod: var(--dd-businesses); --dd-mod-rgb: 167, 139, 250; }

/* The panel. mb.css paints it gainsboro with a double 5px darkcyan border --
   the single most dated element on the page. The 8 grid pages repeated that
   inline too; the converter strips the inline copy so this wins without
   !important.

   It gets the same vignette as the dashboard tiles: an accent-lit corner in the
   MODULE's own colour plus a 1px light seam along the top, so People reads as
   cyan-lit, Vehicles amber-lit, and the grid is visibly the same surface you
   clicked on the dashboard. Paint-time only -- no blur, nothing the Pi has to
   read back per frame. */
[data-bs-theme="dark"] .dd-panel {
    position: relative;
    background-color: var(--dd-s1);
    background-image: radial-gradient(115% 90% at 0% 0%,
                                      rgba(var(--dd-mod-rgb), .22) 0%,
                                      rgba(var(--dd-mod-rgb), 0) 72%);
    /* A tinted frame on all four edges rather than one bright accent bar. The
       4px module-coloured left rule was carried over from the dashboard tiles,
       where it reads as an accent because the tile is small; stretched across a
       full-width panel it read as a stray stripe with three dead edges. 35% of
       the module colour is visible as a frame without shouting, and the corner
       glow plus the title still carry the identity. */
    border: 1px solid rgba(var(--dd-mod-rgb), .5);
    border-radius: var(--dd-r-md);
    box-shadow: var(--dd-e2);
    padding: var(--dd-3);

    /* DEFAULT for the ghost-button pair, and it is load-bearing. The
       .btn:not(.dt-button) block far below reads --dd-btn / --dd-btn-rgb, but
       only five variants (.btn-success/-info/-warning/-danger/-primary, plus
       .selectrow, .dd-more and #Addnew) ever DEFINE them. Any other button in a
       panel -- a bare .btn, a .btn-outline-*, the btn-outline-success file-count
       control -- hit an undefined custom property, and the failure is silent and
       asymmetric: `color: var(--dd-btn)` is invalid-at-computed-value-time so it
       falls back to inherit and still looks fine, but
       `--bs-btn-hover-bg: rgba(var(--dd-btn-rgb), .18)` becomes the
       guaranteed-invalid value, so background-color computes to `initial` and
       HOVER DOES NOTHING AT ALL. A neutral default makes those buttons behave;
       the five variants still override it. */
    --dd-btn: var(--dd-muted);
    --dd-btn-rgb: 164, 176, 189;
}
[data-bs-theme="dark"] .dd-panel::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .16), transparent);
    pointer-events: none;
}

/* Page title. The 8 pages set this three different ways -- .text-info, an
   inline color:#28a745, and a .vehiclestitles class -- so the converter
   normalises them all to .dd-module-title and this colours it. A soft text
   glow in the module colour picks up the vignette behind it. */
[data-bs-theme="dark"] .dd-module-title {
    color: var(--dd-mod);
    text-shadow: 0 0 18px rgba(var(--dd-mod-rgb), .35);
}

/* <thead><tr class="table-info|success|warning|danger">. These four Bootstrap
   contextual classes are hardcoded light (.table-info is #cff4fc on #000) and
   do NOT respond to data-bs-theme, so the grid gets a glaring pale band across
   the top -- 18:1 against the panel, i.e. maximum glare. They express their
   colours as --bs-table-* custom properties, so overriding the properties is
   enough and the markup keeps its semantic class. */
[data-bs-theme="dark"] .table > thead > tr.table-info,
[data-bs-theme="dark"] .table > thead > tr.table-success,
[data-bs-theme="dark"] .table > thead > tr.table-warning,
[data-bs-theme="dark"] .table > thead > tr.table-danger {
    --bs-table-color: var(--dd-text);
    --bs-table-bg: var(--dd-s2);
    --bs-table-border-color: var(--dd-line);
}
/* Header icons are hardcoded inline as color:#ff5000 -- the brand orange, not a
   utility class -- so this needs !important to reach them. Retinted to the
   module colour, which is the "you are in People" signal and also lifts them
   from 4.99:1 (orange on --dd-s2, the weakest contrast on the page) to 8.74:1.

   That leaves a clean split worth keeping: the module colour means IDENTITY
   (which drawer you are in), brand orange means SYSTEM STATE (focus rings, row
   selection). Revert by deleting this one rule if you want the orange back.

   Scoped to thead, so the red and amber status icons in the BODY -- where the
   colour carries meaning -- are untouched. */
[data-bs-theme="dark"] .dd-panel table.dataTable > thead [class*="fa-"] {
    color: var(--dd-mod) !important;
}

/* Row density. The 75px avatar -- not the `compact` class, which the avatar
   overrode -- drove a ~93px row, so a full-height window showed four records.
   Shrinking it takes the row to ~63px, about eight.

   44px, matching the action buttons. Below that the avatar is no longer what
   sets the row height -- the 44px touch targets are -- so a smaller face buys
   no extra rows and only costs recognisability.

   Done in CSS, not the markup: the size is set with width="75" height="75"
   ATTRIBUTES, which are presentational hints that ANY css rule outranks, so
   this needs no !important and no per-page edit. Middle-aligning the cells
   matters more once the image no longer dictates the row height. */
[data-bs-theme="dark"] .dd-panel table.dataTable td img.rounded-circle {
    width: 44px;
    height: 44px;
}
[data-bs-theme="dark"] .dd-panel table.dataTable > tbody > tr > td { vertical-align: middle; }

/* Table body. bs5-compat.css already makes the cells transparent so the panel
   shows through, and BS5 computes striping from a SEPARATE variable
   (--bs-table-striped-bg, applied as an inset box-shadow), so striping keeps
   working and flips to white-5% on its own. Only the rules need toning down. */
[data-bs-theme="dark"] table.dataTable > tbody > tr > * { border-color: var(--dd-line-subtle); }
[data-bs-theme="dark"] table.dataTable.table-bordered > :not(caption) > * { border-color: var(--dd-line-subtle); }

/* The export button and its dropdown. className is 'btn-light btn-outline-light
   text-dark' and the label is an inline color:black span -- all written for a
   light page. */
[data-bs-theme="dark"] .dt-buttons .btn-light,
[data-bs-theme="dark"] .dt-buttons .btn-outline-light {
    --bs-btn-bg: var(--dd-s2);
    /* Export sits on the panel's top-left glow, where --dd-line measured
       1.13:1 -- effectively no edge. See --dd-line-control. The fill cannot
       help (--dd-s2 is 1.47:1 on the glow), so the border carries this alone.
       Neutral white rather than module cyan keeps Export secondary to the
       solid-filled Add button next to it. */
    --bs-btn-border-color: var(--dd-line-control);
    --bs-btn-hover-bg: var(--dd-s3);
    --bs-btn-hover-border-color: rgba(255, 255, 255, .55);
    --bs-btn-active-bg: var(--dd-s3);
    --bs-btn-active-border-color: rgba(255, 255, 255, .55);
}
[data-bs-theme="dark"] .dt-buttons .btn,
[data-bs-theme="dark"] .dt-buttons .btn *,
[data-bs-theme="dark"] .dt-button-collection .dt-button,
[data-bs-theme="dark"] .dt-button-collection .dt-button * { color: var(--dd-text) !important; }
/* The open menu had the same invisible-edge problem twice over: the collection's
   --dd-s2 surface is 1.09:1 on --dd-s1, so the shadow was doing all the work,
   and every item inside carried its own --dd-s2 fill + --dd-line border -- five
   invisible boxes stacked inside an invisible box. The menu now carries the
   surface; the items are plain rows. */
[data-bs-theme="dark"] .dt-button-collection {
    --bs-dropdown-border-color: var(--dd-line-control);
    /* beats DataTables' :root[data-bs-theme=dark] div.dropdown-menu.dt-button-collection.fixed */
    border-color: var(--dd-line-control) !important;
}
[data-bs-theme="dark"] .dt-button-collection .dt-button {
    background-color: transparent !important;
    border-color: transparent !important;
    border-radius: var(--dd-r-sm);
    min-height: 44px;   /* these are not .dropdown-item, so the 44px rule misses them */
}
[data-bs-theme="dark"] .dt-button-collection .dt-button:hover {
    background-color: var(--dd-s3) !important;
}

/* Alert-count bubble on the select button: the markup hardcodes
   background-color: black with border-radius 50%, which is invisible against a
   dark grid. The converter drops the inline background and adds .dd-bubble;
   mb.css keeps the black for the unthemed detail pages. currentColor picks up
   the text-danger / text-warning the bubble already carries, so the ring still
   signals severity. */
[data-bs-theme="dark"] .dd-bubble {
    background-color: var(--dd-s3);
    box-shadow: inset 0 0 0 1px currentColor;
}

/* Search box, info line and paging come from the DataTables bundle's own
   [data-bs-theme=dark] rules; only the search field's placeholder needs help. */
[data-bs-theme="dark"] .dt-search input::placeholder { color: var(--dd-dim); }

/* DataTables 2's bs5 layout wraps search / info / paging in Bootstrap .row
   elements carrying margin: 0 -12px negative gutters. The panel's padding used
   to absorb them, so any rule that REDUCES that padding makes them poke out and
   the panel scroll horizontally. That bit twice -- once from the phone padding,
   then again from the short-viewport padding at a width the phone rule did not
   cover -- so it is unconditional now rather than per-breakpoint. Harmless at
   full size: the rows simply align to the panel's own padding. */
[data-bs-theme="dark"] .dd-panel .dt-container > .row {
    --bs-gutter-x: 0;
    margin-left: 0;
    margin-right: 0;
}

/* Pagination's active page was the only Bootstrap blue left on the page. */
[data-bs-theme="dark"] .dt-paging .page-item.active .page-link {
    background-color: rgba(var(--dd-mod-rgb), .22);
    border-color: var(--dd-mod);
    color: var(--dd-text);
}
[data-bs-theme="dark"] .dt-paging .page-link { color: var(--dd-muted); }
[data-bs-theme="dark"] .dt-paging .page-link:hover {
    background-color: var(--dd-s3);
    color: var(--dd-text);
}

/* ==========================================================================
   ACTION BUTTONS

   Every row carried FOUR saturated fills -- green, cyan, amber, red, each with
   a black glyph -- inherited unchanged from the light design. On a dark surface
   four solid blocks per row read as four competing alerts rather than controls,
   and they were the loudest thing left on the page.

   Ghost them: the button becomes a quiet outline and the semantic colour moves
   to the GLYPH. Delete is still unmistakably the red one, at roughly a tenth of
   the coloured pixel area. The colour fills in on hover, so the affordance gets
   stronger exactly when you are aiming at it.

   Bootstrap exposes all of this as --bs-btn-* variables, so no !important --
   except the glyph, which the markup hardcodes as style="color:black" inside a
   JS string literal. An author !important beats a non-important inline style,
   which is what reaches it without editing 8 files' worth of strings.
   ========================================================================== */
[data-bs-theme="dark"] .dd-panel .btn:not(.dt-button) {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--dd-text);
    --bs-btn-border-color: var(--dd-line);
    --bs-btn-hover-bg: rgba(var(--dd-btn-rgb), .18);
    --bs-btn-hover-border-color: var(--dd-btn);
    --bs-btn-hover-color: var(--dd-text);
    --bs-btn-active-bg: rgba(var(--dd-btn-rgb), .30);
    --bs-btn-active-border-color: var(--dd-btn);
    /* WCAG 2.5.8 wants 24px; the practical touch minimum is 44. These were 38,
       with Delete sitting 1px from Edit. */
    min-width: 44px;
    min-height: 44px;
    transition: background-color .12s ease, border-color .12s ease, box-shadow .12s ease;
}
[data-bs-theme="dark"] .dd-panel .btn:not(.dt-button):hover {
    box-shadow: 0 0 0 3px rgba(var(--dd-btn-rgb), .12);
}
/* The glyph carries the colour. */
[data-bs-theme="dark"] .dd-panel .btn:not(.dt-button) > span[class*="fa"] {
    color: var(--dd-btn) !important;
}

/* ---- Export button: the :not(.dt-button) exemption above does NOT hold ----
   Buttons 3.2.3's bootstrap5 integration replaces dt-button on a TOOLBAR button
   with 'btn btn-secondary', so Export renders as
     btn btn-secondary buttons-collection btn-light btn-outline-light
     text-dark dropdown-toggle
   -- no dt-button, so :not(.dt-button) matches it and the generic ghost styling
   above applies to it after all. That rule is 0,4,0 and sits later in this file
   than the .dt-buttons .btn-light block, which is 0,3,0, so it also won the
   --bs-btn-border-color there and put --dd-line (1.30:1 on the panel, 1.13:1 on
   the module glow) back on the one control that most needed an edge. Measured
   in the real generated DOM, not inferred.

   Restated here at 0,5,0 and after the rule it has to beat. Only the edge is
   restated: the transparent fill is left as-is so Export still reads as a ghost
   next to the other ghost buttons. The collection ITEMS keep .dt-button and are
   correctly exempt, so they are unaffected by any of this. */
[data-bs-theme="dark"] .dd-panel .dt-buttons .btn.btn-light {
    --bs-btn-border-color: var(--dd-mod);
    --bs-btn-hover-border-color: var(--dd-mod);
    --bs-btn-active-border-color: var(--dd-mod);
}

/* ---- One module-coloured edge across the whole toolbar ----
   Archived, Export, Add, Back and the search field all carry border-color:
   var(--dd-mod) so the controls read as one set. Add is handled in the
   ACTION BUTTONS block; the others each need their own rule because each
   is overridden by something different:
     - #Archived and #gobackfarc by the generic .btn:not(.dt-button) ghost rule
       (0,4,0), which an id selector (1,2,0) clears outright.
     - Export by that same rule -- hence the 0,5,0 restatement just above.
     - the search input by Bootstrap's --bs-border-color, which is --dd-line.

   NOTE the toolbar differs between the live grids and the ARCHIVED views:
   live is Export + Archived + Add + search, archived is Export + Back + search
   (there is no #Addnew and no #Archived on an archived page). #gobackfarc is
   the archived pages' occupant of that slot and had NO rule here at all until
   2026-08-14, so it sat at --dd-line while everything beside it was
   module-edged -- on all four archived pages, not just Businesses.

   This also IMPROVES the contrast problem that started all of this: solid
   --dd-mod measures better on both the panel and the module glow than the
   rgba(255,255,255,.38) edge it replaces on Export (which was 3.58:1 / 3.03:1).
   --dd-line-control is still used by the export dropdown, so the token stays. */
[data-bs-theme="dark"] .dd-panel #Archived {
    --bs-btn-border-color: var(--dd-mod);
    --bs-btn-hover-border-color: var(--dd-mod);
    --bs-btn-active-border-color: var(--dd-mod);
    /* ...and the GLYPH, which until 2026-08-14 was module-coloured only BY
       COINCIDENCE. #Archived carries a different Bootstrap variant on every page
       -- btn-info on People, btn-success on Properties, btn-warning on Vehicles,
       btn-danger on Businesses -- because pre-dark markup used those classes as
       a way of saying "the module colour", not as a severity. The theme's
       semantic tokens then happened to be IDENTICAL to the module tokens
       (--dd-info == --dd-people, --dd-ok == --dd-properties, --dd-warn ==
       --dd-vehicles, and --dd-err == --dd-businesses while Businesses was red),
       so .btn-danger's --dd-btn resolved to the right colour by accident.

       Moving Businesses to blue-violet broke that on exactly one page: a violet
       button with a red archive glyph. Naming --dd-mod explicitly makes all four
       pages correct on purpose instead of by luck, and immune to the next accent
       change. An id (1,1,0) beats .dd-panel .btn-danger (0,2,0).

       A full sweep of the 8 grid pages found EXACTLY THREE id'd controls using a
       variant class as a module stand-in -- #Addnew, #Archived and #gobackfarc --
       plus .selectrow, which is not id'd but was already handled explicitly in
       the ACTION BUTTONS block. All four are now named. Every other btn-danger /
       btn-warning in the markup is genuinely semantic (deleterow, the
       .dd-bubble, the fa-lock indicator) and must keep its severity colour.
       CORRECTION: editrow was listed here as semantic and is NOT -- editing a
       row is not a warning about it. All 92 of them are now bound to --dd-mod
       beside .selectrow; see the note there.
       If you ever retint People, Properties or Vehicles, re-run that sweep --
       a hex grep cannot find this class of bug, only a class grep can. */
    --dd-btn: var(--dd-mod);
    --dd-btn-rgb: var(--dd-mod-rgb);
}
/* Back, on the four ARCHIVED pages: same slot, same treatment, same reason.
   It is navigation rather than a primary action, so it takes the peer-control
   look (transparent fill, module edge, module glyph) that #Archived has -- not
   the solid fill. Its variant class varies per page exactly like the others'. */
[data-bs-theme="dark"] .dd-panel #gobackfarc {
    --bs-btn-border-color: var(--dd-mod);
    --bs-btn-hover-border-color: var(--dd-mod);
    --bs-btn-active-border-color: var(--dd-mod);
    --dd-btn: var(--dd-mod);
    --dd-btn-rgb: var(--dd-mod-rgb);
}
/* Export's glyph joins the set. Only the icon, not the label: "the glyph carries
   the colour" is the rule the row actions already follow, and a fully cyan
   button would compete with the module title. !important and 0,5,0 to beat
   .dt-buttons .btn * { color: var(--dd-text) !important } above, which is 0,3,0,
   and the inline style="color:black" the JS text config hardcodes. */
[data-bs-theme="dark"] .dd-panel .dt-buttons .btn i[class*="fa-"] {
    color: var(--dd-mod) !important;
}
/* Bootstrap fills .form-control with --bs-body-bg (--dd-s0) on both rest and
   focus, so both states need clearing to match the buttons. */
[data-bs-theme="dark"] .dd-panel .dt-search input {
    border-color: var(--dd-mod);
    background-color: transparent;
}
[data-bs-theme="dark"] .dd-panel .dt-search input:focus {
    border-color: var(--dd-mod);
    background-color: transparent;
    box-shadow: 0 0 0 3px rgba(var(--dd-mod-rgb), .12);
}

/* Per-variant accents, dark-corrected. */
[data-bs-theme="dark"] .dd-panel .btn-success { --dd-btn: var(--dd-ok);   --dd-btn-rgb:  74, 222, 128; }
[data-bs-theme="dark"] .dd-panel .btn-info    { --dd-btn: var(--dd-info); --dd-btn-rgb:  62, 207, 224; }
[data-bs-theme="dark"] .dd-panel .btn-warning { --dd-btn: var(--dd-warn); --dd-btn-rgb: 251, 191,  36; }
[data-bs-theme="dark"] .dd-panel .btn-danger  { --dd-btn: var(--dd-err);  --dd-btn-rgb: 248, 113, 113; }
[data-bs-theme="dark"] .dd-panel .btn-primary { --dd-btn: var(--dd-mod);  --dd-btn-rgb: var(--dd-mod-rgb); }

/* The row-open chevron is the most-used control on the page, so it keeps the
   module colour rather than a semantic one. */
[data-bs-theme="dark"] .dd-panel .selectrow { --dd-btn: var(--dd-mod); --dd-btn-rgb: var(--dd-mod-rgb); }

/* The edit pencil, for the same reason and by a longer road.
   .editrow ships as btn-warning in the markup -- 92 of them across the 13 app
   pages -- so it was inheriting --dd-warn from the per-variant rule above. That
   made amber mean THREE things at once: Vehicles identity, warning severity,
   and "edit". A comment further down this file lists editrow among the
   "genuinely semantic" btn-warnings; that was wrong, and this rule is the
   correction. Editing a row is not a warning about it, and the pencil is amber
   on all four modules so it was never module furniture either.

   Module colour rather than neutral, matching .selectrow directly above: both
   are per-row controls belonging to the page, and the toolbar's own Add and
   Archive are already --dd-mod. A row now reads as "the page's controls in the
   page's colour, destruction in red" -- and --dd-warn is left to mean severity
   and nothing else. --dd-btn-rgb is the var, not a literal, so it follows any
   future retint on its own. */
[data-bs-theme="dark"] .dd-panel .editrow   { --dd-btn: var(--dd-mod); --dd-btn-rgb: var(--dd-mod-rgb); }

/* Send Notification: ORANGE, because it is the one row control that leaves the
   module. The icon vocabulary reads "module = the page's own controls, orange =
   cross-module action" -- this button mails a person from the People grid, so
   it is the definition of the second case. It shipped as btn-info, i.e. cyan.

   That was invisible until the pencil moved to --dd-mod above, because
   --dd-people is byte-identical to --dd-info: the pencil and this button became
   the same cyan, side by side in a four-button row. Recolouring THIS one is the
   correction that the vocabulary already implied, rather than pushing the
   pencil back onto a severity token to dodge the clash.

   --dd-accent-hover (#ff7a3d), NOT --dd-accent (#ff5000). Measured on this
   file's own surfaces, #ff5000 runs 5.81 / 5.43 / 4.99 / 4.41 and FAILS AA on
   --dd-s3, which is the hover row -- the glyph would drop below 4.5:1 exactly
   when the pointer is on it. #ff7a3d runs 7.36 / 6.88 / 6.32 / 5.59 and clears
   all four; the file already uses it as the legible orange for --bs-link-color
   and the focus ring. The -rgb is a literal because --dd-accent-hover has no
   -rgb companion; if one is ever added, switch to it.

   Only on mbupload.php and mbupload_archived.php -- .notifyrow exists nowhere
   else, so this cannot collide with the orange selection wash, which lives only
   on the detail pages. */
[data-bs-theme="dark"] .dd-panel .notifyrow { --dd-btn: var(--dd-accent-hover); --dd-btn-rgb: 255, 122, 61; }

/* "Add" — outlined, matching the .dd-count badge: near-black centre, module
   colour on the glyph and the border. #Addnew is the id all 8 grid pages use.

   This was previously the page's one SOLID fill, deliberately, so that the
   primary action stood out against the ghosted row actions. Outlining it is a
   considered trade: the toolbar now reads as one calm set, at the cost of Add no
   longer announcing itself as the primary action by weight alone. Hover fills in
   the module tint, so the affordance still strengthens on approach. */
[data-bs-theme="dark"] .dd-panel #Addnew {
    /* transparent, not --dd-s0: every control in this toolbar lets the panel's
       module glow show through, so a filled centre on one of them read as a
       different kind of control. */
    --bs-btn-bg: transparent;
    --bs-btn-border-color: var(--dd-mod);
    --bs-btn-hover-bg: rgba(var(--dd-mod-rgb), .18);
    --bs-btn-hover-border-color: var(--dd-mod);
    --bs-btn-active-bg: rgba(var(--dd-mod-rgb), .30);
    --bs-btn-active-border-color: var(--dd-mod);
}
[data-bs-theme="dark"] .dd-panel #Addnew:hover {
    box-shadow: 0 0 0 3px rgba(var(--dd-mod-rgb), .12);
}
/* !important because the markup hardcodes style="color:black" on the glyph. */
[data-bs-theme="dark"] .dd-panel #Addnew > span[class*="fa"] { color: var(--dd-mod) !important; }

/* ---- The avatar IS the "open this person" control ----
   The duplicate ">" column is collapsed by dd-grid.js, so the picture has to
   carry that affordance on its own. It cannot rely on hover: on a touchscreen
   there is none, so the ring is PERSISTENT and only intensifies on hover.
   The cell already had cursor:pointer and a "Select Person" tooltip. */
/* No display:none here on purpose. Hiding the cells with CSS left DataTables
   still modelling four columns, so it reserved the old column's width and the
   table ended with an empty band on the right that columns.adjust() would not
   reclaim. dd-grid.js hides it through column().visible(false) instead, which
   removes the cells and re-measures properly. */
/* The cell must centre its own content. The image carries Bootstrap's
   "mx-auto d-block", which centred it while it was a direct child of the cell --
   but .dd-avatar wraps it in an inline-block that shrinks to the image, so
   mx-auto became a no-op and the wrapper fell back to the cell's text-align
   (start), parking the avatar 82px left of the centred column header. */
[data-bs-theme="dark"] .dd-panel td.selectrow {
    cursor: pointer;
    text-align: center;
}
[data-bs-theme="dark"] .dd-avatar {
    position: relative;
    display: inline-block;
    line-height: 0;
}
[data-bs-theme="dark"] .dd-avatar > img {
    border-radius: 50%;
    box-shadow: 0 0 0 2px rgba(var(--dd-mod-rgb), .55);
    transition: box-shadow .15s ease, transform .15s ease;
}
[data-bs-theme="dark"] .dd-panel td.selectrow:hover .dd-avatar > img {
    box-shadow: 0 0 0 2px var(--dd-mod), 0 0 14px rgba(var(--dd-mod-rgb), .55);
    transform: scale(1.06);
}
/* A chevron on the avatar's corner says "this opens something" without needing
   hover. Same glyph the removed column used, so the meaning carries over. */
[data-bs-theme="dark"] .dd-avatar::after {
    content: "\f105";                      /* fa-angle-right */
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 17px;
    height: 17px;
    line-height: 17px;
    text-align: center;
    font-size: 11px;
    border-radius: 50%;
    color: var(--dd-s0);
    background: var(--dd-mod);
    pointer-events: none;
}
/* The alert bubble moves here from the old column. Top-left, opposite the
   chevron, so the two never collide. currentColor keeps the text-danger /
   text-warning severity the markup already carries. */
[data-bs-theme="dark"] .dd-avatar .dd-bubble {
    position: absolute;
    top: -4px;
    left: -4px;
    /* !important because the markup hardcodes width:25px;height:25px inline --
       25px on a 44px avatar is 57% of its diameter and covered a fifth of the
       face. 18px sits in the corner instead of on top of the person. width:auto
       + min-width so a two-digit count still fits. */
    width: auto !important;
    height: 18px !important;
    min-width: 18px;
    padding: 0 4px;
    line-height: 18px;
    font-size: 11px;
    font-weight: 700;
    text-align: center;
    border-radius: 999px;
    background-color: var(--dd-s0);
    box-shadow: inset 0 0 0 1px currentColor, 0 1px 3px rgba(0, 0, 0, .6);
}
/* ...but currentColor comes from Bootstrap's raw .text-danger / .text-warning,
   which this file's corrected hues never reached -- the one place on the grid
   still running BS4-era mid-tones picked against white. Measured on --dd-s0:
   #dc3545 is 4.21:1 and #ffc107 is 11.4:1. At 11px/700 the bubble is not "large
   text", so AA needs 4.5 -- the DANGER bubble, the single most important signal
   on the page, was the one thing failing. --dd-err takes it to 6.90:1.

   !important because .text-* is !important. Scoped to .dd-bubble so status
   colours elsewhere in the table body are untouched, exactly as the thead and
   navbar rules are scoped. */
[data-bs-theme="dark"] .dd-avatar .dd-bubble.text-danger  { color: var(--dd-err)  !important; }
[data-bs-theme="dark"] .dd-avatar .dd-bubble.text-warning { color: var(--dd-warn) !important; }
@media (prefers-reduced-motion: reduce) {
    [data-bs-theme="dark"] .dd-panel td.selectrow:hover .dd-avatar > img { transform: none; }
}

/* ---- Count badge on a toolbar button (Archived) ----
   Same 18px pill geometry as .dd-bubble above so the two read as one family,
   but filled in the module colour rather than ringed in a severity colour: an
   archive count is a quantity, not an alert. Solid --dd-mod on --dd-s0 text
   matches the avatar chevron, which is the other module-coloured marker on this
   screen.

   Absolute, so the count no longer widens the button as it grows -- the whole
   point. min-width 18px + padding keeps 1 and 999 both legible. Offsets are
   -4px, which stays inside .dd-panel's own padding, so .dd-panel's overflow:auto
   (from mb.css) has nothing to clip. */
[data-bs-theme="dark"] .dd-panel #Archived { position: relative; }
[data-bs-theme="dark"] .dd-panel .dd-count {
    position: absolute;
    /* Top-LEFT, matching .dd-bubble on the avatar, so every count on the screen
       sits in the same corner. */
    top: -4px;
    left: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    line-height: 18px;
    font-size: 11px;
    font-weight: 700;
    text-align: center;
    border-radius: 999px;
    /* Outlined rather than filled: near-black centre, module-coloured digits and
       a 1px module-coloured ring. Same construction as .dd-bubble above (which
       rings in currentColor for severity), so the two are visibly one family --
       this one just states the module colour directly, since a count on a
       toolbar button has no severity to carry. */
    color: var(--dd-mod);
    background: var(--dd-s0);
    box-shadow: inset 0 0 0 1px var(--dd-mod), 0 1px 3px rgba(0, 0, 0, .6);
    pointer-events: none;   /* the tooltip belongs to the button underneath */
}

/* ---- Row overflow menu ----
   Edit stays out; Account Settings / Send Notification / Delete move into a
   "..." menu. See docs/dark-convert.py for why, and v2/mb/js/dd-grid.js for the
   Popper fixed-strategy trick that stops .dd-panel's overflow clipping it. */
[data-bs-theme="dark"] .dd-actions {
    display: flex;
    justify-content: center;
    align-items: center;
}
/* Both layouts are in the DOM; CSS picks one. Only ever one is visible, so the
   shared setuprow / notifyrow / deleterow handlers cannot fire twice.

   576px, and it is MEASURED, not inherited: the four-button layout starts
   fitting at 520px (it overflows at 480), so Bootstrap's sm boundary gives ~56px
   of headroom for longer names. This deliberately does NOT match the navbar's
   768px -- that one is set by what the nav needs (744px) and this one by what
   the table needs. An earlier version reused 768 here for symmetry and that was
   simply wrong: it pushed every landscape phone at 667 or 736 CSS px onto the
   overflow menu even though they had room to spare. */
[data-bs-theme="dark"] .dd-actions-wide { display: none; }
[data-bs-theme="dark"] .dd-actions-narrow { display: inline-flex; align-items: center; gap: var(--dd-2); }
@media (min-width: 576px) {
    [data-bs-theme="dark"] .dd-actions-wide { display: inline-flex; align-items: center; gap: var(--dd-2); }
    [data-bs-theme="dark"] .dd-actions-narrow { display: none; }
}
/* The "..." toggle is deliberately neutral -- it is a container, not an action. */
[data-bs-theme="dark"] .dd-panel .dd-more { --dd-btn: var(--dd-muted); --dd-btn-rgb: 164, 176, 189; }
[data-bs-theme="dark"] .dd-panel .dd-more::after { display: none; }  /* no caret */

[data-bs-theme="dark"] .dd-panel .dropdown-menu {
    --bs-dropdown-bg: var(--dd-s2);
    --bs-dropdown-border-color: var(--dd-line);
    --bs-dropdown-color: var(--dd-text);
    --bs-dropdown-link-color: var(--dd-text);
    --bs-dropdown-link-hover-bg: var(--dd-s3);
    --bs-dropdown-link-hover-color: var(--dd-text);
    --bs-dropdown-link-active-bg: var(--dd-s3);
    --bs-dropdown-link-active-color: var(--dd-text);
    --bs-dropdown-divider-bg: var(--dd-line);
    box-shadow: var(--dd-e3);
}
[data-bs-theme="dark"] .dd-panel .dropdown-item {
    display: flex;
    align-items: center;
    min-height: 44px;          /* touch target, same as the buttons */
    font-size: var(--dd-t-md);
}
[data-bs-theme="dark"] .dd-panel .dropdown-item > span[class*="fa"] { color: var(--dd-muted); }

/* Delete is destructive and is the one item that must not be mistaken for its
   neighbours, so it keeps its colour and sits below a divider. */
[data-bs-theme="dark"] .dd-panel .dropdown-item.dd-danger,
[data-bs-theme="dark"] .dd-panel .dropdown-item.dd-danger > span[class*="fa"] { color: var(--dd-err); }
[data-bs-theme="dark"] .dd-panel .dropdown-item.dd-danger:hover,
[data-bs-theme="dark"] .dd-panel .dropdown-item.dd-danger:focus {
    background-color: rgba(248, 113, 113, .15);
    color: var(--dd-err);
}

/* ==========================================================================
   THE GRID TOOLBAR

   The title, Export, Archived and Add used to land on three separate bands,
   costing ~258px above the first data row at 390px -- four records' worth of
   chrome. Three independent causes, none of them taste:

   1. datatables.min.css has, below 768px, div.dt-buttons{float:none;width:100%}
      and Bootstrap has .btn-group > .btn{flex:1 1 auto}. The bs5 integration
      builds the container as div.dt-buttons.btn-group, so Export was stretched
      to the full panel width (~356px at 390px). That is why it could not share
      a line with the title and why it read as a floating label, not a button.
   2. #Addnew and #Archived both carry float-end. Floats are out of flow, so
      they landed after the h3's line boxes -- a band of their own. It also
      reversed them: source order is Add-then-Archived.
   3. The search box is not in this div at all; it is in DataTables' layout,
      where col-md-auto does not apply below 768px, so the cell is a full-width
      block and dt-search gets text-align:center.

   576px is used below because that is where Export's text label stops fitting.
   That is a third independent measurement -- it coincides with the action-column
   breakpoint, it does not inherit from it.
   ========================================================================== */

/* Un-stretch Export. 0,3,0 against DataTables' 0,1,1 and dd-theme loads last,
   so no !important is needed. */
[data-bs-theme="dark"] .dd-panel .dt-buttons {
    float: none;
    width: auto;
    margin-bottom: 0;
    text-align: start;
}
/* NOT .dt-button. Buttons 3.2.3's bootstrap5 integration REPLACES the default
   dt-button class on a toolbar button with 'btn btn-secondary', so the rendered
   class list is 'btn btn-secondary buttons-collection btn-light
   btn-outline-light text-dark dropdown-toggle' -- no dt-button anywhere. (The
   collection's ITEMS do keep it: they render as
   <a class="dt-button dropdown-item ...">, which is why the menu rules below
   still key off .dt-button.) Verified against the real generated DOM. */
[data-bs-theme="dark"] .dd-panel .dt-buttons > .btn { flex: 0 0 auto; }
/* The 44px minimum deliberately excludes .dt-button; Export was the one control
   on the page still at 38px. */
[data-bs-theme="dark"] .dd-panel .dt-buttons .btn { min-height: 44px; }

/* The toolbar row itself. flex-wrap so it degrades rather than overflows when
   the viewport shrinks (Samsung "Screen zoom" narrows the CSS viewport to
   ~360px without scaling type down with it). */
[data-bs-theme="dark"] .dd-panel > .clearfix {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--dd-2);
    row-gap: var(--dd-2);
    margin-bottom: var(--dd-2);
}
/* .clearfix::after is display:block, so inside a flex container it becomes a
   zero-width flex ITEM and the gap adds 8px of dead space after the last
   button -- enough to force a wrap at 360px. The floats it existed to clear
   stop being floats in here anyway. */
[data-bs-theme="dark"] .dd-panel > .clearfix::after { display: none; }

/* As display:inline the h3's margin-bottom was inert; blockified as a flex item
   it comes back and adds 8px. --dd-t-xl also replaces Bootstrap's RFS
   calc(1.3rem + .6vw), which is 23.1px at 390px and 28px above 1200px. */
[data-bs-theme="dark"] .dd-module-title {
    margin: 0;
    line-height: 1.2;
    font-size: var(--dd-t-xl);
    white-space: nowrap;
}

/* The button cluster, pinned right. display needs !important ONLY because the
   markup carries .d-inline, which is display:inline!important. The h3 and the
   buttons do not need it -- flex blockification happens after the cascade, so
   display:inline on a flex item is used as block regardless. float-end on the
   children needs no edit either: floats do not apply to flex items, so it goes
   inert on its own -- but the order flips back, hence the explicit order below. */
[data-bs-theme="dark"] .dd-panel #stdusr,
[data-bs-theme="dark"] .dd-panel .dd-toolbar-actions {
    display: flex !important;
    align-items: center;
    gap: var(--dd-2);
    margin-left: auto;
}
[data-bs-theme="dark"] .dd-panel #stdusr .my-export-wrapper,
[data-bs-theme="dark"] .dd-panel .dd-toolbar-actions .my-export-wrapper { display: inline-flex; }
/* Rarest to most frequent, left to right: Export, Archived, Add. Add is the
   only solid fill and sits nearest the right thumb. */
[data-bs-theme="dark"] .dd-panel #stdusr #Addnew,
[data-bs-theme="dark"] .dd-panel .dd-toolbar-actions #Addnew { order: 2; }
/* #Archived carries .me-1, which would stack 4px on top of the gap. .me-1 is a
   Bootstrap utility, hence !important. */
[data-bs-theme="dark"] .dd-panel #stdusr #Archived,
[data-bs-theme="dark"] .dd-panel .dd-toolbar-actions #Archived { margin-right: 0 !important; }

/* Phones. With the overflow menu the table is ~412px against ~361px of usable
   panel at 390px wide, so it is close but not there. Reclaiming cell and panel
   padding closes the rest -- the alternative would be dropping the Picture
   column, which costs more than it saves now that the avatar is only 44px.
   MUST STAY AFTER the rules it overrides: same specificity, source order wins. */
@media (max-width: 575.98px) {
    [data-bs-theme="dark"] .dd-panel { padding: var(--dd-1); }
    [data-bs-theme="dark"] .dd-panel table.dataTable > thead > tr > th,
    [data-bs-theme="dark"] .dd-panel table.dataTable > tbody > tr > td {
        padding-left: 3px;
        padding-right: 3px;
    }
    [data-bs-theme="dark"] .dd-actions { gap: var(--dd-1); }

    [data-bs-theme="dark"] .dd-module-title { font-size: 1.25rem; }

    [data-bs-theme="dark"] .dd-panel .dt-buttons .btn { min-width: 44px; }

    /* Search: left-aligned, full width, and a real touch target. At
       form-control-sm's 31px/0.875rem it was the only sub-44px control on a
       page whose whole premise is 44px, and 14px inputs contradict the 16px
       rule stated in the type-scale block above. */
    [data-bs-theme="dark"] .dd-panel .dt-search { text-align: start; }
    [data-bs-theme="dark"] .dd-panel .dt-search input {
        width: 100%;
        max-width: none;
        margin-left: 0;
        min-height: 44px;
        font-size: var(--dd-t-base);
    }
    /* .mt-2 is a Bootstrap utility and is itself !important. */
    [data-bs-theme="dark"] .dd-panel .dt-container > .row:first-child { margin-top: 0 !important; }

    /* 54px at the top of every screen for a 38px logo. Only -y, and only inside
       the dark scope, so bs5-compat.css's deliberate .navbar{--bs-navbar-padding-x}
       pin stays intact for the pages still on the light theme. */
    [data-bs-theme="dark"] .navbar { --bs-navbar-padding-y: .25rem; }

    /* (the negative-gutter fix this used to hold is unconditional now -- see
       the rule above the media queries) */
    /* 44 -> 40 on the avatar and dropping the vertical cell rules are the last
       ~9px. Both are invisible at this size; truncating names would not be. */
    [data-bs-theme="dark"] .dd-panel table.dataTable td img.rounded-circle {
        width: 40px;
        height: 40px;
    }
    [data-bs-theme="dark"] .dd-panel table.dataTable.table-bordered > :not(caption) > * > * {
        border-left-width: 0;
        border-right-width: 0;
    }
}

/* Export goes icon-only, but ONLY when genuinely cramped -- deliberately NOT at
   the 575.98px boundary above. Measured: the cluster is 177px with the label and
   153px without, so at a typical 411-480px phone the label fits comfortably on
   the single toolbar row and dropping it would just lose a word for nothing.

   420px is a judgement call, not a measurement, and it is the one number here I
   could not verify: Chrome on Android also applies the OS font-scale multiplier
   to computed font sizes, so a device with a large "Font size and style" setting
   wraps at a viewport width that no px media query can predict. If the toolbar
   still wraps on a real device, the robust fix is a ResizeObserver in dd-grid.js
   that toggles a class when the row wraps -- not a bigger number here.

   The label is the <span> right after the <i class="fa-file-download">; the
   .dropdown-toggle caret stays, so the "opens a menu" affordance survives. */
@media (max-width: 420px) {
    [data-bs-theme="dark"] .dd-panel .dt-buttons .btn i.fa-file-download + span { display: none; }
}

/* ==========================================================================
   NAVBAR

   The nav labels are <h4> elements used as a font-size utility inside links --
   21.9px, which is why the inline nav needed 963px and why the bar collapsed to
   a hamburger all the way up to 1200px (navbar-expand-xl), wasting 218px of
   empty logo row on a phone and a lot more on a tablet.

   At --dd-t-md with tighter link padding the same nav needs 744px, so the
   converter drops the breakpoint to navbar-expand-md (768px) and the inline nav
   comes back for landscape phones and tablets. Measured: 963 -> 785 desktop,
   744 with the smaller brand below lg.

   Restyled here rather than in the markup because these are 6 <h4> tags per
   page across 12 pages; replacing the tags is worth doing, but it is a
   different change from the theme and should not ride along with it.
   ========================================================================== */
[data-bs-theme="dark"] .navbar { --bs-navbar-nav-link-padding-x: .4rem; }
[data-bs-theme="dark"] .navbar h4 {
    font-size: var(--dd-t-md);
    font-weight: 500;
    margin: 0;
}
@media (max-width: 991.98px) {
    /* Was 38px, which is now LARGER than the 36px base -- the logo would have
       grown as the screen shrank. Each breakpoint must step DOWN from the one
       above it: 44 desktop / 38 here / 32 on a landscape phone. Keep every step
       at or above 38px if you can -- below that the wordmark drops under the
       nav-link text and the brand starts losing to its own menu. */
    [data-bs-theme="dark"] .navbar-brand img { height: 38px; width: auto; }
}

/* ---- The bar's own surface ----
   .bg-dark resolves to rgba(var(--bs-dark-rgb), 1), and Bootstrap 5.3 does NOT
   redefine --bs-dark-rgb under [data-bs-theme=dark] -- it stays 33,37,41 from
   :root. So the navbar rendered at #212529 while this file declares
   --dd-s2: #1b2027 as "raised: navbar, ...". The navbar was the one surface in
   the app ignoring its own token, and at #212529 over a #0d1013 canvas it was
   the LIGHTEST large area on the screen -- brighter than the panel it sits
   above. Chrome out-shouting content.

   Overriding the variable rather than the property because .bg-dark is
   !important. Scoped to .navbar.bg-dark deliberately: a bare --bs-dark-rgb
   override would also repaint .text-dark / .border-dark elsewhere on the page. */
[data-bs-theme="dark"] .navbar.bg-dark { --bs-dark-rgb: 13, 16, 19; }   /* --dd-s0 */
[data-bs-theme="dark"] .navbar { border-bottom: 1px solid var(--dd-line-subtle); }

/* ---- Link and glyph colours ----
   navbar-dark ships --bs-navbar-color: rgba(255,255,255,.55), which composites
   to #9b9d9f. That passes AA but reads as DISABLED. */
[data-bs-theme="dark"] .navbar {
    --bs-navbar-color:        var(--dd-muted);
    --bs-navbar-hover-color:  var(--dd-text);
    --bs-navbar-active-color: var(--dd-text);
}
/* Three of the six nav glyphs failed AA on the old bar: text-success 3.40:1,
   text-danger 3.41:1, text-primary 3.43:1. The corrected hues at the top of this
   file exist for exactly this reason ("muddy and vibrate" on a dark surface);
   the navbar never got them. They stay COLOURED rather than muted because in
   here the four read as a wayfinding legend matching the dashboard tiles, not
   as one page's accent. .text-* is !important, so these must be too. Scoped to
   .navbar .nav-link so body-cell status colours are untouched. */
[data-bs-theme="dark"] .navbar .nav-link .text-info    { color: var(--dd-people)     !important; }
[data-bs-theme="dark"] .navbar .nav-link .text-success { color: var(--dd-properties) !important; }
[data-bs-theme="dark"] .navbar .nav-link .text-warning { color: var(--dd-vehicles)   !important; }
[data-bs-theme="dark"] .navbar .nav-link .text-danger  { color: var(--dd-businesses) !important; }
[data-bs-theme="dark"] .navbar .nav-link .text-primary { color: var(--dd-muted)      !important; }

/* Bootstrap 5 styles .nav-link.active, NOT .nav-item.active -- BS4 did the
   latter, so --bs-navbar-active-color has been dead since the port even though
   the markup carries the class correctly on all four grids. */
[data-bs-theme="dark"] .navbar .nav-item.active > .nav-link { color: var(--dd-text); }

/* ---- Toggler ----
   The default border is rgba(255,255,255,.1) = 1.36:1: visible enough to notice
   as a box, not enough to read as a control. Dropping the box rather than
   strengthening it -- the toolbar directly below now owns four module-bordered
   controls, and a fifth outlined rectangle above them competes. The stroke
   colour is baked into the data-URI, so recolouring the glyph means replacing
   the token, not setting `color`. */
[data-bs-theme="dark"] .navbar-toggler {
    border-color: transparent;
    --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23e8edf2' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
/* --dd-accent, not --dd-mod: the navbar is a SIBLING that precedes
   .personsborders, so --dd-mod is not in scope here and would resolve to
   nothing. The brand orange is the right colour for chrome regardless -- it
   matches the logo mark and leaves the module accent to the panel. */
[data-bs-theme="dark"] .navbar-toggler:focus { box-shadow: 0 0 0 3px var(--dd-accent-ring); }

/* ---- Brand: legible wordmark, and stop paying for dead space ----
   images/mb50.png has the "DigiDrawer" wordmark baked in as 394 near-black
   #101013 pixels -- 1.23:1 on the old bar and exactly 1.00:1 on --dd-s0, i.e.
   invisible. No CSS fixes pixel colour, so mb50-light.png is the same art with
   those pixels (and their antialiased edges, 779 in total) moved to --dd-text
   for 13.1:1. The orange #FF5000 mark is untouched.

   NOTE: this does NOT fix sharpness. The source is 50px tall in a 38px box at
   DPR 3, a 2.3x upscale, and resampling cannot add detail that was never
   captured. A real fix needs vector art -- no SVG of this logo exists anywhere
   in the repo.

   Of the ~60px bar at <576px only 38 was logo: 8 navbar padding, 10 brand
   padding, ~4 inline-image baseline descender. The last two are pure dead
   space. display:block kills the baseline gap, which matters more than it
   looks -- its size depends on the OS font scale, so removing it is robust
   where trimming padding to compensate would not be. */
[data-bs-theme="dark"] .navbar-brand img { content: url("../web/images/mb50-light.png?v=20260826"); }
[data-bs-theme="dark"] .navbar-brand { padding-top: 0; padding-bottom: 0; }
[data-bs-theme="dark"] .navbar-brand img { display: block; }

/* ---- Size: 50px -> 44px, and take the rest out of PADDING, not the logo ----
   Measured off the actual art before choosing a number, and the measurement
   changed the answer. The art has since been REPLACED (2026-08-24) with a
   supplied white-wordmark PNG, cropped of 134px of dead right margin and
   rendered at 417x132 -- 3x the 44px it displays at, so it is finally sharp on
   a high-DPR screen. Its aspect is 3.16:1 rather than the old 3.4:1, which
   changes nothing here because width is auto. The proportions below were
   measured on the PREVIOUS 170x50 art and are kept because the reasoning about
   wordmark legibility still drove the 44px number:
     - the orange mark occupies the FULL 50px height (cols 0-64)
     - the "DigiDrawer" wordmark cap height is only 19px, 38% of the art
   i.e. the icon is 2.63x the wordmark. That ratio is baked into the raster.

   The consequence is that shrinking the lockup shrinks the WORDMARK fastest,
   and the wordmark is what has to hold its own against 18px nav links:
       50px logo -> 19.0px wordmark   (just above the nav text -- balanced)
       44px      -> 16.7px
       40px      -> 15.2px
       36px      -> 13.7px            (clearly subordinate to the navigation)
   A first pass shipped 36px and the brand visibly lost to its own menu.

   So: only a modest 12% off the logo, and the rest of the saving comes from the
   navbar's own 8+8 padding, which costs the same pixels and costs the brand
   nothing. 44 + 4 + 4 + 1 = 53px, exactly what the 36px version bought, with
   the wordmark still reading as a peer of the nav rather than a footnote.

   Downscaling is the safe direction for this asset, and as of the 2026-08-24
   replacement it is also finally the ACTUAL direction: the file is 132px tall
   and displays at 44, so a DPR 3 phone gets the pixels it asks for. The older
   note here said "it is still not sharp -- vector art is the real fix", which
   was true of the 170x50 raster; an SVG would still be better, but the shortfall
   is no longer visible.

   width:auto so the width attribute -- 170 in nine of these pages, now wrong
   for the new 3.16:1 art -- cannot fight the height and squash the aspect. */
[data-bs-theme="dark"] .navbar-brand img { height: 44px; width: auto; }
[data-bs-theme="dark"] .navbar { padding-top: var(--dd-1); padding-bottom: var(--dd-1); }

/* The brand sat 28px from the viewport edge against the panel's 12px: the nav
   is inside a container-fluid (12px gutter) AND bs5-compat.css adds
   --bs-navbar-padding-x: 1rem on top. That pin exists because BS5 expects an
   inner .container and these navs have none, so un-pinning is only safe where
   an OUTER container supplies the gutter. The `container-fluid >` qualifier is
   what confines this to the 8 grid pages: the 4 detail pages and mb.php have
   no wrapper, and there the logo would go flush to the viewport edge.
   NEVER edit the pin in bs5-compat.css -- it is loaded by 30 files. */
[data-bs-theme="dark"] .container-fluid > .navbar { --bs-navbar-padding-x: 0; }

/* mb.php's navbar is a direct child of <body> with no wrapper, so the rule above
   deliberately misses it and the 1rem pin still supplies its gutter. But its
   content sits in <main class="dd-page container">, and .container's inset is
   --bs-gutter-x * .5 = 12px, so the brand sat 4px outside the content column.
   .75rem matches it. body > .navbar cannot reach the grid pages (theirs is
   inside container-fluid) nor the 4 unthemed detail pages. */
[data-bs-theme="dark"] body > .navbar { --bs-navbar-padding-x: .75rem; }

/* The label size only has to be small where it is fighting the breakpoint. The
   nav needs 744px at --dd-t-md with the reduced brand, which is what lets it go
   inline at 768. From 992 up there is nothing to fight, so the labels grow back
   towards their old presence -- 14px was a big drop from the original 21.9px
   and read as undersized on a desktop screen.

   Measured inline width at each size (full-size brand): .875rem 765, 1rem 812,
   1.125rem 857, 1.25rem 901. 1.125rem at the 992 breakpoint leaves ~135px of
   slack for a longer username, which is the part that varies. */
@media (min-width: 992px) {
    [data-bs-theme="dark"] .navbar { --bs-navbar-nav-link-padding-x: .6rem; }
    [data-bs-theme="dark"] .navbar h4 { font-size: 1.125rem; }
}

/* When it IS collapsed, everything in the panel aligns to the same edge. The
   user/logout group sits in a second <ul> carrying justify-content-end + ms-auto
   (both !important), so four links sat left and two sat right -- the two groups
   pinned to opposite sides of the dropped panel. */
@media (max-width: 767.98px) {
    /* .navbar-expand-md qualifies every rule in this block. Without it these two
       also hit mb.php, whose navbar is .navbar-expand (always inline, one Logout
       link): flex-start + margin-left:0 cancelled its ms-auto and pulled Logout
       up against the brand instead of the right edge. Only navbars that actually
       collapse should be re-aligned into a column. */
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse .navbar-nav {
        justify-content: flex-start !important;
        margin-left: 0 !important;
    }
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse .nav-link { padding-left: 0; }

    /* The dropped panel is the app's entire primary navigation and had no
       styling at all: bare links on the bar's own colour, no separator, 37px
       rows, and the glyph jammed against its label because the markup has no
       whitespace between </span> and <h4>.

       flex goes on .nav-link (the <a>), NEVER on .nav-item: every nav <li>
       ships as "nav-item d-none" and the per-security-level JS reveals it with
       classList.add('d-block'), which works via bs5-compat's
       .d-none.d-block{display:block!important}. A display on .nav-item would
       lose to that !important and break the gating. Flex-item blockification
       then handles the d-inline !important on the child span and h4 anyway. */
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse {
        border-top: 1px solid var(--dd-line-subtle);
        margin-top: var(--dd-2);
    }
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse .nav-link {
        display: flex;
        align-items: center;
        gap: var(--dd-2);
        min-height: 44px;
        padding-right: var(--dd-2);
        border-radius: var(--dd-r-sm);
    }
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse .nav-link:hover { background: var(--dd-s2); }
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse .nav-item.active > .nav-link {
        background: var(--dd-s2);
        box-shadow: inset 3px 0 0 var(--dd-accent);   /* --dd-mod is out of scope here */
    }
    /* User + logout are account actions, not modules -- separate them. */
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse #shbanner1 { margin-top: var(--dd-2); }
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse #shbanner1 > .nav-link {
        border-top: 1px solid var(--dd-line-subtle);
        border-radius: 0;
    }
    /* .25rem of navbar padding is too tight under the last link once open. */
    [data-bs-theme="dark"] .navbar-expand-md .navbar-collapse.show { padding-bottom: var(--dd-2); }
}

/* ==========================================================================
   DESKTOP COLUMN WIDTHS

   The table stretches to 100% and DataTables handed the slack to the wrong
   columns: 411px of Picture for a 44px avatar and 527px of Actions for two
   buttons, leaving the content marooned. Cap both and let Name -- the only
   column whose content actually varies -- absorb the remainder.

   !important because DataTables writes the computed widths inline, and an
   inline style beats a plain rule.
   ========================================================================== */
@media (min-width: 992px) {
    [data-bs-theme="dark"] .dd-panel table.dd-select-collapsed > thead > tr > th:first-child {
        width: 130px !important;
    }
    [data-bs-theme="dark"] .dd-panel table.dd-select-collapsed > thead > tr > th:last-child {
        width: 170px !important;
    }
}

/* ==========================================================================
   SHORT VIEWPORTS (landscape phones)

   232px of chrome sat above the first row in a 383px viewport, leaving 2 of 4
   records visible. Same treatment the dashboard already has: shrink the
   furniture, not the data.
   ========================================================================== */
@media (max-height: 520px) {
    [data-bs-theme="dark"] .navbar { padding-top: 0; padding-bottom: 0; }
    [data-bs-theme="dark"] .navbar-brand { padding-top: 0; padding-bottom: 0; }
    [data-bs-theme="dark"] .navbar-brand img { height: 32px; width: auto; }
    [data-bs-theme="dark"] .dd-panel { padding: var(--dd-2); }
    [data-bs-theme="dark"] .dd-module-title { font-size: 1.15rem; }
    /* DataTables stacks title / search / table as separate rows with mt-2 each. */
    [data-bs-theme="dark"] .dd-panel .dt-container > .row { margin-top: 0 !important; }
    [data-bs-theme="dark"] .dd-panel .dt-search { margin-bottom: 0; }
}

/* Bootstrap INVERTS tooltips in dark mode (--bs-tooltip-bg is the emphasis
   colour, i.e. white), which on this page fires a stark white box next to every
   button you aim at. There are ~530 tooltips in the app, so this matters. A
   raised dark surface reads as part of the page and still runs 12.29:1. */
[data-bs-theme="dark"] .tooltip {
    --bs-tooltip-bg: var(--dd-s3);
    --bs-tooltip-color: var(--dd-text);
    --bs-tooltip-opacity: 1;
}

/* ==========================================================================
   DATATABLES EDITOR  --  the dialogs, the bubble, the error text

   NOT scoped to .dd-panel, and that is the whole point. Editor's bootstrap5
   display appends its modal to <body>, so every rule in this file that keys off
   .dd-panel stops at the panel edge and NONE of them reach the dialog. The
   editing surface -- which is the actual product -- was the one part of the app
   the dark rollout never touched.

   These are scoped to [data-bs-theme="dark"] like everything else, so the four
   unthemed mbuploadsingle* detail pages keep their light Editor until they are
   converted.
   ========================================================================== */

/* ---- 1. The dialog buttons had invisible icons ----
   The Cancel / Save / Send / Delete labels are HTML string literals in each
   page's JS:

     text: '<span style="color:black" class="far fa-times-circle">
              <span style="color:white"> Cancel</span></span>'

   -- the icon is hardcoded black, the word is white. On a dark modal the glyph
   ran 1.06:1, so every dialog in the product showed a floating word with no
   icon. 56 sites across the 8 grid pages; some pages use a classed variant
   (.saveicon/.savetxt) with no inline colour and were always fine.

   Both spans are forced to inherit so the button's own --bs-btn-color governs.
   An author !important is what beats a non-important inline style -- the same
   mechanism this file already uses for the row-action glyphs -- and it reaches
   all 56 without editing eight files' worth of string literals. */
[data-bs-theme="dark"] div.DTE div.DTE_Form_Buttons button span {
    color: inherit !important;
}

/* ---- 2. ...and so did the dialog TITLES ----
   .title() strings carry '<span style="color: black;"> : </span>' as the
   separator between "Edit Person" and the record name -- 20 more sites. The
   :not([class]) is doing real work: the SAME titles also carry
   <span class="fas fa-user-edit text-info">, and that icon must keep its colour.
   The separator span is the only one in the header with no class at all. */
[data-bs-theme="dark"] div.DTE div.DTE_Header span:not([class]) {
    color: inherit !important;
}

/* ---- 2b. The module icon in the dialog title ----
   The SAME titles carry a module glyph -- fa-user/-home/-car/-industry -- keyed
   by the identical four-colour legend the navbar uses: text-info = People,
   text-success = Properties, text-warning = Vehicles, text-danger = Businesses.
   This is the THIRD place that legend appears (thead and navbar are the other
   two) and the only one the rollout never scoped, so it was still painting raw
   Bootstrap mid-tones: measured live, the Businesses glyph computed to
   rgb(220,53,69) on a --dd-s1 modal.

   It stopped being merely a contrast bug when Businesses moved to blue-violet:
   the dialog title was then the one place in the whole module still asserting
   the old red identity, directly contradicting the panel, the grid and the
   dashboard tile. Same mapping and same reasoning as the navbar block above.

   --dd-mod is deliberately NOT used here: it is declared on .dd-panel and the
   modal is appended to <body>, so it does not resolve in this subtree. If the
   accents are ever moved onto <body class="dd-mod-*">, these four collapse into
   one rule.

   SCOPED TO ICONS, and that qualifier is load-bearing. The SAME four utility
   classes appear in modal titles on the DETAIL pages meaning something
   completely different -- see the semantic block directly below. */
[data-bs-theme="dark"] div.DTE div.DTE_Header span[class*="fa-"].text-info    { color: var(--dd-people)     !important; }
[data-bs-theme="dark"] div.DTE div.DTE_Header span[class*="fa-"].text-success { color: var(--dd-properties) !important; }
[data-bs-theme="dark"] div.DTE div.DTE_Header span[class*="fa-"].text-warning { color: var(--dd-vehicles)   !important; }
[data-bs-theme="dark"] div.DTE div.DTE_Header span[class*="fa-"].text-danger  { color: var(--dd-businesses) !important; }

/* ---- 2b-ii. DETAIL-PAGE modal titles carry the SECTION, not the action ----
   THE RULE: the header says WHERE YOU ARE, the footer says WHAT WILL HAPPEN.
   Title = --dd-mod (which drawer). Primary button = the action colour (orange
   Save, red Delete). One fact, one place. Do not re-encode the action in the
   title.

   What the markup does, and why this needs overriding. Six shared helpers build
   every modal on all four detail pages, in TWO families, and they disagree:
     editorAddRow  / EditRow  / DeleteRow   -> plain title, NO colour  (16 editors,
                                               the record's own sections)
     editorAddRowBis/EditRowBis/DeleteRowBis-> .title('<span class="fw-bold
                                               text-success">Add Utility</span>')
                                               (4 editors, the EDIT LISTS only:
                                               Contractors, Cybercompanies,
                                               Institutes, Utilities)
   So only the four edit lists were ever coloured, by ACTION -- success=Add,
   warning=Edit, danger=Delete -- while the other sixteen had no title colour at
   all. That inconsistency is in the original code and predates the dark theme.

   Two bugs came out of it on the way here, both worth remembering. The raw
   Bootstrap values are the uncorrected BS4-era mid-tones this file replaced
   everywhere else (#198754 measures ~3.2:1 on the modal). And the ICON rule
   above used to match these text spans too, so once Businesses moved to
   blue-violet the DELETE title rendered VIOLET -- measured at rgb(167,139,250).
   That was the third hit from the semantic/module token coincidence
   (--dd-ok == --dd-properties, --dd-warn == --dd-vehicles), which is exactly the
   kind of accident this section-colour rule removes: there is now no path by
   which an action token can reach a title.

   --dd-mod RESOLVES HERE only because the detail-page converter puts it on
   <body class="dd-mod-*"> -- a modal is appended to <body>, so it inherits.
   Verified live: #4ade80 on a body child of the Properties page. That is also
   why this is scoped to body[class*="dd-mod-"]: on the GRID pages --dd-mod is
   declared on .dd-panel and would not resolve in a modal, and those titles are
   handled by the icon rule above (their titles also carry the record NAME, which
   should stay --dd-text rather than becoming module-coloured). */
[data-bs-theme="dark"] body[class*="dd-mod-"] div.DTE div.DTE_Header .modal-title,
[data-bs-theme="dark"] body[class*="dd-mod-"] div.DTE div.DTE_Header .fw-bold.text-success,
[data-bs-theme="dark"] body[class*="dd-mod-"] div.DTE div.DTE_Header .fw-bold.text-warning,
[data-bs-theme="dark"] body[class*="dd-mod-"] div.DTE div.DTE_Header .fw-bold.text-danger {
    color: var(--dd-mod) !important;
}

/* ---- 2c. The dialog's own buttons: ONE solid, and it is the action ----
   The dialogs shipped raw Bootstrap: Cancel .btn-secondary #6c757d (24 sites),
   Save/Send .btn-primary #0d6efd (16), Delete .btn-danger #dc3545 (8). Two
   problems. #0d6efd is a colour that exists NOWHERE else in this product -- it
   is simply Bootstrap's default leaking through. And Cancel and Save carried the
   same visual weight, so the footer had no primary action: you had to read the
   labels to find the one you wanted rather than see it.

   The rule adopted here is: ONE filled button per dialog, and it is whatever the
   dialog is FOR. Everything else is a ghost.

   Save/Send take BRAND ORANGE, not a module colour, and that is the same split
   this file draws for the thead icons: module colour means IDENTITY (which
   drawer you are in), brand orange means SYSTEM STATE. Committing a record is
   system state. It also answers the standing complaint that #ff5000 -- 717
   occurrences tree-wide, the one thing that is DigiDrawer rather than "a
   Bootstrap app" -- had become invisible until you interacted with something.
   (--dd-mod could not be used here even if we wanted it: it is declared on
   .dd-panel and the modal is appended to <body>.)

   Text on the filled buttons is --dd-s0, NOT white: white on #ff5000 is 3.28:1
   and fails AA, which is the same trap this file already documents for the
   selected row. Dark-on-orange is 5.76:1, dark-on-hover-orange 7.29:1, and
   dark-on-#f87171 6.86:1. This works only because rule 1 above forces the label
   spans to inherit -- the markup hardcodes style="color:white" on them. */
/* ---- 2c-i. Modal buttons never got the 44px touch minimum ----
   `min-height: 44px` is set on `.dd-panel .btn`, and a modal is appended to
   <body>, so it never reached one. Measured: 44px on every panel control,
   38px on the dialog footer -- the one place where a mis-tap costs you the
   whole form. Applies to the GRIDS as much as the detail pages.
   Excludes the bubble, whose wrapper button is deliberately padding:0 with the
   44px carried by the span inside it (see 2d). */
[data-bs-theme="dark"] div.DTE:not(.DTE_Bubble) div.DTE_Form_Buttons button {
    min-height: 44px;
}

[data-bs-theme="dark"] div.DTE div.DTE_Form_Buttons button.btn-secondary {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--dd-text);
    --bs-btn-border-color: var(--dd-line-control);
    --bs-btn-hover-bg: rgba(255, 255, 255, .08);
    --bs-btn-hover-color: var(--dd-text);
    --bs-btn-hover-border-color: var(--dd-line-control);
    --bs-btn-active-bg: rgba(255, 255, 255, .14);
    --bs-btn-active-color: var(--dd-text);
    --bs-btn-active-border-color: var(--dd-line-control);
}
[data-bs-theme="dark"] div.DTE div.DTE_Form_Buttons button.btn-primary {
    --bs-btn-bg: var(--dd-accent);
    --bs-btn-color: var(--dd-s0);
    --bs-btn-border-color: var(--dd-accent);
    --bs-btn-hover-bg: var(--dd-accent-hover);
    --bs-btn-hover-color: var(--dd-s0);
    --bs-btn-hover-border-color: var(--dd-accent-hover);
    --bs-btn-active-bg: var(--dd-accent);
    --bs-btn-active-color: var(--dd-s0);
    --bs-btn-active-border-color: var(--dd-accent);
    /* Editor disables the submit button mid-request; without these it reverts to
       Bootstrap blue at 65% opacity for the length of the round trip. */
    --bs-btn-disabled-bg: var(--dd-accent);
    --bs-btn-disabled-color: var(--dd-s0);
    --bs-btn-disabled-border-color: var(--dd-accent);
}
/* Delete keeps a SOLID fill rather than becoming a ghost: in the delete-confirm
   dialog it is the primary action, so the same "one filled button" rule applies
   -- it just happens that here the fill is the error colour, which is the point.
   #fca5a5 is the lighter step of the same red, standing in the same relation to
   --dd-err as --dd-accent-hover does to --dd-accent; there is no --dd-err-hover
   token yet, and adding one for a single hover state was not worth it. */
[data-bs-theme="dark"] div.DTE div.DTE_Form_Buttons button.btn-danger {
    --bs-btn-bg: var(--dd-err);
    --bs-btn-color: var(--dd-s0);
    --bs-btn-border-color: var(--dd-err);
    --bs-btn-hover-bg: #fca5a5;
    --bs-btn-hover-color: var(--dd-s0);
    --bs-btn-hover-border-color: #fca5a5;
    --bs-btn-active-bg: var(--dd-err);
    --bs-btn-active-color: var(--dd-s0);
    --bs-btn-active-border-color: var(--dd-err);
    --bs-btn-disabled-bg: var(--dd-err);
    --bs-btn-disabled-color: var(--dd-s0);
    --bs-btn-disabled-border-color: var(--dd-err);
}

/* ---- 2d. The BUBBLE editor's buttons (inline cell editing) ----
   Clicking a cell on a detail page opens Editor's bubble, and its footer had
   raw Bootstrap grey + Bootstrap blue while every other surface had been
   themed. 162 sites across the 4 detail pages; ZERO on the grids, which is why
   the grid pass never surfaced it.

   The markup is the reason rule 2c missed them. The coloured pill is NOT the
   button -- it is a <span> INSIDE the button, carrying .btn plus the variant
   plus the Font Awesome glyph classes all at once:

     <button class="btn"><span class="btn btn-secondary far fa-times-circle"
                               style="color:black"></span></button>
     <button class="btn"><span class="btn btn-primary far fa-save"
                               style="color:black"></span></button>

   so `button.btn-primary` matches nothing here. Target the span instead.

   EVERY selector in this block is scoped to div.DTE_Bubble, NOT div.DTE, and
   that is not tidiness. An unscoped `div.DTE ... button.btn` rule here matches
   the MODAL's buttons too -- identical specificity to rule 2c's
   `button.btn-primary` but later in the file, so it won and stripped the orange
   fill off Save, leaving dark --dd-s0 glyph text on a transparent background,
   i.e. an invisible Save button on every detail-page modal. Keep the scope.

   Same language as the dialog footer: one solid button = the action, ghost =
   dismiss. These are icon-only, so the glyph carries the colour and there is no
   label to fight it. `color` needs !important AND a higher specificity than the
   rule-1 `button span { color: inherit !important }` above -- this selector is
   0,4,3 against its 0,3,3, so it wins. The outer <button> keeps its own padding
   as the 44px touch target; only the span is painted. */
[data-bs-theme="dark"] div.DTE_Bubble div.DTE_Form_Buttons button > span.btn-secondary {
    --bs-btn-bg: transparent;
    --bs-btn-border-color: var(--dd-line-control);
    --bs-btn-hover-bg: rgba(255, 255, 255, .08);
    --bs-btn-hover-border-color: var(--dd-line-control);
    color: var(--dd-text) !important;
}
[data-bs-theme="dark"] div.DTE_Bubble div.DTE_Form_Buttons button > span.btn-primary {
    --bs-btn-bg: var(--dd-accent);
    --bs-btn-border-color: var(--dd-accent);
    --bs-btn-hover-bg: var(--dd-accent-hover);
    --bs-btn-hover-border-color: var(--dd-accent-hover);
    color: var(--dd-s0) !important;
}
/* The wrapper <button> is itself a .btn, so without this the pill sits inside a
   second invisible button box and the pair drifts apart. Zeroing its padding
   moves the touch target onto the span, which then has to carry the 44px
   minimum itself -- measured at 32px before the min-height went on, i.e. it had
   silently dropped below the size every other control in this app holds to. */
[data-bs-theme="dark"] div.DTE_Bubble div.DTE_Form_Buttons button.btn {
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    padding: 0;
}
[data-bs-theme="dark"] div.DTE_Bubble div.DTE_Form_Buttons button.btn > span.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
}

/* ---- 3. Validation errors were unreadable ----
   datatables.min.css ships div.DTE div.DTE_Form_Error{color:#b11f1f} with no
   dark override at all: 2.80:1 on --dd-s0. The one message that has to be read
   was the least readable text in the app. --dd-err is 6.90:1. */
[data-bs-theme="dark"] div.DTE div.DTE_Form_Error {
    color: var(--dd-err);
}

/* ---- 4. The dialog was the same colour as the page behind it ----
   Bootstrap sets --bs-modal-bg: var(--bs-body-bg), which this file maps to
   --dd-s0 -- so the modal and the canvas were identical and only the 50%
   backdrop separated them. --dd-s1 is the panel surface, which is what a dialog
   should read as: the same card the grid sits on, lifted. Header/footer take
   --dd-s2 (the "raised" token, already used for the navbar and thead) so the
   title bar and the button bar bracket the form. */
[data-bs-theme="dark"] .modal,
[data-bs-theme="dark"] div.DTE.modal-content {
    --bs-modal-bg: var(--dd-s1);
    --bs-modal-color: var(--dd-text);
    --bs-modal-border-color: var(--dd-line);
    --bs-modal-header-border-color: var(--dd-line);
    --bs-modal-footer-border-color: var(--dd-line);
}
[data-bs-theme="dark"] div.DTE div.DTE_Header,
[data-bs-theme="dark"] div.DTE div.DTE_Footer {
    background-color: var(--dd-s2);
}

/* ---- 5. The inline BUBBLE editor still rendered on #212529 ----
   The bundle ships :root.dark{--dt-html-background: rgb(33,37,41)} and its two
   dark bubble rules -- .DTE_Bubble_Liner and .DTE_Bubble_Triangle -- are the
   only consumers of that token. #212529 is the exact surface rejected for the
   navbar for being the lightest large area on screen (see the NAVBAR block
   above); it was still live for bubble editing. Retargeting the token fixes the
   liner and the triangle together, which is why this is one line and not two.

   :root.dark -- not [data-bs-theme] -- because that is the selector the vendor
   rule uses, and class="dark" is on <html> for exactly this reason (see the
   file header). */
:root.dark {
    --dt-html-background: var(--dd-s2);
}

/* ==========================================================================
   DETAIL PAGES  (mbuploadsingle*.php -- 4 of them)

   These are a different animal from the grids. A grid page is one panel with
   one table; a detail page is up to 36 panels spread across two levels of tab
   bar, and the treatments that make a single panel look good actively hurt when
   they repeat 36 times. Everything in this block exists because of that
   difference -- see each rule.

   PREREQUISITE: the module accent is declared on <body class="dd-mod-*"> for
   these pages, not on the panel. Both tab bars and the banner sit outside every
   panel, so --dd-mod could not reach them otherwise. dark-convert-single.py
   adds the body class.
   ========================================================================== */

/* The four module accents, hoisted to <body>. The .personsborders etc.
   declarations further up are UNCHANGED and still serve the grids; inside a
   panel they simply win as the nearer ancestor, and they carry the same value,
   so there is no conflict either way. */
[data-bs-theme="dark"] body.dd-mod-people     { --dd-mod: var(--dd-people);     --dd-mod-rgb:  62, 207, 224; }
[data-bs-theme="dark"] body.dd-mod-properties { --dd-mod: var(--dd-properties); --dd-mod-rgb:  74, 222, 128; }
[data-bs-theme="dark"] body.dd-mod-vehicles   { --dd-mod: var(--dd-vehicles);   --dd-mod-rgb: 219, 194, 179; }
[data-bs-theme="dark"] body.dd-mod-businesses { --dd-mod: var(--dd-businesses); --dd-mod-rgb: 167, 139, 250; }

/* ---- Module colour in the TITLE AREA ----
   The detail pages had no module colour anywhere above the content: the banner
   is a plain title on the canvas (see below) and the panels are deliberately
   flat, so the only thing saying "this is a Vehicle" was the icon glyph. The
   grids, the dashboard tiles and the auth pages all carry an accent-lit wash;
   these were the one surface that did not.

   A page-level vignette rather than a fill, which is the distinction that makes
   this safe: the note below records that this banner USED to be a full-width
   bar filled with a module gradient and black 150% text, and that it was the
   brightest object in the product. Nothing here fills anything or touches a
   text colour -- the canvas is simply lit from the top edge, so the title and
   the tab bar sit in module-tinted air and the hierarchy is unchanged.

   HEIGHT IN PIXELS, NOT PERCENT, and that is load-bearing. A percentage radial
   is sized against its box; body on these pages runs to several thousand
   pixels, so `55%` would wash half the document instead of the header. 380px
   covers the banner and both tab bars and has faded out before the first panel.
   Same trap the auth card hit, written up there.

   NOT background-attachment:fixed, unlike body.dd-auth. Fixed anchors the wash
   to the VIEWPORT, so it would follow the reader down a long record instead of
   scrolling away with the title it belongs to.

   .13 rather than the grids' .22: that one lights a panel corner behind a
   toolbar, this lights bare canvas behind large type, and the same alpha reads
   twice as strong with nothing on top of it. */
[data-bs-theme="dark"] body[class*="dd-mod-"] {
    background-image: radial-gradient(90% 380px at 50% 0%,
                                      rgba(var(--dd-mod-rgb), .13) 0%,
                                      rgba(var(--dd-mod-rgb), 0) 70%);
    background-repeat: no-repeat;
}

/* ---- The record banner becomes the page title ----
   It was a full-width bar filled with a radial-gradient in the module colour and
   black 150% text -- the brightest object anywhere in the product on a #0d1013
   canvas, and it announced the record by shouting rather than by hierarchy.
   These pages have no <h1> and no page title at all otherwise, so the fix is to
   let this BE the title: large, left-aligned, on the canvas, with the module
   colour carried by the icon and a hairline rule instead of a fill.
   The JS only ever writes innerHTML, so this is pure presentation. */
[data-bs-theme="dark"] .dd-banner {
    padding: var(--dd-4) 0 var(--dd-3);
    margin-bottom: var(--dd-4);
    border-bottom: 1px solid var(--dd-line);
    color: var(--dd-text);
    font-size: var(--dd-t-xl);
    font-weight: 600;
    line-height: 1.2;
    text-align: start;
    letter-spacing: .01em;
}
[data-bs-theme="dark"] .dd-banner [class*="fa-"] {
    color: var(--dd-mod);
    margin-inline-end: var(--dd-2);
}
/* Empty until the record loads; without this the rule and the padding flash as
   a stray line above the tabs on every page load. */
[data-bs-theme="dark"] .dd-banner:empty {
    padding: 0;
    margin: 0;
    border-bottom: 0;
}

/* ---- Panels go FLAT here ----
   .dd-panel gives each panel a module-tinted 1px frame plus a 22% radial glow in
   the corner. That reads as an accent on a grid page, which has exactly one
   panel. Thirty-six of them stacked down a tab pane is a rash: the frames turn
   into stripes and every control landing on a glow has to fight it.
   On these pages the module identity is carried by the banner icon, the tab bar
   and the panel titles -- which is plenty -- so the panel itself is just a
   surface. Cheaper to paint on the Pi, too. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel {
    background-image: none;
    border: 1px solid var(--dd-line);
    padding: var(--dd-4);
    margin-bottom: var(--dd-4);
}
[data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel::before { content: none; }

/* ---- Panel titles: the quiet sibling of .dd-module-title ----
   .dd-module-title is 24px, nowrap, with a text-shadow glow -- right for the ONE
   title on a grid page, and 36 glowing headings down a detail page. This is the
   same colour at a heading's weight and nothing else. text-align is restated
   because mb.css centres these (.homestitles { text-align: center }), which
   floats the heading over the middle of a full-width table with the Export
   button stranded beside it. */
[data-bs-theme="dark"] .dd-panel-title {
    display: inline-block;
    font-size: var(--dd-t-lg);
    font-weight: 600;
    color: var(--dd-mod);
    text-align: start;
    text-shadow: none;
    margin-bottom: var(--dd-2);
}

/* ==========================================================================
   THE TWO TAB LEVELS

   The problem being solved is hierarchy, not colour. Both bars were rendered
   IDENTICALLY -- same boxed tabs, same bg-dark, same border -- so a 9-wide
   second-level bar sitting directly under a 9-wide first-level bar read as
   eighteen peers, and nothing on screen said which one you were navigating.

   Level 1 becomes an underline bar (the primary axis). Level 2 becomes pills
   (clearly subordinate, clearly inside the content). That difference is doing
   all the work; the module colour is only the finish.

   ACTIVE STATE: keyed off .nav-link.active, NOT .nav-item.active. Bootstrap 5
   styles the LINK -- the markup's `active` on the first <li> is a BS4 leftover
   that Bootstrap never clears, so keying off it would leave the first tab
   permanently lit alongside whichever tab you actually opened. The pages call
   .tab('show') on load for both levels, so the link-level class is always set.
   ========================================================================== */

/* Kill the boxed-tab chrome the converter's class removal left behind, on both
   levels. mb.css sets display:-webkit-box on .nav-tabs; flex is the same intent
   in this decade and allows a gap. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--dd-1);
    border-bottom: 1px solid var(--dd-line);
    scrollbar-width: thin;
}
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs > li {
    border: 0;
    background: none;
    flex: 0 0 auto;
}

/* ---- Level 1: the primary axis, an underline bar ---- */
[data-bs-theme="dark"] #ul0.nav-tabs > li > .nav-link {
    padding: var(--dd-2) var(--dd-3);
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: none;
    color: var(--dd-muted);
    font-weight: 600;
    white-space: nowrap;
    transition: color .12s ease, border-color .12s ease;
}
[data-bs-theme="dark"] #ul0.nav-tabs > li > .nav-link:hover {
    color: var(--dd-text);
    border-bottom-color: var(--dd-line-strong);
}
[data-bs-theme="dark"] #ul0.nav-tabs > li > .nav-link.active {
    color: var(--dd-mod);
    border-bottom-color: var(--dd-mod);
    background: none;
}

/* ---- Level 2: subordinate, pills ----
   Smaller, lighter, and NOT underlined, so it can never be mistaken for the
   primary axis. The bar loses its own bottom rule -- one hairline per screen is
   enough, and a second one directly under the first was what made the two bars
   read as a pair. */
[data-bs-theme="dark"] [id^="sul"].nav-tabs {
    border-bottom: 0;
    gap: var(--dd-1);
    margin-bottom: var(--dd-3);
    padding-bottom: var(--dd-1);
}
[data-bs-theme="dark"] [id^="sul"].nav-tabs > li > .nav-link {
    padding: var(--dd-1) var(--dd-3);
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--dd-muted);
    font-size: var(--dd-t-md);
    font-weight: 500;
    white-space: nowrap;
    transition: background-color .12s ease, color .12s ease;
}
[data-bs-theme="dark"] [id^="sul"].nav-tabs > li > .nav-link:hover {
    background-color: rgba(255, 255, 255, .06);
    color: var(--dd-text);
}
[data-bs-theme="dark"] [id^="sul"].nav-tabs > li > .nav-link.active {
    background-color: rgba(var(--dd-mod-rgb), .16);
    color: var(--dd-mod);
}

/* ---- The alert markers on tab labels ----
   Each tab can carry a <span class="d-none"><i class="fa-exclamation"></i></span>
   that JS reveals when that section needs attention. It inherited the link
   colour, so on an inactive (muted) tab the one thing you needed to notice was
   the same grey as everything else. Severity colour, always, active or not.

   THE SEVERITY LIVES ON THE SPAN, NOT THE ICON. The page JS puts text-warning
   or text-danger on the <span> and leaves the <i> classless, so the marker used
   to take its colour by inheritance. Pinning the icon's own colour therefore
   overrode that inheritance and painted EVERY marker amber -- a danger section
   computed text-danger correctly, the span really was red, and the only visible
   part of it was still amber. The two rules below read the span's class back,
   so the amber default only applies when JS has set no severity at all. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs .nav-link .fa-exclamation {
    color: var(--dd-warn);
}
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs .nav-link .text-warning .fa-exclamation {
    color: var(--dd-warn);
}
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs .nav-link .text-danger .fa-exclamation {
    color: var(--dd-err);
}

/* ---- Scroll affordance on both tab bars ----
   mb.css gives .nav-tabs overflow-x:auto, and on a phone that is doing real
   work: measured at a 476px viewport the Properties bars are 873px and 666px
   wide. Nothing said so. The first-level bar simply ended mid-word on whatever
   tab happened to straddle the edge, which reads as a clipping bug rather than
   as "there is more this way".

   The classic pure-CSS scroll shadow, no JS and no extra elements: two COVER
   gradients painted in the canvas colour with background-attachment:local, so
   they travel with the content, parked over two SHADOW gradients attached to
   the box edges with background-attachment:scroll. At either end the cover sits
   on the shadow and hides it; as soon as you scroll away the shadow appears.
   So each edge shows a shadow if and only if there is content past it. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .nav-tabs {
    background-image:
        linear-gradient(to right, var(--dd-s0), rgba(var(--dd-s0-rgb), 0)),
        linear-gradient(to left,  var(--dd-s0), rgba(var(--dd-s0-rgb), 0)),
        linear-gradient(to right, rgba(255, 255, 255, .20), rgba(255, 255, 255, 0)),
        linear-gradient(to left,  rgba(255, 255, 255, .20), rgba(255, 255, 255, 0));
    background-position: left center, right center, left center, right center;
    background-size: 30px 100%, 30px 100%, 14px 100%, 14px 100%;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
}

/* ==========================================================================
   DETAIL PAGES ON A PHONE

   Measured before this block, at a 500x667 viewport on the Properties page:
   398px of the 667 -- SIXTY PERCENT of the screen -- was chrome above the first
   table row. navbar 49, banner 58, tab bars 42 + 33, then 147px of panel header
   and DataTables search furniture. Two records fitted.

   Same principle the dashboard and the short-viewport block already use: shrink
   the FURNITURE, never the data. Nothing here reduces a touch target below 44px
   and nothing changes a font size that carries content.
   ========================================================================== */
@media (max-width: 575.98px) {
    /* The banner is a page title, not a hero. --dd-t-lg still reads as the
       largest thing on screen once everything around it has tightened. */
    [data-bs-theme="dark"] .dd-banner {
        font-size: var(--dd-t-lg);
        padding: var(--dd-2) 0 var(--dd-1);
        margin-bottom: var(--dd-2);
    }
    /* 16px of panel padding on a 390px screen is 8% of the width spent on air,
       twice. The panel still reads as a panel at 8. */
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel {
        padding: var(--dd-2);
        margin-bottom: var(--dd-2);
    }
    [data-bs-theme="dark"] #ul0.nav-tabs > li > .nav-link,
    [data-bs-theme="dark"] [id^="sul"].nav-tabs > li > .nav-link {
        padding-inline: var(--dd-2);
    }
    /* DataTables stacks title / search / table / info as separate .row.mt-2, so
       the furniture pays 8px four times over. Same fix the grids' short-viewport
       block already applies. */
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dt-container > .row {
        margin-top: 0 !important;
    }
    /* On a narrow screen DataTables leaves the length-menu half of its top row
       empty; it still collects a row-gap and its own 8px margin. */
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dt-container > .row > div:empty {
        display: none;
    }
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel > .clearfix {
        margin-bottom: var(--dd-1);
    }
}

/* Landscape phones: the same page with ~390px of HEIGHT. The grids already have
   a max-height:520px block for this; detail pages need it too, and they have
   TWO tab bars plus a banner to pay for rather than one toolbar. */
@media (max-height: 520px) {
    /* At 273px of viewport the banner is 11% of the screen just to repeat the
       record name that the row you clicked already told you. --dd-t-base keeps
       it readable as a title without it being furniture. */
    [data-bs-theme="dark"] .dd-banner {
        font-size: var(--dd-t-base);
        padding: 2px 0;
        margin-bottom: var(--dd-1);
        border-bottom: 0;
    }
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel {
        padding: var(--dd-2);
        margin-bottom: var(--dd-2);
    }
    [data-bs-theme="dark"] #ul0.nav-tabs > li > .nav-link { padding-block: var(--dd-1); }
    [data-bs-theme="dark"] [id^="sul"].nav-tabs > li > .nav-link { padding-block: 2px; }
    [data-bs-theme="dark"] [id^="sul"].nav-tabs { margin-bottom: var(--dd-1); padding-bottom: 0; }
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dt-container > .row { margin-top: 0 !important; }
    [data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel > .clearfix { margin-bottom: var(--dd-1); }
}

/* ---- External-link and key icons: the colour now lives in the markup ----
   This block used to force `.fa-link, .fa-cogs, .fa-key { color: var(--dd-ok)
   !important }` here, to correct the BS4-era #28a745 those icons carried
   inline (about 3.2:1 on --dd-s1). The inline literals are gone -- the key is
   var(--dd-accent) and the links are var(--dd-muted) in the markup itself --
   so this rule had become an override of the fix, and !important meant it
   won: the key and both link icons still rendered green on all four pages
   after the markup pass. Removed rather than retuned, because the rest colour
   belongs in one place and the markup is now that place.

   The hover lift further down is what remains, and it beats the inline style
   the same way this rule did. */

/* ---- One <thead> ships a LIGHT-MODE background inline ----
   `<thead style="background-color:#f0f0f5">` on #tblDetails, the first table on
   the People page -- the one you land on. Sole inline-styled thead in all four
   pages. It is currently masked because the <th> cells paint over it, so it does
   not show today, but it is a pale band sitting one CSS change away from being
   visible (a cell gap, a border, a transparent th) and it survives every
   stylesheet because it is inline. Neutralised so the cells govern the surface. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel table thead[style] {
    background-color: transparent !important;
}

/* ---- Detail-page TOOLBAR buttons carry the module colour as a variant class ----
   FIFTH instance of the semantic/module coincidence (after #Archived,
   #gobackfarc, the dialog-title icons and the modal titles), and the first on
   the detail pages. Every Add / Edit <list> / Back button in a panel header uses
   the PAGE'S module stand-in class, not a severity:
       People     btn-info    x47      Vehicles   btn-warning x15
       Properties btn-success x41      Businesses btn-danger  x27
   Three of those coincide with a semantic token (--dd-info == --dd-people,
   --dd-ok == --dd-properties, --dd-warn == --dd-vehicles) and so were right by
   accident. Businesses does not: `.dd-panel .btn-danger` resolves --dd-btn to
   --dd-err, so 16 Add, 5 "Edit <list>" and 6 Back buttons rendered RED glyphs on
   a violet page.

   Named for all four so it is correct on purpose rather than by luck, and so a
   future accent change cannot resurrect this.

   SCOPED TO .dd-toolbar-actions, which is what makes it safe: the row-action
   buttons live in table cells, never in the header wrapper, so deleterow keeps
   --dd-err and editrow keeps --dd-warn. Verified: all 141 Add/Back/Edit toolbar
   buttons across the four pages are inside the wrapper, none outside.
   0,4,1 beats `.dd-panel .btn-*` at 0,3,0. */
[data-bs-theme="dark"] body.dd-mod-people     .dd-toolbar-actions .btn-info,
[data-bs-theme="dark"] body.dd-mod-properties .dd-toolbar-actions .btn-success,
[data-bs-theme="dark"] body.dd-mod-vehicles   .dd-toolbar-actions .btn-warning,
[data-bs-theme="dark"] body.dd-mod-businesses .dd-toolbar-actions .btn-danger {
    --dd-btn: var(--dd-mod);
    --dd-btn-rgb: var(--dd-mod-rgb);
}

/* ---- Bootstrap text-* utilities in the detail-page BODY ----
   The theme had remapped these only inside specific scopes -- navbar, thead,
   Editor headers, .dd-bubble -- so everywhere else they still rendered raw
   Bootstrap values. Counted in the page bodies (navbar excluded):
     text-dark 27/38/18/18   text-danger 56/89/22/31   text-warning 41/56/14/8
     text-success 4/16/5/2   text-info 19/9/5/7
   The worst is `.text-dark`, which is #212529 -- near-black, i.e. EFFECTIVELY
   INVISIBLE on a dark page. Found on the "Credentials -" panel-header glyph and
   on content icons like fa-plane. `.text-success` is BS5's #198754 at ~3.2:1.
   The rest are the BS4-era mid-tones this file corrected everywhere else.

   Safe as a blanket remap because every MODULE-identity use of these classes is
   already claimed by a higher-specificity rule: the module icons appear only
   inside .title() strings, i.e. in div.DTE_Header (0,4,3), and the navbar
   legend is (0,4,0). This selector is (0,3,1), so it loses to both and only
   picks up the genuinely semantic uses in page content. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-dark    { color: var(--dd-text) !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-success { color: var(--dd-ok)   !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-warning { color: var(--dd-warn) !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-danger  { color: var(--dd-err)  !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-info    { color: var(--dd-info) !important; }

/* ---- .text-bg-* status badges (expiry / due dates) ----
   55 across the four detail pages (warning 26, danger 20, success 9). Bootstrap's
   .text-bg-* sets BOTH colour and background, so these kept raw BS fills --
   #dc3545, #ffc107, #198754 -- the last uncorrected mid-tones on these pages.
   Several also carry a contradictory `.text-dark`, which the utility remap above
   would otherwise turn into light-on-red at about 4.0:1.

   Same combination the Delete button already uses: the corrected token as the
   fill with --dd-s0 text, which measures 6.86:1 on --dd-err, 11.4:1 on --dd-warn
   and 9.6:1 on --dd-ok -- and stays unambiguously red / amber / green, because
   on a status badge the colour IS the message.

   MUST come after the .text-* block above: `.text-dark` there is the same
   specificity (0,3,1), so source order decides which wins on these badges. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-danger,
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-warning,
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-success {
    color: var(--dd-s0) !important;
}
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-danger  { background-color: var(--dd-err)  !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-warning { background-color: var(--dd-warn) !important; }
[data-bs-theme="dark"] body[class*="dd-mod-"] .text-bg-success { background-color: var(--dd-ok)   !important; }

/* ---- thead icons that have not been DataTables-initialised yet ----
   The grid rule above is scoped to `table.dataTable`, which the detail pages do
   not always satisfy: a drill-down table gets its DataTable built only when its
   tab is first opened, so until then its header icons keep the hardcoded inline
   #ff5000 instead of the module colour. Same treatment, without the .dataTable
   requirement, so it holds before and after init. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel table > thead [class*="fa-"] {
    color: var(--dd-mod) !important;
}

/* ---- The glyph JS injects into a drill-down panel title ----
   `#SelectedHomesContractors` and friends are filled at runtime with
     '<span class="fas fa-edit ms-1 me-1" style="color:black"></span>Credentials - X'
   so the icon beside "Credentials -" was BLACK ON THE PANEL, i.e. invisible.
   48 sites across the four pages (13/19/6/10). The converter could not catch it
   -- the markup only exists inside a JS string at runtime -- and .dd-panel-title
   colours the heading, not an inline-styled child. !important beats the inline.

   Covers every icon in a panel title, not just fa-edit, so a future title glyph
   cannot reintroduce this. */
[data-bs-theme="dark"] body[class*="dd-mod-"] .dd-panel-title [class*="fa-"] {
    color: var(--dd-mod) !important;
}

/* ---- The row-disclosure 3-dot: rotate it, do not turn it red ----
   `td.details-control` on #tblDetails (all four pages) and #tblEmployment
   (Businesses) toggles the child row. Three things were wrong with it:

   - It was the CSS keyword `green` (#008000) at 3.47:1 on the panel, the
     dimmest interactive thing on the page, and an orphan colour: on the
     Properties page it read as a dim --dd-properties, which is why it looked
     nearly right there and wrong on the other three. Now var(--dd-mod).
   - Open state was keyword `red`. Red is Delete in this product and the row's
     delete glyph sits in the same row, so a harmless red teaches people to
     stop reading red as dangerous. Open is now --dd-muted plus this rotation.
   - Open state also span (`fa-spin`) for as long as the row stayed open -- a
     loading idiom used as a state, ignoring prefers-reduced-motion and
     repainting forever on a Pi that thermal-throttles.

   The rotation is a TRANSFORM rather than a swap to fa-ellipsis-h because a
   transform costs no layout: the horizontal glyph's box is ~21px against the
   vertical one's ~5px at fa-lg, inside a column DataTables measured at init,
   so the table would jump every time a row opened.

   `tr.shown` is toggled by the page's own handler, so no JS knows about this.
   .fas is display:inline-block in FA 5.4.1, so the transform applies.

   THE BOX HAS TO BE SQUARE, and the first version was not: a transform paints
   outside its box without reserving any space, and fa-ellipsis-v's advance
   width is only ~0.25em against a ~1em height, so once rotated the glyph
   spilled roughly half its length past each side of the cell and sat on the
   panel's left border. `width: 1em` (of the icon's own font-size) plus
   centring gives the rotation exactly the room it needs. Reserving it at rest
   also means the column is measured at its final width, so nothing jumps --
   which was the whole reason for rotating instead of swapping the glyph. */
[data-bs-theme="dark"] body[class*="dd-mod-"] td.details-control i.fas {
    display: inline-block;
    width: 1em;
    text-align: center;
    transition: transform .15s ease, color .15s ease;
}

[data-bs-theme="dark"] body[class*="dd-mod-"] tr.shown td.details-control i.fas {
    transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
    [data-bs-theme="dark"] body[class*="dd-mod-"] td.details-control i.fas {
        transition: none;
    }
}

/* ---- Links out of the app: neutral at rest, brand orange on hover ----
   65 x fa-link "<X> Website" plus 6 x fa-cogs "Management Link", all inside
   an <a target="_blank">, in table cells and in the Editor dialogs. They were
   #28a745 -- the third unrelated control wearing the same green, which is how
   green came to mean "clickable" here and therefore nothing.

   Not the module colour: "opens an external site" is the same meaning on all
   four pages, and green is literally --dd-properties, so on Properties every
   link wore the page's identity colour. Not brand orange either, which the
   key now owns -- the key is rare and drills into secrets, while these appear
   once per row across many tables and are secondary. Neutral at rest
   (8.08:1, brighter than the green's 5.69:1), lifting to the accent on hover
   and keyboard focus, which is free because they are already anchors.

   !important because the rest colour is an inline style inside a JS-built
   string; the hover state has nowhere else to live. Not scoped to .dd-panel:
   the Editor dialog is appended to <body>, outside every panel. */
[data-bs-theme="dark"] body[class*="dd-mod-"] a .fa-link,
[data-bs-theme="dark"] body[class*="dd-mod-"] a .fa-cogs {
    transition: color .15s ease;
}

[data-bs-theme="dark"] body[class*="dd-mod-"] a:hover .fa-link,
[data-bs-theme="dark"] body[class*="dd-mod-"] a:hover .fa-cogs,
[data-bs-theme="dark"] body[class*="dd-mod-"] a:focus-visible .fa-link,
[data-bs-theme="dark"] body[class*="dd-mod-"] a:focus-visible .fa-cogs {
    color: var(--dd-accent) !important;
}

/* ============================================================================
   THE SIX AUTH PAGES — mbloginv2.php (repo ROOT), plus mbregister /
   forgot_password / reset_password / verify_email / resend_verification
   under v2/mb/web.

   These were hand-rolled dark long before this file existed, and they were the
   last corner of the product still running Bootstrap's defaults:
     body   rgb(0,0,0)   pure black -- the value --dd-s0 (#0d1013) deliberately
                         reversed: it haloes on OLED and leaves nowhere to go
                         for a recessed surface
     box    #1e1e1e  + 1px solid #007bff   -- Bootstrap FOUR's primary, a fossil
                         two frameworks back, 4.19:1 on the box
     links  #17a2b8      -- BS4 info, and it collides with People's identity cyan
     submit btn-primary  -- BS5 #0d6efd, white label at 4.50:1 (a bare pass)
   None of those three blues appears anywhere else in the product.

   They get one shared hook -- body.dd-auth + .dd-auth-box -- because the box
   class differs per page (.login-box, .reset-box, .verify-box); styling three
   names would have meant a fourth going unstyled the next time someone adds a
   page.

   TRAP for whoever touches these next: this file's print fix (an UNSCOPED
   `body { background:#fff }`, deliberate, because the print popup loses the
   <html> attributes) means linking dd-theme.css on a page that does NOT set
   data-bs-theme="dark" turns that page WHITE. The attribute and the <link>
   must always land together.
   ============================================================================ */
[data-bs-theme="dark"] body.dd-auth {
    background-color: var(--dd-s0);
    /* Page vignette: a wide, weak wash from the top edge. Kept at .10 -- the
       BOX carries the glow here, and two glows competing just muddies both. */
    background-image: radial-gradient(70% 55% at 50% 0%,
                                      rgba(var(--dd-accent-rgb), .10) 0%,
                                      rgba(var(--dd-accent-rgb), 0) 70%);
    background-attachment: fixed;
    background-repeat: no-repeat;
    color: var(--dd-text);
    min-height: 100vh;
}

/* The card. Same recipe the dashboard tiles settled on and the user accepted:
   radial accent from the top edge at .26, reach 74%, plus a 1px white seam. No
   backdrop-filter -- frosted glass forces a full-surface readback every frame
   on the Pi's VideoCore; a radial gradient is paint-time only. */
[data-bs-theme="dark"] body.dd-auth .dd-auth-box {
    position: relative;
    background-color: var(--dd-s1);
    /* Sized in PIXELS, not percentages. A percentage radial grows with its
       box, so the 400px login card got a neat pool at the top edge while the
       800px sign-up card got the whole upper half washed orange. A fixed
       560x260 pool reads the same on both. */
    background-image: radial-gradient(560px 260px at 50% 0%,
                                      rgba(var(--dd-accent-rgb), .26) 0%,
                                      rgba(var(--dd-accent-rgb), 0) 74%);
    border: 1px solid rgba(var(--dd-accent-rgb), .38);
    border-radius: 14px;
    box-shadow: 0 20px 55px rgba(0, 0, 0, .55);
}

[data-bs-theme="dark"] body.dd-auth .dd-auth-box::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: rgba(255, 255, 255, .16);
    border-radius: 14px 14px 0 0;
    pointer-events: none;
}

/* Links: brand, not BS4 cyan. The lighter step at rest so the accent proper is
   left to mean "primary action". */
[data-bs-theme="dark"] body.dd-auth a { color: var(--dd-accent-hover); }
[data-bs-theme="dark"] body.dd-auth a:hover,
[data-bs-theme="dark"] body.dd-auth a:focus-visible { color: var(--dd-accent); }

/* The one primary action per page. Dark label on the orange fill, never white:
   white on #ff5000 is 3.28:1, --dd-s0 on it is 5.81:1. Same call as the Editor
   Save button. Disabled is named too -- Bootstrap otherwise reverts to blue for
   the length of a submit. */
[data-bs-theme="dark"] body.dd-auth .btn-primary {
    --bs-btn-bg:                  var(--dd-accent);
    --bs-btn-border-color:        var(--dd-accent);
    --bs-btn-color:               var(--dd-s0);
    --bs-btn-hover-bg:            var(--dd-accent-hover);
    --bs-btn-hover-border-color:  var(--dd-accent-hover);
    --bs-btn-hover-color:         var(--dd-s0);
    --bs-btn-active-bg:           var(--dd-accent-hover);
    --bs-btn-active-border-color: var(--dd-accent-hover);
    --bs-btn-active-color:        var(--dd-s0);
    --bs-btn-disabled-bg:         var(--dd-accent);
    --bs-btn-disabled-border-color: var(--dd-accent);
    --bs-btn-disabled-color:      var(--dd-s0);
    font-weight: 600;
}

/* Inputs sit ON the card, so they need the recessed surface to read as fields
   rather than as more card. */
[data-bs-theme="dark"] body.dd-auth .form-control,
[data-bs-theme="dark"] body.dd-auth .form-select {
    background-color: var(--dd-s0);
    border-color: var(--dd-line-control);
    color: var(--dd-text);
}

[data-bs-theme="dark"] body.dd-auth .form-control:focus,
[data-bs-theme="dark"] body.dd-auth .form-select:focus {
    background-color: var(--dd-s0);
    border-color: var(--dd-accent);
    box-shadow: 0 0 0 .2rem rgba(var(--dd-accent-rgb), .25);
    color: var(--dd-text);
}

[data-bs-theme="dark"] body.dd-auth .form-control::placeholder { color: var(--dd-muted); }

/* The leading icon capsule on each field. Its glyphs ship as .text-info, which
   is People's identity cyan -- meaningless here and the loudest thing on the
   card, so it goes neutral. */
[data-bs-theme="dark"] body.dd-auth .input-group-text {
    border-color: var(--dd-line-control);
    color: var(--dd-muted);
}

[data-bs-theme="dark"] body.dd-auth .input-group-text [class*="fa-"] {
    color: var(--dd-muted) !important;
}

/* Same #212529 problem the app navbar had: Bootstrap 5.3 does not redefine
   --bs-dark-rgb under [data-bs-theme=dark], so .bg-dark stayed brighter than
   the page it sits on. */
[data-bs-theme="dark"] body.dd-auth .navbar.bg-dark {
    --bs-dark-rgb: 13, 16, 19;
    border-bottom: 1px solid var(--dd-line-subtle);
}

/* ---- Two defects the first render of the themed auth pages exposed ----

   1. CHROME'S AUTOFILL, not our surface. A returning user with saved
      credentials gets `rgba(70, 90, 126, .4)` painted over both fields by the
      browser -- measured on prod -- so the two inputs came up a muddy
      blue-grey against everything else on the card. That is most users, most
      of the time, on the product's first screen. The -webkit-box-shadow inset
      is the only way to repaint an autofilled field (background-color is
      ignored), and the absurd transition delay is the standard trick to stop
      Chrome re-applying its own fill a frame later.

   2. THE FOOTER WAS INVISIBLE, and this one predates the re-tint: the
      copyright line carries Bootstrap's `.text-dark`, i.e. rgb(33,37,41),
      which measured 1.24:1 on the new --dd-s0 page -- and was only 1.6:1 on
      the old pure black, so it has never been readable. .text-dark is an
      !important utility, hence !important here; 0,3,1 beats its 0,1,0. */
[data-bs-theme="dark"] body.dd-auth input:-webkit-autofill,
[data-bs-theme="dark"] body.dd-auth input:-webkit-autofill:hover,
[data-bs-theme="dark"] body.dd-auth input:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px var(--dd-s0) inset;
    -webkit-text-fill-color: var(--dd-text);
    caret-color: var(--dd-text);
    transition: background-color 9999s ease-in-out 0s;
}

[data-bs-theme="dark"] body.dd-auth .text-dark {
    color: var(--dd-muted) !important;
}

/* ---- Sign-up's rule hints were Bootstrap's grey slab ----
   The two password/username rules render as
   `<h6 class="list-group bg-secondary text-white">`, i.e. rgb(108,117,125)
   full-bleed bars -- the loudest thing on the page, louder than the Sign Up
   button, for text that is advisory. Recessed onto --dd-s0 with muted text so
   they read as help rather than as an alert; .text-white is an !important
   utility, hence !important here. */
[data-bs-theme="dark"] body.dd-auth .bg-secondary {
    background-color: var(--dd-s0) !important;
    border-left: 3px solid rgba(var(--dd-accent-rgb), .55);
    border-radius: 4px;
    padding: .45rem .6rem;
}

[data-bs-theme="dark"] body.dd-auth .bg-secondary.text-white {
    color: var(--dd-muted) !important;
    font-weight: 400;
}

/* ---- Sign-up's Reset: ghost, same as the Editor's Cancel ----
   ONE filled button per form, and it is whatever the form is FOR -- the rule
   the modal dialogs already follow. Reset was Bootstrap's solid grey
   btn-secondary sitting opposite the orange Sign Up, which gave a destructive
   action (it clears six filled fields) the same visual weight as the action
   the page exists for. Values mirror div.DTE .btn-secondary exactly so the two
   read as one control, not two.

   The only btn-secondary in the six pages, so this cannot catch anything else.
   Note it is an <input type="reset">, not a <button>, which is why the
   selector cannot be reused from the DTE block. */
[data-bs-theme="dark"] body.dd-auth .btn-secondary {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--dd-text);
    --bs-btn-border-color: var(--dd-line-control);
    --bs-btn-hover-bg: rgba(255, 255, 255, .08);
    --bs-btn-hover-color: var(--dd-text);
    --bs-btn-hover-border-color: var(--dd-line-control);
    --bs-btn-active-bg: rgba(255, 255, 255, .14);
    --bs-btn-active-color: var(--dd-text);
    --bs-btn-active-border-color: var(--dd-line-control);
}

/* ---- Sign-up's action row overlapped its own text in portrait ----
   Reset and Sign Up were `float-start` / `float-end` inside a div with no
   clearfix, so that div collapsed to ZERO height and the "Already have an
   account? Login here" paragraph below flowed up THROUGH the buttons. On a
   desktop width there is enough room that nothing visibly collides, which is
   why it only showed on a phone in portrait: the sentence wrapped to three
   lines and ran under the Sign Up button.

   Flex fixes it structurally -- the container now has height, so the
   paragraph sits below where it belongs -- and floats are ignored on flex
   items, which is the same interaction documented for the detail-page
   toolbars. The float classes came off the markup rather than being left to
   be silently ignored, and the two inputs were swapped so DOM order matches
   the order they are read in. wrap so a narrow phone can stack them. */
[data-bs-theme="dark"] body.dd-auth .dd-auth-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
}

/* ---------------------------------------------------------------------------
   PASSPORT SCAN MESSAGE (v2/mb/js/dd-passport-scan.js)

   Editor's own form-info slot is where the scan reports what it read. That slot
   is plain --dd-muted body text, which is right for the one-line hints it
   normally carries and wrong for this: the scan's message is several sentences,
   it is the only feedback the user gets that a machine just filled in their
   form, and part of it is an instruction to go and check the values.

   The colour vocabulary is the settled one, not a new set. Green and amber here
   are the SEMANTIC tokens doing semantic work -- "this read cleanly" against
   "some characters were unclear, check them" -- which is the one job amber is
   reserved for. It is deliberately not --dd-mod: this is not one of the page's
   own controls, it is a statement about data quality, and colouring it People
   cyan would say the wrong thing.

   The left border rather than a filled panel is because this sits INSIDE the
   Editor modal, which is already a raised surface; a second fill would stack two
   panels and the form would lose its shape. --------------------------------- */
/* SELECTORS KEYED TO OUR OWN CLASSES, NOT TO EDITOR'S WRAPPER.
   ============================================================
   Every rule in this block used to be scoped to `.DTE_Field_Message` or
   `.DTE_Form_Info`, and NONE OF THEM EVER MATCHED. Editor on Bootstrap 5 does
   not render a field message in either: it emits

       div.form-text.text-secondary.small  >  div.dd-scan.dd-scan-busy

   so the whole scan-report treatment -- the left rule, the muted body, the
   severity colours, the smaller type -- was dead, and the message inherited
   Bootstrap's .text-secondary grey. It looked deliberate enough that nobody
   questioned it until the pulse was asked to go orange and did not.

   Found by reading the LIVE element's ancestor chain rather than the theme, and
   the lesson is that: an unmatched selector fails silently and looks like a
   design decision. The dd-scan classes are ours and appear nowhere else, so
   they need no wrapper to qualify them -- which also means Editor can change
   its markup again without breaking this.

   The report lives on the upload FIELD's message, not the form-wide info area,
   because a bubble never attaches the latter (Editor's bubble defaults carry
   message:false). Both selectors are kept so the block looks the same wherever
   it lands -- form, bubble or inline. */
[data-bs-theme="dark"] .dd-scan {
    text-align: left;
    padding: .5rem .75rem;
    border-left: 3px solid var(--dd-dim);
    border-radius: var(--dd-r-sm);
    background: rgba(255, 255, 255, .03);
    line-height: 1.5;
}

/* COLOUR IS SET ON THE DOUBLED CLASS, AND THAT IS NOT A TYPO.
   ==========================================================
   The bundle ships

       div.DTE_Field div:not([data-dte-e="msg-error"]) { color: inherit }

   at 0,2,2 -- two elements, a class and the attribute inside :not(). A single
   class under [data-bs-theme] is 0,2,0 and loses to it, so `color` alone went
   on inheriting Bootstrap's .text-secondary grey while every other property in
   this block applied normally. That is a nasty shape of bug: the rule visibly
   works, so the one declaration being ignored looks like a choice.
   `.dd-scan.dd-scan-busy` is 0,3,0 and wins, without borrowing any of Editor's
   own class names to do it.

   Every block carries both classes -- say() writes
   `class="dd-scan dd-scan-<tone>"` -- so the pairing always holds, and 'info'
   is spelled out here rather than left to the base rule for the same reason. */
[data-bs-theme="dark"] .dd-scan.dd-scan-info {
    color: var(--dd-text);
}
[data-bs-theme="dark"] .dd-scan.dd-scan-busy {
    border-left-color: var(--dd-mod);
    color: var(--dd-muted);
}
/* THE PULSING WORD CARRIES THE BRAND ORANGE, and only that word. Muted grey at
   a quarter opacity in the trough is a very quiet signal for the one thing on
   screen that is still happening, and this can run for twenty seconds.

   --dd-accent-hover rather than --dd-accent: it IS the brand orange as this
   theme renders it on dark -- the same token every link uses -- and it measures
   7.36:1 on --dd-s0 against the flatter #ff5000, which matters here more than
   anywhere because the animation spends part of every cycle at 25% opacity and
   whatever contrast it starts with is what gets divided.

   The surrounding sentence stays muted deliberately. One accented word reads as
   a signal; an orange paragraph reads as an error. */
[data-bs-theme="dark"] .dd-scan.dd-scan-busy .dd-scan-pulse {
    color: var(--dd-accent-hover);
}
/* THE PULSE IS THE PROGRESS BAR. A scan can take twenty seconds and has
   nothing to report while it runs, so the only useful signal is that it is
   still running. One word that breathes says that; a static line naming the
   file being read said nothing and was four times as long.

   Not theme-scoped, because it changes opacity rather than colour and so is
   right whatever the surface underneath. Motion is the entire content here, so
   a reader who has asked for less of it gets the word without it -- which was
   the informative half anyway. */
.dd-scan-busy .dd-scan-pulse {
    font-weight: 600;
    /* Slower than the original 1.1s, so the plateau at the top is long enough
       to be seen as a pause rather than as a corner being turned. */
    animation: dd-scan-pulse 1.4s ease-in-out infinite;
}
@keyframes dd-scan-pulse {
    /* HOLDS AT FULL BRIGHTNESS, then falls. The first version was a plain
       three-stop ease -- 1, .35, 1 -- which only TOUCHES full opacity for an
       instant at each end and spends the whole cycle on its way somewhere else.
       Reported exactly that way: it never gets to full power before it dims.
       A flat stop at the top gives it something to arrive at, and a deeper
       trough makes the beat read across the room rather than being a shimmer. */
    0%, 30%  { opacity: 1; }
    65%      { opacity: .25; }
    100%     { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .dd-scan-busy .dd-scan-pulse { animation: none; }
}

[data-bs-theme="dark"] .dd-scan.dd-scan-ok   { border-left-color: var(--dd-ok); }
[data-bs-theme="dark"] .dd-scan.dd-scan-warn { border-left-color: var(--dd-warn); }

/* The bolded bits are the headline ("Filled in from the passport:") and the
   holder's name, which is the value the user has to eyeball hardest. --dd-text
   is already the brightest text token, so weight is what carries the emphasis
   and there is no second colour to introduce. */
[data-bs-theme="dark"] .dd-scan.dd-scan strong {
    color: var(--dd-text);
    font-weight: 600;
}

/* Editor styles a field message at font-size:11px/line-height:1em, which suits
   "max 50 characters" and not several sentences that may include a warning about
   whose passport this is. Overridden on the BLOCK rather than on its container:
   `.DTE_Field_Message:has(.dd-scan)` would read better but :has() is exactly the
   kind of recent selector this theme already avoids -- see the note on color-mix
   -- because the appliance's browser floor is not established. Specificity here
   is 0,3,0 against Editor's 0,2,1, so it wins without !important. */
[data-bs-theme="dark"] .dd-scan {
    font-size: .8125rem;
    line-height: 1.5;
    text-align: left;
}

/* THE REPORT PANEL, AND WHY IT MOVED OUT OF EDITOR'S FIELD MESSAGE
   ===============================================================
   Everything the scan says -- the pulsing "Scanning...", the list of filled
   fields, the wrong-person warning, the final "check before you save" -- used
   to land in the upload field's MESSAGE, which Editor renders below the entire
   upload widget: below the choose button, below the drop zone and below the
   file list.

   Measured on an empty Add form at 1568x737, before a single attachment
   existed, the modal body already scrolled -- 529px of content in 520px -- and
   every file attached pushes the message further down. So the user started a
   scan and had to scroll to see whether it was running, then scroll again to
   read what it found. On a phone it was worse: the whole report sat below the
   fold with nothing on screen to say it was there.

   dd-passport-scan.js now injects .dd-scan-report between the switch and
   Editor's eu_table, so the panel sits directly under "Scan and fill in" and
   directly above "Choose file...". Reading order becomes: set the mode, read
   what happened, act on the files.

   Only the gap is needed here -- the block inside is still .dd-scan and every
   rule above applies to it unchanged, which is the payoff for having keyed
   those to our own classes rather than to Editor's wrapper. */
.dd-scan-report:not(:empty) {
    margin: 0 0 .5rem;
}

/* THE STANDING HINT IS NOT A RESULT, and must not look like one.
   It is present before anything has happened, so giving it the left rule and
   the raised background of a report would put a permanent alert-shaped panel on
   a form where nothing is wrong. Quiet muted text, the way it read when it was
   an ordinary field message -- the panel treatment is earned by having
   something to say. */
[data-bs-theme="dark"] .dd-scan.dd-scan-hint {
    border-left-color: transparent;
    background: none;
    padding-left: 0;
    color: var(--dd-muted);
}

/* "not recorded" in the travel grid's Name-on-document column. Dim, because it
   is the absence of a value rather than a value, and italic so it cannot be
   mistaken for somebody actually called that. */
[data-bs-theme="dark"] .dd-noname {
    color: var(--dd-dim);
    font-style: italic;
}

/* ===========================================================================
   BUBBLE FIT -- the inline bubble editor kept inside the window
   ===========================================================================
   Partner to v2/mb/js/dd-bubble.js, which re-decides which side of the cell the
   bubble goes on and sets the max-height. Read that file's header first; it
   carries the measurements and the reasoning. Everything here is the half that
   is genuinely CSS: the centring, and the internal scroller the cap needs.

   NOT scoped to [data-bs-theme="dark"], unlike almost everything above it. This
   is geometry, not colour -- it has to hold whatever palette is in force, and
   the public pages share these vendor class names. Same reasoning as the
   .dd-scan-pulse block: theme-scope what a theme can change.

   ---- 1. The panel was centred for a width it stopped having ----
   The bundle ships:

       div.DTE_Bubble div.DTE_Bubble_Liner { min-width: 300px; margin-left: -150px }

   -150px is half of 300px, so the panel centres on the cell only while it is
   exactly at its minimum width. Editor then compounds the error: its horizontal
   overflow correction computes the panel's left edge as `centre - width/2`, an
   assumption the fixed margin only satisfies at 300px wide.

   Widening the attachments bubble to four fields took it to 442px, at which
   point the two disagree by 71px in the same direction -- measured right edge
   1976 in a 1920 window, so the correction fired, subtracted its 17px, and left
   the panel 56px off-screen anyway.

   translateX(-50%) centres at any width, which makes Editor's own arithmetic
   true instead of working around it, and is identical to the shipped behaviour
   at 300px so no narrow bubble moves. The transform is on the liner; the
   pointer triangle is a sibling and stays on the cell. */
/* The transition is switched off deliberately, and it is not a style choice.
   The bundle ships `transition: top .5s ease-in-out` on the wrapper, and Editor
   re-runs its positioning on every window scroll and resize event -- so the
   panel spends those half-seconds easing towards a target it has already been
   given a new one for, visibly trailing the row it belongs to. It also makes
   the panel unmeasurable: dd-bubble.js has to read where the liner has landed
   in order to cap its height against the window edge, and mid-flight
   getBoundingClientRect() answers with an animation frame rather than the
   destination. Removing it makes the bubble track its cell exactly. */
div.DTE_Bubble {
    transition: none;
}

div.DTE_Bubble div.DTE_Bubble_Liner {
    margin-left: 0;
    transform: translateX(-50%);
    /* Lets dd-bubble.js's max-width actually bite. The liner is shrink-to-fit,
       so its floor is its min-content width, and a max-width cannot push it
       below that -- and on the attachments bubble min-content is one uploaded
       filename, "ElanRomPass_240513_112956_240520_072604.jpg", 43 characters
       with nothing to break on. That single token held the panel at 497px
       inside a 500px window.

       `anywhere` and not `break-word`: only `anywhere` counts toward the
       intrinsic min-content size, which is the whole point here. `break-word`
       would wrap the name once the box was already narrow and do nothing to
       make it narrow in the first place. */
    overflow-wrap: anywhere;
}

/* ---- 2. The scroller, applied only when the height is actually capped ----
   dd-bubble.js puts .dd-bubble-capped on the wrapper when it has had to limit
   the height, and only then. The gate matters: overflow-y on a box forces
   overflow-x to `auto` as well, which would clip a field's inline message or
   validation text sideways on every bubble in the app, including the ~19 per
   page that were never too tall in the first place.

   The scrolling region is DTE_Form_Content, not the liner, so that the close
   button, the error line and the Save/Cancel pair stay put while the fields
   move under them -- a form whose Save button scrolls out of reach is the same
   bug in a smaller box. That means the max-height set on the liner has to reach
   the fields, hence the flex chain: each of the three boxes between them passes
   the constraint down, and min-height:0 stops the default `min-content` floor
   from refusing to shrink (without it a flex item will not scroll -- it just
   overflows its parent and nothing appears to happen).

   Editor's date picker attaches to <body>, not into the field, so it is not
   clipped by this and can still open past the panel's edge. */
div.DTE_Bubble.dd-bubble-capped div.DTE_Bubble_Liner,
div.DTE_Bubble.dd-bubble-capped div.DTE_Bubble_Liner div.DTE_Bubble_Table {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The form is split out for one reason: !important, and it is load-bearing.
   Editor shows the form with jQuery, which writes `display: block` as an INLINE
   style on the element, and an inline declaration outranks any selector however
   long. Without !important this one link in the chain stays a block, the
   constraint stops there, and the fields overflow the panel exactly as if
   nothing had been done -- which is precisely what the first attempt did, and it
   looks like the whole fix has failed rather than one rule.

   Nothing else here needs it: the liner and the table are styled by sheet only.
   Verified by reading form.getAttribute('style') on prod with a bubble open. */
div.DTE_Bubble.dd-bubble-capped div.DTE_Bubble_Liner div.DTE_Bubble_Table > form {
    display: flex !important;
    flex-direction: column;
    min-height: 0;
}
/* The long selector is not decoration. The bundle already carries

       div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { padding: 0 }

   at 0,4,5, so the shorter form of this rule loses the padding half of it and
   the labels clip. Matching its shape and adding .dd-bubble-capped takes it to
   0,5,5.

   That padding is here because every DTE_Field is a Bootstrap .row, which
   carries margin-left/right: -12px. Turning on overflow-y silently turns
   overflow-x into `auto` as well, and the first thing it clips is those 12px
   -- one character off the front of every label ("ame on document:").

   BOTH SIDES, and the right one was a bug for a while. This used to set
   padding-left alone, on the reasoning that the column padding meant there was
   "nothing to see anyway" on the right. The arithmetic says otherwise: with a
   content width W, a row whose margins are -12px spans W + 24px starting 12px
   left of the content box, so cancelling only the left leaves a padding box of
   W + 12 holding something W + 24 wide. The field overhung by exactly 12px on
   the right and overflow-x: hidden ate it -- the last character of the scan
   hint, reported from a phone as "the e in page is missing".

   It clipped rather than wrapped because layout never knew: the text fits the
   element, the element does not fit its clipping ancestor.

   12px on each side cancels both margins exactly, so every field spans its
   padding box precisely -- nothing overhangs, nothing is clipped, and no
   horizontal scrollbar appears. */
div.DTE_Bubble.dd-bubble-capped div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding-left: .75rem;
    padding-right: .75rem;
    /* The only sign that the panel scrolls. Chrome on macOS draws overlay
       scrollbars, which are invisible until you are already scrolling, and
       scrollbar-color does not bring them back -- tested, the track stays
       hidden and clientWidth does not change. So a form cut off mid-field would
       otherwise look like a form that simply ends there. An inset shadow on the
       bottom edge is painted against the padding box and does not scroll away,
       which is the point: it says "this region scrolls" for as long as it does.

       Only on .dd-bubble-capped, so it appears exactly when there is something
       below to find. */
    box-shadow: inset 0 -18px 16px -16px rgba(0, 0, 0, .75);
}


/* ===========================================================================
   ATTACHMENT FILENAMES ARE TEXT, NOT ACTIONS
   ===========================================================================
   Editor renders every uploaded file as a plain <a> -- in the bubble, in the
   modal, on all four detail pages -- and it inherited --bs-link-color, which
   this theme maps to the brand orange. So the most ordinary thing on the panel,
   the name of the file you just attached, was painted in the one colour reserved
   for the thing you are meant to press.

   Orange is the app's cross-module ACTION colour (see the icon-colour
   vocabulary). A filename is not an action -- it is the record of what is
   already there, sitting directly under the Save button it was competing with.

   Scoped to the rendered file list, NOT done by changing --bs-link-color, which
   is global and would repaint every link in the application. Both of Editor's
   upload field types render through div.rendered, so one rule covers single and
   multiple uploads everywhere they appear.

   Underline kept: it is still a link, it still opens the document, and removing
   the affordance to fix the colour would trade one problem for another. */
div.DTE div.rendered a,
div.eu_table div.rendered a {
    color: var(--dd-text);
}
div.DTE div.rendered a:hover,
div.eu_table div.rendered a:hover {
    color: var(--dd-text);
    text-decoration-thickness: 2px;
}


/* ===========================================================================
   ATTACHING A FILE IS NOT ALWAYS A STATEMENT ABOUT THE ROW
   ===========================================================================
   The travel-document scanner has no button: attaching the passport photo IS
   the trigger, and what it triggers REPLACES the country, type, number, dates
   and holder name. That is right for the case it was built for and wrong for
   the ordinary one -- adding a second page, a visa, the back of the card -- to
   a row whose details are already correct and hand-checked.

   So the widget gains one switch, above Editor's own "Choose file...", and the
   reading order becomes: set the mode, do the thing, read the result. The
   result is the field message below, which is why the switch goes at the TOP
   rather than beside the drop zone -- and why it is not beside the drop zone in
   the other sense either: this field renders in a bubble 246px wide, where
   nothing sits next to anything.

   MODULE COLOUR, NOT ORANGE. This is one of the page's own controls, which is
   exactly what --dd-mod is reserved for in the icon-colour vocabulary. Orange
   is the cross-module action colour and this is not one.

   WHY THE OVERRIDES EXIST AT ALL. bootstrap.min.css hardcodes
   `.form-check-input:checked{background-color:#0d6efd}` and a blue :focus halo
   with no variable behind either, and this file had no form-check-input rule
   before. Editor's own eight checkbox fields on the detail pages are NOT
   affected: its checkbox type emits a bare <input type="checkbox"> with no
   .form-check-input class, so these selectors cannot reach them.

   TRAP: uploadMany._showHide() writes `display: block` INLINE on every
   .limitHide node, and this container borrows that class so it disappears with
   the choose button at the 20-file limit. An inline style beats any selector
   short of !important, so .dd-scanmode must stay block-layout; the flex lives
   on .form-check inside it, where nothing overwrites it. --------------------- */
[data-bs-theme="dark"] .dd-scanmode {
    /* The 12px sides are Editor's own inset, restored by hand. Its div.row
       carries Bootstrap's .row negative margins and its div.cell children pad
       them back; this node lives outside that table (see dd-passport-scan.js)
       so it has to pay the padding itself, or it sits 12px proud of the choose
       button, the drop zone and the file list. */
    box-sizing: border-box;
    margin: 0 12px var(--dd-2);
    /* Horizontal padding kept to --dd-2 rather than --dd-3: every pixel here is
       taken straight out of the label's budget, and on a phone that budget is
       what decides whether the label wraps. */
    padding: var(--dd-2);
    border: 1px solid var(--dd-line);
    border-radius: var(--dd-r-sm);
    background: rgba(255, 255, 255, .03);
}
/* FLEX, NOT BOOTSTRAP'S FLOAT. The switch ships as `float: left`, which only
   pushes the FIRST line aside: in a 246px bubble the label wraps, and line two
   reclaims the full width and slides back under the control -- measured at a
   32px outdent, exactly the switch's own width. Two flex columns wrap properly
   at any width.

   Safe here specifically because .form-check is INSIDE .dd-scanmode rather than
   being it: Editor writes `display: block` inline on the .limitHide node above,
   and an inline style would beat this. */
[data-bs-theme="dark"] .dd-scanmode .form-check {
    margin: 0;
    padding-left: 0;
    display: flex;
    /* CENTRED, NOT TOP-ALIGNED WITH A MAGIC NUMBER. This used to pin the switch
       with `margin-top: 14px`, arithmetic derived from a 17.6px line box -- so on
       a phone rendering the label larger than this stylesheet asks for, the text
       sank well below the control it belongs to. Centring is arithmetic-free and
       right at every text size. */
    align-items: center;
    gap: var(--dd-1);
}

/* The whole strip is the target, because <label for> toggles the input: 44px is
   this product's touch floor, and 13px at 1.35 is a 17.6px line, so (44-17.6)/2
   = 13.2px of padding gets there. */
[data-bs-theme="dark"] .dd-scanmode .form-check-label {
    display: block;
    /* NO `overflow-wrap: anywhere` HERE, deliberately. It is right for an
       unbreakable 43-character filename (see the bubble liner) and wrong for a
       three-word label: it drops the min-content contribution to ONE CHARACTER,
       so the moment anything over-constrains this flex row the label collapses
       and breaks mid-word instead of holding its ground. `min-width: 0` alone
       lets it shrink to the longest word and no further. */
    min-width: 0;
    min-height: 44px;
    padding: 13px 0;
    color: var(--dd-text);
    font-size: var(--dd-t-sm);
    line-height: 1.35;
    cursor: pointer;
}

/* The switch keeps its full 1rem font-size -- 32x16px rather than the 26x13px
   it would inherit from the smaller label -- and is pinned to the first line so
   it stays put when the label wraps to two in a narrow bubble. */
[data-bs-theme="dark"] .dd-scanmode .form-check-input {
    float: none;
    flex: 0 0 auto;
    margin-left: 0;
    margin-top: 0;
    font-size: 1rem;
    background-color: var(--dd-s0);
    border-color: var(--dd-line);
    cursor: pointer;
}
[data-bs-theme="dark"] .dd-scanmode .form-check-input:checked {
    background-color: var(--dd-mod);
    border-color: var(--dd-mod);
}

/* Bootstrap's halo is on :focus, not :focus-visible, so it fires on a mouse
   click. This product has exactly one focus ring and it is declared above. */
[data-bs-theme="dark"] .dd-scanmode .form-check-input:focus {
    border-color: var(--dd-line);
    box-shadow: none;
}


/* ===========================================================================
   THE DROP ZONE IS DEAD WEIGHT ON A TOUCHSCREEN
   ===========================================================================
   "Drag and drop a file here to upload" describes a gesture that does not exist
   on a phone. Editor renders it anyway: 3px dashed border, 1em/2em padding, and
   in a 300px bubble the label wraps to two lines -- about 83px of a panel that
   is barely taller than that, directly above the file list you actually came to
   look at. Removing it is most of the space the switch above costs back.

   TRAP: do NOT use Editor's own .noDrop class for this. Its rule is
   `div.editor_upload.noDrop div.row.second { display: none }`, and div.rendered
   -- the list of attached files -- lives in that same row. Hide div.drop only;
   in .multi mode the cells are display:block with no min-height of their own,
   so the emptied cell collapses to nothing.

   (hover: none) and (pointer: coarse) together, not a width query: a narrow
   desktop window still has a mouse and can still drag. -------------------- */
@media (hover: none) and (pointer: coarse) {
    div.editor_upload div.drop {
        display: none;
    }
}

/* The knob. Bootstrap paints it rgba(255,255,255,.25) at rest -- barely legible
   on a #0d1013 track, and this switch's whole job is to be readable at a glance
   -- and swaps it to its own #86b7fe on :focus, which is a blue dot appearing in
   a cyan control the moment you click it. One neutral grey says "off" without
   borrowing a colour that means something else here.

   :not(:checked) IS load-BEARING. This file loads after Bootstrap, so without it
   the grey also won when the switch was ON, and the knob stayed grey on the cyan
   track -- invisible at desktop size, obvious in a photo of a phone. The checked
   knob is Bootstrap's white, which is right on --dd-mod already. */
[data-bs-theme="dark"] .dd-scanmode .form-check-input:not(:checked),
[data-bs-theme="dark"] .dd-scanmode .form-check-input:not(:checked):focus {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='%23a4b0bd'/></svg>");
}

/* ---- A superseded alert -------------------------------------------------
   A lease re-signed or a policy renewed is entered as a NEW ROW beside the old
   one. The old row's date has not moved, so its *_danger generated column still
   fires -- but a newer record already covers a later date and the alert is
   stale. The roll-up views drop it (migrations/2026-08-22-alerts-supersede.sql)
   and dd-alerts.js swaps the pill's colour class for this one, in place, on the
   row itself.

   It stays a PILL rather than reverting to plain text on purpose. Plain text
   reads as a date that never alerted, and a red date silently turning black is
   the kind of change that generates a support question. Same shape, no colour,
   dashed edge: "this was flagged, and it has been handled".

   No !important and no colour token borrowed from the severity set -- the JS
   removes text-bg-danger / text-bg-warning / text-dark before adding this, so
   there is nothing left to out-specify.

   THE EDGE IS --dd-line-control, NOT --dd-line-strong. This started on the
   latter and was reported as barely visible, which it was: measured in the
   browser it came to 1.87:1 against the panel, where a control boundary needs
   3:1. --dd-line-strong and --dd-line are DIVIDER tokens, for hairlines between
   rows that are meant to recede; --dd-line-control exists for exactly this and
   measures 3.54:1. See the Borders block at the top of this file, which says so
   and which I should have read the first time. */
.badge.rounded-pill.dd-superseded {
    background-color: transparent;
    color: var(--dd-dim);
    border: 1px dashed var(--dd-line-control);
    font-weight: 400;
}

/* ---- A record that has not started yet ---------------------------------
   Signed, entered, but its start date is still ahead: pending, not new. The
   `_success` generated column used to call this green, because it had a lower
   bound and no upper one -- a lease starting in 2030 was green for four years
   (fixed in 2026-08-22-success-upper-bound.sql).

   Green and this have to read as different states, so this borrows nothing from
   the severity palette and nothing from --dd-ok: a solid edge and dimmed text
   on the page's own ground. Beside a green pill it reads as "not yet"; beside a
   red one it reads as "not the problem".

   --dd-line-control for the edge, for the same reason as .dd-superseded above:
   this began on --dd-line and measured 1.42:1, which is a border you cannot
   actually see. Solid here against the other's dashed, so the two neutral
   states stay distinguishable without a second colour. */
.badge.rounded-pill.dd-pending {
    background-color: transparent;
    color: var(--dd-muted);
    border: 1px solid var(--dd-line-control);
    font-weight: 400;
}

/* ---- A start-date pill built by dd-alerts.js ---------------------------
   The column renderers draw their own pill as

       <div class="text-dark badge rounded-pill text-bg-success">
         <font size="3">2025-06-01</font></div>

   where .badge sets font-size: .75em and the inner <font size="3"> puts it
   back to `medium`. A pill created in JS has no inner element, so without this
   it renders a size smaller than the identical pill in the row above -- which
   is exactly how it was spotted.

   THE SIZE HAS TO GO ON THE INNER ELEMENT, NOT THE BADGE. Putting `font-size:
   medium` on .dd-startpill itself fixed the text and broke the box: Bootstrap
   spells .badge's padding `.35em .65em`, em-relative to the badge's OWN
   font-size, so raising that from .75em to medium scales the padding by 4/3
   as well. Measured on prod, the created pill came out 113x29 beside a
   rendered 111x24 and a superseded 106x26 -- the same defect this block was
   added to fix, one layer out. Reproducing the renderer's two-element shape
   is what actually makes them measure the same: 107x26 against 106x26.

   `medium` rather than 1rem on purpose: that is the keyword size="3" maps to,
   and unlike rem it does not move if the root font-size is ever changed. */
.badge.rounded-pill.dd-startpill {
    font-size: .75em;
}

.badge.rounded-pill.dd-startpill > .dd-startpill-t {
    font-size: medium;
}

/* ==========================================================================
   MULTI-SELECT  --  the row tick box and the selection bar

   The tick box lives INSIDE the existing Actions cell, not in a column of its
   own. Eighty-one bubble-editor handlers across the four detail pages select
   their cells positionally (`td:not(:last-child)`, `:not(:first-child)`,
   `:not(:nth-last-child(-n+3))`), and DataTables drops hidden columns from the
   DOM -- so adding a column silently re-points all of them. See dd-select.js.
   ========================================================================== */

/* The tap target is the LABEL, not the 20px box. 44px is this file's touch
   floor (see the .dd-panel .btn rule); above 576px the box only needs to clear
   the pointer, and the Actions column is already fighting for width -- 26
   tables declare it 110px and the three-button ones are over that. */
[data-bs-theme="dark"] .dd-panel label.dd-selbox-wrap {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    min-height: 44px;
    margin: 0 var(--dd-1) 0 0;
    vertical-align: middle;
    cursor: pointer;
}

/* The label joins the button row INSIDE `<div align="center">`, so it inherits
   that div's centring and sits on the buttons' own line. Nothing here may make
   it a block: as a sibling of that div it would take a line of its own and add
   ~30px to every row in every table. */

/* dt-select-checkbox is Select's own class, so datatables.min.css already
   carries the [data-bs-theme=dark] treatment plus :checked:after. Only the
   accent is overridden here.

   NOT .form-check-input: bootstrap.min.css hardcodes background-color #0d6efd
   on :checked with no variable behind it, and this file's only
   .form-check-input overrides are scoped to .dd-scanmode -- so a bare one would
   render Bootstrap blue, a colour that exists nowhere else in this product. */
[data-bs-theme="dark"] .dd-panel input.dd-selbox {
    width: 20px;
    height: 20px;
    margin: 0;
    flex: 0 0 auto;
    accent-color: var(--dd-accent);
    border-color: var(--dd-line-control);
    cursor: pointer;
}

/* Orange rather than --dd-mod, following the rule and not the exception.
   .dd-scanmode reads "module colour: this is one of the page's own controls",
   which would argue for --dd-mod here. It does not apply: selection is SYSTEM
   STATE, not module identity -- the same category as the orange Save button.

   This block used to lean on a second, harder reason: --dd-vehicles was
   byte-identical to --dd-warn, so --dd-mod would have painted severity amber
   into tables carrying amber severity pills. Vehicles is now #dbc2b3 and that
   collision is gone, so only the reasoning above is load bearing. The
   conclusion is unchanged. */

/* One destructive control live at a time. While anything is ticked the per-row
   trash is out of service, so the bar's "Delete N" is the only way to delete. */
[data-bs-theme="dark"] table.dataTable.dd-selecting .deleterow {
    opacity: .35;
    pointer-events: none;
}

[data-bs-theme="dark"] table.dataTable.dd-selecting > tbody > tr {
    cursor: pointer;
}

@media (hover: none) and (pointer: coarse) {
    /* Tap-to-toggle drags a text selection across the row otherwise. */
    [data-bs-theme="dark"] table.dataTable.dd-selecting > tbody > tr > * {
        -webkit-user-select: none;
        user-select: none;
    }
}

/* --------------------------------------------------------------------------
   The bar. A CHILD OF <body>, ON PURPOSE.

   Docked above .dt-container it would land in the vertical budget that the
   max-height:520px block above exists to defend: ~61px out of the ~150px of
   rows still visible on a landscape phone, taken at the moment the user is
   trying to read them -- and that block has already spent the banner and both
   tab bars, so there is nothing left to absorb it. It would also be caught by
   that block's `.dt-container > .row { margin-top: 0 !important }`.

   Out here it matches nothing in that block, so there is no specificity contest
   to win. It occludes pagination furniture instead of data, it cannot be
   scrolled off a 12,000-line page, and it is one element rather than ~70.
   Corollary: never give it a .row class and never nest it under .dt-container.
   -------------------------------------------------------------------------- */
[data-bs-theme="dark"] .dd-selbar {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: max(var(--dd-2), env(safe-area-inset-bottom));
    z-index: 1030;                  /* under the modal (1055) and its backdrop (1050):
                                       when the confirm opens, the dialog is the only
                                       live thing and the bar belongs behind it */
    display: flex;
    align-items: center;
    gap: var(--dd-3);
    max-width: calc(100vw - var(--dd-4));
    padding: var(--dd-2) var(--dd-3);
    background: var(--dd-s2);
    border: 1px solid var(--dd-line);
    border-radius: var(--dd-r-md);
    box-shadow: var(--dd-e3);
}

/* THE 44px FLOOR CANNOT REACH HERE. It is set on `.dd-panel .btn:not(.dt-button)`
   and this bar is outside .dd-panel -- the same trap that left the Editor modal
   footer sitting at 38px until block 2c-i restated it. Restate it again, along
   with the ghost recipe: --dd-btn is consumed by .dd-panel rules that do not
   apply out here, so these buttons set the Bootstrap variables directly. */
[data-bs-theme="dark"] .dd-selbar .btn {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

[data-bs-theme="dark"] .dd-selbar .dd-selbar-count {
    color: var(--dd-text);
    font-size: var(--dd-t-md);
    white-space: nowrap;
}

[data-bs-theme="dark"] .dd-selbar .dd-selbar-del {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--dd-err);
    --bs-btn-border-color: var(--dd-err);
    --bs-btn-hover-bg: rgba(248, 113, 113, .18);
    --bs-btn-hover-color: var(--dd-err);
    --bs-btn-hover-border-color: var(--dd-err);
    --bs-btn-active-bg: rgba(248, 113, 113, .26);
    --bs-btn-active-color: var(--dd-err);
    --bs-btn-active-border-color: var(--dd-err);
    white-space: nowrap;
}

[data-bs-theme="dark"] .dd-selbar .dd-selbar-clear {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--dd-muted);
    --bs-btn-border-color: var(--dd-line-control);
    --bs-btn-hover-bg: rgba(255, 255, 255, .08);
    --bs-btn-hover-color: var(--dd-text);
    --bs-btn-hover-border-color: var(--dd-line-control);
    --bs-btn-active-bg: rgba(255, 255, 255, .12);
    --bs-btn-active-color: var(--dd-text);
    --bs-btn-active-border-color: var(--dd-line-control);
    white-space: nowrap;
}

/* While the bar is up, give the page room to scroll clear of it. The bar is
   position:fixed, so on a short viewport it otherwise sits on top of the last
   rows with no way to move them out from under it -- measured at 900x323, where
   it covered the first row outright. Only applied while something is selected,
   and below the fold at any normal height. */
[data-bs-theme="dark"] body.dd-selbar-open {
    padding-bottom: 76px;
}

@media (max-height: 520px) {
    [data-bs-theme="dark"] body.dd-selbar-open {
        padding-bottom: 64px;
    }
}

/* Landscape phones: tighten the FURNITURE, never the button. ~61px -> ~54px. */
@media (max-height: 520px) {
    [data-bs-theme="dark"] .dd-selbar {
        bottom: max(var(--dd-1), env(safe-area-inset-bottom));
        padding: var(--dd-1) var(--dd-3);
        gap: var(--dd-2);
    }
}

/* Portrait phones: full width, so "Delete N" sits under the right thumb. */
@media (max-width: 575.98px) {
    [data-bs-theme="dark"] .dd-selbar {
        left: var(--dd-2);
        right: var(--dd-2);
        transform: none;
        max-width: none;
        justify-content: space-between;
    }
    /* The tap target grows; the box inside it does not. */
    [data-bs-theme="dark"] .dd-panel label.dd-selbox-wrap {
        width: 44px;
    }
}
