/* ============================================================================
   Raptors Freight — global theme layer ("Nocturne").

   This file carries the design tokens and the re-skin of the Bootstrap-4
   template that every admin page is built on. It is loaded once, globally,
   from views/inc/head_scripts.php.

   THE RULE THIS FILE OBEYS: it changes how the application LOOKS and nothing
   else. It sets colour, type, radius, border and elevation on selectors the
   template already emits. It never changes layout structure, never hides a
   control, and never touches an id or a class that JavaScript binds to —
   because the working screens depend on that markup being exactly where it
   was. A page that has never heard of this file still renders correctly with
   it loaded.

   TOKENS. Every colour below is a custom property. The light/dark switch
   (dataJs/rf_theme.js) rewrites the property block on <html> and nothing
   else, so a theme change is one attribute flip rather than a second
   stylesheet. The accent ramp MIRRORS in light mode: a token used as text
   stays a text colour, a token used as fill stays a fill, so no rule needs a
   light-mode variant of its own.

   TYPE. Sora for headings, Mulish for body copy — applied at the element
   level so a heading inside a card, a modal or a DataTable picks it up
   without the view knowing.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=Mulish:wght@400;500;600;700;800&display=swap');

/* == 1. Tokens ============================================================ */

:root,
html[data-rf-theme="dark"] {
    --rf-bg: #161826;
    --rf-surface: #232532;
    --rf-text: #e9e9ed;
    --rf-divider: color-mix(in srgb, #e9e9ed 16%, transparent);
    --rf-line: color-mix(in srgb, #e9e9ed 11%, transparent);
    --rf-muted: color-mix(in srgb, #e9e9ed 56%, transparent);
    --rf-faint: color-mix(in srgb, #e9e9ed 34%, transparent);
    --rf-page: radial-gradient(1300px 780px at 88% -220px, color-mix(in srgb, #2b2741 62%, transparent), transparent 60%), #161826;

    --rf-accent: #9184d9;
    --rf-a100: #f5f4ff;
    --rf-a200: #e7e5fe;
    --rf-a300: #d2cefd;
    --rf-a400: #b5abfc;
    --rf-a500: #968ae0;
    --rf-a600: #796cbf;
    --rf-a700: #5d5294;
    --rf-a800: #423a6a;
    --rf-a900: #2b2741;

    --rf-section: #262a60;
    --rf-section-glow: #353b80;

    --rf-ok:   oklch(0.74 0.115 162);
    --rf-warn: oklch(0.80 0.125 82);
    --rf-bad:  oklch(0.68 0.150 24);
    --rf-info: oklch(0.73 0.110 236);
    --rf-sea:  oklch(0.72 0.105 296);

    --rf-c-teal: #6fc9a8;
    --rf-c-indigo: #5b6bd6;
    --rf-c-peri: #7f8fe8;
    --rf-c-sky: #3fa9dd;
    --rf-c-rose: #e0a3a3;
    --rf-c-pink: #d98fb5;
    --rf-c-hero: #8fd0fb;
    --rf-c-amber: #e08a3e;

    --rf-sk-rest: #5d5294;
    --rf-sk-dim: #423a6a;
    --rf-sk-hot: #b5abfc;

    /* Elevation is a hairline edge plus ambient darkness on a dark ground. */
    --rf-shadow-sm: 0 0 0 1px color-mix(in srgb, #e9e9ed 9%, transparent);
    --rf-shadow-md: 0 0 0 1px color-mix(in srgb, #e9e9ed 12%, transparent), 0 6px 18px rgba(0,0,0,.55);
    --rf-shadow-lg: 0 0 0 1px color-mix(in srgb, #e9e9ed 16%, transparent), 0 16px 40px rgba(0,0,0,.65);

    --rf-radius-sm: 4px;
    --rf-radius-md: 8px;
    --rf-radius-lg: 14px;

    --rf-font-heading: "Sora", system-ui, sans-serif;
    --rf-font-body: "Mulish", system-ui, sans-serif;

    /* The financial-permission mask. Any figure a role may not see is wrapped
       in an element reading these three, so a server-side permission check
       only has to set them. */
    --fin-blur: 0px;
    --fin-op: 1;
    --fin-pe: auto;

    color-scheme: dark;
}

html[data-rf-theme="light"] {
    --rf-bg: #eef0fa;
    --rf-surface: #fbfcff;
    --rf-text: #232532;
    --rf-divider: color-mix(in srgb, #232532 20%, transparent);
    --rf-line: color-mix(in srgb, #232532 15%, transparent);
    --rf-muted: color-mix(in srgb, #232532 64%, transparent);
    --rf-faint: color-mix(in srgb, #232532 48%, transparent);
    --rf-page: radial-gradient(1300px 780px at 88% -220px, color-mix(in srgb, #b5abfc 26%, transparent), transparent 62%), #eef0fa;

    /* Mirrored ramp — see the note at the top of the file. */
    --rf-a100: #2b2741;
    --rf-a200: #423a6a;
    --rf-a300: #5d5294;
    --rf-a400: #796cbf;
    --rf-a500: #968ae0;
    --rf-a600: #b5abfc;
    --rf-a700: #d2cefd;
    --rf-a800: #e7e5fe;
    --rf-a900: #f5f4ff;

    --rf-section: #dedcf7;
    --rf-section-glow: #cbc7f2;

    --rf-ok:   oklch(0.55 0.115 162);
    --rf-warn: oklch(0.58 0.115 82);
    --rf-bad:  oklch(0.53 0.170 24);
    --rf-info: oklch(0.54 0.120 236);
    --rf-sea:  oklch(0.54 0.105 296);

    --rf-c-teal: #2f8f6d;
    --rf-c-indigo: #3f4eb5;
    --rf-c-peri: #5a6bd0;
    --rf-c-sky: #1d7fae;
    --rf-c-rose: #b26a6a;
    --rf-c-pink: #a85a83;
    --rf-c-hero: #2b8ec4;
    --rf-c-amber: #a8622a;

    --rf-sk-rest: #9a8ee4;
    --rf-sk-dim: #c9c3f4;
    --rf-sk-hot: #5d5294;

    --rf-shadow-sm: 0 1px 2px rgba(35,37,50,.06), 0 0 0 1px color-mix(in srgb, #232532 8%, transparent);
    --rf-shadow-md: 0 4px 14px rgba(35,37,50,.10), 0 0 0 1px color-mix(in srgb, #232532 10%, transparent);
    --rf-shadow-lg: 0 16px 40px rgba(35,37,50,.16), 0 0 0 1px color-mix(in srgb, #232532 12%, transparent);

    color-scheme: light;
}

/* == 2. Type ============================================================== */

body {
    font-family: var(--rf-font-body);
    font-size: 14px;
    line-height: 1.55;
    letter-spacing: 0;
    background: var(--rf-page) fixed;
    color: var(--rf-text);
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
.page-title, .card-title, .modal-title,
.display-1, .display-2, .display-3, .display-4 {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--rf-text);
}

h6, .h6 { letter-spacing: .04em; }

.text-muted, small, .small { color: var(--rf-muted) !important; }

a { color: var(--rf-a300); }
a:hover, a:focus { color: var(--rf-accent); }

::selection { background: color-mix(in srgb, var(--rf-accent) 30%, transparent); }

/* STRICTLY TWO FAMILIES, EVERYWHERE (owner, 2026-08-28).
   `body { font-family }` is not enough on its own, and this is why:

     * the template sets .popover and .tooltip to Nunito Sans, and the
       drag-handle in .myadmin-dd to Montserrat — neither inherits from body,
       so three surfaces kept the old typeface however the tokens were set;
     * form controls and buttons do not inherit font-family from the document
       in any browser. The template happens to say `inherit` for them, but that
       is the template's opinion and a stylesheet loaded after it could take it
       away again;
     * a component rendered into a shadow-free portal (the datepicker, select2's
       dropdown, SweetAlert) attaches to <body> and picks up whatever it finds.

   So the body face is restated on the things that would otherwise choose their
   own, and the heading face on the things that are headings. Icon fonts are
   excluded immediately below — a glyph rendered in Mulish is an empty box, not
   a fallback — and Phosphor exempts itself with !important in its own sheet. */
button, input, optgroup, select, textarea,
.btn, .form-control, .custom-select, .input-group-text,
table, .table, th, td,
.popover, .popover-header, .popover-body,
.tooltip, .tooltip-inner,
.dropdown-menu, .dropdown-item,
.nav, .nav-link, .navbar, .breadcrumb,
.badge, .label, .alert, .list-group-item,
.select2-container, .select2-selection, .select2-results__option,
.swal2-container, .swal2-popup,
.bootstrap-datetimepicker-widget,
.myadmin-dd .dd-list .dd-item .dd-handle,
.rfnav, .rfnav-row, .rfnav-text {
    font-family: var(--rf-font-body);
}

/* Headings keep the display face wherever they are drawn, including inside the
   components above. */
.card-title, .modal-title, .page-title,
.popover-header, .swal2-title,
h1, h2, h3, h4, h5, h6 {
    font-family: var(--rf-font-heading);
}

/* Icon fonts keep their own family: the body rule above would erase them.
   Every icon set the application loads is restated here by its class prefix,
   because a glyph rendered in Mulish is an empty box, not a fallback.
   UIcons is gone from this list with the font itself -- it was carrying one
   glyph, and it also caught `fi-group-id`, an ordinary class name on a hidden
   input, because the selector could not tell the two apart. */
[class^="mdi-"], [class*=" mdi-"] { font-family: "Material Design Icons" !important; }
[class^="ti-"],  [class*=" ti-"]  { font-family: "themify" !important; }
[class^="fa-"],  [class*=" fa-"],
.fa, .fas, .far, .fab             { font-family: "Font Awesome 5 Free", "FontAwesome" !important; }
[class^="flag-icon"], [class*=" flag-icon"] { font-family: inherit !important; }
/* Phosphor (.ph / .ph-fill) is absent on purpose: its own sheet sets the family
   with !important, which already beats everything above. */

/* == 3. Shell ============================================================= */

#main-wrapper { background: transparent; }
.page-wrapper { background: transparent; }
.page-breadcrumb { background: transparent; }

.topbar,
.topbar .navbar-header,
.topbar .top-navbar,
.topbar .navbar-collapse {
    background: color-mix(in srgb, var(--rf-bg) 86%, transparent) !important;
    backdrop-filter: blur(12px);
    box-shadow: none;
}

.topbar { border-bottom: 1px solid var(--rf-line); }
.topbar .navbar-header { border-right: 1px solid var(--rf-line); }

.topbar .nav-link,
.topbar .navbar-nav .nav-link {
    color: var(--rf-text) !important;
    opacity: .82;
    transition: opacity .15s, color .15s;
}
.topbar .nav-link:hover { opacity: 1; color: var(--rf-a200) !important; }

/* The Authorization Form item sits on its own red bar and is marked
   .text-white in the markup. The rule above is !important (it has to beat the
   template) and was overriding that, leaving dark text on red. */
.topbar .nav-link.text-white,
.topbar .nav-link.text-white span,
.topbar .nav-link.text-white i { color: #fff !important; opacity: 1; }

/* ── The navigation rail ───────────────────────────────────────
   Not here. assets/css/rf_nav.css is the rail, in full and on its own.

   Roughly 180 lines used to sit at this point re-skinning the template's
   .left-sidebar / .sidebar-item / .sidebar-link markup, including the 5px dot
   drawn on every child row that the owner asked to have taken off. The rail no
   longer uses that markup, so all of it matched nothing. Deleting it is the
   removal, rather than another rule switching a previous rule back off. */

/* ── The record bar (design package, Shipment Details) ───────────────────────
   The way back, what this record IS, and the two states a person opens it to
   check — all on one line above the detail, instead of a heading on the left
   and a menu on the right with the states buried further down. Wraps rather
   than truncating: a long status is worth a second line. */
/* THE RECORD BAR. What this record is, and what you can do to it, on one
   line and in one band.

   It used to be a bare strip of text on the page's own ground, which made the
   most important line on the screen the least visible thing on it: a small
   grey word, a number, two pills, and — in the other half of a Bootstrap row,
   as though it were unrelated — the actions menu.

   A surface of its own, a rule of accent down its leading edge, and the number
   set large enough to be the thing you read first. */
.cdp-recbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    min-height: 52px;

    /* FULL BLEED, AND BACK IN AGAIN. The band had its own 16px of padding on
       top of the card's 1.3rem, so the number sat further right than the
       "Costs" heading and the detail columns underneath it -- and against a
       header that was inset twice, everything below looked pushed out to the
       card's edge. Out to the card's edges, then padded by exactly the card's
       own inset, so the header's first letter and the content's first letter
       are on the same line. */
    margin: -1.3rem -1.3rem 18px;
    padding: 13px 1.3rem;

    border: 0;
    border-bottom: 1px solid var(--rf-line);
    border-left: 3px solid var(--rf-accent);
    border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
    background: color-mix(in srgb, var(--rf-accent) 5%, transparent);
}
[dir="rtl"] .cdp-recbar { border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0; }

/* An empty answer holder is not a 16px gap. These sit under the header waiting
   for a message and spend most of their life with nothing in them. */
.card div[id^="resultados_ajax"]:empty { margin-bottom: 0 !important; }

/* == THE TWO HUNDRED PIXELS BETWEEN THE FIRST CARD AND THE SECOND ========

   The template gives the page's container a floor:

       .page-wrapper > .container-fluid { min-height: calc(100vh - 180px) }

   so a page with one short card still fills the window and the footer sits at
   the bottom instead of floating half way up. Reasonable, where the container
   IS the page.

   On the record screens it is not. courier_view, customer_packages_view and
   both consolidation views put the first card inside .container-fluid and then
   close it — tracking history, the user log, the item table, the sender and
   recipient panels are all SIBLINGS of the container, not children of it. So
   the floor was not holding a short page open. It was propping the first card
   open to the height of the viewport and leaving the difference, two hundred
   pixels of nothing, between it and the card underneath. On a shipment with no
   costs recorded it was nearer three hundred.

   The floor means something only when the container is the last thing on the
   page. Where content follows it, the content holds the page open.

   ANYTHING that follows it counts, not only a .row: these screens close the
   container and then carry on with a col, a section, a bare form, another
   container. What does NOT count is a modal (parked at the end of the page and
   never in flow), the footer (which is exactly what the floor exists to push
   down), or a script. */
.page-wrapper > .container-fluid:has(~ *:not(.modal):not(footer):not(.footer):not(script):not(style)) {
    min-height: 0;
}

/* AND THE BAND IS THE CARD'S HEADER, not a block inside its body. Where it is
   the only thing in that body — which is the shape on every record screen —
   the body's own bottom padding lands underneath it as well: 18px of the
   band's margin plus 1.3rem of the body's, close to forty pixels of blank
   card between the header and the first thing under it. That was the space
   showing between the first card and the one after it. The body's share is
   taken back, leaving the 18 the band asked for. */
.card-body > .cdp-recbar:only-child { margin-bottom: calc(18px - 1.3rem); }
.cdp-recbar-id { display: inline-flex; align-items: baseline; gap: 9px; min-width: 0; }
.cdp-recbar-kind {
    font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
    color: var(--rf-faint);
}
.cdp-recbar-no {
    font-family: var(--rf-font-heading);
    font-weight: 600; font-size: 21px; letter-spacing: -0.01em;
    color: var(--rf-text);
}

/* The actions go to the far end, by margin. A float would take them out of
   the strip's flow and they would stop being part of the same line the moment
   the pills grew. */
.cdp-recbar-actions { margin-left: auto; }
.cdp-recbar-actions .btn-group > .btn { white-space: nowrap; }
[dir="rtl"] .cdp-recbar { border-left: 1px solid var(--rf-line); border-right: 3px solid var(--rf-accent); }
[dir="rtl"] .cdp-recbar-actions { margin-left: 0; margin-right: auto; }

/* On a phone the actions button takes the width it is given rather than
   sitting in a corner of its own. */
@media (max-width: 575.98px) {
    .cdp-recbar-actions { margin-left: 0; width: 100%; }
    .cdp-recbar-actions .btn-group { width: 100%; }
}

/* ── Quick filter chips (design package, Shipment Register) ──────────────── */
.cdp-chipbar { display: flex; flex-wrap: wrap; gap: 8px; }
.cdp-chip {
    border: 1px solid var(--rf-line);
    background: transparent;
    color: var(--rf-muted);
    font-family: var(--rf-font-body);
    font-size: 12.5px; font-weight: 600; line-height: 1.4;
    padding: 5px 13px;
    border-radius: 999px;
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.cdp-chip:hover {
    color: var(--rf-a200);
    border-color: color-mix(in srgb, var(--rf-accent) 40%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 8%, transparent);
}
.cdp-chip.is-on {
    background: color-mix(in srgb, var(--rf-accent) 18%, transparent);
    border-color: color-mix(in srgb, var(--rf-accent) 46%, transparent);
    color: var(--rf-a100);
}

/* ── A ROW OF FORM FIELDS THAT FILLS THE WIDTH IT IS GIVEN ───────────────────
   A Bootstrap row is a rigid twelve-column grid. Four col-md-4 fields is
   sixteen units: three sit on the first line and the fourth drops onto a line
   of its own with two thirds of it empty. Every settings and edit screen that
   reads as "scattered" is doing exactly that, and it is arithmetic rather than
   taste — a row whose widths do not tile leaves a hole.

   One class instead of hand-tuning each row's widths: the fields share the
   space equally and grow into whatever is left over, so the last line fills.
   240px is the basis at which a label and its input stay readable; below that
   they wrap to fewer per line rather than being crushed.

   Applied only to rows of FIELDS. A row of cards sizes itself and is left
   alone — see .sw-row for those. */
.rf-fields {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0 1.25rem;
    margin-left: 0;
    margin-right: 0;
}
.rf-fields > [class*="col-"] {
    flex: 1 1 240px;
    min-width: 0;
    max-width: 100%;
    padding-left: 0;
    padding-right: 0;
}
/* A field that asked for the whole line still gets it. */
.rf-fields > .col-12:not([class*="col-sm-"]):not([class*="col-md-"]):not([class*="col-lg-"]):not([class*="col-xl-"]) {
    flex: 1 1 100%;
}

@media (max-width: 575.98px) {
    .rf-fields > [class*="col-"] { flex: 1 1 100%; }
}

.footer {
    background: transparent;
    color: var(--rf-faint);
    border-top: 1px solid var(--rf-line);
}

/* == 4. Surfaces ========================================================== */

.card,
.modal-content,
.dropdown-menu,
.popover,
.list-group-item,
.accordion .card {
    background-color: var(--rf-surface);
    border: 1px solid var(--rf-line);
    border-radius: var(--rf-radius-lg);
    color: var(--rf-text);
    box-shadow: none;
}

.card { box-shadow: var(--rf-shadow-sm); }
.modal-content { box-shadow: var(--rf-shadow-lg); }
.dropdown-menu { box-shadow: var(--rf-shadow-md); border-radius: var(--rf-radius-md); padding: 6px; }

.card-header, .card-footer, .modal-header, .modal-footer {
    background-color: transparent;
    border-color: var(--rf-line);
}

.dropdown-item { color: var(--rf-text); border-radius: var(--rf-radius-sm); }
.dropdown-item:hover, .dropdown-item:focus {
    background: color-mix(in srgb, var(--rf-accent) 14%, transparent);
    color: var(--rf-a100);
}
.dropdown-divider { border-color: var(--rf-line); }

.modal-backdrop.show { opacity: .68; background: #05060c; }

hr, .border, .border-top, .border-bottom, .border-left, .border-right {
    border-color: var(--rf-line) !important;
}

/* == 5. Data ============================================================== */

.table {
    color: var(--rf-text);
    border-color: var(--rf-line);
}

.table thead th {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    font-size: 11.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--rf-muted);
    border-bottom: 1px solid var(--rf-divider);
    border-top: 0;
    background: transparent;
}

.table td, .table th { border-top-color: var(--rf-line); vertical-align: middle; }

.table-striped tbody tr:nth-of-type(odd) {
    background-color: color-mix(in srgb, var(--rf-text) 3%, transparent);
}
/* ROW HOVER IS THE DEFAULT, not something a table opts into.
   Bootstrap makes it opt-in through .table-hover, and the class was written
   on most tables and forgotten on the rest — the Financial Sheet's own
   tables, the commercial invoice, the two locker-package summaries and the
   warehouse item list among them. So the same kind of list responded to the
   cursor on one screen and sat dead on the next. The row you are pointing at
   is a property of a table, not a decision per page. */
.table tbody tr:hover,
.table-hover tbody tr:hover {
    background-color: color-mix(in srgb, var(--rf-accent) 10%, transparent);
    color: var(--rf-text);
}
/* Except where a row is not a row: a table used purely for layout inside a
   printed document, and the totals foot, have nothing to point at. */
.table tfoot tr:hover,
.rf-no-hover tbody tr:hover { background-color: transparent; }
.table-bordered, .table-bordered td, .table-bordered th { border-color: var(--rf-line); }

/* DataTables chrome */
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate { color: var(--rf-muted) !important; }

.dataTables_wrapper .dataTables_paginate .paginate_button {
    color: var(--rf-text) !important;
    border-radius: var(--rf-radius-sm);
    border: 1px solid transparent;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current,
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
    background: color-mix(in srgb, var(--rf-accent) 18%, transparent) !important;
    border-color: color-mix(in srgb, var(--rf-accent) 40%, transparent) !important;
    color: var(--rf-a100) !important;
}

.page-link {
    background: transparent;
    border-color: var(--rf-line);
    color: var(--rf-text);
}
.page-item.active .page-link {
    background: var(--rf-accent);
    border-color: var(--rf-accent);
    color: #fff;
}

/* == 6. Controls ========================================================== */

.form-control,
.custom-select,
.select2-container--default .select2-selection--single,
.select2-container--default .select2-selection--multiple,
textarea.form-control {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border: 1px solid var(--rf-line);
    border-radius: var(--rf-radius-md);
    color: var(--rf-text);
    font-family: var(--rf-font-body);
    transition: border-color .15s, box-shadow .15s;
}

.form-control:focus,
.custom-select:focus,
.select2-container--default.select2-container--focus .select2-selection--multiple {
    background-color: color-mix(in srgb, var(--rf-surface) 88%, transparent);
    border-color: var(--rf-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 22%, transparent);
    color: var(--rf-text);
}

.form-control::placeholder { color: var(--rf-faint); }
.form-control:disabled, .form-control[readonly] {
    background-color: color-mix(in srgb, var(--rf-text) 6%, transparent);
    color: var(--rf-muted);
}

label, .col-form-label { color: var(--rf-muted); }

.select2-container--default .select2-selection--single .select2-selection__rendered { color: var(--rf-text); }
.select2-dropdown {
    background: var(--rf-surface);
    border-color: var(--rf-line);
    border-radius: var(--rf-radius-md);
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
    background: color-mix(in srgb, var(--rf-accent) 26%, transparent);
    color: var(--rf-a100);
}
.select2-container--default .select2-search--dropdown .select2-search__field {
    background: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border-color: var(--rf-line);
    color: var(--rf-text);
}

.btn {
    font-family: var(--rf-font-heading);
    font-weight: 500;
    border-radius: var(--rf-radius-md);
    letter-spacing: 0;
    transition: background .15s, border-color .15s, color .15s;
}

.btn-primary, .btn-info {
    background: var(--rf-accent);
    border-color: var(--rf-accent);
    color: #fff;
}
.btn-primary:hover, .btn-info:hover,
.btn-primary:focus, .btn-info:focus {
    background: var(--rf-a500);
    border-color: var(--rf-a500);
    color: #fff;
}

/* .btn-default is Bootstrap 3's name for the neutral button. Bootstrap 4
   dropped it, but the template kept a rule painting it a flat #2962FF — so
   seventy-three buttons across twenty-six screens are a blue that belongs to
   no palette, sitting beside the accent-coloured buttons they were meant to
   match. It is the same button as .btn-secondary and it is dressed like one. */
.btn-dark, .btn-secondary, .btn-default {
    background: color-mix(in srgb, var(--rf-accent) 88%, black);
    border-color: color-mix(in srgb, var(--rf-accent) 88%, black);
    color: #fff;
}
.btn-default:hover, .btn-default:focus {
    background: color-mix(in srgb, var(--rf-accent) 78%, black);
    border-color: color-mix(in srgb, var(--rf-accent) 78%, black);
    color: #fff;
}

.btn-success { background: var(--rf-ok);   border-color: var(--rf-ok);   color: #fff; }
.btn-warning { background: var(--rf-warn); border-color: var(--rf-warn); color: #16181d; }
.btn-danger  { background: var(--rf-bad);  border-color: var(--rf-bad);  color: #fff; }

.btn-light {
    background: color-mix(in srgb, var(--rf-text) 8%, transparent);
    border-color: var(--rf-line);
    color: var(--rf-text);
}

.btn-outline-dark, .btn-outline-secondary, .btn-outline-primary {
    background: transparent;
    border-color: var(--rf-line);
    color: var(--rf-text);
}
.btn-outline-dark:hover, .btn-outline-secondary:hover, .btn-outline-primary:hover {
    background: color-mix(in srgb, var(--rf-accent) 14%, transparent);
    border-color: var(--rf-accent);
    color: var(--rf-a100);
}

.btn-link { color: var(--rf-a300); }

.custom-control-input:checked ~ .custom-control-label::before {
    background-color: var(--rf-accent);
    border-color: var(--rf-accent);
}
.custom-control-label::before {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border-color: var(--rf-line);
}
/* Superseded by section 31, which draws these controls rather than
   relying on accent-color — which only ever paints the checked state. */

.nav-tabs { border-bottom-color: var(--rf-line); }
.nav-tabs .nav-link { color: var(--rf-muted); border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0; }
.nav-tabs .nav-link.active {
    background: transparent;
    border-color: var(--rf-line) var(--rf-line) transparent;
    color: var(--rf-a200);
}

/* Pills were never themed: the template paints an inactive .nav-link black,
   which disappears on the dark ground (the customer dashboard's Pre Alert /
   Registered Packages pills, the customer 360 tabs). */
.nav-pills .nav-link:not(.active) { color: var(--rf-muted); }
.nav-pills .nav-link:not(.active):hover { color: var(--rf-text); }

.input-group-text {
    background: color-mix(in srgb, var(--rf-text) 6%, transparent);
    border-color: var(--rf-line);
    color: var(--rf-muted);
}

/* == 7. Status ============================================================ */

.badge-primary { background: color-mix(in srgb, var(--rf-accent) 22%, transparent); color: var(--rf-a200); }
.badge-success { background: color-mix(in srgb, var(--rf-ok) 20%, transparent);     color: var(--rf-ok); }
.badge-warning { background: color-mix(in srgb, var(--rf-warn) 20%, transparent);   color: var(--rf-warn); }
.badge-danger  { background: color-mix(in srgb, var(--rf-bad) 20%, transparent);    color: var(--rf-bad); }
.badge-info    { background: color-mix(in srgb, var(--rf-info) 20%, transparent);   color: var(--rf-info); }
/* A count badge is a figure someone is meant to read, so it takes full-strength
   text: muted grey on a 10% wash of the same grey was barely legible. */
.badge-light, .badge-secondary {
    background: color-mix(in srgb, var(--rf-text) 14%, transparent);
    color: var(--rf-text);
}

.alert { border-radius: var(--rf-radius-md); border-width: 1px; }
.alert-success { background: color-mix(in srgb, var(--rf-ok) 14%, transparent);   border-color: color-mix(in srgb, var(--rf-ok) 38%, transparent);   color: var(--rf-ok); }
.alert-warning { background: color-mix(in srgb, var(--rf-warn) 14%, transparent); border-color: color-mix(in srgb, var(--rf-warn) 38%, transparent); color: var(--rf-warn); }
.alert-danger  { background: color-mix(in srgb, var(--rf-bad) 14%, transparent);  border-color: color-mix(in srgb, var(--rf-bad) 38%, transparent);  color: var(--rf-bad); }
.alert-info    { background: color-mix(in srgb, var(--rf-info) 14%, transparent); border-color: color-mix(in srgb, var(--rf-info) 38%, transparent); color: var(--rf-info); }

/* helpers/pills.php — the status vocabulary. It sets its own hue inline;
   this only settles the shape and weight. */
.cdp-pill {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    border-radius: 999px;
    letter-spacing: .01em;
}

/* == 8. Third-party chrome ================================================ */

.swal2-popup {
    background: var(--rf-surface) !important;
    color: var(--rf-text) !important;
    border-radius: var(--rf-radius-lg) !important;
    border: 1px solid var(--rf-line);
    font-family: var(--rf-font-body);
}
.swal2-title { color: var(--rf-text) !important; font-family: var(--rf-font-heading); }
.swal2-html-container { color: var(--rf-text) !important; font-family: var(--rf-font-body); }
.swal2-styled.swal2-confirm { background: var(--rf-accent) !important; border-radius: var(--rf-radius-md) !important; }
.swal2-styled.swal2-cancel  { background: color-mix(in srgb, var(--rf-text) 14%, transparent) !important; color: var(--rf-text) !important; border-radius: var(--rf-radius-md) !important; }
.swal2-input, .swal2-select, .swal2-textarea {
    background: color-mix(in srgb, var(--rf-surface) 70%, transparent) !important;
    border: 1px solid var(--rf-line) !important;
    color: var(--rf-text) !important;
}

.apexcharts-text, .apexcharts-legend-text, .apexcharts-title-text {
    fill: var(--rf-muted) !important;
    color: var(--rf-muted) !important;
    font-family: var(--rf-font-body) !important;
}
.apexcharts-gridline { stroke: var(--rf-line) !important; }
.apexcharts-tooltip {
    background: var(--rf-surface) !important;
    border: 1px solid var(--rf-line) !important;
    color: var(--rf-text) !important;
    box-shadow: var(--rf-shadow-md) !important;
}
.apexcharts-tooltip-title {
    background: color-mix(in srgb, var(--rf-text) 6%, transparent) !important;
    border-bottom-color: var(--rf-line) !important;
}

/* Native date/number pickers follow the ground rather than the OS default. */
input[type="date"], input[type="datetime-local"], input[type="time"], input[type="month"] { color-scheme: inherit; }

/* == 9. Scrollbars ======================================================== */

* { scrollbar-color: color-mix(in srgb, var(--rf-text) 22%, transparent) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--rf-text) 18%, transparent);
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--rf-accent) 50%, transparent);
    background-clip: content-box;
}

/* == 10. The financial-permission mask ==================================== */

.rf-fin {
    filter: blur(var(--fin-blur, 0px));
    opacity: var(--fin-op, 1);
    pointer-events: var(--fin-pe, auto);
    transition: filter .3s, opacity .3s;
}

/* == 11. Print ============================================================
   Documents (labels, receipts, invoices, manifests) are measured to fit a
   physical sheet and must stay black on white. The theme is a screen concern
   only. */
@media print {
    :root, html[data-rf-theme="dark"], html[data-rf-theme="light"] {
        --rf-bg: #fff; --rf-surface: #fff; --rf-text: #000;
        --rf-line: #ddd; --rf-muted: #444; --rf-faint: #777;
        --rf-page: #fff;
        color-scheme: light;
    }
    body { background: #fff !important; color: #000 !important; }
    .card, .modal-content { box-shadow: none !important; border-color: #ddd !important; }
}

/* == 12. Shared controls introduced with the new design ===================
   Small pieces of chrome that more than one screen needs. They live here
   rather than in each page's <style> block so the switchboards, the access
   queue and the dashboards cannot drift apart. */

/* Topbar icon button — the theme switch, and anything that joins it later. */
.rf-topbar-btn {
    width: 34px;
    height: 34px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--rf-line);
    background: transparent;
    color: var(--rf-text);
    cursor: pointer;
    padding: 0;
    transition: border-color .15s, color .15s;
}
.rf-topbar-btn:hover {
    border-color: color-mix(in srgb, var(--rf-accent) 40%, transparent);
    color: var(--rf-a200);
}
.rf-topbar-btn i { font-size: 17px; line-height: 1; }

/* On/off switch. One implementation, used by the component switchboard and
   the access queue; the feature-gate page's own .fg-switch is re-tinted to
   match rather than being left on its light-theme greys. */
.rf-switch { position: relative; display: inline-block; width: 46px; height: 24px; vertical-align: middle; margin: 0; }
.rf-switch input { opacity: 0; width: 0; height: 0; }
.rf-switch-slider,
.fg-slider {
    position: absolute; inset: 0; cursor: pointer; border-radius: 24px;
    background: color-mix(in srgb, var(--rf-text) 18%, transparent);
    transition: background .18s ease;
}
.rf-switch-slider::before,
.fg-slider::before {
    content: ""; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px;
    border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.35);
    transition: transform .18s ease;
}
.rf-switch input:checked + .rf-switch-slider,
.fg-switch input:checked + .fg-slider { background: var(--rf-ok); }
.rf-switch input:checked + .rf-switch-slider::before,
.fg-switch input:checked + .fg-slider::before { transform: translateX(22px); }
.rf-switch input:focus-visible + .rf-switch-slider,
.fg-switch input:focus-visible + .fg-slider {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-ok) 32%, transparent);
}

/* The feature-gate page hard-codes a light child-row ground. */
.fg-children { background: color-mix(in srgb, var(--rf-text) 4%, transparent) !important; }
.fg-children td { border-top-color: var(--rf-line) !important; }
.fg-key, .rf-key {
    color: var(--rf-faint);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .75rem;
}
.fg-caret { color: var(--rf-faint) !important; }

/* Section kicker — the uppercase rule-and-label that opens each block of the
   new design. */
.rf-kicker {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--rf-font-heading);
    font-size: 11.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--rf-accent);
    margin-bottom: 10px;
}
.rf-kicker::before {
    content: "";
    width: 26px;
    height: 1px;
    background: var(--rf-accent);
    flex: none;
}

/* An empty state that reads as a decision rather than as a failure. */
.rf-empty {
    border: 1px dashed var(--rf-line);
    border-radius: var(--rf-radius-lg);
    padding: 28px 22px;
    text-align: center;
    color: var(--rf-muted);
    font-size: 13.5px;
}

/* == 13. Locked surfaces ==================================================
   A figure a role may not see is blurred rather than removed, so the shape of
   the panel is the same for everyone and nobody has to wonder whether a card
   is missing or simply not theirs. The overlay on top is how they ask for it.

   The real value never reaches the page — helpers/dashboard_data.php sends a
   constant placeholder when the viewer may not see money — so the blur is a
   presentation choice on top of a server-side decision, not the decision
   itself. */

.rf-lock-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 0;
    border-radius: inherit;
    background: color-mix(in srgb, var(--rf-bg) 34%, transparent);
    color: var(--rf-a200);
    font-family: var(--rf-font-body);
    cursor: pointer;
    transition: background .15s;
}
.rf-lock-overlay:hover { background: color-mix(in srgb, var(--rf-bg) 20%, transparent); }
.rf-lock-overlay i { font-size: 20px; }
.rf-lock-overlay span {
    font-size: 11.5px;
    letter-spacing: .06em;
    text-transform: uppercase;
    text-align: center;
    padding: 0 10px;
}

.rf-lock-inline {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border-radius: var(--rf-radius-md);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 40%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 12%, transparent);
    color: var(--rf-a200);
    font-family: var(--rf-font-body);
    font-size: 12.5px;
    cursor: pointer;
    transition: background .15s, border-color .15s;
}
.rf-lock-inline:hover {
    background: color-mix(in srgb, var(--rf-accent) 20%, transparent);
    border-color: var(--rf-accent);
}

/* A grant already asked for, or already held, is not an invitation to ask
   again — the button stays visible so the state is legible, but it stops
   looking like a call to action. */
.rf-lock-overlay[data-state="pending"],
.rf-lock-inline[data-state="pending"] { color: var(--rf-warn); cursor: default; }
.rf-lock-overlay[data-state="live"],
.rf-lock-inline[data-state="live"] { color: var(--rf-ok); cursor: default; }

/* The card a lock sits on has to be a positioning context, and the blurred
   content must not be tabbable underneath the overlay. */
.rf-lockable { position: relative; }
.rf-lockable .rf-fin { pointer-events: none; }

/* == 14. Control-panel blocks introduced by the new design ================
   Three shapes the panels did not have before, rendered by
   helpers/dashboard_blocks.php:

     .rf-filterbar   the freight-mode and date-range control strip
     .rf-desk        "your desk" — what this role is here to do today
     .rf-hero        the headline figure for the selected freight mode

   They lay out with grid and flex and carry their own wrap behaviour, so a
   block switched off in the component switchboard leaves no hole: it is
   simply not in the flow, and its neighbours take the width. */

/* -- Filter bar --------------------------------------------------------- */

.rf-filterbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 14px;
    margin-bottom: 1.4rem;
    border-radius: var(--rf-radius-lg);
    border: 1px solid var(--rf-line);
    background: color-mix(in srgb, var(--rf-surface) 55%, transparent);
}

.rf-filterbar-label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rf-faint);
}

.rf-seg {
    display: inline-flex;
    gap: 3px;
    padding: 3px;
    border-radius: 999px;
    border: 1px solid var(--rf-line);
}
.rf-seg a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 13px;
    border-radius: 999px;
    font-size: 12.5px;
    color: var(--rf-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: background .15s, color .15s;
}
.rf-seg a:hover { color: var(--rf-a100); }
.rf-seg a.is-on {
    background: color-mix(in srgb, var(--rf-accent) 20%, transparent);
    color: var(--rf-a100);
}

.rf-filterbar-spacer { flex: 1; min-width: 12px; }

.rf-dates { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.rf-fchip {
    padding: 6px 12px;
    border-radius: 999px;
    font-size: 12.5px;
    white-space: nowrap;
    border: 1px solid var(--rf-line);
    background: transparent;
    color: var(--rf-muted);
    text-decoration: none;
    transition: border-color .15s, color .15s;
}
.rf-fchip:hover { border-color: var(--rf-accent); color: var(--rf-a100); }
.rf-fchip.is-on {
    border-color: color-mix(in srgb, var(--rf-accent) 55%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 14%, transparent);
    color: var(--rf-a100);
}

.rf-daterange {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    color: var(--rf-muted);
}
.rf-daterange input {
    font-family: var(--rf-font-body);
    font-size: 12.5px;
    color: var(--rf-text);
    background: transparent;
    border: 1px solid var(--rf-line);
    border-radius: var(--rf-radius-sm);
    padding: 5px 8px;
}

/* -- Your desk ---------------------------------------------------------- */

.rf-desk-row {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: 1.4rem;
}

.rf-desk {
    min-width: 0;
    border-radius: var(--rf-radius-lg);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 28%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 9%, transparent);
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 11px;
}
.rf-desk-kicker {
    display: flex;
    align-items: center;
    gap: 9px;
    font-family: var(--rf-font-heading);
    font-size: 11.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--rf-a300);
}
.rf-desk-brief { margin: 0; font-size: 13.5px; line-height: 21px; color: var(--rf-muted); }
.rf-desk-links { display: flex; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 4px; }
.rf-desk-links a {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 12px;
    border-radius: var(--rf-radius-md);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 34%, transparent);
    color: var(--rf-a300);
    font-size: 12.5px;
    text-decoration: none;
    white-space: nowrap;
    transition: background .15s, color .15s;
}
.rf-desk-links a:hover {
    background: color-mix(in srgb, var(--rf-accent) 14%, transparent);
    color: var(--rf-a200);
}

.rf-count {
    position: relative;
    min-width: 0;
    border-radius: var(--rf-radius-lg);
    border: 1px solid var(--rf-line);
    background: var(--rf-surface);
    padding: 16px 18px;
    text-decoration: none;
    display: block;
    color: inherit;
    overflow: hidden;
    transition: border-color .15s, background .15s, transform .15s, box-shadow .15s;
}
/* The figure's own colour, washed across the card on the way in — the same
   language the intake bars and the status segments already speak. */
.rf-count::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--rf-count-accent, var(--rf-accent));
    opacity: 0;
    transition: opacity .15s;
    pointer-events: none;
}
a.rf-count:hover,
a.rf-count:focus-visible {
    color: inherit;
    text-decoration: none;
    border-color: color-mix(in srgb, var(--rf-count-accent, var(--rf-accent)) 55%, transparent);
    transform: translateY(-2px);
    box-shadow: var(--rf-shadow-md);
}
a.rf-count:hover::before,
a.rf-count:focus-visible::before { opacity: .09; }
a.rf-count:hover .rf-count-n { color: var(--rf-count-accent, var(--rf-accent)); }

/* The arrow says the figure goes somewhere, and leans out when you point. */
.rf-count-go {
    margin-left: auto;
    font-size: 13px;
    opacity: .35;
    transition: opacity .15s, transform .15s;
}
a.rf-count:hover .rf-count-go { opacity: 1; transform: translate(2px, -2px); }

@media (prefers-reduced-motion: reduce) {
    .rf-count, .rf-count-go { transition: none; }
    a.rf-count:hover { transform: none; }
    a.rf-count:hover .rf-count-go { transform: none; }
}
.rf-count-label {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rf-muted);
}
.rf-count-n {
    position: relative;
    transition: color .15s;
    font-family: var(--rf-font-heading);
    font-weight: 600;
    font-size: 25px;
    letter-spacing: -0.025em;
    margin-top: 12px;
    color: var(--rf-text);
}
.rf-count-sub { position: relative; font-size: 12px; color: var(--rf-faint); margin-top: 4px; }

/* The counter's own fortnight — the intake card's bar strip, at tile scale.
   Same markup and same class names, so it inherits the same per-bar tooltip
   and the same hover; only the height and the caption differ, because a tile
   is not a card. */
.rf-count-bars {
    position: relative;
    margin-top: 14px;
}
.rf-count-bars .sw-bars {
    height: 40px;
    gap: 3px;
    margin-top: 0;
}
.rf-count-bars .sw-bar {
    border-radius: 2px 2px 0 0;
    background: color-mix(in srgb, var(--kpi, var(--rf-accent)) 45%, transparent);
}
.rf-count-bars .sw-bar:hover { background: var(--kpi, var(--rf-accent)); }
.rf-count-bars-foot {
    display: flex;
    justify-content: space-between;
    margin-top: 6px;
    font-size: 10.5px;
    color: var(--rf-faint);
}
a.rf-count:hover .rf-count-bars .sw-bar {
    background: color-mix(in srgb, var(--kpi, var(--rf-accent)) 62%, transparent);
}
a.rf-count:hover .rf-count-bars .sw-bar:hover { background: var(--kpi, var(--rf-accent)); }

/* -- Freight-mode headline ---------------------------------------------- */

.rf-hero {
    position: relative;
    overflow: hidden;
    border-radius: var(--rf-radius-lg);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 26%, transparent);
    background:
        radial-gradient(680px 340px at 88% -60%, color-mix(in srgb, var(--rf-section-glow) 62%, transparent), transparent 64%),
        var(--rf-section);
    padding: 24px 26px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 18px;
    min-height: 210px;
    margin-bottom: 1.4rem;
}
.rf-hero-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    font-size: 11.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: color-mix(in srgb, var(--rf-text) 70%, transparent);
}
.rf-hero-top > span { display: inline-flex; align-items: center; gap: 9px; }
.rf-hero-mid { display: flex; align-items: flex-end; gap: 26px; flex-wrap: wrap; }
.rf-hero-n {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    font-size: clamp(38px, 5.4vw, 66px);
    line-height: .95;
    letter-spacing: -0.035em;
    color: var(--rf-text);
}
.rf-hero-sub { font-size: 13px; color: color-mix(in srgb, var(--rf-text) 62%, transparent); margin-top: 8px; }
.rf-hero-spark { flex: 1; min-width: 180px; height: 74px; }
.rf-hero-foot {
    display: flex;
    gap: 22px;
    flex-wrap: wrap;
    padding-top: 14px;
    border-top: 1px solid color-mix(in srgb, var(--rf-text) 14%, transparent);
}
.rf-hero-foot > div { display: flex; align-items: center; gap: 9px; font-size: 13px; color: color-mix(in srgb, var(--rf-text) 70%, transparent); }
.rf-hero-foot strong { font-family: var(--rf-font-heading); font-weight: 600; font-size: 15px; color: var(--rf-text); }

/* -- Tablet and phone --------------------------------------------------- */

@media (max-width: 991px) {
    /* One-and-a-bit columns of desk plus counters does not fit a tablet; the
       brief takes the full width and the counters grid under it. */
    .rf-desk-row { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
    .rf-desk { grid-column: 1 / -1; }
}

@media (max-width: 767px) {
    .rf-filterbar { padding: 10px; }
    .rf-filterbar-spacer { display: none; }
    /* On a phone the seg control and the date chips each get their own line
       rather than wrapping mid-control. */
    .rf-seg, .rf-dates { width: 100%; }
    .rf-seg { justify-content: space-between; }
    .rf-seg a { flex: 1; justify-content: center; padding: 8px 6px; }
    .rf-daterange { width: 100%; }
    .rf-daterange input { flex: 1; min-width: 0; }

    .rf-hero { padding: 18px; min-height: 0; }
    .rf-hero-spark { display: none; }
    .rf-hero-foot { gap: 12px 18px; }
}

/* == 15. Small screens ====================================================
   The template was built for a desk. Two of its habits push a phone page
   sideways, and a page that scrolls horizontally is a page where the primary
   action is off-screen — so both are corrected here rather than left to the
   individual views.

   These are layout corrections, not a re-layout: nothing moves, nothing is
   hidden, and the desktop rendering is untouched. */

/* The page loader. Its spinner box is 40px, but the animated GIF inside it is
   220px wide at natural size and was never constrained — on a desk the extra
   is absorbed, on a 375px phone it drags the whole document sideways before
   the loader has even faded. It is capped at every width, not only small
   ones, because a loader has no business setting the page width anywhere. */
#preloader { overflow: hidden; }
#preloader .spinner img { max-width: 100%; height: auto; display: block; }

@media (max-width: 767.98px) {
    /* 1. The brand wordmark carries its pixel width as an HTML attribute
          (thumb_w in the settings), so a logo sized for a desktop header is
          220px wide on a 375px phone and drags the fixed topbar — and with it
          the whole document — past the viewport. The mark is allowed to
          shrink instead. */
    .topbar .navbar-header,
    .topbar .navbar-brand {
        min-width: 0;
        max-width: 100%;
    }
    .topbar .navbar-brand {
        overflow: hidden;
        display: flex;
        align-items: center;
    }
    .topbar .navbar-brand img {
        max-width: min(150px, 42vw);
        height: auto;
        width: auto;
    }

    /* 2. Anything genuinely wider than a phone — a data table, the calendar
          heatmap — scrolls inside its own box. Most already do; this catches
          the tables that were only ever seen on a desk. */
    .sw-table-wrap,
    .sw-heat-wrap,
    .table-responsive {
        -webkit-overflow-scrolling: touch;
    }

    /* 3. The page gutters were sized for a sidebar that is not there. */
    .page-wrapper .container-fluid { padding-left: 12px; padding-right: 12px; }
    .page-breadcrumb { padding-left: 12px; padding-right: 12px; }

    /* 4. A row of quick actions wraps rather than running off the edge. */
    .sw-quick-actions { flex-wrap: wrap; gap: .4rem; }
    .sw-dash-hello { gap: .6rem; }
}

@media (max-width: 991.98px) {
    /* Tablet: the fluid row still wants two cards abreast, but an xl card at
       640px basis plus its neighbour does not fit — the basis is relaxed so
       they wrap instead of overflowing. */
    .sw-c-xl { flex-basis: 100%; }
    .sw-c-lg { flex-basis: 100%; }
}

/* == 16. The hidden money figure =========================================
   A figure the viewer may not see is a blurred placeholder, and the
   placeholder is the way to ask for the real one. It has to sit inside table
   cells, list rows and headline strips without changing any of their metrics,
   so it inherits its type and carries no padding of its own. */

.sw-money-lock {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
    vertical-align: baseline;
}
.sw-money-lock i { font-size: .8em; color: var(--rf-faint); transition: color .15s; }
.sw-money-lock:hover i { color: var(--rf-accent); }
.sw-money-lock[data-state="pending"] { cursor: default; }
.sw-money-lock[data-state="pending"] i { color: var(--rf-warn); }
.sw-money-lock .sw-blur { pointer-events: none; }

/* == 17. Topbar search ====================================================
   The new design opens the topbar with a search field. It sits at the left,
   after the sidebar toggle, and it is the widest thing in the bar because it
   is the thing most often reached for. */

.rf-topbar-search { padding: 0 10px; }

.rf-topbar-search form {
    position: relative;
    display: flex;
    align-items: center;
    width: clamp(220px, 24vw, 420px);
    margin: 0;
}

/* The magnifier the field's 34px of left padding is reserved for. At
   --rf-faint it was 42% of the page's own ink — about two to one against the
   topbar — so the icon read as nothing and the space it sits in read as an
   unexplained gap before the text. An icon that is there should look like it
   is there. */
.rf-topbar-search i {
    position: absolute;
    left: 12px;
    font-size: 16px;
    color: var(--rf-muted);
    pointer-events: none;
}
.rf-topbar-search input:focus + i,
.rf-topbar-search form:focus-within i { color: var(--rf-accent); }

.rf-topbar-search input {
    width: 100%;
    min-height: 36px;
    padding: 6px 12px 6px 34px;
    font-family: var(--rf-font-body);
    font-size: 13.5px;
    color: var(--rf-text);
    background: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border: 1px solid var(--rf-line);
    border-radius: var(--rf-radius-md);
    transition: border-color .15s, box-shadow .15s;
}
.rf-topbar-search input::placeholder { color: var(--rf-faint); }
.rf-topbar-search input:focus {
    outline: none;
    border-color: var(--rf-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 22%, transparent);
}

/* == 18. The content ground and the status pills =========================

   Two corrections that only became visible once the whole application was on
   a dark ground.

   1. The template paints .page-wrapper #f6f7f9 with !important — a light
      sheet under every page, which on a dark theme left light text on light
      grey wherever a card did not cover it. It has to be beaten at its own
      game; there is no other way past an !important in a minified file.

   2. Status pills carry their hue from cdb_styles, and an operator picks
      those hues in Tools. Several of them (#1d2c9e, #c53030, #9a6c00) were
      chosen against white paper and disappear against a dark ground. The hue
      is data and must not change, so what changes is how it is USED: the text
      is lifted towards the light on a dark ground and pushed towards the ink
      on a light one, while the tint and the border keep the hue recognisable.
      helpers/pills.php emits the raw value as --pill for exactly this.

      The inline declarations these override are left in place, so a print
      sheet or an emailed document — neither of which loads this file — still
      renders the pill exactly as it always did. */

.page-wrapper { background: transparent !important; }

html[data-rf-theme="dark"] .cdp-pill {
    color: color-mix(in srgb, var(--pill, currentColor) 62%, #ffffff) !important;
    background: color-mix(in srgb, var(--pill, transparent) 20%, transparent) !important;
    border-color: color-mix(in srgb, var(--pill, currentColor) 52%, transparent) !important;
}

html[data-rf-theme="light"] .cdp-pill {
    /* 62/38 is what brings the palette's brightest member (#e0ce07) past
       4.5:1 on white; 82% left it at barely 2:1. */
    color: color-mix(in srgb, var(--pill, currentColor) 62%, #000000) !important;
    background: color-mix(in srgb, var(--pill, transparent) 14%, transparent) !important;
    border-color: color-mix(in srgb, var(--pill, currentColor) 45%, transparent) !important;
}

/* A print sheet is white paper again, whatever the screen was set to. */
@media print {
    html[data-rf-theme="dark"] .cdp-pill,
    html[data-rf-theme="light"] .cdp-pill {
        color: var(--pill, #000) !important;
        background: transparent !important;
        border-color: var(--pill, #999) !important;
    }
}

/* == 19. Bootstrap colour utilities ======================================
   .bg-light appears in 103 view files and .bg-white in 37 — usually on a
   read-only field, a panel header or a well. Both paint a near-white ground
   from a hardcoded hex, so on a dark theme they became white boxes containing
   light text: the figure was there and unreadable.

   They are remapped onto the ground rather than removed, because what those
   views wanted was "a shade away from the surface behind me", and that is a
   relationship, not a colour. .text-dark and .text-white are pinned to the
   ends of the current palette for the same reason.

   The layout utilities Bootstrap ships alongside these are untouched. */

.bg-light,
.bg-white {
    background-color: color-mix(in srgb, var(--rf-text) 6%, transparent) !important;
    color: var(--rf-text);
}

.bg-secondary { background-color: color-mix(in srgb, var(--rf-text) 12%, transparent) !important; }

/* Bootstrap's list group pins its link colour to #495057, a mid grey chosen
   against white. tools.php builds its whole left-hand index out of these. */
.list-group-item-action { color: var(--rf-text); }
.list-group-item-action:hover,
.list-group-item-action:focus {
    background-color: color-mix(in srgb, var(--rf-accent) 12%, transparent);
    color: var(--rf-a100);
}
.list-group-item.active {
    background-color: color-mix(in srgb, var(--rf-accent) 22%, transparent);
    border-color: color-mix(in srgb, var(--rf-accent) 45%, transparent);
    color: var(--rf-a100);
}
.list-group-item.disabled, .list-group-item:disabled { color: var(--rf-faint); background-color: transparent; }
/* tools.php builds its index inside .email-app, and the template has a
   four-class rule for that case which outranks the plain one above. */
.email-app .list-group .list-group-item .list-group-item-action { color: var(--rf-text); }
.email-app .list-group .list-group-item .list-group-item-action:hover,
.email-app .list-group .list-group-item .list-group-item-action.active { color: var(--rf-a100); }

.text-dark { color: var(--rf-text) !important; }
.text-white { color: #fff !important; }

/* .text-success / .text-danger and friends carry meaning, so they keep their
   hue — but the template's are tuned for white paper and go muddy on a dark
   ground, so they are re-pointed at the theme's own status colours. */
.text-success { color: var(--rf-ok) !important; }
.text-warning { color: var(--rf-warn) !important; }
.text-danger  { color: var(--rf-bad) !important; }
.text-info    { color: var(--rf-info) !important; }
.text-primary { color: var(--rf-a300) !important; }

/* The footer line was on --rf-faint, which is a hint colour, not a reading
   colour. */
.footer { color: var(--rf-muted); }

/* == 20. — ==============================================================
   Was "Sidebar text": a dozen rules re-pointing the template's hard-coded
   white sidebar copy at the tokens. The rail draws from the tokens directly
   now, so there is nothing left to correct. */

/* == 21. — ==============================================================
   Was "The sidebar skin": an !important-for-!important fight with the
   template's skin5 rules, which pinned the rail to a fixed navy whatever
   theme was chosen. The rail carries no data-sidebarbg attribute, so the skin
   has nothing to pin. */

/* == 22. Hint colour =====================================================

   --rf-faint was set at 34% of the text colour, which is the design's own
      figure and reads correctly as a hint against the design's own artwork.
      In the application it also carries real captions — a delta, a "vs same
      period", a filter label — and 34% put those at 2.7:1. Lifting it to 42%
      brings them past 3:1 while keeping the three-step hierarchy (text,
      muted, faint) clearly separated. */

:root,
html[data-rf-theme="dark"] { --rf-faint: color-mix(in srgb, #e9e9ed 42%, transparent); }
html[data-rf-theme="light"] { --rf-faint: color-mix(in srgb, #232532 56%, transparent); }

/* == 23. Selecting rows ==================================================

   Ticking a checkbox used to write `background:#fff8e1` onto the row — a pale
   cream picked against a white table, set inline so no stylesheet could
   correct it. Eleven files did it. They now toggle .rf-row-selected and the
   look lives here, once, in the theme.

   A selected row reads three ways at the same time, because a list of forty
   rows needs more than a faint tint to say which four are chosen: a wash of
   the accent, a solid accent bar down the leading edge, and full-strength
   text where the rest of the table is quieter. */

.rf-row-selected > td,
.rf-row-selected > th,
tr.selected-row > td,
tr.marked-row > td {
    background-color: color-mix(in srgb, var(--rf-accent) 15%, transparent) !important;
    color: var(--rf-text);
}

/* The leading edge. An inset shadow rather than a border, so the row does not
   grow by 3px when it is picked and the table stops jumping. */
.rf-row-selected > td:first-child,
tr.selected-row > td:first-child,
tr.marked-row > td:first-child {
    box-shadow: inset 3px 0 0 0 var(--rf-accent);
}

/* Hover must not wash the selection out. */
.table-hover tbody tr.rf-row-selected:hover > td,
.table-hover tbody tr.selected-row:hover > td {
    background-color: color-mix(in srgb, var(--rf-accent) 22%, transparent) !important;
}

/* == 24. The bulk-action bar =============================================

   #div-actions-checked was a bare button group with the words "Selected 3"
   beside it, sitting wherever the page happened to put it. On a long list you
   selected rows at the bottom and the only acknowledgement was off-screen.

   It is now a bar: tinted so it reads as a mode the list is in, with the
   count as a figure rather than running text, and stuck to the bottom of its
   own container so it stays with you while you keep choosing. Sticky, not
   fixed — it takes part in the page's layout and cannot collide with the
   topbar or the view-as banner. */

#div-actions-checked:not(.hide) {
    position: sticky;
    bottom: 8px;
    z-index: 5;
    display: flex !important;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding: 10px 16px;
    margin-top: 12px;
    border-radius: var(--rf-radius-lg);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 45%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 14%, var(--rf-surface));
    box-shadow: var(--rf-shadow-md);
    animation: rfSelectIn .18s ease both;
}

@keyframes rfSelectIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: none; }
}

#div-actions-checked:not(.hide) .btn-group { margin-top: 0 !important; }

/* The count. It was <strong>0</strong> in a sentence; it is the number of
   things about to be acted on, so it is set as a figure. */
#countChecked {
    display: inline-grid;
    place-items: center;
    min-width: 30px;
    height: 30px;
    padding: 0 9px;
    border-radius: 999px;
    background: var(--rf-accent);
    color: #fff;
    font-family: var(--rf-font-heading);
    font-weight: 700;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}
#countChecked.hide { display: none; }

#div-actions-checked:not(.hide) span > strong:first-child,
#div-actions-checked:not(.hide) > span {
    font-size: 12px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--rf-a200);
    margin: 0 !important;
}

/* The action itself is the primary thing to do once rows are picked. */
#div-actions-checked:not(.hide) .btn.dropdown-toggle {
    background: var(--rf-accent);
    border-color: var(--rf-accent);
    color: #fff;
    font-weight: 600;
}
#div-actions-checked:not(.hide) .btn.dropdown-toggle:hover { background: var(--rf-a500); }

@media (max-width: 575.98px) {
    #div-actions-checked:not(.hide) { padding: 9px 12px; gap: 10px; }
}

/* == 25. Consolidation totals ============================================

   The builder's running totals — weight, cost, and how many packages are on
   the consolidation — were plain cells in a table footer, the same weight as
   any other row. They are the figures the whole screen exists to produce, so
   the footer is treated as a summary band and the figures are set as
   figures. */

#invoice-item-table tfoot tr,
#invoice-item-table tfoot tr td {
    background: color-mix(in srgb, var(--rf-accent) 10%, transparent);
    border-top: 1px solid color-mix(in srgb, var(--rf-accent) 40%, transparent) !important;
}
#invoice-item-table tfoot td {
    padding-top: 13px;
    padding-bottom: 13px;
    color: var(--rf-muted);
    font-size: 12px;
    letter-spacing: .06em;
    text-transform: uppercase;
}
#invoice-item-table tfoot td b { color: var(--rf-a200); font-weight: 600; }

#total_weight_sum,
#total_cost_sum {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    font-size: 17px;
    letter-spacing: -0.01em;
    text-transform: none;
    color: var(--rf-text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* A .bg-inverse table header is a hardcoded navy band with white text on it —
   fixed against the old light page and wrong on either theme now. This was
   corrected on the invoice item table alone; a dozen more tables carry the
   same band, so a list with one sat beside a list without one. Every table
   header on screen reads the same way. (Print and Excel sheets load no
   stylesheet, so their own literals are untouched.) */
thead.bg-inverse,
thead.bg-inverse th,
tr.bg-inverse, tr.bg-inverse th {
    background-color: color-mix(in srgb, var(--rf-text) 7%, transparent) !important;
    color: var(--rf-muted) !important;
    border-color: var(--rf-line) !important;
}
thead.bg-inverse .text-white,
thead.bg-inverse.text-white th { color: var(--rf-muted) !important; }

/* == 26. The package picker's selection bar ==============================

   Choosing packages for a consolidation is an accumulating decision: how many
   so far, how much they weigh, what they come to. Those three figures were
   bold running text beside a button — the same visual weight as the table
   behind them, and easy to miss entirely while clicking down a list of a
   hundred and seventy rows.

   They are set as figures now, on a bar tinted to say the list is in a
   selecting state, and the bar sticks to the top of the scrolling modal so
   the running total stays in view while you keep choosing. */

.rf-selbar {
    position: sticky;
    top: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    padding: 11px 16px;
    margin-bottom: 12px;
    border-radius: var(--rf-radius-lg);
    border: 1px solid color-mix(in srgb, var(--rf-accent) 45%, transparent);
    background: color-mix(in srgb, var(--rf-accent) 14%, var(--rf-surface));
    box-shadow: var(--rf-shadow-md);
}

.rf-selbar-spacer { flex: 1 1 auto; }

/* The count: the figure first, its noun after it and quiet. */
.rf-selbar-count { display: inline-flex; align-items: baseline; gap: 7px; }
.rf-selbar-count b {
    font-family: var(--rf-font-heading);
    font-weight: 700;
    font-size: 21px;
    line-height: 1;
    color: var(--rf-a100);
    font-variant-numeric: tabular-nums;
}
.rf-selbar-count span {
    font-size: 11.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--rf-a200);
}

.rf-selbar-totals { display: inline-flex; align-items: center; gap: 18px; flex-wrap: wrap; }

/* Each total is a label over its figure, so the two read as a pair rather
   than as one sentence separated by a pipe. */
.rf-selbar-stat { display: inline-grid; gap: 1px; }
.rf-selbar-stat i {
    font-style: normal;
    font-size: 10.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--rf-muted);
}
.rf-selbar-stat b {
    font-family: var(--rf-font-heading);
    font-weight: 600;
    font-size: 15px;
    line-height: 1.15;
    color: var(--rf-text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    .rf-selbar { gap: 12px; padding: 10px 12px; }
    .rf-selbar-spacer { display: none; }
    .rf-selbar .btn { width: 100%; }
}

/* == 27. Vendor widgets ==================================================

   Three third-party stylesheets ship a complete white-page look and are
   loaded by individual pages AFTER this file — the date range picker, the
   Froala editor and the DataTables chrome. Source order therefore goes the
   wrong way, and only !important reaches them.

   They are corrected here rather than edited in place: they are vendor files,
   they get replaced on upgrade, and a fix written into one of them is a fix
   that disappears the next time somebody drops in a new version. */

/* ── Date range picker ─────────────────────────────────────────────────── */

.daterangepicker {
    background-color: var(--rf-surface) !important;
    border-color: var(--rf-line) !important;
    color: var(--rf-text) !important;
    box-shadow: var(--rf-shadow-lg) !important;
    border-radius: var(--rf-radius-md) !important;
}
.daterangepicker::before, .daterangepicker::after { display: none !important; }

.daterangepicker .calendar-table {
    background-color: var(--rf-surface) !important;
    border-color: var(--rf-line) !important;
}
.daterangepicker td, .daterangepicker th { color: var(--rf-text) !important; border-color: transparent !important; }
.daterangepicker th.month { color: var(--rf-text) !important; font-family: var(--rf-font-heading); }
.daterangepicker td.off,
.daterangepicker td.off.in-range,
.daterangepicker td.off.start-date,
.daterangepicker td.off.end-date {
    background-color: transparent !important;
    color: var(--rf-faint) !important;
}
.daterangepicker td.available:hover,
.daterangepicker th.available:hover {
    background-color: color-mix(in srgb, var(--rf-accent) 18%, transparent) !important;
    color: var(--rf-a100) !important;
}
.daterangepicker td.in-range {
    background-color: color-mix(in srgb, var(--rf-accent) 14%, transparent) !important;
    color: var(--rf-text) !important;
}
.daterangepicker td.active,
.daterangepicker td.active:hover {
    background-color: var(--rf-accent) !important;
    color: #fff !important;
}
.daterangepicker .ranges li {
    color: var(--rf-text) !important;
    border-radius: var(--rf-radius-sm);
}
.daterangepicker .ranges li:hover {
    background-color: color-mix(in srgb, var(--rf-accent) 14%, transparent) !important;
}
.daterangepicker .ranges li.active {
    background-color: var(--rf-accent) !important;
    color: #fff !important;
}
.daterangepicker .drp-buttons { border-top-color: var(--rf-line) !important; }
.daterangepicker .drp-selected { color: var(--rf-muted) !important; }
.daterangepicker select,
.daterangepicker .input-mini {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent) !important;
    border-color: var(--rf-line) !important;
    color: var(--rf-text) !important;
}

/* ── Froala editor ─────────────────────────────────────────────────────── */

.fr-box .fr-wrapper,
.fr-box .fr-toolbar,
.fr-box .second-toolbar,
.fr-popup,
.fr-desktop .fr-command:hover {
    background: var(--rf-surface) !important;
    color: var(--rf-text) !important;
    border-color: var(--rf-line) !important;
}
.fr-element, .fr-view { color: var(--rf-text) !important; background: var(--rf-surface) !important; }
.fr-toolbar .fr-command.fr-btn i,
.fr-popup .fr-command.fr-btn i { color: var(--rf-muted) !important; }
.fr-toolbar .fr-command.fr-btn:hover i { color: var(--rf-a100) !important; }

/* ── DataTables chrome ─────────────────────────────────────────────────── */

table.dataTable tbody tr { background-color: transparent !important; }
table.dataTable.stripe tbody tr.odd,
table.dataTable.display tbody tr.odd {
    background-color: color-mix(in srgb, var(--rf-text) 3%, transparent) !important;
}
table.dataTable.hover tbody tr:hover,
table.dataTable.display tbody tr:hover {
    background-color: color-mix(in srgb, var(--rf-accent) 10%, transparent) !important;
}
.dataTables_wrapper .dataTables_length select,
.dataTables_wrapper .dataTables_filter input {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent) !important;
    border: 1px solid var(--rf-line) !important;
    color: var(--rf-text) !important;
    border-radius: var(--rf-radius-sm);
}

/* ── Bootstrap-4 modal chrome the template leaves light ────────────────── */

.modal-header .close,
.modal-header .close span { color: var(--rf-text); opacity: .7; text-shadow: none; }
.modal-header .close:hover { opacity: 1; color: var(--rf-a200); }

/* ── The old form skin (assets/css/forms.css) ──────────────────────────── */

.xform { background: transparent; color: var(--rf-text); }

/* == 28. Two widgets that load after this file ===========================

   select2.min.css and customClassPagination.css are pulled in by individual
   pages, and both come AFTER the theme in source order — so a plain
   declaration here loses to theirs however specific it is. select2 paints its
   control white; the pagination paints a white pill and keeps painting it for
   the disabled state at a specificity the base rule cannot reach.

   These are the only two places in the sweep that needed !important to win on
   order rather than on merit. */

.select2-container--default .select2-selection--single,
.select2-container--default .select2-selection--multiple,
.select2-container--default.select2-container--focus .select2-selection--multiple {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent) !important;
    border-color: var(--rf-line) !important;
    color: var(--rf-text) !important;
}
.select2-container--default .select2-selection--single .select2-selection__rendered { color: var(--rf-text) !important; }
.select2-container--default .select2-selection--single .select2-selection__placeholder { color: var(--rf-muted) !important; }
.select2-container--default .select2-selection--single .select2-selection__arrow b { border-top-color: var(--rf-muted) !important; }
.select2-dropdown { background: var(--rf-surface) !important; border-color: var(--rf-line) !important; }
.select2-container--default .select2-results__option { color: var(--rf-text); }
.select2-container--default .select2-results__option[aria-selected="true"] {
    background: color-mix(in srgb, var(--rf-accent) 18%, transparent) !important;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
    background: color-mix(in srgb, var(--rf-accent) 20%, transparent) !important;
    border-color: color-mix(in srgb, var(--rf-accent) 40%, transparent) !important;
    color: var(--rf-a100) !important;
}

/* Pagination moved to section 38, where the links are drawn as controls
   rather than repainted with !important on top of the vendor rules. This
   block stayed behind and, because every declaration in it was important,
   it was beating the newer one: the page links kept a transparent ground
   with no hit area, and a disabled one kept --rf-faint, which is where the
   1.1-against-the-card reading came from. */

/* == 29. Cargo category badge ============================================
   Same arrangement as the status pill: the category's hue is data (it names
   General Goods, Electronics, Auto Part, Dangerous Goods) and does not
   change; what changes is how far it is carried towards the ground's opposite
   before it is used as text. The pastel fills these were built with are light
   islands on a dark page. helpers/querys.php emits --cargo for this.

   Print keeps the literals underneath: a label sheet loads no stylesheet. */

html[data-rf-theme="dark"] .cdp-cargo-badge {
    color: color-mix(in srgb, var(--cargo, currentColor) 62%, #ffffff) !important;
    background: color-mix(in srgb, var(--cargo, transparent) 20%, transparent) !important;
    border-color: color-mix(in srgb, var(--cargo, currentColor) 52%, transparent) !important;
}
html[data-rf-theme="light"] .cdp-cargo-badge {
    color: color-mix(in srgb, var(--cargo, currentColor) 62%, #000000) !important;
    background: color-mix(in srgb, var(--cargo, transparent) 14%, transparent) !important;
    border-color: color-mix(in srgb, var(--cargo, currentColor) 45%, transparent) !important;
}
@media print {
    html[data-rf-theme="dark"] .cdp-cargo-badge,
    html[data-rf-theme="light"] .cdp-cargo-badge {
        color: var(--cargo, #000) !important;
        background: transparent !important;
        border-color: var(--cargo, #999) !important;
    }
}

/* == 30. Waiting states ==================================================

   Anything that says "not yet" has to belong to the page it interrupts. The
   SweetAlert spinner ships as a light ring, and the template's own spinners
   are pinned to the template's blue — both read as a hole punched in a dark
   page. */

.swal2-loader {
    border-color: var(--rf-accent) transparent var(--rf-accent) transparent !important;
}
.swal2-timer-progress-bar { background: color-mix(in srgb, var(--rf-accent) 60%, transparent) !important; }

/* Bootstrap's spinners, wherever a view reaches for one. */
.spinner-border, .spinner-grow { color: var(--rf-accent); }

/* The template's double-bounce spinner (assets/template) and the loader boxes
   the older views leave in place. */
#status .spinner .double-bounce1,
#status .spinner .double-bounce2 { background-color: var(--rf-accent) !important; }

/* A list swapping its rows out shows a plain line of text mid-table; give it
   the weight of a note rather than of content. */
#resultados_ajax > :not(div):not(table),
.outer_div > p:only-child {
    color: var(--rf-muted);
    font-size: 13px;
}

/* == 31. Checkboxes and radios ===========================================

   An UNCHECKED box was invisible. `accent-color` only paints the checked
   state, so everything else was left to the browser — and under
   `color-scheme: dark` the browser draws a dark box with a dark border on a
   dark surface. There was nothing to click at until you had already clicked
   it.

   They are drawn here instead: a real box with a real border in both states,
   the accent only once ticked, and a focus ring that follows the keyboard.
   Two paths, because the application uses both — the native control, and
   Bootstrap's version where the input is hidden at opacity 0 and the label
   paints the box in its pseudo-elements. */

/* ── The native control ────────────────────────────────────────────────── */

input[type="checkbox"]:not(.custom-control-input),
input[type="radio"]:not(.custom-control-input) {
    appearance: none;
    -webkit-appearance: none;
    width: 17px;
    height: 17px;
    margin: 0 2px 0 0;
    flex: none;
    display: inline-grid;
    place-content: center;
    vertical-align: text-bottom;
    cursor: pointer;
    background: color-mix(in srgb, var(--rf-text) 6%, var(--rf-surface));
    border: 1.5px solid color-mix(in srgb, var(--rf-text) 38%, transparent);
    transition: background .12s, border-color .12s;
}

input[type="checkbox"]:not(.custom-control-input) { border-radius: 4px; }
input[type="radio"]:not(.custom-control-input) { border-radius: 50%; }

input[type="checkbox"]:not(.custom-control-input):hover,
input[type="radio"]:not(.custom-control-input):hover {
    border-color: var(--rf-accent);
}

input[type="checkbox"]:not(.custom-control-input):checked,
input[type="radio"]:not(.custom-control-input):checked {
    background: var(--rf-accent);
    border-color: var(--rf-accent);
}

/* The tick is drawn rather than typed, so it stays crisp at any zoom and
   needs no icon font. */
input[type="checkbox"]:not(.custom-control-input)::before {
    content: "";
    width: 10px;
    height: 10px;
    transform: scale(0);
    transition: transform .12s ease-in-out;
    background: #fff;
    clip-path: polygon(14% 44%, 0 65%, 43% 100%, 100% 22%, 82% 6%, 40% 65%);
}
input[type="checkbox"]:not(.custom-control-input):checked::before { transform: scale(1); }

input[type="radio"]:not(.custom-control-input)::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    transform: scale(0);
    transition: transform .12s ease-in-out;
    background: #fff;
}
input[type="radio"]:not(.custom-control-input):checked::before { transform: scale(1); }

input[type="checkbox"]:not(.custom-control-input):focus-visible,
input[type="radio"]:not(.custom-control-input):focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 32%, transparent);
}

input[type="checkbox"]:not(.custom-control-input):disabled,
input[type="radio"]:not(.custom-control-input):disabled {
    opacity: .45;
    cursor: not-allowed;
}

/* Bootstrap's switch markup keeps its own shape — it is a track and a knob,
   not a box, and the rules above would square it off. */
.custom-switch input[type="checkbox"],
.rf-switch input[type="checkbox"],
.fg-switch input[type="checkbox"] {
    appearance: auto;
    -webkit-appearance: auto;
    width: 0;
    height: 0;
    border: 0;
    background: none;
}

/* ── Bootstrap's drawn control ─────────────────────────────────────────── */
/* Here the input is hidden and the LABEL paints the box, so the border has to
   be strengthened there instead. */

.custom-control-label::before {
    background-color: color-mix(in srgb, var(--rf-text) 6%, var(--rf-surface)) !important;
    border: 1.5px solid color-mix(in srgb, var(--rf-text) 38%, transparent) !important;
}

.custom-control-input:hover ~ .custom-control-label::before,
.custom-control-label:hover::before {
    border-color: var(--rf-accent) !important;
}

.custom-control-input:checked ~ .custom-control-label::before {
    background-color: var(--rf-accent) !important;
    border-color: var(--rf-accent) !important;
}

.custom-control-input:focus-visible ~ .custom-control-label::before {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 32%, transparent) !important;
}

.custom-control-input:disabled ~ .custom-control-label::before { opacity: .45; }
.custom-control-input:disabled ~ .custom-control-label { color: var(--rf-faint); }

/* ── The button-group toggles (btn-group-toggle) ───────────────────────── */
/* Their input is hidden and the LABEL is the control, so an unpicked one has
   to read as a real, pressable thing rather than as text. */

.btn-group-toggle > .btn,
.btn-group-toggle > .btn-group > .btn {
    border-color: var(--rf-line);
    color: var(--rf-muted);
}
.btn-group-toggle > .btn:hover { color: var(--rf-a100); border-color: var(--rf-accent); }
.btn-group-toggle > .btn.active {
    background: color-mix(in srgb, var(--rf-accent) 22%, transparent);
    border-color: var(--rf-accent);
    color: var(--rf-a100);
}

/* == 32. The pricing field row ===========================================

   Ten charge fields — price per lb, discount, insurance, duties, tax and the
   rest — were laid out at col-lg-2 each. Twenty columns in a twelve-column
   grid, so they wrapped six and four and left a third of the second line
   empty on nine different forms.

   Ten fields cannot divide twelve, so the grid is the wrong tool. The row
   becomes fluid instead: each field asks for about 150px and grows to share
   whatever is left, so every line fills completely and the count of fields
   per line follows the screen rather than a hardcoded twelfth. That also
   means adding an eleventh charge field later needs no arithmetic. */

.row.input-container {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
}

.row.input-container > [class*="col-"] {
    flex: 1 1 150px;
    max-width: none;
    min-width: 0;
}

/* Below the tablet breakpoint two per line stays readable; one per line
   wastes the height a long form has none of. */
@media (max-width: 575.98px) {
    .row.input-container > [class*="col-"] { flex-basis: 130px; }
}

/* == 33. Dialogs =========================================================

   Most of this application's real work happens in a dialog — recording a
   payment, clearing a debt, changing a status, assigning a driver — and they
   were all wearing SweetAlert's out-of-the-box alert dressing: a very large
   centred title, centred body copy, and buttons in the middle.

   That is right for "Are you sure?" and wrong for a form. A title like
   "Record Payment — Sampson Quarmy" set at 1.875em and centred wraps across
   two lines and outweighs the fields underneath it.

   So a dialog gets a real header: the title left, one size up from the body,
   on its own rule. Content reads left. Actions sit right, where the eye
   finishes. An alert with no form in it still centres, because that is the
   one case the original dressing had right. */

.swal2-popup {
    padding: 0 !important;
    width: min(680px, calc(100vw - 32px)) !important;
    max-width: calc(100vw - 32px) !important;
}

/* .swal2-modal.swal2-popup .swal2-title carries `max-width: 16em` in the
   vendor stylesheet — which is why the title wrapped at half the dialog
   however wide the dialog was. The selector is matched here so the cap can be
   lifted on source order rather than with !important. */
.swal2-modal.swal2-popup .swal2-title,
.swal2-popup .swal2-title {
    display: block;
    justify-self: stretch;
    width: auto;
    max-width: 100%;
    margin: 0 !important;
    padding: 18px 54px 15px 22px;
    font-family: var(--rf-font-heading);
    font-size: 1.2rem !important;
    font-weight: 600;
    line-height: 1.35;
    letter-spacing: -0.01em;
    text-align: left;
    overflow-wrap: anywhere;
    border-bottom: 1px solid var(--rf-line);
}

.swal2-popup .swal2-html-container {
    margin: 0 !important;
    padding: 18px 22px 4px !important;
    font-size: 14px;
    line-height: 1.55;
    text-align: left;
    overflow: visible !important;
}

.swal2-popup .swal2-actions {
    margin: 0 !important;
    padding: 14px 22px 18px;
    justify-content: flex-end;
    gap: 8px;
    border-top: 1px solid var(--rf-line);
    margin-top: 16px !important;
}
.swal2-popup .swal2-actions .swal2-styled { margin: 0 !important; }

/* The close control has to be visible on a dark ground, and out of the
   title's way. */
.swal2-popup .swal2-close {
    position: absolute;
    top: 10px;
    right: 12px;
    width: 32px;
    height: 32px;
    font-size: 26px;
    color: var(--rf-faint) !important;
    box-shadow: none !important;
}
.swal2-popup .swal2-close:hover { color: var(--rf-bad) !important; background: transparent; }

/* An icon-led alert — no form, just a question — keeps the centred
   presentation it was designed for. */
.swal2-popup:has(.swal2-icon:not([style*="display: none"])) .swal2-title,
.swal2-popup:has(.swal2-icon:not([style*="display: none"])) .swal2-html-container {
    text-align: center;
    border-bottom: 0;
}
.swal2-popup:has(.swal2-icon:not([style*="display: none"])) .swal2-actions {
    justify-content: center;
    border-top: 0;
}
.swal2-popup .swal2-icon { margin: 20px auto 4px !important; }

/* Fields inside a dialog are the same fields as on a page. */
.swal2-popup .form-control,
.swal2-popup .custom-select {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border: 1px solid var(--rf-line);
    color: var(--rf-text);
}
.swal2-popup label { color: var(--rf-muted); font-size: 12.5px; }
.swal2-popup .input-group-text {
    background: color-mix(in srgb, var(--rf-text) 8%, transparent);
    border-color: var(--rf-line);
    color: var(--rf-muted);
}
.swal2-popup .text-muted { color: var(--rf-muted) !important; }
.swal2-popup hr { border-color: var(--rf-line); }

/* A dialog taller than the screen scrolls its BODY, so its header and its
   actions stay put — a Save button you have to scroll to find is a Save
   button people miss. */
.swal2-popup .swal2-html-container { max-height: min(58vh, 520px); overflow-y: auto !important; }

@media (max-width: 575.98px) {
    .swal2-popup { width: calc(100vw - 20px) !important; }
    .swal2-popup .swal2-title { padding: 15px 48px 12px 16px; font-size: 1.08rem !important; }
    .swal2-popup .swal2-html-container { padding: 14px 16px 4px !important; max-height: 62vh; }
    .swal2-popup .swal2-actions { padding: 12px 16px 15px; flex-direction: column-reverse; }
    .swal2-popup .swal2-actions .swal2-styled { width: 100%; }
}

/* == 34. Bootstrap modals ================================================
   The same shape, so the two kinds of dialog in this application do not read
   as two different products. */

.modal-header {
    padding: 16px 20px;
    align-items: center;
}
.modal-title {
    font-family: var(--rf-font-heading);
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.35;
    overflow-wrap: anywhere;
    padding-right: 12px;
}
.modal-body { padding: 18px 20px; }
.modal-footer { padding: 14px 20px; gap: 8px; }
.modal-footer > * { margin: 0 !important; }

/* A tall modal scrolls its body, not the page behind it. */
.modal-dialog:not(.modal-dialog-scrollable) .modal-body {
    max-height: min(66vh, 620px);
    overflow-y: auto;
}

@media (max-width: 575.98px) {
    .modal-dialog { margin: 8px; max-width: none; }
    .modal-header { padding: 13px 15px; }
    .modal-body { padding: 15px; max-height: 68vh; }
    .modal-footer { padding: 12px 15px; flex-direction: column-reverse; align-items: stretch; }
    .modal-footer > .btn { width: 100%; }
}

/* == 35. Attachments =====================================================

   Photographs, films and documents hung on a shipment. The old presentation
   was a fixed 180×180 thumbnail — so a portrait photograph was cropped square
   and a video was a grey no-preview placeholder — inside a Bootstrap
   img-thumbnail, which is a white box with a light border and reads as a hole
   on a dark page.

   Tiles size themselves to the row now, keep a 4:3 frame so a wall of them
   lines up, and carry the type of the file when it is not something the
   browser can show. helpers/media_gallery.php builds the markup;
   dataJs/rf_lightbox.js opens it. */

.rf-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
}
.rf-gallery--single { grid-template-columns: minmax(0, 320px); }

/* These galleries sit inside a Bootstrap .row, which is a flex container — so
   the grid arrived as a flex item sized to its own content and collapsed to
   two columns however wide the card was. */
.row > .rf-gallery { flex: 1 1 100%; width: 100%; min-width: 0; }

.rf-media { margin: 0; min-width: 0; }

.rf-media img,
.rf-media video,
.rf-media .rf-media__doc,
.rf-media .rf-media__vid {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--rf-radius-md);
    border: 1px solid var(--rf-line);
    background: color-mix(in srgb, var(--rf-text) 6%, var(--rf-surface));
}

.rf-media img { cursor: zoom-in; transition: border-color .14s, transform .14s; }
.rf-media img:hover { border-color: var(--rf-accent); }

/* A film keeps its first frame under a play mark, so the tile says what it
   is before anyone clicks it. */
.rf-media .rf-media__vid { position: relative; cursor: zoom-in; overflow: hidden; padding: 0; }
.rf-media .rf-media__vid video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 0;
    pointer-events: none;
}
.rf-media .rf-media__vid i {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    font-size: 40px;
    color: #fff;
    text-shadow: 0 2px 12px rgba(0, 0, 0, .6);
    background: linear-gradient(180deg, transparent 40%, rgba(0, 0, 0, .35));
}
.rf-media .rf-media__vid:hover { border-color: var(--rf-accent); }

/* Anything the browser will not render in place says what it is. */
.rf-media .rf-media__doc {
    display: grid;
    place-content: center;
    gap: 4px;
    text-align: center;
    color: var(--rf-muted);
    text-decoration: none;
}
.rf-media .rf-media__doc i { font-size: 34px; color: var(--rf-accent); }
.rf-media .rf-media__doc span { font-size: 11px; letter-spacing: .08em; font-weight: 600; }
.rf-media .rf-media__doc:hover { border-color: var(--rf-accent); color: var(--rf-text); }

.rf-media figcaption {
    margin-top: 6px;
    font-size: 12px;
    line-height: 17px;
    color: var(--rf-muted);
    overflow-wrap: anywhere;
}
.rf-media figcaption a { color: var(--rf-text); font-weight: 600; }
.rf-media figcaption a:hover { color: var(--rf-accent); }
.rf-media figcaption small { display: block; color: var(--rf-faint); font-size: 11px; }

.rf-media-empty { color: var(--rf-faint); font-size: 13px; margin: 0; }

@media (max-width: 575.98px) {
    .rf-gallery { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
    .rf-gallery--single { grid-template-columns: minmax(0, 1fr); }
}

/* A standing proof picture — the signature panel, the delivery photograph.
   These were set to a hard 400×250, so a portrait photograph off a phone was
   squashed and a wide signature strip was cut. They size to their column now
   and keep their own proportions. */

.rf-shot {
    display: block;
    width: 100%;
    max-width: 420px;
    height: auto;
    border-radius: var(--rf-radius-md);
    border: 1px solid var(--rf-line);
    background: color-mix(in srgb, var(--rf-text) 6%, var(--rf-surface));
    cursor: zoom-in;
    transition: border-color .14s;
}
.rf-shot:hover { border-color: var(--rf-accent); }

/* == 37. The page header =================================================

   views/inc/page_header.php renders this on every page. Before it existed,
   a hundred and seven pages opened with a hand-written .page-breadcrumb and a
   hundred and thirty-six had no header at all — on those, the first card's
   title was doing the job, so the name of the page sat forty pixels lower,
   two sizes smaller, and inside a box.

   One rule set, so the name lands in the same place whichever page you are
   on, and the content below it starts at the same height. */

.page-breadcrumb {
    padding: 22px clamp(14px, 2vw, 24px) 4px;
    background: transparent;
}

.page-breadcrumb .page-title {
    margin: 0;
    font-family: var(--rf-font-heading);
    font-size: clamp(1.15rem, 1.6vw, 1.42rem);
    font-weight: 500;
    letter-spacing: -0.02em;
    line-height: 1.2;
    color: var(--rf-text);
    overflow-wrap: anywhere;
}
.page-breadcrumb .page-title > i {
    margin-right: 8px;
    color: var(--rf-accent);
    font-size: .92em;
    vertical-align: -1px;
}

/* One line of context under the name, where a page has something worth
   saying. Never a sentence explaining what the buttons do. */
.page-lede {
    margin: 5px 0 0;
    font-size: 13px;
    line-height: 19px;
    color: var(--rf-muted);
    max-width: 76ch;
}

/* Actions and filters that belong to the page rather than to a card sit at
   the far end of the header row, and drop under the name on a phone. */
.page-head-aside {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}

@media (max-width: 767.98px) {
    .page-breadcrumb { padding-top: 16px; }
    .page-head-aside { justify-content: flex-start; margin-top: 10px; }
}

/* The first card under the header no longer needs to re-announce the page, so
   it starts where every other card starts. */
.page-breadcrumb + .container-fluid,
.page-breadcrumb + form > .container-fluid { padding-top: 14px; }

/* == 38. Pagers ==========================================================

   The page links were drawn at 42% of the page's own ink — under three to one
   against the ground, which is why a report with three hundred and forty-five
   pages read as a report with no pagination at all. They are controls; they
   are drawn as controls.

   The bar around them says what the report found and lets the reader choose
   how much of it arrives at a time, and it appears above the table as well as
   below it — a hundred rows is a long way to scroll before you learn there is
   a second page. */

.rf-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 2px;
    font-size: 13px;
    color: var(--rf-muted);
}
.rf-pager--top { border-bottom: 1px solid var(--rf-line); margin-bottom: 6px; }
.rf-pager--bottom { border-top: 1px solid var(--rf-line); margin-top: 6px; }

.rf-pager__count { font-variant-numeric: tabular-nums; }
.rf-pager__count b { color: var(--rf-text); font-weight: 600; }

.rf-pager__controls { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

.rf-pager__size { display: flex; align-items: center; gap: 7px; margin: 0; }
.rf-pager__size > span {
    font-size: 11.5px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--rf-faint);
}
.rf-pager__size select { width: auto; min-width: 74px; }

/* A pager inside the bar has already been positioned by it. */
.rf-pager nav { margin: 0; }
.rf-pager .pagination { margin: 0; float: none !important; }

/* — the links themselves — */

.pagination { flex-wrap: wrap; gap: 4px; }

.pagination .page-link {
    min-width: 34px;
    padding: 6px 10px;
    text-align: center;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    color: var(--rf-text);
    background: color-mix(in srgb, var(--rf-text) 5%, transparent);
    border: 1px solid var(--rf-line);
    border-radius: var(--rf-radius-sm);
    transition: background .12s, border-color .12s, color .12s;
}
.pagination .page-item + .page-item .page-link { margin-left: 0; }

.pagination .page-link:hover {
    color: var(--rf-text);
    background: color-mix(in srgb, var(--rf-accent) 20%, transparent);
    border-color: var(--rf-accent);
}

.pagination .page-item.active .page-link,
.pagination .active .page-link {
    color: #fff;
    background: var(--rf-accent);
    border-color: var(--rf-accent);
    font-weight: 600;
}

.pagination .page-item.disabled .page-link,
.pagination .disabled .page-link {
    /* Readable, but plainly not a control — at --rf-faint it was under 1.2
       against the card and read as missing rather than as unavailable. */
    color: color-mix(in srgb, var(--rf-text) 46%, transparent);
    background: transparent;
    border-color: var(--rf-line);
    cursor: default;
}

/* The gap markers are not controls and should not look like one. The active
   page has no href either, so it has to be excluded by name — otherwise this
   rule, which is more specific than the active one, strips the page you are
   on of the very thing that says you are on it. */
.pagination .page-item:not(.active):not(.disabled) > .page-link:not([href]):not([onclick]) {
    background: transparent;
    border-color: transparent;
    color: var(--rf-faint);
}

@media (max-width: 575.98px) {
    .rf-pager { justify-content: center; text-align: center; }
    .rf-pager__controls { justify-content: center; width: 100%; }
    .pagination .page-link { min-width: 32px; padding: 6px 8px; }
}

/* == 39. Small screens, properly ==========================================

   The application was built for a desk. On a phone the shell mostly holds up
   — the sidebar collapses, the cards stack — but three things do not, and
   they are the three that make a page feel broken rather than narrow.

   1. A table wider than the screen with nowhere to scroll drags the whole
      document sideways. dataJs/rf_responsive.js puts a scroller around any
      table that needs one; this is what that scroller looks like.

   2. A scroller with no sign that it scrolls reads as a table with columns
      missing. It gets a fade at the edge that still has something behind it.

   3. Controls sized for a mouse. A 17px checkbox and a 26px icon button are
      fine under a cursor and a coin-toss under a thumb. */

.rf-scroll {
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}
.rf-scroll > table { margin-bottom: 0; }

/* The fade sits on the container, so it travels with the scroll position
   rather than with the content. */
.rf-scroll.is-scrollable::after,
[class*="table-responsive"].is-scrollable::after {
    content: "";
    position: sticky;
    float: right;
    top: 0;
    right: 0;
    width: 34px;
    height: 100%;
    margin-left: -34px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--rf-bg) 88%, transparent));
}
[class*="table-responsive"] { position: relative; }

/* A horizontal scrollbar you can actually see and grab. */
.rf-scroll::-webkit-scrollbar,
[class*="table-responsive"]::-webkit-scrollbar { height: 9px; }
.rf-scroll::-webkit-scrollbar-thumb,
[class*="table-responsive"]::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--rf-text) 22%, transparent);
    border-radius: 5px;
}
.rf-scroll::-webkit-scrollbar-thumb:hover,
[class*="table-responsive"]::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--rf-accent) 60%, transparent);
}

/* — touch — */

@media (hover: none) and (pointer: coarse) {
    /* Anything you tap gets a thumb-sized target. The control keeps its own
       size; the hit area grows around it, so nothing moves. */
    .btn, .page-link, .custom-control-label, .rf-check, a.dropdown-item {
        touch-action: manipulation;
    }
    .btn-sm, .btn-xs { min-height: 34px; }
    .btn:not(.btn-sm):not(.btn-xs) { min-height: 40px; }

    input[type="checkbox"]:not(.custom-control-input),
    input[type="radio"]:not(.custom-control-input) {
        width: 20px;
        height: 20px;
    }
    .custom-control-label::before,
    .custom-control-label::after { width: 20px; height: 20px; }
    .custom-control { min-height: 26px; }

    /* An icon button in a table row — Edit, Delete, View. Under a cursor 26px
       is generous; under a thumb it is a miss. */
    td .btn, th .btn { min-width: 34px; }
}

/* — phones — */

@media (max-width: 575.98px) {
    /* Cards were separated by a desktop gutter on both sides, which on a
       375px screen is a tenth of the width spent on nothing. */
    .page-wrapper .container-fluid { padding-left: 10px; padding-right: 10px; }
    .card { margin-bottom: 12px; }
    .card-body { padding: 14px; }

    /* A row of buttons wraps instead of running off the edge, and each one is
       wide enough to read. */
    .card-body > .btn-group,
    .d-md-flex > .btn-group { flex-wrap: wrap; gap: 6px; }
    .btn-group > .btn { flex: 0 1 auto; }

    /* Bootstrap's floats leave a control stranded on the right of a narrow
       card, half off the screen. */
    .float-right { float: none !important; }

    /* A form row is one field per line — two 150px fields side by side is two
       fields nobody can read. */
    .form-row > [class*="col-"],
    .row.input-container > [class*="col-"] { flex: 1 1 100%; max-width: 100%; }

    /* Buttons that submit or cancel a form span the width, which is where a
       thumb expects them. */
    .card-body > .text-right > .btn,
    .card-footer > .btn { width: 100%; margin: 0 0 8px; }

    /* A definition-style row stacks rather than squeezing its value into
       forty pixels beside its label. */
    .rf-row { flex-direction: column; gap: 2px; }
    .rf-row dd { text-align: left; }
}

/* — tablets — */

@media (min-width: 576px) and (max-width: 991.98px) {
    /* Three-up strips become two-up rather than three 110px columns. */
    .rf-auth-grid,
    .sw-band { grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }
    .page-wrapper .container-fluid { padding-left: 14px; padding-right: 14px; }
}

/* A number field in a table cell has to stay wide enough to read what is in
   it. In the items table on a phone the quantity box was eighteen pixels —
   two digits and nothing else. */
@media (max-width: 991.98px) {
    td .form-control, td .form-control-sm { min-width: 58px; }

    /* A date range reads "2026/8/1 - 2026/8/31". At sixty pixels it reads
       "2026/8..." and the picker beside it is a mystery. */
    #daterange, input[name="daterange"] { min-width: 168px; }
}

/* A .row carries negative side margins that assume a padded container around
   it. Put one straight inside an unpadded block — as the user list does —
   and it hangs ten pixels off each edge of the screen. */
.page-wrapper > .row,
.page-wrapper > form > .row,
.page-wrapper > div:not([class*="container"]):not([class*="col-"]) > .row,
.page-wrapper > form > div:not([class*="container"]):not([class*="col-"]) > .row {
    margin-left: 0;
    margin-right: 0;
    /* Cancelling the negative margin alone left the columns inside carrying
       their own ten pixels against nothing, so a card in one of these rows sat
       ten pixels shy of every other card on the page. Ten here plus the
       column's ten is the page's twenty. */
    padding-left: 10px;
    padding-right: 10px;
}

@media (max-width: 575.98px) {
    .page-wrapper > .row,
    .page-wrapper > form > .row { padding-left: 0; padding-right: 0; }
}

/* A floor under the type. Bootstrap sizes <small> at 80% of its parent, and
   with a 12.5px parent that is ten pixels — readable on a monitor at arm's
   length, not on a phone. */
@media (max-width: 991.98px) {
    small, .small, .text-muted small, .font-12, .fs-12 { font-size: max(11.5px, .82em); }
    .badge { font-size: 11.5px; }
}

/* == 40. Filter rows =====================================================

   The filters above a list were laid out as Bootstrap columns: search on
   three, customer on three, status on two, kind on two, dates on three, rows
   on two. Fifteen columns in a row that holds twelve, so the last two dropped
   to a second line and sat under half an empty row — and because each column
   is a fixed twelfth, a rows-per-page picker holding "25 rows" was given the
   same width as a date range holding "2026/8/1 - 2026/8/31".

   A filter is as wide as what it holds. This makes the row a strip: controls
   side by side, sized to their content, wrapping only when they genuinely run
   out of room. The markup keeps its col-* classes — this only stops them
   deciding the width. */

.rf-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin: 0 0 1rem;
}

.rf-filters > [class*="col-"] {
    flex: 0 1 auto;
    width: auto;
    max-width: 100%;
    min-width: 0;
    padding: 0;
    margin: 0;
}

.rf-filters .input-group { width: auto; flex-wrap: nowrap; }

.rf-filters .form-control,
.rf-filters .custom-select,
.rf-filters select {
    width: auto;
    min-width: 150px;
}
/* A date range and a search line hold more than a picker does. */
.rf-filters input[id*="daterange"] { min-width: 210px; }
.rf-filters input[type="text"],
.rf-filters input[type="search"] { min-width: 180px; }
.rf-filters select { min-width: 132px; }

/* Anything that is an action rather than a filter goes to the far end, where
   the eye finishes. */
.rf-filters .rf-filters__end { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* THE ONE SHAPE A LIST TOOLBAR HAS: what you can DO on the left, what you can
   NARROW BY on the right, on one line.

   It was neither of those things. Twenty-three list screens put the Add button
   on a row of its own and the search box on the row beneath it, because the two
   were written as sibling blocks rather than as one strip — Roles, Modules,
   Offices, Categories, Cities and the rest. Ten more put the create button
   INSIDE the filter strip, so on the consolidation lists "Create Consolidation"
   sat in a line of dropdowns as though it were one.

   .rf-act marks the doing. Everything after it is pushed to the right by the
   auto margin, so a strip reads left-to-right as: act, then narrow. */
.rf-filters > .rf-act {
    margin-right: auto !important;
    display: flex;
    align-items: center;
    gap: 8px;
}
.rf-filters > .rf-act .form-group { margin-bottom: 0; }
.rf-filters > .rf-act a { text-decoration: none; }

/* A search box is a search box on every screen: the magnifier sits inside the
   field rather than as a separate button beside it, which is what made some
   strips two controls wide and others one. */
.rf-filters .rf-search { position: relative; display: flex; align-items: center; }
.rf-filters .rf-search > i {
    position: absolute;
    left: 11px;
    font-size: 15px;
    color: var(--rf-faint);
    pointer-events: none;
}
.rf-filters .rf-search > input { padding-left: 33px; }
[dir="rtl"] .rf-filters .rf-search > i { left: auto; right: 11px; }
[dir="rtl"] .rf-filters .rf-search > input { padding-left: .75rem; padding-right: 33px; }

@media (max-width: 575.98px) {
    /* On a phone a strip of eight controls is a wall. One per line, full
       width, in the order they are written. */
    .rf-filters > [class*="col-"] { flex: 1 1 100%; }
    .rf-filters .input-group,
    .rf-filters .form-control,
    .rf-filters .custom-select,
    .rf-filters select,
    .rf-filters input[type="text"],
    .rf-filters input[type="search"] { width: 100%; min-width: 0; }
    .rf-filters .rf-filters__end { margin-left: 0; width: 100%; }
    /* On one column there is no left and right to divide. */
    .rf-filters > .rf-act { margin-right: 0 !important; }
    .rf-filters .rf-search { width: 100%; }
}

/* == 41. The permissions editor ==========================================

   Modules were chunked into rows of two (six, on the add screen) counted out
   in PHP. They are of very different heights — one module has three actions,
   another has thirty — so a fixed pair per row left a column of white space
   beside every short one, and the bookkeeping that produced those rows is what
   the stray closing divs were hiding in.

   A grid instead: as many columns as fit, each module as tall as it needs to
   be, and nothing to count. */

.cdp-perm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
    gap: 16px;
    align-items: start;
}
.cdp-perm-grid > .module-container { min-width: 0; margin: 0; }

.cdp-perm-bulk {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 12px;
    margin-bottom: 14px;
    border-radius: var(--rf-radius-md);
    border: 1px solid var(--rf-line);
    background: color-mix(in srgb, var(--rf-surface) 60%, transparent);
}
.cdp-perm-bulk b { font-size: 13px; color: var(--rf-muted); font-weight: 600; }
.cdp-perm-bulk input[type="search"] { max-width: 240px; margin-left: auto; }

@media (max-width: 575.98px) {
    .cdp-perm-bulk input[type="search"] { max-width: none; margin-left: 0; width: 100%; }
}

/* == 42. Panel components ================================================

   Every dashboard panel has a different number of components — one has four,
   another has nineteen — and they were laid out two to a row. So beside every
   short card sat a column of empty space the height of the tall one next to
   it, and the page was twice as long as its content.

   A column flow instead: a card that finishes early is followed by the next
   one starting there. break-inside keeps a card whole, so nothing is split
   down the middle by the column break. */

.pc-masonry {
    columns: 2;
    column-gap: 18px;
}
.pc-masonry > .pc-brick {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
    display: block;
    width: 100%;
}
.pc-masonry > .pc-brick > .card { margin-bottom: 18px; }

@media (max-width: 1199.98px) {
    .pc-masonry { columns: 1; }
}

/* Bootstrap's file input is a real input hidden under a label that impersonates
   one, and the label ships painted white with a white "Browse" tab on its right
   built from a pseudo-element. Twenty-two screens use it — every upload on
   every delivery, consolidation and settings form — and all of them were a
   white slab on a dark page. */
.custom-file-label,
.custom-file-label::after {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    color: var(--rf-text);
    border-color: var(--rf-line);
}
.custom-file-label::after {
    background: color-mix(in srgb, var(--rf-text) 10%, transparent);
    color: var(--rf-muted);
    border-left: 1px solid var(--rf-line);
}
.custom-file-input:focus ~ .custom-file-label {
    border-color: var(--rf-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 20%, transparent);
}
.custom-file-input { cursor: pointer; }

/* Two more grounds the template paints light and nothing was overriding.

   .input-container input is the template's own rule for the item rows on the
   booking forms: a white input at eighty per cent opacity with a #ccc border.
   Four screens use it — every package and shipment form — and every field in
   those rows was a white slab.

   .img-thumbnail is Bootstrap's picture frame: white box, light border. It is
   what the older attachment thumbnails still sit in. */
.input-container input,
.input-container select,
.input-container textarea {
    background-color: color-mix(in srgb, var(--rf-surface) 70%, transparent);
    border: 1px solid var(--rf-line);
    color: var(--rf-text);
}
.input-container input:focus,
.input-container select:focus {
    border-color: var(--rf-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rf-accent) 18%, transparent);
}

.img-thumbnail {
    background-color: color-mix(in srgb, var(--rf-text) 6%, var(--rf-surface));
    border-color: var(--rf-line);
}

/* Bootstrap's two header variants paint a light grey and a near-black band.
   Both were chosen against white paper; on this ground the light one is a
   bright stripe across the top of a table and the dark one is a hole. A table
   header is a table header — it takes the same treatment as every other one
   in the application. */
.table .thead-light th,
.table .thead-dark th {
    background-color: color-mix(in srgb, var(--rf-text) 7%, transparent);
    color: var(--rf-muted);
    border-color: var(--rf-line);
}

/* A label and the controls it names do not get separated by a wrap. */
.rf-filterbar-group {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}
.rf-filterbar-break { flex: 1 0 100%; height: 0; margin: 0; }

/* == 43. One gutter, whatever shape the page is ==========================

   The template sets the page's outer gutter as
   `.page-wrapper > .container-fluid { padding: 20px }` — a DIRECT-child
   selector. A page that puts its container straight under the wrapper gets
   twenty pixels; a page that wraps it in a <form>, or in a spacing div, gets
   Bootstrap's default ten. So the shipment form's cards sat ten pixels wider
   than every list's, and moving between the two the whole page jumped.

   Fifteen pages wrap the container in a div and every form-driven page wraps
   it in a form. They all get the same gutter now, and the descendant
   selector is written to lose to the small-screen overrides further down. */
.page-wrapper .container-fluid {
    padding-left: 20px;
    padding-right: 20px;
}

/* A container nested inside a card is content, not the page's own gutter. */
.page-wrapper .card .container-fluid,
.page-wrapper .modal .container-fluid {
    padding-left: 0;
    padding-right: 0;
}

/* == 44. The report card =================================================

   A page under Reports opens with one card carrying the whole report, and it
   marks itself with a rule across the top. That is a real convention — it is
   how a report says it is a report, and thirty-three pages follow it — but it
   was written as an inline `border-top: 3px solid #bbb` thirty-four separate
   times. A grey chosen against white paper, so on this ground it read as a
   pale scar rather than as a marker; and two reports never got it while the
   tracking page, which is not a report, did.

   Declared once, in the theme's own accent, and applied by the class alone. */
/* ...AND IT IS NOT A CONVENTION ANY MORE (owner, 2026-08-29).

   The rule stayed, the difference went. A card that marks itself out with a
   3px accent rule across the top says "this page is a different kind of page",
   and the reader's answer was the right one: it is not. The Financial Sheet,
   the commercial invoice and thirty-one reports were wearing a top border
   that nothing else in the application wears, on cards that are otherwise
   identical to every other card. One card treatment, everywhere.

   The class is left in the markup rather than stripped from thirty-three
   files: it is now simply a card, and this is where anyone looking for the
   difference will come. */
.card.card-outline {
    /* The template gives .card `border: 0 solid transparent`. A card-outline is
       now exactly that and nothing more. */
    border-top: 0;
}

/* == 46. The brand mark follows the theme ================================

   Logo And Favicon takes an optional dark version of each image (migration
   077). Both versions are written into the markup and CSS chooses between
   them — no script, so there is never a frame of the wrong logo while the page
   loads. A dark version that was never uploaded is not rendered at all, and
   these rules then have nothing to hide.

   Here rather than in rf_nav.css because it is about the brand, not the
   navigation: the maintenance page draws a logo and does not load the rail. */

.rf-brand-dark { display: none; }

html[data-rf-theme="dark"] .rf-brand-light { display: none; }
html[data-rf-theme="dark"] .rf-brand-dark  { display: inline-block; }

/* == 45. A card title is a card title ====================================

   The heading inside a card is written as an <h3> on forty-seven pages, an
   <h4> on eighty-seven and an <h5> on seven — so the same thing, naming the
   same kind of block, arrives three visibly different sizes depending on
   which page you are on. The tag is whatever each author reached for; the
   class is the intent, so the class decides the size.

   A few also carry ad-hoc spacing (ml-4, mt-3, m-t-10) pushing them out of
   line with the card's own padding. The margin is set here so they sit where
   every other card title sits. */
.card-title,
h1.card-title, h2.card-title, h3.card-title,
h4.card-title, h5.card-title, h6.card-title {
    font-size: 1.02rem;
    font-weight: 600;
    line-height: 1.35;
    margin: 0 0 14px;
}

/* .card-title.mb-0 is used where the title shares a row with a control, and
   that intent is kept. */
.card-title.mb-0 { margin-bottom: 0; }

/* Ad-hoc offsets on a handful of titles: the card's own padding already sets
   where its heading starts. */
.card-body > .card-title.ml-4,
.card-body > .card-title.m-t-10,
.card > .card-title.ml-4 { margin-left: 0; margin-top: 0; }

/* A title in a card that has no card-body still needs the card's inset. */
.card > .card-title { padding: 1.3rem 1.3rem 0; margin-bottom: 14px; }
.card > .card-body .card-title { padding: 0; }

/* == 46. Columns standing outside their grid =============================

   A Bootstrap column carries half the row's gutter as its own padding, and
   expects the row's matching negative margin around it. Put one where there
   is no row and that padding is uncompensated, so the block sits ten pixels
   further in than everything beside it. There are a hundred and sixty-five of
   them in this application and they are what makes a page look like its cards
   were dropped from different heights.

   Two shapes do real damage, and both are addressed by where they sit rather
   than by editing each site:

   A column directly inside a card body — its siblings are ordinary blocks at
   the card's own padding, and it alone is inset. */
.card-body > [class*="col-"]:not(.row):not([class*="offset"]) {
    padding-left: 0;
    padding-right: 0;
}

/* A column directly under the page wrapper or under a bare form — outside any
   container, so it has the column's ten pixels where the page's twenty
   belong. The shipment editor's last card sat there. */
.page-wrapper > [class*="col-"],
.page-wrapper > form > [class*="col-"] {
    padding-left: 20px;
    padding-right: 20px;
}

/* A ROW THAT NEVER MADE IT INTO THE CARD BODY. `.card > .row` — no padding of
   any kind, because the card itself has none and the row cancels its columns'
   with a negative margin. The shipment screen's Agency / Office / Shipping
   Mode block is one: it sat three pixels from the card's border while the
   heading above it and the table beside it were at the card's own twenty, so
   the whole card read as though its contents had slid to the edge.

   Given the card's inset here, so it lines up with everything that IS in the
   body. The row's own -10px and the column's +10px cancel as usual. */
.card > .row {
    padding-left: 1.3rem;
    padding-right: 1.3rem;
}

/* And a column inside a flex strip that spaces its children with `gap`. The
   control panels put a col-12 inside .sw-row, which has no negative margin to
   answer the column's padding — so one card on every dashboard sat ten pixels
   deeper than the rest of them. */
.sw-row > [class*="col-"] {
    padding-left: 0;
    padding-right: 0;
}

@media (max-width: 575.98px) {
    .page-wrapper > [class*="col-"],
    .page-wrapper > form > [class*="col-"] { padding-left: 10px; padding-right: 10px; }
}

/* == 47. pull-left / pull-right ==========================================

   Bootstrap 3 named its float helpers .pull-left and .pull-right; Bootstrap 4
   renamed them .float-left and .float-right and dropped the old names. This
   application is on Bootstrap 4 and still writes the old ones — a hundred and
   twenty-five times. Every one of them is a no-op: an action group meant to
   sit at the right of its row sits in normal flow at the left instead, under
   whatever came before it.

   They are given the intent rather than the mechanism. margin-left:auto right-
   aligns in a flex row and in normal flow alike, and unlike a float it can
   never fall out of its parent's height and land on top of the next block —
   which is the other half of what was going wrong. */

.pull-right:not([class*="col-"]) {
    margin-left: auto;
    width: fit-content;
    max-width: 100%;
}
.pull-left:not([class*="col-"]) {
    margin-right: auto;
    width: fit-content;
    max-width: 100%;
}

/* A COLUMN carrying the class means "this column sits at the right end of its
   row" — its width is the grid's business, not ours. */
[class*="col-"].pull-right { margin-left: auto; }
[class*="col-"].pull-left { margin-right: auto; }

/* Inside a table cell or a heading, aligning the text is enough and a block
   of its own would break the line. */
td > .pull-right, th > .pull-right,
h1 > .pull-right, h2 > .pull-right, h3 > .pull-right,
h4 > .pull-right, h5 > .pull-right { float: right; width: auto; margin-left: 0; }

@media (max-width: 575.98px) {
    /* On a phone an action group goes under what it belongs to, full width,
       rather than squeezing into the right-hand end of a narrow row. */
    .pull-right:not([class*="col-"]),
    .pull-left:not([class*="col-"]) { width: 100%; margin-left: 0; margin-right: 0; }
    td > .pull-right, th > .pull-right { float: none; }
}

/* A caution line under the template editors — "Do not replace variables
   between [ ]". It is written with Bootstrap 3's .label2 .label-important,
   which nothing has styled since the upgrade, so the warning has been reading
   as an ordinary sentence in the flow. It looks like a caution now. */
.label2.label-important {
    display: block;
    margin-top: 10px;
    padding: 8px 12px;
    border-radius: var(--rf-radius-md);
    border: 1px solid color-mix(in srgb, var(--rf-warn) 40%, transparent);
    background: color-mix(in srgb, var(--rf-warn) 12%, transparent);
    color: var(--rf-warn);
    font-size: 12.5px;
    line-height: 18px;
}


/* == 46. WAITING =========================================================

   Every screen that talked to the server drew its own wait, and they all drew
   the same thing: <img src="assets/images/loader.gif"> written into whichever
   result div was nearest, on nineteen call sites across ten files.

   It was wrong in three ways at once. The picture is a raster image with a
   light ground baked into it, so on the dark theme it sat in a pale box. It
   was written INTO the element that was about to carry the answer, so the
   answer and the waiting for it competed for the same space. And it appeared
   between blocks that were already laid out, so the page jumped by the height
   of it and back again a second later.

   Two shapes replace it, and which one to use is a question about where the
   reader is looking. dataJs/rf_busy.js is the other half of this.

     .rf-busy      something the page started by itself — a status line, in a
                   row that already exists and already has a height
     .is-busy      something the reader started. The wait belongs on the
                   control they pressed: the label is replaced in place, so
                   the button neither resizes nor moves, and it is locked for
                   the duration, which also stops a second submit

   The ring is one element and no markup: a circle with three sides the accent
   at low opacity and the fourth solid, turning. */

.rf-spinner {
    flex: none;
    width: 15px;
    height: 15px;
    border: 2px solid color-mix(in srgb, var(--rf-accent) 28%, transparent);
    border-top-color: var(--rf-accent);
    border-radius: 50%;
    animation: rfSpin .68s linear infinite;
}

@keyframes rfSpin { to { transform: rotate(360deg); } }

.rf-busy {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    font-size: 12.5px;
    color: var(--rf-muted);
}
.rf-busy[hidden] { display: none; }

/* IN A BUTTON, THE RING IS THE BUTTON'S OWN COLOUR. These buttons are not one
   kind: .rf-btn on the sign-in screens, .btn-primary / .btn-success /
   .btn-danger behind it, each with a different label colour. currentColor is
   the only answer that is right on all of them. */
button .rf-spinner,
.btn .rf-spinner,
.rf-btn .rf-spinner {
    border-color: color-mix(in srgb, currentColor 34%, transparent);
    border-top-color: currentColor;
}

/* INLINE-FLEX WHILE BUSY, whatever the button was before.

   Most of these are Bootstrap's .btn, which is inline-block and lays its
   children out on a baseline. Dropping a 15px ring into that line box grew the
   button by 2px, which is exactly the kind of movement putting the wait on the
   button was meant to stop. Centring the two children removes the baseline
   from the question; the height stays what the padding says it is. */
.is-busy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    pointer-events: none;
}
.is-busy:disabled { opacity: .82; }

@media (prefers-reduced-motion: reduce) {
    /* Still moving, because a frozen ring reads as a broken one — just slowly
       enough not to be the thing the eye is dragged to. */
    .rf-spinner { animation-duration: 2.4s; }
}


/* == 48. THE REPORT LINKS ================================================

   Every report on the General Reports screen is an `a.link` inside a card,
   and the template paints that class #3e5569 -- a slate grey chosen for a
   white page and written as a literal, so it does not move when the theme
   does. On the dark ground the cards use (#232532) it is very nearly the
   card itself: thirty links, all but unreadable, on the screen whose entire
   job is to be a list of links.

   The caret in front of each one was #00D900 inline, a green that is in no
   palette here and was the brightest thing on the page.

   Both are tokens now, so the list reads on either ground and the caret
   supports the label instead of shouting over it. */
.card a.link {
    color: var(--rf-text);
    font-weight: 500;
}
.card a.link:hover,
.card a.link:focus { color: var(--rf-accent); }
.card a.link .ph-caret-right {
    color: var(--rf-a300);
    font-size: 13px;
    vertical-align: -1px;
}
.card a.link:hover .ph-caret-right { color: var(--rf-accent); }


/* == 49. A TABLE INSIDE A TABLE ===========================================

   The template carries Bootstrap's `.table .table { background-color: #eef5f9 }`
   — a near-white slab for a nested table, chosen for a white page and written
   as a literal, so it does not move when the theme does. On the dark ground it
   is a bright panel with the dark theme's light ink on it: the KYC Review
   queue's document drawer was unreadable, and so is every other row that opens
   a table inside its parent (the charges list, the payment-gateway lists, the
   consolidation package lists).

   Transparent is the right answer in BOTH themes: a nested table should sit on
   whatever its row already is, which is what the row chose to shade it. */
.table .table,
.table .table > thead > tr > th,
.table .table > tbody > tr > td { background-color: transparent; }
