/*
 * Birmingham admin UI refresh
 * Shared, presentation-only improvements kept separate for a simple rollback.
 * Builds on the design tokens in css/tokens.css: use a token, not a literal
 * value (tests/phpunit/characterization/DesignSystemRatchetTest.php).
 */
/* Design tokens - colours, type, spacing, radii, shadows, layers - live in
   css/tokens.css, which include/head.php loads before every other stylesheet. */

html {
    background: var(--ui-canvas);
}

body {
    color: var(--ui-ink);
    font-family: var(--ui-font);
    line-height: var(--ui-lh-body);
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--ui-link);
}

/* Focus, in the site colour (--ui-focus, tokens.css).
   A link, button or disclosure shows a 3px outline for KEYBOARD focus only;
   after a mouse click the outline used to stay on the button until the next
   click somewhere else. (The old ring, rgba(201,21,29,.22), was 1.47:1.) */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:is([type="submit"], [type="button"], [type="reset"], [type="file"], [type="image"], [type="range"], [type="color"]):focus-visible {
    outline: 3px solid var(--ui-focus) !important;
    outline-offset: 2px;
}

a:focus:not(:focus-visible),
button:focus:not(:focus-visible),
summary:focus:not(:focus-visible),
input:is([type="submit"], [type="button"], [type="reset"], [type="file"], [type="image"], [type="range"], [type="color"]):focus:not(:focus-visible) {
    outline: none !important;
}

/* A field shows focus while it holds the caret, however it got there, as ONE
   ring: its border in the focus colour plus a soft halo (--ui-ring). It used
   to get that AND a 3px outline 2px outside it - a heavy double frame. */
input:not(:is([type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="reset"], [type="file"], [type="image"], [type="range"], [type="color"], [type="hidden"])):focus,
select:focus,
textarea:focus {
    outline: none !important;
    border-color: var(--ui-focus) !important;
    box-shadow: var(--ui-ring) !important;
}

/* A checkbox or radio is ticked with the mouse far more often than reached with
   Tab. Its ring shows for keyboard focus only (as browsers do natively), and it
   follows the control - a circle round a radio, not a square round its box. */
input:is([type="checkbox"], [type="radio"]):focus:not(:focus-visible) {
    outline: none !important;
}

/* Chrome resets border-radius on a native checkbox or radio, so a drawn
   outline or shadow stays a square box round it. The browser's own ring
   (outline: auto) follows the control's shape - a circle round a radio. */
input:is([type="checkbox"], [type="radio"]):focus-visible {
    outline: auto !important;
    outline-offset: 1px;
}

::placeholder {
    color: var(--ui-faint);
    opacity: 1;
}

fieldset {
    min-width: 0;
}

/* Pinned to the page's top-left corner. Left at its own place, a hidden label in a
   row of a list wider than the screen (the driver-assignment lists on a tablet) was
   positioned from <body>, outside the list's scroll box, and widened the whole page. */
.ui-visually-hidden {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    clip-path: inset(50%) !important;
    border: 0 !important;
    white-space: nowrap !important;
}

.navbar-fixed-top {
    min-height: 72px;
    border: 0;
    box-shadow: var(--ui-shadow-sm);
}

.headerbg {
    min-height: 72px;
    padding-bottom: var(--ui-space-10);
}

.navbar-brand {
    height: 66px;
    padding: var(--ui-space-8) var(--ui-space-16);
}

.navbar-brand img {
    width: auto;
    max-height: 52px;
}

#toggleLeftPanelBtn {
    margin: 0 !important;
}

.welcome {
    margin-top: var(--ui-space-12);
    color: var(--ui-ink);
}

.site-location {
    margin-bottom: var(--ui-space-4);
    font-size: var(--ui-fs-2xl);
    font-style: normal;
    font-weight: var(--ui-fw-bold);
    text-align: right;
    text-decoration: none;
}

.impersonation-banner {
    display: inline-block;
    margin-top: var(--ui-space-2);
    padding: var(--ui-space-6) var(--ui-space-10);
    border-radius: var(--ui-radius-sm);
    color: var(--ui-text-inverse);
    background: var(--ui-danger);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-bold);
}

.navbar .topbg {
    height: 30px;
    line-height: 26px;
    border: 1px solid rgba(36, 90, 141, .15);
    background: rgba(255, 255, 255, .72);
}

.bodybg {
    margin-top: 72px;
    padding: var(--ui-space-16) var(--ui-space-18) var(--ui-space-28);
    min-height: calc(100vh - 128px);
    background-color: var(--ui-canvas);
}

.app-layout {
    width: 100%;
    table-layout: fixed;
    margin-top: var(--ui-space-10);
    margin-bottom: var(--ui-space-24);
}

#leftPanelTd {
    width: 250px !important;
}

#panelSpacerTd {
    width: 18px !important;
}

#contentTd {
    width: auto !important;
    min-width: 0;
    /*
     * `clip` not `hidden`. Both stop a wide table blowing out the layout, but
     * `hidden` makes this cell a scroll container, and a scroll container
     * captures position:sticky from every descendant - which silently disabled
     * the sticky action rail on the order detail screen. `clip` does not
     * establish one, so sticky resolves against the viewport as intended.
     */
    overflow: clip;
}

#navigation {
    width: 250px;
    overflow: visible;
    border: 1px solid #b8cbd8;
    border-radius: var(--ui-radius-sm);
    box-shadow: var(--ui-shadow-sm);
}

#navigation > ul {
    border-radius: var(--ui-radius-sm);
    overflow: visible;
}

#navigation ul li {
    min-height: 36px;
    height: auto;
    line-height: 36px;
    padding-right: var(--ui-space-28);
    border-bottom-color: #8fb0c7;
}

#navigation ul li.subico,
#navigation ul li.downicon {
    background-position: 230px center;
}

#navigation ul li.smoothmenus {
    min-height: 34px;
    height: auto;
    line-height: 34px;
}

#navigation ul li.smoothmenus a {
    width: 220px;
}

#navigation ul ul {
    left: 248px;
    box-shadow: var(--ui-shadow-md);
}

#navigation ul ul li,
#navigation ul ul li a {
    width: 280px;
}

/* The header's paint is the Cards component's; its title colour is set by the
   "Page-header bar" block near the end of the file. */
.heading {
    clear: both;
    padding: var(--ui-space-12) var(--ui-card-inset);
}

.heading h1 {
    display: flex;
    min-height: 36px;
    align-items: center;
    gap: var(--ui-space-8);
    margin: 0;
    font-size: var(--ui-fs-xl);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-snug);
    letter-spacing: 0;
    flex-wrap: wrap;
}

.ui-heading-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin-left: auto;
    flex-wrap: wrap;
}

.heading h1 > .pull-right {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin-left: auto;
    margin-top: 0 !important;
    float: none !important;
    flex-wrap: wrap;
}

.heading h1 > .pull-right .btn,
.heading h1 > .pull-right > a {
    margin: 0 !important;
    white-space: nowrap;
}

.ui-heading-actions .btn {
    margin: 0 !important;
    white-space: nowrap;
}

.contentdiv {
    max-width: 100%;
    min-height: 0;
    margin-bottom: var(--ui-card-gap);
    padding: var(--ui-card-inset);
    overflow-x: auto;
    overflow-y: visible;
}

.searchdiv {
    padding: var(--ui-space-16) var(--ui-card-inset) !important;
}

/*
 * Filter panels: one grid, every field the same width.
 *
 * The search forms sized each field by its template's Bootstrap column, so
 * the same kind of field was a different length on every page: col-md-1
 * squeezed Postcode to 73px, col-md-8/10 stretched a search box to 700-900px,
 * and a col-xs-12 wrapper pushed half the panels' fields 15px further in.
 * Now the form is ONE grid of equal columns - as many --ui-filter-column-wide
 * columns as the panel fits, sharing the spare width - whatever col-md-N the
 * template says:
 *   - every field takes one column, the free-text search included (giving
 *     it two pushed five daily lists down by a whole extra filter line);
 *   - the Search / Clear buttons take the next cell (two with three buttons)
 *     and sit level with the inputs, because every cell aligns to its bottom.
 * The template's .row wrappers are flattened (display: contents), so a filter
 * split over two rows in the markup flows as one list, and an empty spacer
 * column takes no cell. Labels are inserted at run time
 * by Layout.FilterLabels. :has() finds the form without per-page classes;
 * without :has() the old float layout is left alone.
 */
.searchdiv > .col-xs-12:has(form) {
    padding-right: 0;
    padding-left: 0;
}

.searchdiv .row:has(> form) {
    margin-right: 0;
    margin-left: 0;
}

.searchdiv form:has(> [class*="col-md-"], > .row > [class*="col-md-"]) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--ui-filter-column), 1fr));
    align-items: end;
    gap: var(--ui-space-12) var(--ui-space-20);
}

/* Flattened wrappers: the rows, and a column whose form-group holds a whole
   set of columns of its own (Order Route Plan). Their Bootstrap clearfix
   pseudo-elements would otherwise become grid cells. */
.searchdiv form > .row:has(> [class*="col-md-"]),
.searchdiv form > [class*="col-md-"]:has(> .form-group > [class*="col-md-"] ~ [class*="col-md-"]),
.searchdiv form > [class*="col-md-"]:has(> .form-group > [class*="col-md-"] ~ [class*="col-md-"]) > .form-group {
    display: contents;
}

.searchdiv form > .row::before,
.searchdiv form > .row::after,
.searchdiv form > [class*="col-md-"] > .form-group::before,
.searchdiv form > [class*="col-md-"] > .form-group::after {
    display: none;
}

/* The cells. The gap spaces them, so a template's own spacing hack (an
   inline margin-right: 15px on Products' first column) is dropped. */
.searchdiv form > [class*="col-md-"],
.searchdiv form > .row > [class*="col-md-"],
.searchdiv form > [class*="col-md-"] > .form-group > [class*="col-md-"] {
    float: none;
    width: auto;
    min-width: 0;
    margin-right: 0 !important;
    margin-left: 0 !important;
    padding-right: 0 !important;
    padding-left: 0 !important;
}

.searchdiv form > [class*="col-md-"]:has(.btn ~ .btn ~ .btn),
.searchdiv form > .row > [class*="col-md-"]:has(.btn ~ .btn ~ .btn) {
    grid-column: span 2;
}

/* A search box marked ui-filter-wide takes the line up to its last two cells
   (New Order's customer search: the field picker and Search), as the sales
   team asked - company names and emails ran past one cell. Only on a panel
   with nothing else on that line; see above for why the free-text search is
   not wide everywhere. A phone gives it the whole line. */
.searchdiv form > .ui-filter-wide {
    grid-column: 1 / -3;
}

@media (max-width: 560px) {
    .searchdiv form > .ui-filter-wide {
        grid-column: 1 / -1;
    }
}

/* An empty spacer column (Collection Note's col-md-1: whitespace only, which
   :empty does not match) takes no cell. A column holding anything - a control,
   or just a note - keeps its cell. */
.searchdiv form > [class*="col-md-"]:not(:has(*)),
.searchdiv form > .row > [class*="col-md-"]:not(:has(*)) {
    display: none;
}

/* !important: several templates (Packing List v2, Stock Statement, Purchase
   Orders) put an inline margin-left: 1px on each form-group, which would
   nudge a cell 1px off its column. */
.searchdiv form .form-group {
    margin-right: 0 !important;
    margin-left: 0 !important;
}

/* A filter that is only a checkbox ("Online" on Orders and Quotations) goes
   after the fields, beside Search / Clear: in its source place its lone box
   took a whole cell and split "Date from" and "Date to" over two lines. The
   buttons stay last. */
.searchdiv form > [class*="col-md-"]:has(> .checkbox-inline:only-child),
.searchdiv form > .row > [class*="col-md-"]:has(> .checkbox-inline:only-child) {
    order: 1;
}

.searchdiv form > [class*="col-md-"]:has(.btn),
.searchdiv form > .row > [class*="col-md-"]:has(.btn) {
    order: 2;
}

/* A float inside a cell (Stock Level's "Show All Sizes", pull-right) would
   land mid-panel at the edge of its cell: keep it in the button row. */
.searchdiv form .pull-right {
    float: none !important;
}

/* Buttons a template puts after the filter form (Order Route Plan's Download
   CSV / Upload to FTP) keep the grid's line spacing from it. */
.searchdiv .row:has(> form) ~ .btn {
    margin-top: var(--ui-space-12);
}

/* A checkbox filter ("Online", "Is having stock", "Failed / refused only")
   takes exactly an input's height, box centred: its label then lines up with
   the other labels on the line, and the box with the inputs. */
.searchdiv form .checkbox,
.searchdiv form .checkbox-inline {
    display: flex;
    min-height: var(--ui-control-height);
    align-items: center;
    margin: 0 !important;
    padding-top: 0;
    padding-bottom: 0;
}

/* A bare checkbox and its text (Products' "Is having stock") under the label
   Layout.FilterLabels inserts: label across the top, box and text on one line
   exactly an input's height, so the cell matches its neighbours. */
.searchdiv form [class*="col-md-"]:has(> input[type="checkbox"]) {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto var(--ui-control-height);
    align-items: center;
    column-gap: var(--ui-space-6);
}

.searchdiv form [class*="col-md-"]:has(> input[type="checkbox"]) > label {
    grid-column: 1 / -1;
}

/* A note in a filter cell (Credit Assign Driver's "Pending notes with goods
   to collect") sits level with the inputs beside it. */
.searchdiv form .form-control-static {
    display: flex;
    min-height: var(--ui-control-height);
    align-items: center;
    margin: 0;
    padding: 0;
}

.searchdiv .form-group > [class*="col-"] {
    float: none;
    width: 100%;
    padding-right: 0 !important;
    padding-left: 0 !important;
}

.searchdiv .form-control,
.searchdiv select,
.searchdiv .select2-container {
    width: 100% !important;
    max-width: 100%;
}

.searchdiv .form-group {
    margin-bottom: 0;
}

/* A phone keeps two filters per line: half the scroll past 13 stacked
   filters before the list appears. */
@media (max-width: 560px) {
    .searchdiv form:has(> [class*="col-md-"], > .row > [class*="col-md-"]),
    .packing-list-search {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Packing-list filters: the same grid as every other filter panel. Search and
   Clear sit outside the <form> (the button submits it through form=), so the
   PANEL is the grid and the form, the field wrapper and the tools block are
   flattened into it: the buttons take the next cell, as on Orders, instead of
   a line of their own under a rule. */
.packing-list-search {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--ui-filter-column), 1fr));
    align-items: end;
    gap: var(--ui-space-12) var(--ui-space-20);
}

.packing-list-search::before,
.packing-list-search::after {
    display: none;   /* .row-fluid's clearfix would take grid cells */
}

.packing-list-search > form,
.packing-list-search-grid,
.packing-list-search-tools {
    display: contents;
}

.packing-list-search .form-group {
    min-width: 0;
    margin: 0;
}

.packing-list-search label {
    font-size: var(--ui-fs-xs);
}

.packing-list-search-actions,
.packing-list-search-unlock-controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--ui-space-8);
}

.packing-list-search-unlock {
    max-width: 100%;
}

.packing-list-search-unlock-controls .form-control {
    flex: 1 1 160px;
    width: auto !important;
    min-width: 0;
}

.packing-list-search-unlock label {
    margin-bottom: var(--ui-space-4);
    color: var(--ui-muted);
}

.packing-list-search #enterPassword {
    color: var(--ui-danger);
    font-size: var(--ui-fs-xs);
}

.packing-list-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: var(--ui-space-8);
    margin-bottom: var(--ui-space-12);
}

.packing-list-actions form {
    margin: 0;
}

/*
 * Toolbar: the controls that act on the rows ticked in the list below
 * (Orders: cash-up, Export / Print Selected; Products: price unlock, bulk
 * status). They used to float loose between the filters and the table, with
 * nothing saying they belong together or to the list. Opt in with
 * class="row ui-toolbar" on the existing row; its columns keep their widths.
 * Side margins are zeroed (a .row is -15px each side) so the tinted box sits
 * inside the card edge, and the columns' own 15px padding is the inset.
 */
.ui-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    margin: 0 0 var(--ui-space-16);
    padding: var(--ui-space-14) 0 var(--ui-space-2);
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    background: var(--ui-subtle);
}

.ui-toolbar::before,
.ui-toolbar::after {
    display: none;
}

.ui-toolbar > [class*="col-"] {
    float: none;
    margin-bottom: var(--ui-space-12);
}

/* "Export Selected Orders" is wider than its column; wrap rather than spill. */
.ui-toolbar .btn-block {
    white-space: normal;
}

/* .topbuffer (style.css) is 20px above and 80px BELOW - fine between page
   sections, but inside the bar the 80px left an empty band under Export /
   Print Selected. Keep only the top offset, sized to drop the button level
   with the inputs under the "Collection Date" label beside it. */
.ui-toolbar .topbuffer {
    margin-top: var(--ui-space-24);
    margin-bottom: 0;
}

/* The last group (Products: status select + Update) takes the rest of the
   bar, so the select and its button sit side by side instead of stacking in
   a 2/12 column. */
.ui-toolbar > [class*="col-"]:last-child {
    flex: 1 1 auto;
    width: auto;
}

.ui-toolbar .form-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--ui-space-8);
}

/* Manage Customers bulk bar (super admins): what is ticked on the left, what to do
   with it on the right, wrapping to two lines on a narrow screen. With nothing
   ticked it shows only its hint; the controls keep their space (visibility, not
   display) so the bar never changes height and the list does not jump when the
   first row is ticked. */
.ui-bulk-bar-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ui-space-12);
}

.ui-bulk-bar-selection,
.ui-bulk-bar-action {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ui-space-8);
}

.ui-bulk-bar-count {
    font-weight: var(--ui-fw-bold);
}

.ui-bulk-bar-action label {
    margin: 0;
}

.ui-bulk-bar-action select.form-control {
    width: auto;
    min-width: 240px;
}

.ui-bulk-bar--empty .ui-bulk-bar-count {
    color: var(--ui-muted);
    font-weight: var(--ui-fw-regular);
}

.ui-bulk-bar--empty .ui-bulk-bar-clear,
.ui-bulk-bar--empty .ui-bulk-bar-action {
    visibility: hidden;
}

/* Products' toolbar: the price-unlock password, then the bulk status change.
   The field sat 30px in (two nested columns' padding) with its button a
   column away. The toolbar now carries the inset, the field (at most two
   filter columns) and its button sit together, and the status select and
   Update keep the right end - as on Packing List With Costing. */
.ui-toolbar:has(#password_btn) {
    gap: var(--ui-space-12) var(--ui-space-8);
    padding: var(--ui-space-12) var(--ui-space-14);
}

.ui-toolbar:has(#password_btn) > [class*="col-"],
.ui-toolbar:has(#password_btn) > [class*="col-"] .form-group > [class*="col-"] {
    float: none;
    width: auto;
    margin-bottom: 0;
    padding: 0;
}

.ui-toolbar:has(#password_btn) .form-group {
    margin: 0;
}

.ui-toolbar:has(#password_btn) > [class*="col-"]:has(#password) {
    flex: 0 1 calc(var(--ui-filter-column) * 2);
}

/* The price-unlock message ("Enter Password", "Password Incorrect") under the
   field, on Products and Packing List With Costing. !important: the element
   carries an inline color: red. */
#enterPassword:not(:empty) {
    margin-top: var(--ui-space-4);
    color: var(--ui-danger) !important;
    font-size: var(--ui-fs-xs);
}

/* Below md the col-md-* widths stop applying and a flex item would shrink
   to its content; stack the groups full width instead, as Bootstrap would. */
@media (max-width: 991px) {
    .ui-toolbar > [class*="col-"] {
        width: 100%;
    }

    .ui-toolbar .topbuffer {
        margin-top: 0;
    }

    .ui-toolbar .form-inline {
        justify-content: flex-start;
    }
}

/* A column that renders nothing for this user (a permission-gated button)
   should not add a blank line to the bar. */
.ui-toolbar > [class*="col-"]:not(:has(:is(input:not([type="hidden"]), select, button, a, label))) {
    display: none;
}

@media (max-width: 480px) {
    .packing-list-search-unlock {
        width: 100%;
    }
}

.form-group {
    margin-bottom: var(--ui-space-16);
}

.searchdiv .form-group:last-child,
.contentdiv > .form-group:last-child,
.detailfrm > .form-group:last-child {
    margin-bottom: 0;
}

.form-group > label,
.control-label,
.detailfrm label {
    display: inline-block;
    margin-bottom: var(--ui-space-6);
    color: var(--ui-label);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-snug);
}

.searchdiv .ui-filter-actions,
.detailfrm .ui-filter-actions {
    padding-top: var(--ui-space-20);
}

/* label.ui-field-label so it outranks `.form-group > label` above: the filter
   labels Layout.FilterLabels inserts inside a .form-group must match the ones
   inserted beside a bare control. */
.ui-field-label,
.form-group > label.ui-field-label {
    display: block;
    margin: 0 0 var(--ui-space-6);
    color: var(--ui-label);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-tight);
}

/* ===========================================================================
   Content type: one section title, one field label
   ---------------------------------------------------------------------------
   Bootstrap sizes a bare heading by its tag (h2 30px, h3 24px, h4 18px, all
   weight 500), so the same kind of title came out at four sizes: "User Roles"
   and "Group Menus" at 18px, "Active Sessions" at 24px, the cards' and form
   sections' at 16px bold. A bare label was 14px bold beside the 13px
   form-group labels. Now:
   - a section title is 16px bold, whatever its tag;
   - a field label is 13px semibold, in a form, a filter or on its own;
   - a label that wraps its own checkbox, radio or select is an option, not a
     field: 13px regular, like the value in a field.
   Element-level selectors only, so every component that styles its own
   headings or labels (the executive dashboard, the order detail, the report
   pills) keeps them.
   =========================================================================== */

:where(#contentTd) h2,
:where(#contentTd) h3,
:where(#contentTd) h4 {
    margin: var(--ui-space-20) 0 var(--ui-space-12);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
}

:where(#contentTd) :where(h2, h3, h4):first-child {
    margin-top: 0;
}

/* A title alone in a box is a banner (New Order's "Account Manager"), so it
   sits in the middle of it. */
:where(#contentTd) :where(h2, h3, h4):only-child {
    margin: var(--ui-space-10) 0;
}

/* Bootstrap sizes a title's <small> as a share of the title (65%), which at
   16px is 10px; the count beside "Audit Log" became unreadable. */
:where(#contentTd) h2 small,
:where(#contentTd) h3 small,
:where(#contentTd) h4 small {
    color: var(--ui-muted);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-regular);
}

/* The old widget box (Create Registration Link) titles its card with an icon
   and an h5; they read as one title, on one line. */
.widget-title {
    display: flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin-bottom: var(--ui-space-12);
}

.widget-title > h5 {
    margin: 0;
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
}

/* "Note: You can print order details by clicking above" under the old detail
   pages' Print button is an h5; it reads as help text, so it looks like it. */
.btn ~ h5 {
    margin: var(--ui-space-8) 0 0;
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-regular);
    line-height: var(--ui-lh-body);
}

:where(#contentTd) label {
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
}

:where(#contentTd) label:where(:has(> input[type="checkbox"], > input[type="radio"], > select)) {
    font-weight: var(--ui-fw-regular);
}

/* style.css hides the real input inside .checkbox / .radio and draws a
   <span class="cr"> box instead. A label without that span (Activity Log's
   "Failed / refused only") showed its words and no box to tick; it gets the
   real input back. */
.checkbox label:not(:has(.cr)) input[type="checkbox"],
.radio label:not(:has(.cr)) input[type="radio"] {
    display: inline-block;
    position: static;
    margin: 0 var(--ui-space-6) 0 0;
    vertical-align: middle;
}

.form-control,
.select2-container .select2-selection--single {
    height: var(--ui-control-height);
    min-height: var(--ui-control-height);
    padding: var(--ui-space-8) var(--ui-space-10);
    border-color: var(--ui-border-strong);
    border-radius: var(--ui-radius-sm);
    box-shadow: none;
}

.form-control:focus,
.select2-container--focus .select2-selection {
    border-color: var(--ui-focus) !important;
    box-shadow: var(--ui-ring) !important;
}

.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control,
.select2-container--disabled .select2-selection {
    color: var(--ui-muted);
    border-color: var(--ui-neutral-line);
    background: var(--ui-canvas) !important;
    cursor: not-allowed;
    opacity: 1;
}

.form-group > .select2-container,
.searchdiv .select2-container,
.detailfrm .select2-container {
    width: 100% !important;
    max-width: 100%;
}

.form-inline .form-group > .select2-container {
    width: auto !important;
    min-width: 180px;
}

.select2-container .select2-selection--single {
    padding: 0;
}

.select2-container .select2-selection--single .select2-selection__rendered {
    padding-right: var(--ui-space-32);
    padding-left: var(--ui-space-10);
    line-height: 36px;
}

.select2-container .select2-selection--single .select2-selection__arrow {
    right: 3px;
    width: 28px;
    height: 36px;
}

/* Dropdowns draw their own arrow. The browser's sat a few pixels from the right
   edge whatever the padding - cramped beside the text's 10px inset. This one is
   a small caret 12px in, in the muted ink (4.8:1), and the text stops clear of
   it. !important: the disabled/readonly rule above resets the whole background
   shorthand. A multi-select or list box keeps the browser's own look. */
select:not([multiple]):not([size]) {
    -webkit-appearance: none;
    appearance: none;
    padding-right: var(--ui-space-32);
    background-image:
        linear-gradient(45deg, transparent 50%, var(--ui-muted) 50%),
        linear-gradient(135deg, var(--ui-muted) 50%, transparent 50%) !important;
    background-position:
        calc(100% - var(--ui-space-12) - 5px) 50%,
        calc(100% - var(--ui-space-12)) 50% !important;
    background-size: 5px 5px !important;
    background-repeat: no-repeat !important;
}

/* The select2 dropdowns' caret: the same size, colour and place. */
.select2-container--default .select2-selection--single .select2-selection__arrow b {
    margin-left: calc(var(--ui-space-10) / -2);   /* centred: half its 10px width */
    border-width: 5px 5px 0;
    border-top-color: var(--ui-muted);
}

.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
    border-width: 0 5px 5px;
    border-color: transparent transparent var(--ui-muted);
}

.select2-container .select2-selection--multiple {
    min-height: var(--ui-control-height);
    padding: var(--ui-space-2) var(--ui-space-6);
    border-color: var(--ui-border-strong);
    border-radius: var(--ui-radius-sm);
}

.select2-container .select2-selection--multiple .select2-selection__choice {
    margin-top: var(--ui-space-4);
    border-color: var(--ui-neutral-line);
    color: var(--ui-label);
    background: var(--ui-canvas);
}

.select2-dropdown {
    overflow: hidden;
    border-color: var(--ui-muted);   /* was #aeb8be = 2.02:1 */
    border-radius: var(--ui-radius-sm);
    box-shadow: var(--ui-shadow-md);
}

.select2-results__option {
    padding: var(--ui-space-8) var(--ui-space-10);
}

.select2-container--default .select2-results__option--highlighted[aria-selected] {
    background: var(--ui-primary);
}

input.form-control.required,
select.form-control.required,
textarea.form-control.required,
input.form-control:required:valid,
select.form-control:required:valid,
textarea.form-control:required:valid,
input.form-control:required:invalid,
select.form-control:required:invalid,
textarea.form-control:required:invalid {
    background-color: var(--ui-surface) !important;
}

.has-error .form-control,
.form-control[aria-invalid="true"] {
    border-color: var(--ui-danger);
    background-color: var(--ui-danger-wash) !important;
}

textarea.form-control {
    height: auto;
    min-height: 96px;
    resize: vertical;
}

input[type="file"].form-control {
    height: auto;
    min-height: var(--ui-control-height);
    padding: var(--ui-space-6) var(--ui-space-8);
}

select.form-control[multiple],
select.form-control[size] {
    height: auto;
    min-height: 96px;
}

.input-group .form-control {
    height: var(--ui-control-height);
}

.checkbox,
.radio,
.checkbox-inline,
.radio-inline {
    line-height: var(--ui-lh-body);
}

.checkbox-inline,
.radio-inline {
    display: inline-flex;
    min-height: var(--ui-control-height);
    align-items: center;
    padding-top: var(--ui-space-8);
}

.help-block,
.text-muted {
    color: var(--ui-muted);
}

input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--ui-primary);
}

.ui-step-hint {
    margin: 0 0 var(--ui-space-14);
    padding: var(--ui-space-10) var(--ui-space-12);
    border-left: 4px solid var(--ui-blue);
    border-radius: 0 var(--ui-radius-sm) var(--ui-radius-sm) 0;
    color: var(--ui-ink-2);
    background: var(--ui-info-tint);
}

/* ===========================================================================
   Buttons                                                 @component buttons
   ---------------------------------------------------------------------------
   Every button's look - colour, size, shape, state - is defined in this
   section and nowhere else. DesignSystemRatchetTest counts button styling
   written outside it, and fails a btn-* class this section does not define.

   A button is .btn + ONE variant + at most ONE size (+ .btn-block):
     .btn-primary   the action. It is the site colour (--ui-primary).
                    .btn-warning, .btn-info and .btn-success are older names
                    for the same button - see "One primary" below.
     .btn-default   secondary: navigation, Clear, Cancel, export. Outlined.
     .btn-danger    destructive only. The same red on every site.
     .btn-link      a button that reads as a link.
     .btn-xs  28px  row actions (Edit, Del, the reorder arrows)
     .btn-sm  32px  page-header buttons, the export group
     (none)   38px  everything else - the height of an input beside it
     .btn-lg  44px  the one action of a screen (Login, Final Upload)
   Layout around buttons (margins, widths inside a toolbar or header) stays
   with that layout. The contexts at the end are the places that decide a
   button's role whatever its markup says.
   =========================================================================== */

/* --- base -------------------------------------------------------------- */

.btn {
    display: inline-flex;
    min-height: var(--ui-control-height);
    align-items: center;
    justify-content: center;
    gap: var(--ui-space-6);
    padding: var(--ui-space-8) var(--ui-space-14);
    border-radius: var(--ui-radius-sm);
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-tight);
    box-shadow: none;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}

.btn .fa,
.btn .glyphicon {
    flex: 0 0 auto;
}

/* The flex gap is the space between icon and label (templates no longer pad
   it with &nbsp;). bootstrap.css drops every glyphicon 1px (top: 1px), which
   put "Show All"'s icon 2px below its label; lift it to the label's centre. */
.btn .glyphicon {
    top: calc(var(--ui-icon-lift) * -1);
}

.btn-block {
    width: 100%;
}

/* --- sizes -------------------------------------------------------------
   bootstrap.css gives each size its own radius and line-height; .btn above
   comes later at the same specificity, so every size keeps the one shape. */

.btn-lg {
    min-height: var(--ui-control-height-lg);
    padding: var(--ui-space-10) var(--ui-space-18);
    font-size: var(--ui-fs-md);
}

.btn-sm {
    min-height: var(--ui-control-height-sm);
    padding: var(--ui-space-6) var(--ui-space-10);
    font-size: var(--ui-fs-xs);
}

.btn-xs {
    min-height: var(--ui-control-height-xs);
    padding: var(--ui-space-4) var(--ui-space-8);
    font-size: var(--ui-fs-2xs);
}

/* --- variants ------------------------------------------------------------
   One primary. The legacy markup uses four names for "do the thing" -
   .btn-warning (~250 uses: Search, Add, Update, Generate, Assign Driver...),
   .btn-primary / .btn-info (Print, Add payment line...) and .btn-success
   (~100: Search on some pages, Complete Order, Filter, Export Selected). They
   carried no consistent meaning - Search was red on Customers and green on
   Orders - so they all render the one primary, which is the site colour (see
   --ui-primary). Hierarchy comes from solid vs outlined (.btn-default), and
   red is reserved for .btn-danger, which is the same on every site.

   Each variant restates every state Bootstrap 3.2 paints in its own palette
   (:hover, :focus, :active, .active, an open dropdown toggle - all at
   (0,2,0) or (0,3,0) in bootstrap.css); these come later, so they win.
   This Bootstrap has no :active:hover rules (3.3.5 added them), so there
   are none to restate. */

.btn-primary,
.btn-primary:focus,
.btn-warning,
.btn-warning:focus,
.btn-info,
.btn-info:focus,
.btn-success,
.btn-success:focus {
    color: var(--ui-text-inverse);
    background-color: var(--ui-primary);
    border-color: var(--ui-primary-border);
}

.btn-primary:hover,
.btn-primary:active,
.btn-primary.active,
.btn-warning:hover,
.btn-warning:active,
.btn-warning.active,
.btn-info:hover,
.btn-info:active,
.btn-info.active,
.btn-success:hover,
.btn-success:active,
.btn-success.active,
.open > .dropdown-toggle.btn-primary,
.open > .dropdown-toggle.btn-warning,
.open > .dropdown-toggle.btn-info,
.open > .dropdown-toggle.btn-success {
    color: var(--ui-text-inverse);
    background-color: var(--ui-primary-hover);
    border-color: var(--ui-primary-border);
}

.btn-default {
    color: var(--ui-label);
    border-color: var(--ui-border-strong);
    background: var(--ui-surface);
}

/* Focus keeps the hover fill, as Bootstrap draws it (and as before). */
.btn-default:hover,
.btn-default:focus,
.btn-default:active,
.btn-default.active,
.open > .dropdown-toggle.btn-default {
    color: var(--ui-ink);
    border-color: var(--ui-border-hover);
    background: var(--ui-subtle);
}

.btn-danger,
.btn-danger:focus {
    color: var(--ui-text-inverse);
    background-color: var(--ui-danger);
    border-color: var(--ui-danger-border);
}

.btn-danger:hover,
.btn-danger:active,
.btn-danger.active,
.open > .dropdown-toggle.btn-danger {
    color: var(--ui-text-inverse);
    border-color: var(--ui-danger-border);
    background: var(--ui-danger-hover);
}

.btn-link,
.btn-link:focus,
.btn-link:hover,
.btn-link:active {
    color: var(--ui-link);
    background-color: transparent;
    border-color: transparent;
}

.btn-link:hover {
    text-decoration: underline;
}

/* --- disabled --------------------------------------------------------- */

.btn.disabled,
.btn[disabled],
fieldset[disabled] .btn {
    box-shadow: none;
    cursor: not-allowed;
    opacity: .58;
}

/* Doubled .btn = (0,3,0), which beats Bootstrap's (0,2,0)
   .btn-warning.disabled { background-color: #f0ad4e } - otherwise the 2013
   palette shows through the opacity on every disabled primary. */
.btn.btn.disabled,
.btn.btn[disabled] {
    background: var(--ui-subtle);
    border-color: var(--ui-muted);
    color: var(--ui-faint);
}

/* A disabled link-button stays a link, only fainter. */
.btn.btn-link.disabled,
.btn.btn-link[disabled] {
    border-color: transparent;
    background: transparent;
}

/* --- groups ------------------------------------------------------------ */

.input-group-btn > .btn {
    min-height: var(--ui-control-height);
}

/* Copy / Excel / CSV / PDF / Print / Export All read as ONE control - a
   joined group of small secondary buttons - so six equal boxes stop
   competing with the page's real actions. (A single "Export" dropdown would
   need a JS config change.) */
.dataTables_wrapper .dt-buttons {
    display: inline-flex;
    flex-wrap: wrap;
}

.dataTables_wrapper .dt-buttons .btn,
.dataTables_wrapper .dt-buttons .dt-button {
    display: inline-flex;   /* the Buttons CDN sheet makes them inline-block */
    align-items: center;
    justify-content: center;
    gap: var(--ui-space-6);
    min-height: var(--ui-control-height-sm);
    margin: 0;
    padding: var(--ui-space-6) var(--ui-space-10);
    border: 1px solid var(--ui-border-strong);
    border-radius: 0;
    color: var(--ui-label);
    background: var(--ui-surface);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-tight);   /* the Buttons CDN sheet's 1.6em would make it 33.2px */
}

.dataTables_wrapper .dt-buttons .btn + .btn,
.dataTables_wrapper .dt-buttons .dt-button + .dt-button {
    margin-left: -1px;
}

.dataTables_wrapper .dt-buttons > .btn:first-child,
.dataTables_wrapper .dt-buttons > .dt-button:first-child {
    border-radius: var(--ui-radius-sm) 0 0 var(--ui-radius-sm);
}

.dataTables_wrapper .dt-buttons > .btn:last-child,
.dataTables_wrapper .dt-buttons > .dt-button:last-child {
    border-radius: 0 var(--ui-radius-sm) var(--ui-radius-sm) 0;
}

.dataTables_wrapper .dt-buttons > .btn:only-child,
.dataTables_wrapper .dt-buttons > .dt-button:only-child {
    border-radius: var(--ui-radius-sm);
}

/* DataTables wraps a button's label in a <span>: that is where Export All's
   icon meets its text, so the span gets the button's icon gap. */
.dataTables_wrapper .dt-buttons .dt-button > span {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-6);
}

/* The hovered button sits above its neighbours so its whole border shows. */
.dataTables_wrapper .dt-buttons .btn:hover,
.dataTables_wrapper .dt-buttons .btn:focus {
    position: relative;
    z-index: 1;
    border-color: var(--ui-border-hover);
    color: var(--ui-ink);
    background: var(--ui-subtle);
}

/* --- contexts ------------------------------------------------------------
   Page header. Header buttons are navigation - "Show All" (32 pages), "View
   All" (26), "Add New" (23) - so they render .btn-default whatever their
   markup says, except "Add New", the one that starts work, which is the
   primary. It is recognised by its link (every Add New goes to action=add)
   rather than by a new class on 23 templates. Hover/active are restated
   because the primary's :hover above is (0,2,0) too; .btn-default is
   restated because a header link takes the branch's deep shade from
   .heading h1 a (0,1,2), which beats the plain variant. */
.heading .btn-default,
.heading .btn-default:focus,
.heading .btn-warning,
.heading .btn-warning:focus,
.heading .btn-success,
.heading .btn-success:focus {
    border-color: var(--ui-border-strong);
    color: var(--ui-label);
    background: var(--ui-surface);
}
.heading .btn-default:hover,
.heading .btn-default:active,
.heading .btn-warning:hover,
.heading .btn-warning:active,
.heading .btn-success:hover,
.heading .btn-success:active {
    border-color: var(--ui-border-hover);
    color: var(--ui-ink);
    background: var(--ui-subtle);
}
.heading .btn[href*="action=add"],
.heading .btn[href*="action=add"]:focus {
    border-color: var(--ui-primary-border);
    color: var(--ui-text-inverse);
    background: var(--ui-primary);
}
.heading .btn[href*="action=add"]:hover,
.heading .btn[href*="action=add"]:active {
    border-color: var(--ui-primary-border);
    color: var(--ui-text-inverse);
    background: var(--ui-primary-hover);
}
/* Show All carries a glyphicon; on the white bar it takes the text colour. */
.heading .btn .glyphicon {
    color: inherit;
}

/* The column chooser in a list's Action header: a square secondary icon
   button, the size of the row-action icons beneath it. The !importants beat
   the DataTables Buttons stylesheet, which the list pages link after this one. */
.ui-action-column .ui-column-visibility {
    display: inline-flex !important;
    width: var(--ui-action-size);
    height: var(--ui-action-size);
    margin-left: var(--ui-space-6);
    padding: 0 !important;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-border-strong) !important;
    border-radius: var(--ui-radius-sm);
    color: var(--ui-label) !important;
    background: var(--ui-surface) !important;
    vertical-align: middle;
}

.ui-action-column .ui-column-visibility:hover,
.ui-action-column .ui-column-visibility:focus {
    border-color: var(--ui-border-hover) !important;
    color: var(--ui-ink) !important;
    background: var(--ui-subtle) !important;
}

.ui-action-column .ui-column-visibility .fa {
    font-size: var(--ui-icon-md);
}

/* The impersonation banner's "Return to ..." reads as a link on the banner. */
.app-topbar .impersonation-banner button {
    padding: 0;
    border: 0;
    color: var(--ui-text-inverse);
    background: transparent;
    font: inherit;
    font-weight: var(--ui-fw-bold);
    text-decoration: underline;
}

/* Order detail: an action beside a field is exactly the field's height. */
.od-inline-action .btn { height: var(--ui-control-height); }

/* @end-component buttons */

/* ===========================================================================
   Cards                                                     @component cards
   ---------------------------------------------------------------------------
   Every panel on the page canvas is one card: a 1px border, 8px corners, the
   resting shadow and a white fill, 16px from the next card, its content
   18px in from the edge (--ui-card-inset), so the text of every card on a
   screen - page title, filters, list, detail sections - starts on one line.
   A card's paint - border, corners, shadow, fill - is written in this
   section and nowhere else; DesignSystemRatchetTest counts card paint
   written outside it. Where a card sits, and its inner layout, stay with
   the screen.

   The cards:
     .heading           the page header: the site's tint and a 4px
                        site-coloured top edge
     .contentdiv        the page's panels: a form, a list
       .searchdiv       a filter panel, on the quiet fill
     .h_bg, .h_bg1      the old detail pages' panels
     .rpt-card          a report's form
     .od-card           the order detail's cards (.od-card-danger: Cancel)
     .panel, .well      Bootstrap's panel; a well is on the quiet fill
     .ui-disclosure     a card that opens (Add User)

   Before this section, the header and the panel under it were one card with
   5px corners, while every other panel was its own card with 8px corners and
   a fainter shadow. Where the two met the joins broke: a filter panel's
   rounded top against the header's square bottom, the list under a filter
   panel with no top edge at all, a detail page's header with square bottom
   corners, Brands' list glued to its form.
   =========================================================================== */

.heading,
.contentdiv,
.h_bg,
.h_bg1,
.rpt-card,
.od-card,
.panel,
.well,
.ui-disclosure {
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    background: var(--ui-surface);   /* also drops style.css's gradient images */
    box-shadow: var(--ui-shadow-sm);
}

.searchdiv,
.well {
    background: var(--ui-subtle);
}

/* The site cue: staff keep the three sites open side by side. */
.heading {
    margin-bottom: var(--ui-card-gap);
    border-color: var(--ui-branch-line);
    border-top: 4px solid var(--ui-branch, var(--ui-muted));
    background: var(--ui-branch-wash);
}

/* The header is the head of the panel straight under it: one card, the
   header's bottom border the divider. Something that draws nothing may sit
   between (Stock Statement's .clearfix, the dashboard lock's stylesheet
   <link>). Anything else under a header - a detail page's panels, a note -
   leaves the header a card of its own. */
.heading:has(+ .contentdiv, + :is(.clearfix, link, script, style) + .contentdiv) {
    margin-bottom: 0;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
    box-shadow: none;
}

.heading + .contentdiv,
.heading + :is(.clearfix, link, script, style) + .contentdiv {
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

.od-card-danger {
    border-color: var(--ui-danger-line);
}

/* A card inside a card is a section of it: the outer card carries the lift.
   A panel inside a disclosure, or Cancellation Reason inside a detail panel,
   is not a card at all. */
.contentdiv :is(.contentdiv, .h_bg, .h_bg1, .rpt-card, .panel, .well) {
    box-shadow: none;
}

.ui-disclosure > .contentdiv,
#printArea :is(.h_bg, .h_bg1) .h_bg1 {
    border: 0;
    box-shadow: none;
    background: none;
}

.ui-disclosure > .contentdiv {
    margin-bottom: 0;
}

/* A staff Ctrl+P of a screen (the invoice prints from its own iframe). */
@media print {
    .heading { border: 0 !important; }
    .od-card { border-color: #999; box-shadow: none; }
}

/* @end-component cards */

/* ===========================================================================
   Messages                                               @component messages
   ---------------------------------------------------------------------------
   Every message the admin shows is one box, in one of four colours:
     success   "it worked"        .ok (Message() in include/msgs.php), .alert-success
     danger    "it did not"       .error (Error(), and the JS checks in
                                  js/common.js / js/new-order.js), .alert-danger
     warning   "check this first" .alert-warning
     info      "for your notes"   .alert-info, .mandatory
   The box is the variant's tint with a 4px edge and an icon in its strong
   colour, so "it worked" and "it did not" are told apart before the words
   are read; the words are body text (14px, regular), and a <strong> inside
   stays bold. A message's look is written in this section and nowhere else;
   DesignSystemRatchetTest counts message styling written outside it.

   Before this section the save/error messages had the edge and the icon and
   the .alert-* boxes did not, the two were 14px and 13px, everything was
   semibold (so a list inside an info box was bold end to end), and the
   danger and info colours were raw hex beside the tokens.
   =========================================================================== */

.ok,
.error,
.alert,
.mandatory {
    position: relative;
    margin: 0 0 var(--ui-space-16);
    padding: var(--ui-space-12) var(--ui-space-14) var(--ui-space-12) 38px;
    border: 1px solid;
    border-left-width: 4px;
    border-radius: var(--ui-radius-sm);
    background-image: none !important;   /* style.css's tick.png / warning.png */
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-regular);
    line-height: var(--ui-lh-body);
    text-align: left;
    text-indent: 0;
}

.ok,
.alert-success {
    border-color: var(--ui-success-line);
    border-left-color: var(--ui-success);
    color: var(--ui-success-ink);
    background: var(--ui-success-tint);
}

.error,
.alert-danger {
    border-color: var(--ui-danger-line);
    border-left-color: var(--ui-danger);
    color: var(--ui-danger-ink);
    background: var(--ui-danger-wash);
}

.alert-warning {
    border-color: var(--ui-warning-line);
    border-left-color: var(--ui-warning);
    color: var(--ui-warning-ink);
    background: var(--ui-warning-tint);
}

.alert-info,
.mandatory {
    border-color: var(--ui-info-line);
    border-left-color: var(--ui-info-ink);
    color: var(--ui-info-ink);
    background: var(--ui-info-tint);
}

/* The icon sits on the first line, in the space the left padding leaves. It
   is positioned, not a flex item: some messages carry a link mid-sentence
   (new_order.php), and a flex box would split the sentence into columns. */
:is(.ok, .error, .alert-success, .alert-danger, .alert-warning, .alert-info)::before {
    position: absolute;
    top: var(--ui-space-12);
    left: var(--ui-space-12);
    font-family: var(--ui-font-icon);
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-regular);
    line-height: var(--ui-lh-body);
}

:is(.ok, .alert-success)::before { color: var(--ui-success); content: "\f058"; }   /* fa-check-circle */
:is(.error, .alert-danger)::before { color: var(--ui-danger); content: "\f06a"; }  /* fa-exclamation-circle */
.alert-warning::before { color: var(--ui-warning); content: "\f071"; }             /* fa-exclamation-triangle */
.alert-info::before { color: var(--ui-info-ink); content: "\f05a"; }               /* fa-info-circle */

/* A message that brings its own icon (the executive dashboard's lock, the
   delete dialog's warning sign) keeps it and gets no second one. The legend
   "Indicates mandatory fields" carries its own image. */
:is(.ok, .error, .alert):not(a):has(> .fa:first-child, > .glyphicon:first-child),
.mandatory {
    padding-left: var(--ui-space-14);
}

:is(.ok, .error, .alert):not(a):has(> .fa:first-child, > .glyphicon:first-child)::before {
    content: none;
}

/* An unresolved message id renders Error() around an empty string - a blank
   pink bar that says "something went wrong" about nothing
   (admin.php?act=system_config&er=8). Hide the empty box and its wrapper. */
div.ok:empty,
div.error:empty,
.bg-success:has(> div.ok:empty),
.bg-danger:has(> div.error:empty) {
    display: none;
}

/* A save/error message on its own (include/message_block.php) is a banner
   between the page header and the first card, not a card. */
.ui-flash {
    clear: both;
}

/* <a class="alert"> marks the status-toggle links in 30 lists; it is not a
   message. */
a.alert {
    position: static;
    display: inline;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    line-height: var(--ui-lh-body);   /* what the icon has always sat on */
    text-align: inherit;
}

a.alert::before {
    content: none;
}

/* @end-component messages */

/* ===========================================================================
   Forms: one placement
   ---------------------------------------------------------------------------
   Every add/edit form - and Profile, SEO, the message and e-mail templates -
   sits in a .margins column, and each template chose its own: full width
   (a two-field form with 1100px inputs), or 10/12, 8/12 or 6/12 centred with
   col-xs-offset-*. So the same kind of screen started 34px, 128px, 222px or
   317px in from the panel: Update Customer floated in the middle, Update
   Area ran edge to edge. Now every form starts at the panel's left edge, like
   the header and the lists, and is at most --ui-form-width wide.
   =========================================================================== */

.contentdiv .margins {
    float: none;
    width: 100%;
    max-width: var(--ui-form-width);
    margin-left: 0;
}

/* A form built around a rich-text editor (email templates, messages, content
   pages) edits a whole email at its real width: capped, the editor's toolbar
   wrapped to a fourth row and the template preview was squeezed. */
.contentdiv .margins:has(textarea.ckeditor, .cke) {
    max-width: none;
}

/* ===========================================================================
   Packing List With Costing: the list card's toolbar
   ---------------------------------------------------------------------------
   The price-unlock password (field and button) and the Export buttons were
   three separate blocks: the field indented 30px by two nested Bootstrap
   columns, its button a column away, and small Export buttons floated right
   on a line of their own. One row now - the password field and its button
   on the left, Export All Results / Export This Page on the right, all one
   height. The Export buttons stay inside their form (#frmPO is
   display: contents - still the form in the DOM and on submit), so nothing a
   script or a submission sees has moved.
   =========================================================================== */

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) {
    display: grid;
    grid-template-columns:
        minmax(var(--ui-filter-column), calc(var(--ui-filter-column) * 2))
        auto minmax(0, 1fr) auto auto;
    align-items: start;
    gap: var(--ui-space-16) var(--ui-space-8);
}

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > *,
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > #frmPO > * {
    grid-column: 1 / -1;
}

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > #frmPO,
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > #frmPO > .col-xs-12:has([name="export_scope"]) {
    display: contents;
}

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"],
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"] .form-group > [class*="col-md-"] {
    float: none;
    width: auto;
    padding: 0;
}

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) .form-group {
    margin: 0;
}

.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"]:has(#password) { grid-row: 1; grid-column: 1; }
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"]:has(#password_btn) { grid-row: 1; grid-column: 2; }
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"]:has(#bulkEditBtnPrice) { grid-row: 1; grid-column: 3; }
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) [name="export_scope"][value="all"] { grid-row: 1; grid-column: 4; }
.contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) [name="export_scope"][value="page"] { grid-row: 1; grid-column: 5; }


@media (max-width: 767px) {
    .contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) > [class*="col-md-"]:has(#bulkEditBtnPrice) { grid-row: auto; grid-column: 1 / -1; }
    .contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) [name="export_scope"][value="all"] { grid-row: 2; grid-column: 1; justify-self: end; }
    .contentdiv:has(> [class*="col-md-"] #password_btn):has(> #frmPO) [name="export_scope"][value="page"] { grid-row: 2; grid-column: 2; }
}

/* ===========================================================================
   Driver assignment bar
   ---------------------------------------------------------------------------
   Pending Assign Drivers, Reassign Driver and the collection- and credit-note
   Assign screens put one bar above the list: delivery date, driver, (loaders),
   status, Assign, Print. The templates build it from Bootstrap columns of
   different widths with inline padding hacks, and Pending Assign Drivers
   nests Assign and Print in two half-columns of their own - so the gaps were
   uneven on a desktop, and on a phone the two buttons stacked, indented from
   the fields and touching. It now uses the filter panel's grid above it: each
   field takes one filter column (so it lines up with the filters), Assign and
   Print follow in the next cell at their own size, the rest of the row stays
   empty - as Search / Clear follow the filters. Found by the delivery date
   field the bar always holds, so no template changes.
   =========================================================================== */

.row:has(> [class*="col-md-"] > .form-group > #delivery_date) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--ui-filter-column), 1fr));
    align-items: center;
    gap: var(--ui-space-12) var(--ui-space-20);
    margin: 0 0 var(--ui-space-16);
}

.row:has(> [class*="col-md-"] > .form-group > #delivery_date)::before,
.row:has(> [class*="col-md-"] > .form-group > #delivery_date)::after {
    display: none;
}

/* !important: the loaders and status columns carry inline padding. */
.row:has(> [class*="col-md-"] > .form-group > #delivery_date) > [class*="col-md-"] {
    float: none;
    width: auto;
    min-width: 0;
    padding: 0 !important;
}

.row:has(> [class*="col-md-"] > .form-group > #delivery_date) > [class*="col-md-"]:has(.btn) {
    display: flex;
    align-items: center;
    gap: var(--ui-space-8);
}

.row:has(> [class*="col-md-"] > .form-group > #delivery_date) > [class*="col-md-"]:has(.btn) > [class*="col-md-"] {
    float: none;
    width: auto;
    padding: 0;
}

.row:has(> [class*="col-md-"] > .form-group > #delivery_date) .form-group {
    margin-bottom: 0;
}

/* !important: the buttons carry an inline margin-bottom: 15px. */
.row:has(> [class*="col-md-"] > .form-group > #delivery_date) .btn {
    margin: 0 !important;
}

/* ===========================================================================
   Order entry: Place New Order and New Quotation
   ---------------------------------------------------------------------------
   Their blocks carry .topbuffer (style.css: 20px above, 80px below), which
   left ~100px of empty panel between the unpaid notice, the credit line, the
   product pickers and the order lines, and ~150px under the last one. Here
   the blocks are 16px apart, like the rest of the admin. The panel is found
   by its Run-on-Flat filter or, on a credit-blocked New Order (which stops
   before the filters), its address choice - no other .h_bg1 panel has either.
   =========================================================================== */

/* 16px on both sides: neighbouring margins collapse, so blocks are 16px apart
   whether the next one is another .topbuffer or the product filters. */
.h_bg1:has(#run_on_flat, input[name="address_name"]) .topbuffer {
    margin-top: var(--ui-space-16);
    margin-bottom: var(--ui-space-16);
}

/* The notices already say mb-0 (a Bootstrap 4 class this admin lacks). */
.h_bg1:has(#run_on_flat, input[name="address_name"]) .topbuffer .alert {
    margin-bottom: 0;
}

/* A radio in the address table need not be a form control's height: it made
   every address row 61px tall for one line of text. */
.h_bg1:has(#run_on_flat, input[name="address_name"]) table .radio-inline {
    min-height: 0;
    padding-top: 0;
}

/* ===========================================================================
   Select Product: the tyre list under the box
   ---------------------------------------------------------------------------
   Place New Order, New Quotation and Stock Statement. Each tyre was one line
   of Arial at one weight - "255/35ZR18 FARROAD FRD26 94Y XL (Plus Vat Price:
   30.20, Inc. VAT Price: 36.24, Local STK: 308)" - so the size and the stock
   were hard to find in a long list. Each row is now five columns in the same
   order: the size in bold, the tyre, the price + VAT, the price inc VAT
   (quieter) and the local stock as a tag - green 4 or more, amber 1 to 3,
   grey none (the whole row greys too). The columns line up from row to row
   (subgrid: every row shares the list's columns; a browser without subgrid
   gets fixed widths). The rows are drawn by common.js (productOption), the
   list itself is jQuery UI's; the list comes most-stock-first
   (product.php?act=livesearch).
   =========================================================================== */

.ui-autocomplete.product-picker {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr) max-content max-content max-content;
    column-gap: var(--ui-space-20);
    max-width: calc(100vw - 2 * var(--ui-space-16));
    padding: var(--ui-space-4) 0;
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    background: var(--ui-surface);
    box-shadow: var(--ui-shadow-lg);
    color: var(--ui-ink);
    font-family: var(--ui-font);
    font-size: var(--ui-fs-base);
    line-height: var(--ui-lh-snug);
}

.product-picker > .ui-menu-item {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
}

.product-picker > .ui-menu-item + .ui-menu-item {
    border-top: 1px solid var(--ui-border);
}

/* jQuery UI's item padding, and its active row: a bright blue fill, a border
   and a -1px margin. Here the active row (pointer or arrow keys) is the
   site-colour wash with a rail on its left edge, so the stock tags stay
   readable on it. */
.product-picker > .ui-menu-item > .ui-menu-item-wrapper {
    grid-column: 1 / -1;
    margin: 0;
    padding: var(--ui-space-6) var(--ui-space-14);
    border: 0;
}

.product-picker > .ui-menu-item > .ui-menu-item-wrapper.ui-state-active {
    margin: 0;
    border: 0;
    background: var(--ui-branch-wash);
    color: var(--ui-ink);
    box-shadow: inset var(--ui-rail-width) 0 0 var(--ui-primary);
}

.product-picker .product-option {
    display: grid;
    grid-template-columns: 7.5em minmax(0, 1fr) 7em 7.5em 8em;   /* without subgrid */
    grid-template-columns: subgrid;
    align-items: center;
    font-variant-numeric: tabular-nums;
}

.product-option-size {
    color: var(--ui-ink);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    white-space: nowrap;
}

.product-option-price,
.product-option-vat {
    text-align: right;
    white-space: nowrap;
}

.product-option-price {
    font-weight: var(--ui-fw-semibold);
}

.product-option-vat {
    color: var(--ui-muted);
}

.product-option-price small,
.product-option-vat small {
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-regular);
}

.product-option-stock {
    justify-self: end;
    padding: var(--ui-space-2) var(--ui-space-10);
    border: 1px solid var(--ui-success-line);
    border-radius: var(--ui-radius-pill);
    background: var(--ui-success-tint);
    color: var(--ui-success-ink);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-bold);
    white-space: nowrap;
}

.product-option-low .product-option-stock {
    border-color: var(--ui-warning-line);
    background: var(--ui-warning-tint);
    color: var(--ui-warning-ink);
}

.product-option-out .product-option-stock {
    border-color: var(--ui-border);
    background: var(--ui-neutral-tint);
    color: var(--ui-ink-2);
    font-weight: var(--ui-fw-semibold);
}

.product-option-out .product-option-size,
.product-option-out .product-option-name,
.product-option-out .product-option-price {
    color: var(--ui-muted);
}

/* A phone: the list is the box's width (common.js) and a tyre takes three
   lines - size and stock, the tyre, its prices. */
@media (max-width: 767px) {
    .ui-autocomplete.product-picker {
        grid-template-columns: minmax(0, 1fr);
    }

    .product-picker .product-option {
        grid-template-columns: auto auto minmax(0, 1fr);
        grid-template-areas:
            "size size stock"
            "name name name"
            "price vat .";
        gap: var(--ui-space-2) var(--ui-space-12);
    }

    .product-option-size { grid-area: size; }
    .product-option-name { grid-area: name; }
    .product-option-price { grid-area: price; }
    .product-option-vat { grid-area: vat; }
    .product-option-stock { grid-area: stock; }
}

/* ===========================================================================
   Icons: optical centring
   ---------------------------------------------------------------------------
   Font Awesome 4's glyphs render about 1/14 em below the centre of their box,
   and the chevrons further still (measured on every page from the rendered
   ink: row-action icons 1.0-1.8px low in their squares, sidebar icons ~1px
   low against their labels, the sidebar and account chevrons 2px low). Where
   an icon is centred - in a square, or beside a centred label - its glyph is
   lifted by that much. Icons in running text keep the baseline.
   =========================================================================== */

.ui-action-icon::before,
.sidebar-icon::before,
.sidebar-dashboard > .fa::before {
    position: relative;
    top: calc(var(--ui-icon-lift) * -1);
}

.sidebar-chevron::before,
.app-account-menu > summary > .fa::before {
    position: relative;
    top: calc(var(--ui-icon-lift-chevron) * -1);
}

/* An open menu turns its chevron 180deg, which by itself lifts the low glyph
   to the centre; a lift on top would push the turned chevron low again. */
.sidebar-group.is-open > .sidebar-group-toggle .sidebar-chevron::before,
.app-account-menu[open] > summary > .fa::before {
    top: 0;
}

.modal-content {
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    background: var(--ui-surface);
    box-shadow: var(--ui-shadow-sm);
}

.well {
    margin-bottom: var(--ui-card-gap);
    padding: var(--ui-space-16) var(--ui-card-inset);
}

.panel-heading,
.modal-header {
    padding: var(--ui-space-14) var(--ui-space-16);
    border-color: var(--ui-border);
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
    color: var(--ui-label);
    background: var(--ui-subtle);
}

.panel-title,
.modal-title {
    color: var(--ui-label);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    line-height: var(--ui-lh-snug);
}

.panel-body,
.modal-body {
    padding: var(--ui-space-16);
}

.panel-footer,
.modal-footer {
    padding: var(--ui-space-12) var(--ui-space-16);
    border-color: var(--ui-border);
    border-radius: 0 0 var(--ui-radius-md) var(--ui-radius-md);
    background: var(--ui-subtle);
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--ui-space-8);
}

.modal-footer .btn + .btn {
    margin-left: 0;
}

.modal-header .close {
    margin-top: 0;
    color: var(--ui-ink-2);
    opacity: .75;
}

.table-responsive,
.dataTables_wrapper {
    width: 100%;
    max-width: 100%;
}

.table-responsive {
    margin-bottom: 0;
    overflow-x: auto;
    border: 0;
    border-radius: var(--ui-radius-sm);
    -webkit-overflow-scrolling: touch;
}

.table {
    width: 100%;
    margin-bottom: var(--ui-space-16);
    color: var(--ui-ink);
    font-size: var(--ui-fs-sm) !important;
    background: var(--ui-surface);
}

.table-responsive > .table {
    margin-bottom: 0;
}

.table > thead > tr > th {
    padding: var(--ui-space-10) var(--ui-space-8) !important;
    border-color: var(--ui-neutral-line) !important;
    background: var(--ui-neutral-tint) !important;
    color: var(--ui-label);
    font-weight: var(--ui-fw-bold);
    line-height: var(--ui-lh-tight);
    vertical-align: middle;
}

.table > tbody > tr > td,
.table > tfoot > tr > td {
    padding: var(--ui-space-8) var(--ui-space-8) !important;
    border-color: var(--ui-border);
    line-height: var(--ui-lh-snug);
    vertical-align: middle;
    /* Equal-width digits, so amounts and quantities line up down a column. */
    font-variant-numeric: tabular-nums;
}

/* Under scrollX the real <thead> stays in the body table as an invisible
   sizing row; DataTables zeroes its vertical padding and borders INLINE, and
   the !important th padding above beats an inline style - which left an
   empty 20px grey strip under every list header. Zero them again here. */
.dataTables_scrollBody > .table > thead > tr > th,
.dataTables_scrollBody > .table > thead > tr > td {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    border-top-width: 0 !important;
    border-bottom-width: 0 !important;
}

/* Sort indicators on client-sorted lists.

   dataTables.bootstrap4.css draws an up/down pair on EVERY sortable header,
   absolutely positioned at right:0.5-1em, and reserves padding-right:30px for
   it. The !important 8px th padding above wins, so the pairs were drawn on top
   of the labels ("Account Manager", "Brand Category"). Giving the 30px back
   costs ~25px per column - on Products that pushes Status and the edit icon
   off-screen - so instead:

   - an unsorted column shows no arrows at rest (fifteen grey pairs were noise
     anyway); on hover a faint one appears at the right edge, on the header's
     own background so it masks rather than tangles with the label;
   - the sorted column shows ONE arrow, inline after its label. It flows with
     the text, so it cannot overlap it and needs no reserved width.

   The scrollX sizing row keeps bootstrap4's display:none on these pseudos
   (its selector is more specific), so nothing shows twice. */
table.dataTable > thead > tr > th.sorting::before,
table.dataTable > thead > tr > th.sorting::after {
    opacity: 0;
}

table.dataTable > thead > tr > th.sorting:hover::after {
    padding-left: var(--ui-space-4);
    background: var(--ui-neutral-tint);
    content: "\2195";
    opacity: .55;
}

table.dataTable > thead > tr > th:is(.sorting_asc, .sorting_desc)::before {
    display: none;
}

table.dataTable > thead > tr > th:is(.sorting_asc, .sorting_desc)::after {
    position: static;
    display: inline;
    margin-left: var(--ui-space-4);
    color: var(--ui-primary);
    opacity: 1;
}

table.dataTable > thead > tr > th.sorting_asc::after {
    content: "\2191";
}

/* ...but never in the scrollX sizing row: the rules above outrank
   bootstrap4's display:none there, and an inline arrow would give that
   invisible row a height again. */
.dataTables_scrollBody > table.dataTable > thead > tr > th::before,
.dataTables_scrollBody > table.dataTable > thead > tr > th::after {
    display: none;
}

/* The tr.warning / tr.active arms neutralise the legacy $flg zebra striping
   (see UI_CLEANUP.md item 1); the real stripe is tr:nth-child(even) below.
   Delete those two arms once $flg is gone - keep the .table-striped arm. */
.table > tbody > tr.warning > td,
.table > tbody > tr.active > td,
.table-striped > tbody > tr:nth-of-type(odd) > td {
    background: var(--ui-surface);
}

.table > tbody > tr:nth-child(even) > td {
    background: var(--ui-subtle);
}

.table > tbody > tr > td:last-child {
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Content-sized list columns (opt in with .ui-list-auto on the <table>)
   ---------------------------------------------------------------------------
   The problem this solves, measured on admin.php?act=orders: the <th>s carried
   hardcoded width="N%" attributes that summed to ~230% of the table, so the
   browser discarded them and laid every column out at its MIN-CONTENT width
   instead. Two things followed from that.

   1. a.ui-sort below is white-space:nowrap, so a header's min-content is its
      whole label. Columns whose data is nearly always blank were therefore
      pinned to the width of a long name nobody may rename - "Payment Method"
      held 130px, "Card 4 Digit Number" 151px, "Received Amount" 140px, and so
      on: ~400px of a 2573px table spent on columns that render empty.
   2. Columns whose data IS long got min-content too, so the values wrapped -
      "BOND INTERNATIONAL" over two lines, "29/08/26 12:31 AM" over two - which
      is what made rows 2-3 lines tall.

   So: let the HEADER wrap (it is the label that is long, not the data) and stop
   the VALUES wrapping (they are what the column exists to show), and give every
   column a measured pixel width that the browser is actually obliged to honour.
   Free text longer than its column is ellipsised and carries the full string on
   a title attribute.

   Everything is scoped to .ui-list-auto rather than .table - the other 92 list
   pages under bodies/ still carry percentage widths of their own and are
   deliberately not part of this change. Adding the class to one of them is the
   way to opt it in, but its widths have to be measured first: the numbers are
   per-table, not a house scale.

   NOTE for anyone extending this: DataTables scrollX clones the header and
   footer into separate tables and STRIPS THE id (jquery.dataTables.js ~5104),
   so rules keyed on #table never reach the visible header. The clone keeps the
   table's CLASSES, which is why every selector here starts from .ui-list-auto.
   ------------------------------------------------------------------------- */

/* table-layout:fixed is what makes the width= on each <th> mean something. In
   the auto algorithm the browser is free to overrule a declared width with the
   cell's own min/max-content, which is exactly how the old percentages ended up
   inert; max-width on a cell is ignored outright, so ellipsis never fires
   either. Under the fixed algorithm the first row's widths ARE the layout, and
   the table grows to their sum, which is what allows a 25-column table to stay
   wider than the viewport and still scroll inside .dataTables_scrollBody. */
.ui-list-auto {
    table-layout: fixed;
}

/* Density, by padding only. This list is the widest surface in the app - 25 columns
   that cannot be renamed or dropped - so it halves horizontal cell padding, 8px to
   4px, scoped to .ui-list-auto; every other table keeps 8px. Vertical padding is
   untouched, so row height does not change.
   The FONT deliberately stays at the .table default of 13px. Density in a data grid
   belongs in padding and row height, not type size: Carbon ships four table
   densities all at one font size, and Fiori's compact mode cuts row height and
   leaves type alone. The reason is that padding costs whitespace while type size
   costs legibility on every row for every user - and this table is money. Measured
   here, that trade is also just a bad deal: the padding is worth ~200px, dropping
   to 12px bought only ~150px more and made this the one 12px table in the admin.
   !important is needed only because .table > thead > tr > th is itself !important. */
.ui-list-auto > thead > tr > th,
.ui-list-auto > tbody > tr > td,
.ui-list-auto > tfoot > tr > th {
    padding-right: var(--ui-space-4) !important;
    padding-left: var(--ui-space-4) !important;
}

/* Declared widths are the width you get - padding inside, not added on top -
   so the numbers on the <th>s can be read straight off a screenshot. */
.ui-list-auto > thead > tr > th,
.ui-list-auto > tbody > tr > td,
.ui-list-auto > tfoot > tr > th {
    box-sizing: border-box;
}

/* A long header may use two or three lines. This is the loophole that lets a
   column be narrower than its own name, which matters because the names are
   fixed: "Card 4 Digit Number" labels a column of 4 characters. */
.ui-list-auto > thead > tr > th > a.ui-sort {
    white-space: normal;
}

/* A value never wraps: one order is one line. Anything longer than its column
   is ellipsised, and the cells that can realistically overflow carry the full
   string on a title attribute (see bodies/orders.php). */
.ui-list-auto > tbody > tr > td {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* The status badge is nowrap everywhere else in the app (line ~1166) and that is
   right - a badge that breaks mid-phrase reads as two badges. Here it is the one
   exception: "PARTIALLY COMPLETED" is 185px wide but is 0.02% of orders, and
   sizing the column for it would waste 70px on the other 99.98%. Letting just
   that badge take two lines costs one taller row where it occurs and nothing
   anywhere else. Ellipsis is not an option for a status: a clipped one can be
   misread as a different status.

   Both halves are needed, and only for this one column. The badge is an
   inline-block, so it shrink-to-fits against the width available to it; inside
   a nowrap cell that is effectively infinite, so relaxing the badge alone does
   nothing. The cell has to offer a wrapping context too. Every other cell keeps
   its nowrap. */
.ui-list-auto > tbody > tr > td.ui-c-status,
.ui-list-auto > tbody > tr > td.ui-c-status .ui-status {
    white-space: normal;
}

/* Keep returned-order amounts visible through zebra stripes and row hover. */
.ui-list-auto .ui-return-total {
    display: inline-block;
    padding: var(--ui-space-2) var(--ui-space-4);
    border-radius: var(--ui-radius-xs);
    background: #fff4d6;
    color: var(--ui-warning-ink);
    font-weight: var(--ui-fw-bold);
    font-variant-numeric: tabular-nums;
}

/* Drag-to-resize grip, one per header cell; a th opts out with data-col-fixed (orders'
   Action column). See Layout.ColumnResize in script_include.php. It sits above the sort
   link rather than inside it, because
   a.ui-sort is display:block and fills the cell - without a higher z-index every
   drag would land on the sort link instead. */
.ui-list-auto > thead > tr > th {
    position: relative;
}

/* The grip spans the cell's padding box, so it is 2px shorter than the cell's border
   box - those 2px are the cell's own top and bottom border, i.e. the grid line. */
.ui-col-resizer {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    width: 9px;
    cursor: col-resize;
}

.ui-col-resizer::after {
    position: absolute;
    top: 5px;
    bottom: 5px;
    right: 4px;
    width: 1px;
    background: transparent;
    content: "";
}

.ui-col-resizer:hover::after {
    background: var(--ui-muted);
}

/* While dragging, the pointer is often off the grip, so the cue has to be on the
   whole page - and text selection has to stop or the drag selects the header row. */
body.ui-col-resizing {
    cursor: col-resize;
    user-select: none;
}

/* ---------------------------------------------------------------------------
   Resizable list columns (added at run time by Layout.ColumnResize)
   ---------------------------------------------------------------------------
   .ui-list-auto above is orders-only: its widths were measured column by
   column. Every other list keeps its markup and is switched to fixed layout
   HERE, after Layout.ColumnResize has measured what the browser just laid out
   under the auto algorithm and written those pixels onto each <th> - so first
   paint is what it was, and from then on the width attribute IS the layout.

   width:1px is what makes a drag mean what it looks like. .table is width:100%,
   and under fixed layout a 100% table hands any pixels a column gives up back
   to every other column in proportion - including the one being dragged, so
   the edge follows the mouse only part of the way and the stored width never
   matches the screen. At 1px the used width is the sum of the columns: shrink
   one and a blank strip appears on the right; widen past the card and the list
   scrolls sideways inside .dataTables_scrollBody, as orders does.

   Values WRAP when a column is narrowed (nothing is hidden, no title attribute
   needed) - except money, quantities and dates, which would break mid-value
   ("12,345.67" as "12,34" / "5.67", a different amount on an accounts screen).
   Those cells may still wrap at a space, exactly as today ("29/08/26" over
   "12:31 AM"), but never inside a token: too narrow for the token, they show an
   ellipsis as the cue. Cells opt in by being right-aligned or by carrying
   .ui-c-nowrap; the static test in
   tests/phpunit/characterization/ListTablesResizableTest.php enforces it.

   An opted-in table is exactly as wide as its columns, so it no longer follows
   the content column: collapsing the sidebar leaves a blank strip on the right
   (or a sideways scroll) until the next page load freezes it again. */
.ui-list-resizable {
    table-layout: fixed;
    width: 1px;
}

/* Declared widths are the width you get - padding inside, not added on top. */
.ui-list-resizable > thead > tr > th,
.ui-list-resizable > tbody > tr > td,
.ui-list-resizable > tfoot > tr > th,
.ui-list-resizable > tfoot > tr > td {
    box-sizing: border-box;
}

/* position for the grip; overflow-wrap so a one-word label ("Datetime") breaks
   inside its own cell at the 40px floor rather than painting into the next. */
.ui-list-resizable > thead > tr > th {
    position: relative;
    overflow-wrap: anywhere;
}

/* overflow:hidden because buttons, badges and inputs are nowrap boxes that would
   otherwise paint over - and take clicks from - the neighbouring cell. */
.ui-list-resizable > tbody > tr > td,
.ui-list-resizable > tfoot > tr > td {
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* .table > tbody > tr > td:last-child (above) is nowrap at the same specificity and
   earlier; with overflow:hidden that would clip the last column silently. Put it on
   the same wrap path as every other column. The marked-cell arms below are later in
   the sheet, so a marked last cell still gets its own rule. */
.ui-list-resizable > tbody > tr > td:last-child {
    white-space: normal;
}

/* Marked cells: wrap at spaces as today, never inside a token; ellipsis when the
   token itself does not fit. white-space stays normal on purpose - nowrap would
   pull "29/08/26 12:31 AM" onto one line and change first paint. */
.ui-list-resizable > tbody > tr > td.text-right,
.ui-list-resizable > tbody > tr > td[align="right"],
.ui-list-resizable > tbody > tr > td.ui-c-nowrap,
.ui-list-resizable > tfoot > tr > th.text-right,
.ui-list-resizable > tfoot > tr > th[align="right"],
.ui-list-resizable > tfoot > tr > th.ui-c-nowrap,
.ui-list-resizable > tfoot > tr > td.text-right,
.ui-list-resizable > tfoot > tr > td[align="right"],
.ui-list-resizable > tfoot > tr > td.ui-c-nowrap {
    overflow-wrap: normal;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Short enumerated values ("End of the Month", "PLAN 1", a postcode) stay on
   one line: broken over two or three they read as two values. Unlike
   .ui-c-nowrap (which may still wrap at a space) this never wraps; the column
   is measured at this width on load, and a column dragged narrower than the
   value shows an ellipsis. */
.table > tbody > tr > td.ui-c-oneline {
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* On client-ordered tables the DataTables sort glyphs sit exactly under the
   9px grip; move them left by the grip so the arrow is still clickable. */
.ui-list-resizable > thead > tr > th[class*="sorting"]::before {
    right: calc(1em + 9px);
}

.ui-list-resizable > thead > tr > th[class*="sorting"]::after {
    right: calc(0.5em + 9px);
}


/* Paper has no horizontal scrollbar, so everything above is wrong there: a
   2650px fixed-width table would print with its right-hand columns simply gone,
   and an ellipsis on paper loses data with no way to recover it. Hand the sheet
   back to the auto algorithm, which fits the columns to the page and wraps.
   This covers the DataTables Print button, whose popup inherits these
   stylesheets. It does NOT make Ctrl+P on the page itself right: there the
   scrollX header clone keeps the pixel width _fnScrollDraw gave it and prints
   clipped and misaligned over the reflowed body - true before this block
   existed and unchanged by it. */
@media print {
    .ui-list-auto,
    .ui-list-resizable {
        table-layout: auto;
    }

    /* undo the 1px that makes the used width the column sum on screen */
    .ui-list-resizable {
        width: auto;
    }

    .ui-list-auto > thead > tr > th,
    .ui-list-resizable > thead > tr > th {
        width: auto !important;
    }

    .ui-list-auto > tbody > tr > td,
    .ui-list-resizable > tbody > tr > td,
    .ui-list-resizable > tfoot > tr > td {
        overflow: visible;
        white-space: normal;
        text-overflow: clip;
    }
}

/* The Print button's popup is a normal window first and a print job second: until
   the user confirms, its copy of the table is on SCREEN, where the rules above do
   not apply and width:1px + fixed layout would show a crushed table. buttons.print.js
   marks that popup's body with dt-print-view. */
body.dt-print-view .ui-list-resizable {
    table-layout: auto;
    width: auto;
}

body.dt-print-view .ui-list-resizable > tbody > tr > td,
body.dt-print-view .ui-list-resizable > tfoot > tr > td {
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
}

/* Row hover is TRANSIENT FEEDBACK, so it follows the neutral tier - same
   reasoning as the focus ring, and deliberately NOT the branch accent.
   Two reasons. (1) The readability gain here comes from DARKNESS, not hue:
   #eef5fa against the even-row stripe #f8fafb is 1.05:1, which is why tracking
   a row across 12 columns in orders.php is hard. #e4eaee gets that to 1.16:1;
   a 14% branch tint gets 1.16-1.19:1 - i.e. the hue buys nothing measurable.
   (2) This is the densest, most repeated content surface in the app. Accenting
   it would turn the branch colour into wallpaper and stop it working as a
   locator, which is the same reason the table HEADER is not accented either. */
.table-hover > tbody > tr:hover > td {
    background: var(--ui-neutral-tint);
}

.table > thead > tr > td.info,
.table > tbody > tr > td.info,
.table > tfoot > tr > td.info,
.table > thead > tr > th.info,
.table > tbody > tr > th.info,
.table > tfoot > tr > th.info,
.table > thead > tr.info > td,
.table > tbody > tr.info > td,
.table > tfoot > tr.info > td,
.table > thead > tr.info > th,
.table > tbody > tr.info > th,
.table > tfoot > tr.info > th {
    background: var(--ui-border);
}

.table .dataTables_empty,
.dataTables_empty {
    padding: var(--ui-space-32) var(--ui-space-16) !important;
    color: var(--ui-muted);
    background: var(--ui-subtle) !important;
    font-size: var(--ui-fs-base);
    text-align: center;
}

.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dt-buttons {
    margin-bottom: var(--ui-space-12);
}

.dataTables_wrapper .dataTables_filter input {
    min-width: 180px;
    height: var(--ui-control-height);
    padding: var(--ui-space-6) var(--ui-space-10);
    border: 1px solid var(--ui-border-strong);
    border-radius: var(--ui-radius-sm);
}

.dataTables_wrapper .dataTables_paginate .paginate_button {
    border-radius: var(--ui-radius-sm) !important;
}

.pagination {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--ui-space-4);
    margin: var(--ui-space-10) 0;
}

.pagination > li > a,
.pagination > li > span {
    margin-left: 0;
    border-radius: var(--ui-radius-sm);
    color: var(--ui-link);
    border-color: var(--ui-neutral-line);
}

.pagination > .active > a,
.pagination > .active > a:hover {
    color: var(--ui-text-inverse);
    background: var(--ui-primary);
    border-color: var(--ui-primary);
}

.pagination > .disabled > a,
.pagination > .disabled > a:hover,
.pagination > .disabled > span {
    color: #89939a;
    border-color: var(--ui-border);
    background: var(--ui-subtle);
}

.delete-pan {
    display: none;
}

.loader-container {
    display: flex;
    min-height: 200px;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    color: var(--ui-muted);
}

.loader {
    width: 40px;
    height: 40px;
    border: 4px solid var(--ui-border);
    border-top-color: var(--ui-primary);
    border-radius: var(--ui-radius-round);
    animation: ui-loader-spin .9s linear infinite;
}

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

.loader-text {
    margin-top: var(--ui-space-10);
    font-weight: var(--ui-fw-semibold);
}

.ui-action-icon {
    display: inline-flex;
    width: var(--ui-action-size);
    height: var(--ui-action-size);
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-sm);
    color: #35515f;
    background: var(--ui-surface);
    font-size: var(--ui-icon-md);
    vertical-align: middle;
}

a:hover > .ui-action-icon,
.ui-action-icon:hover {
    border-color: #8aa9bb;
    background: var(--ui-info-tint);
}

a:hover > .ui-action-icon:not(.ui-action-danger):not(.ui-action-success),
.ui-action-icon:not(.ui-action-danger):not(.ui-action-success):hover {
    color: #174b70;
}

.ui-action-icon.ui-action-danger {
    color: var(--ui-danger);
}

a:hover > .ui-action-icon.ui-action-danger,
.ui-action-icon.ui-action-danger:hover {
    border-color: #d99a9a;
    background: var(--ui-danger-wash);
}

/* The "active" status toggle (fa-eye). It was a solid green block - the
   loudest thing in every Users / Products row for what is a quiet on/off
   state. Outlined like its neighbours; green ink carries the "on". */
.ui-action-icon.ui-action-success {
    color: var(--ui-success);
    border-color: var(--ui-success-line);
    background: var(--ui-success-tint);
}

a:hover > .ui-action-icon.ui-action-success,
.ui-action-icon.ui-action-success:hover {
    color: var(--ui-success-ink);
    border-color: #6fb07d;
    background: var(--ui-success-tint);
}

/* The print icon pressed last on this list (Layout.LastPrinted, script_include.php),
   so staff back from an invoice tab can find their row. Red at the owner's request
   (2026-10-09) - the one use of red that is not a destructive action. */
.ui-action-icon.ui-action-last,
a:hover > .ui-action-icon.ui-action-last {
    border: 2px solid var(--ui-danger);
}

.ui-action-icon .fa {
    font-size: var(--ui-icon-md);
}

/* Server-side sortable column headers. */
a.ui-sort,
a.ui-sort:hover,
a.ui-sort:focus {
    display: block;
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

a.ui-sort .fa {
    margin-left: var(--ui-space-4);
    opacity: .35;
    font-size: var(--ui-fs-xs);
}

a.ui-sort:hover .fa,
a.ui-sort-active .fa {
    opacity: 1;
}

.ui-action-column {
    white-space: nowrap;
}

div.dt-button-collection.ui-column-visibility-menu {
    padding: var(--ui-space-6) 0;
    border-radius: var(--ui-radius-sm);
}

div.dt-button-collection.ui-column-visibility-menu button.buttons-columnVisibility {
    display: flex;
    width: 100%;
    min-height: var(--ui-control-height);
    margin: 0;
    padding: var(--ui-space-8) var(--ui-space-14);
    align-items: center;
    border: 0 !important;
    border-radius: 0;
    color: #3d465c;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-regular);
    line-height: var(--ui-lh-snug);
    text-align: left;
    text-shadow: none !important;
}

div.dt-button-collection.ui-column-visibility-menu button.buttons-columnVisibility:hover,
div.dt-button-collection.ui-column-visibility-menu button.buttons-columnVisibility:focus {
    color: var(--ui-ink);
    background: var(--ui-canvas) !important;
    outline: 0;
}

div.dt-button-collection.ui-column-visibility-menu button.buttons-columnVisibility::before {
    width: 20px;
    margin-right: var(--ui-space-8);
    color: currentColor;
    content: "\f096";
    font-family: var(--ui-font-icon);
    font-size: var(--ui-fs-base);
    text-align: center;
}

div.dt-button-collection.ui-column-visibility-menu button.buttons-columnVisibility.active::before {
    content: "\f046";
}

/* Status badges: dark ink on a pale tint of the same hue, the model the .label-*
   badges and .exec-pill already use. The solid fills they replace were the
   loudest thing on a 25-column list - every row shouted its status. Hues are
   the ones staff already know (pending amber, dispatched blue, completed
   green, cancelled red); the border keeps a pale badge from dissolving into
   the zebra stripe. Every ink is >= 6.5:1 on its tint. */
.ui-status {
    display: inline-block;
    padding: 1px var(--ui-space-8);
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-pill);
    color: var(--ui-ink-2);
    background: var(--ui-neutral-tint);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: 0;
    white-space: nowrap;
}

/* In a list cell a long status ("PENDING APPROVAL", "PARTIALLY COMPLETED") wraps
   inside its pill, as the plain word did, instead of holding its column wide and
   pushing the list's right-hand columns (Total, Ref No.) out of the card. */
.table td > .ui-status {
    white-space: normal;
    text-align: center;
}

.ui-status-incomplete {
    border-color: var(--ui-neutral-line);
    color: var(--ui-ink-2);
    background: var(--ui-neutral-tint);
}

.ui-status-pending {
    border-color: var(--ui-warning-line);
    color: var(--ui-warning-ink);
    background: var(--ui-warning-tint);
}

.ui-status-pending-approval {
    border-color: var(--ui-status-approval-line);
    color: var(--ui-status-approval-ink);
    background: var(--ui-status-approval-tint);
}

.ui-status-dispatched {
    border-color: var(--ui-info-line);
    color: var(--ui-info-ink);
    background: var(--ui-info-tint);
}

.ui-status-unpaid {
    border-color: var(--ui-status-unpaid-line);
    color: var(--ui-status-unpaid-ink);
    background: var(--ui-status-unpaid-tint);
}

.ui-status-partially-completed {
    border-color: var(--ui-status-partial-line);
    color: var(--ui-status-partial-ink);
    background: var(--ui-status-partial-tint);
}

.ui-status-completed,
.ui-status-approved {
    border-color: var(--ui-success-line);
    color: var(--ui-success-ink);
    background: var(--ui-success-tint);
}

.ui-status-cancelled {
    border-color: var(--ui-danger-line);
    color: var(--ui-danger-ink);
    background: var(--ui-danger-tint);
}

/* Customer status on Manage Customers, and a customer on stop (credit limit 0):
   green active, the neutral grey inactive, amber on stop - red stays for
   cancelled and destructive. */
.ui-status-active {
    border-color: var(--ui-success-line);
    color: var(--ui-success-ink);
    background: var(--ui-success-tint);
}

.ui-status-inactive {
    border-color: var(--ui-neutral-line);
    color: var(--ui-ink-2);
    background: var(--ui-neutral-tint);
}

.ui-status-on-stop {
    border-color: var(--ui-warning-line);
    color: var(--ui-warning-ink);
    background: var(--ui-warning-tint);
}

/* Short customer badges stay whole ("On stop" in a narrow Credit Limit column,
   "Inactive" when the Status column is narrowed and the eye icon drops below
   it), unlike the long order statuses the list-cell rule above lets wrap. */
.table td > .ui-status-on-stop,
.table td > .ui-status-active,
.table td > .ui-status-inactive {
    white-space: nowrap;
}

.ui-action-trigger {
    display: inline-flex;
    width: var(--ui-action-size);
    height: var(--ui-action-size);
    padding: 0 !important;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-neutral-line) !important;
    border-radius: var(--ui-radius-sm);
    color: #35515f !important;
    background: var(--ui-surface) !important;
    font-size: var(--ui-icon-md);
    vertical-align: middle;
    cursor: pointer;
}

.ui-action-trigger.ui-action-warning {
    color: #8a6d3b !important;
}

.ui-action-trigger:hover {
    border-color: #8aa9bb !important;
    background: var(--ui-info-tint) !important;
}

.ui-disclosure {
    clear: both;
    margin: 0 0 var(--ui-card-gap);
}

.ui-disclosure > summary {
    display: flex;
    align-items: center;
    gap: var(--ui-space-12);
    padding: var(--ui-space-14) var(--ui-card-inset);
    color: var(--ui-label);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.ui-disclosure > summary::-webkit-details-marker {
    display: none;
}

.ui-disclosure > summary::after {
    margin-left: auto;
    color: var(--ui-muted);
    font-family: var(--ui-font-icon);
    font-size: var(--ui-fs-sm);
    content: "\f078";
    transition: transform .16s ease;
}

.ui-disclosure[open] > summary::after {
    transform: rotate(180deg);
}

.ui-disclosure[open] > summary {
    border-bottom: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
    background: var(--ui-subtle);
}

.dashboard-grid a {
    display: block;
    height: 100%;
    color: inherit;
    text-decoration: none;
}

.card-counter {
    --tile-accent: var(--ui-blue);
    --tile-border: #c9d9e3;
    --tile-tint: var(--ui-canvas);
    position: relative;
    height: 116px;
    margin: var(--ui-space-8) 0 var(--ui-space-14);
    padding: var(--ui-space-18);
    overflow: hidden;
    border: 1px solid var(--tile-border);
    border-radius: var(--ui-radius-md);
    color: var(--ui-ink) !important;
    /* Flat white. The hue lives in the top edge and the icon chip only; the
       old corner-to-corner gradient tinted every tile and made twenty of them
       read as wallpaper. */
    background: var(--ui-surface) !important;
    box-shadow: var(--ui-shadow-xs);
    isolation: isolate;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}

.card-counter::before {
    position: absolute;
    inset: 0 0 auto;
    z-index: 0;
    height: 3px;
    background: var(--tile-accent);
    content: "";
}

.card-counter:hover,
.dashboard-grid a:focus .card-counter {
    border-color: var(--tile-accent);
    transform: translateY(-2px);
    box-shadow: var(--ui-shadow-md);
}

.dashboard-grid a:focus-visible .card-counter {
    box-shadow: 0 0 0 3px var(--ui-focus), var(--ui-shadow-md);   /* was .16 alpha = 1.31:1 */
}

.dashboard-grid a:focus {
    outline: 0 !important;
}

/* Tile set is deliberately three, not seven: base (metric) + .danger
   (attention) + .orange (action). .primary/.success/.info/.secondary were
   deleted - they fall through to the base at :1178-1181, which is the metric
   look. They carried no consistent meaning: "Online orders" was .success on
   welcome.php:86 and .secondary on :237, i.e. keyed on who is looking. */

/* Status tiles - semantic, fixed across branches.
   Emitted by include/sitefunction.php as live UNPAID counts. */
.card-counter.warning {
    --tile-accent: var(--ui-warning);
    --tile-border: #ead2b5;
    --tile-tint: var(--ui-warning-tint);
}

/* Action tiles - the 9 tiles that emit an icon and a label but NO
   .count-numbers (New order, Create credit note, Assign driver...). They state
   no proposition, so under the contract they are identity, not semantic: this
   is the one content-surface component that carries the branch accent.
   It also removes a collision - Manchester #b45309 against the old tile amber
   #b86a12 is dE 12, so a Manchester metric tile was nearly indistinguishable
   from an action tile. */
.card-counter.orange {
    --tile-accent: var(--ui-branch, var(--ui-muted));
    --tile-border: color-mix(in srgb, var(--ui-branch, var(--ui-muted)) 28%, var(--ui-surface));
    --tile-tint:   color-mix(in srgb, var(--ui-branch, var(--ui-muted))  8%, var(--ui-surface));
}

.card-counter.danger {
    --tile-accent: var(--ui-danger);
    --tile-border: var(--ui-danger-line);
    --tile-tint: var(--ui-danger-tint);
}

.card-counter i {
    position: absolute;
    top: 50%;
    left: 18px;
    z-index: 1;
    display: inline-flex;
    width: 52px;
    height: 52px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--tile-border);
    border-color: color-mix(in srgb, var(--tile-accent) 24%, transparent);
    border-radius: var(--ui-radius-lg);
    color: var(--tile-accent);
    background: var(--ui-surface);
    background: color-mix(in srgb, var(--tile-accent) 10%, var(--ui-surface));
    font-size: var(--ui-fs-2xl);
    opacity: 1;
    transform: translateY(-50%);
}

.card-counter .count-numbers {
    top: 19px;
    right: 20px;
    z-index: 1;
    color: var(--ui-ink);
    font-size: var(--ui-fs-display);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-display);
    line-height: var(--ui-lh-solid);
}

.card-counter .count-name {
    top: auto;
    right: 20px;
    bottom: 20px;
    z-index: 1;
    max-width: calc(100% - 86px);
    color: var(--ui-ink-2);
    font-size: var(--ui-fs-sm);
    font-style: normal;
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-tight);
    text-align: right;
    text-transform: none;
}

.card-counter.orange .count-name {
    top: 50%;
    bottom: auto;
    /* colour deliberately unset - inherits #4b5961 (6.41-6.57:1 on the new
       tints). The old #5e492f was tuned to amber and reads dirty on teal/blue. */
    font-size: var(--ui-fs-base);
    transform: translateY(-50%);
}

/* An attention tile with nothing to attend to (0 cancellations, 0 pending
   approvals) steps back to neutral, so the ones that DO need someone stand
   out. welcome.php adds .is-zero; the count and link are unchanged. */
.card-counter.is-zero {
    --tile-accent: #9aa5ac;
    --tile-border: var(--ui-border);
}

.card-counter.is-zero .count-numbers {
    color: var(--ui-faint);
}

/* ---------------------------------------------------------------------------
   Dashboard tile grid (welcome.php)
   ---------------------------------------------------------------------------
   One grid for every tile, in the order staff know. The tiles used to sit in
   Bootstrap rows of four, so a tile hidden by a permission (or an unpaid type
   with no rows) left a hole in its row; in one grid the following tiles close
   up. Four across as before, two on a tablet, one on a phone. The tiles come
   from two sources - inline in welcome.php and getStatusCount() in
   include/sitefunction.php - and both wrap each tile in its own .col-md-3,
   which the grid neutralises.
   --------------------------------------------------------------------------- */
.dash-tiles {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0 var(--ui-space-32);
}

.dash-tiles > [class*="col-"] {
    width: auto;
    float: none;
    padding: 0;
}

@media (max-width: 991px) {
    .dash-tiles {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: var(--ui-space-16);
    }
}

@media (max-width: 767px) {
    .dash-tiles {
        grid-template-columns: minmax(0, 1fr);
    }
}

.loginstar {
    width: 100%;
    max-width: 520px;
    min-height: calc(100vh - 190px);
    margin: 36px auto 48px;
    padding: var(--ui-space-28) var(--ui-space-16);
    float: none;
    background: none;
}

.loginbox {
    position: relative;
    width: 100%;
    height: auto;
    min-height: 0;
    padding: 0;
    float: none;
    transform: none;
    border: 1px solid var(--ui-border);
    border-top: 4px solid var(--ui-branch, var(--ui-muted));
    border-radius: var(--ui-radius-lg);
    background: var(--ui-surface);
    box-shadow: var(--ui-shadow-lg);
}

.loginbox .topbuffer {
    width: 100%;
    margin: 0;
    padding: var(--ui-space-28) var(--ui-space-32) var(--ui-space-32);
    float: none;
}

.loginbox h3 {
    margin: 0 0 var(--ui-space-18);
    color: var(--ui-ink);
    font-size: var(--ui-fs-xl);
    font-weight: var(--ui-fw-bold);
}

.loginbox table {
    table-layout: fixed;
}

.loginbox .form-control.input-lg {
    height: 44px;
    min-height: 44px;
    padding: var(--ui-space-10) var(--ui-space-12);
    font-size: var(--ui-fs-md);
}

.loginbox .checkbox1,
.loginbox label {
    color: var(--ui-ink-2);
}

.loginbox img {
    max-width: 100%;
    height: auto;
}

@media (min-width: 768px) and (max-width: 991px) {
    .dashboard-grid .row > .col-md-3,
    .dashboard-grid .row > .col-md-4 {
        float: left;
        width: 50%;
    }
}

.footer {
    position: static !important;
    width: 100%;
    height: auto !important;
    min-height: 46px;
    margin-top: 0;
    padding: var(--ui-space-14) var(--ui-space-20) !important;
    color: var(--ui-ink-2) !important;
    background-color: var(--ui-border);
}

.footer p {
    margin: 0;
}

/* Messages (.ok / .error, .alert-*) are the Messages component's, after Cards. */

@media (max-width: 1440px) {
    #leftPanelTd { width: 228px !important; }
    #navigation { width: 228px; }
    #navigation ul li.subico,
    #navigation ul li.downicon { background-position: 208px center; }
    #navigation ul li.smoothmenus a { width: 198px; }
    #navigation ul ul { left: 226px; }
    .bodybg { padding-left: var(--ui-space-12); padding-right: var(--ui-space-12); }
}

@media (max-width: 991px) {
    #leftPanelTd,
    #panelSpacerTd {
        display: none;
    }

    #contentTd {
        width: 100% !important;
    }

    .navbar-fixed-top {
        position: relative;
    }

    .bodybg {
        margin-top: 0;
    }

    .navbar-collapse {
        display: block !important;
    }

    .contentdiv {
        padding: var(--ui-space-14);
    }

    .heading {
        padding: var(--ui-space-12) var(--ui-space-14);
    }

    .heading h1 {
        font-size: var(--ui-fs-lg);
    }

    .dataTables_wrapper .dt-buttons,
    .dataTables_wrapper .dataTables_length,
    .dataTables_wrapper .dataTables_filter {
        float: none !important;
        width: 100%;
        text-align: left;
    }
}

@media (max-width: 767px) {
    .bodybg {
        padding: var(--ui-space-10) var(--ui-space-8) var(--ui-space-24);
    }

    .app-layout {
        margin-top: var(--ui-space-8);
        margin-bottom: var(--ui-space-16);
    }

    .heading {
        padding: var(--ui-space-10) var(--ui-space-12);
    }

    .heading h1 {
        font-size: var(--ui-fs-lg);
    }

    .contentdiv,
    .detailfrm,
    .searchdiv {
        padding: var(--ui-space-12) !important;
    }

    .contentdiv .row,
    .detailfrm .row {
        margin-right: calc(var(--ui-space-6) * -1);
        margin-left: calc(var(--ui-space-6) * -1);
    }

    .contentdiv .row > [class*="col-"],
    .detailfrm .row > [class*="col-"] {
        padding-right: var(--ui-space-6) !important;
        padding-left: var(--ui-space-6) !important;
    }

    .contentdiv .form-horizontal .form-group,
    .detailfrm .form-horizontal .form-group,
    .searchdiv .form-horizontal .form-group {
        margin-right: 0;
        margin-left: 0;
    }

    .searchdiv .ui-filter-actions,
    .detailfrm .ui-filter-actions {
        padding-top: 0;
    }

    .modal-dialog {
        margin: var(--ui-space-10);
    }

    .modal-header,
    .modal-body,
    .modal-footer,
    .panel-heading,
    .panel-body,
    .panel-footer {
        padding-right: var(--ui-space-12);
        padding-left: var(--ui-space-12);
    }

    .dataTables_wrapper .dataTables_filter label,
    .dataTables_wrapper .dataTables_length label {
        display: block;
        width: 100%;
    }

    .dataTables_wrapper .dataTables_filter input,
    .dataTables_wrapper .dataTables_length select {
        width: 100%;
        max-width: 100%;
        margin: var(--ui-space-6) 0 0;
    }

    .form-inline .form-group > .select2-container {
        width: 100% !important;
        min-width: 0;
    }

    .dataTables_wrapper .dataTables_info,
    .dataTables_wrapper .dataTables_paginate {
        float: none !important;
        width: 100%;
        text-align: center;
    }

    .pagination {
        justify-content: center;
    }

    .pagination > .text-left,
    .pagination > .pull-left {
        width: 100%;
        flex-basis: 100%;
        padding: 0 0 var(--ui-space-6) !important;
        text-align: center !important;
    }

    .dashboard-grid .row > [class*="col-"] {
        float: none;
        width: 100%;
    }

    .card-counter {
        margin: var(--ui-space-6) 0 var(--ui-space-12);
    }

    .footer {
        padding: var(--ui-space-12) !important;
        text-align: center;
    }

    .loginstar {
        min-height: calc(100vh - 168px);
        margin: var(--ui-space-18) auto var(--ui-space-28);
        padding: var(--ui-space-16) var(--ui-space-6);
    }

    .loginbox .topbuffer {
        padding: var(--ui-space-24) var(--ui-space-18) var(--ui-space-24);
    }
}

@media (max-width: 560px) {
    .ui-heading-actions,
    .heading h1 > .pull-right {
        width: 100%;
        margin-left: 0;
    }

    .ui-heading-actions .btn,
    .heading h1 > .pull-right .btn,
    .heading h1 > .pull-right > a {
        width: 100%;
        flex: 1 1 100%;
    }

    .modal-footer {
        align-items: stretch;
        flex-direction: column-reverse;
    }

    .modal-footer .btn {
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .btn,
    .ui-disclosure > summary::after,
    .card-counter,
    .loader {
        transition: none;
    }

    .card-counter:hover,
    .dashboard-grid a:focus .card-counter {
        transform: none;
    }

    .loader {
        animation: none;
    }
}

/* Banoze application shell ------------------------------------------------ */
.app-topbar {
    z-index: var(--ui-z-topbar);
    min-height: 64px;
    margin: 0;
    border: 0;
    border-bottom: 3px solid var(--ui-branch, var(--ui-muted));
    border-radius: 0;
    color: var(--ui-ink);
    background: rgba(255, 255, 255, .98);
    box-shadow: var(--ui-shadow-md);
}

.app-topbar-container {
    padding: 0 var(--ui-space-18);
}

.app-topbar-main {
    display: flex;
    min-height: 61px;
    align-items: center;
    gap: var(--ui-space-14);
}

.app-brand {
    display: flex;
    width: 146px;
    min-width: 146px;
    align-items: center;
    gap: var(--ui-space-10);
}

.app-brand-logo {
    display: inline-flex;
    align-items: center;
}

.app-brand-logo img {
    display: block;
    width: 132px;
    height: auto;
    max-height: 46px;
    object-fit: contain;
}

.app-location-badge {
    display: inline-flex;
    min-height: 28px;
    align-items: center;
    gap: var(--ui-space-6);
    padding: var(--ui-space-4) var(--ui-space-8);
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-pill);
    color: var(--ui-ink-2);
    background: var(--ui-subtle);
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-overline);
    line-height: var(--ui-lh-solid);
    text-transform: uppercase;
    white-space: nowrap;
}

.app-location-context .fa {
    color: var(--ui-muted);
    font-size: var(--ui-fs-2xs);
}

.app-sidebar-toggle {
    display: inline-flex;
    width: 38px;
    height: var(--ui-control-height);
    min-width: 38px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-md);
    color: var(--ui-label);
    background: var(--ui-surface);
    transition: background .16s ease, border-color .16s ease, color .16s ease;
}

.app-sidebar-toggle:hover {
    color: var(--ui-ink);
    border-color: var(--ui-border-strong);
    background: var(--ui-subtle);
}

.app-mobile-nav-toggle {
    display: none;
}

.app-page-finder {
    position: relative;
    display: flex;
    width: min(32vw, 420px);
    min-width: 220px;
    height: var(--ui-control-height);
    align-items: center;
    margin-left: var(--ui-space-4);
}

.app-page-finder > .fa {
    position: absolute;
    left: 12px;
    z-index: 1;
    color: var(--ui-faint);
}

.app-page-finder input {
    width: 100%;
    height: var(--ui-control-height);
    padding: var(--ui-space-8) 42px var(--ui-space-8) 34px;
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-md);
    color: var(--ui-ink);
    background: var(--ui-subtle);
    box-shadow: none;
}

.app-page-finder input:focus {
    border-color: var(--ui-focus);
    background: var(--ui-surface);
    box-shadow: var(--ui-ring);
}

.app-page-finder kbd {
    position: absolute;
    right: 10px;
    padding: 1px var(--ui-space-6);
    border: 1px solid var(--ui-neutral-line);
    border-radius: var(--ui-radius-sm);
    color: var(--ui-muted);
    background: var(--ui-surface);
    box-shadow: none;
    font-family: inherit;
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-semibold);
}

.app-topbar-actions {
    display: flex;
    align-items: center;
    gap: var(--ui-space-12);
    margin-left: auto;
}

.app-quick-action {
    display: inline-flex;
    min-height: var(--ui-control-height);
    align-items: center;
    gap: var(--ui-space-8);
    padding: var(--ui-space-8) var(--ui-space-14);
    border: 1px solid var(--ui-primary-border);
    border-radius: var(--ui-radius-md);
    color: var(--ui-text-inverse);
    background: var(--ui-primary);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-bold);
    text-decoration: none;
    box-shadow: var(--ui-shadow-sm);
}

.app-quick-action:hover,
.app-quick-action:focus {
    color: var(--ui-text-inverse);
    background: var(--ui-primary-hover);
    text-decoration: none;
}

.app-date {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-8);
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    white-space: nowrap;
}

.app-date .fa {
    color: var(--ui-muted);
}

.app-account-menu {
    position: relative;
}

.app-account-menu > summary {
    display: flex;
    min-width: 184px;
    min-height: 44px;
    align-items: center;
    gap: var(--ui-space-10);
    padding: var(--ui-space-4) var(--ui-space-8);
    border: 1px solid transparent;
    border-radius: var(--ui-radius-md);
    cursor: pointer;
    list-style: none;
}

.app-account-menu > summary::-webkit-details-marker {
    display: none;
}

.app-account-menu > summary:hover,
.app-account-menu[open] > summary {
    border-color: var(--ui-border);
    background: var(--ui-subtle);
}

.app-avatar {
    display: inline-flex;
    width: 34px;
    height: 34px;
    min-width: 34px;
    align-items: center;
    justify-content: center;
    border-radius: var(--ui-radius-round);
    color: var(--ui-text-inverse);
    background: var(--ui-charcoal-soft);
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-bold);
    box-shadow: inset 0 0 0 2px var(--ui-branch, var(--ui-muted));
}

.app-account-copy {
    display: flex;
    min-width: 0;
    flex-direction: column;
    line-height: var(--ui-lh-tight);
}

.app-account-copy strong {
    max-width: 128px;
    overflow: hidden;
    color: var(--ui-ink);
    font-size: var(--ui-fs-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-account-copy small {
    margin-top: var(--ui-space-4);
    color: var(--ui-faint);
    font-size: var(--ui-fs-2xs);
}

.app-account-menu > summary > .fa {
    margin-left: auto;
    color: var(--ui-faint);
    font-size: var(--ui-icon-xs);
    transition: transform .16s ease;
}

.app-account-menu[open] > summary > .fa {
    transform: rotate(180deg);
}

.app-account-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: var(--ui-z-popover);
    width: 210px;
    padding: var(--ui-space-8);
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    background: var(--ui-surface);
    box-shadow: var(--ui-shadow-lg);
}

.app-account-dropdown a {
    display: flex;
    align-items: center;
    gap: var(--ui-space-10);
    padding: var(--ui-space-10) var(--ui-space-10);
    border-radius: var(--ui-radius-sm);
    color: var(--ui-label);
    font-size: var(--ui-fs-sm);
    text-decoration: none;
}

.app-account-dropdown a:hover {
    color: var(--ui-ink);
    background: var(--ui-subtle);
}

.app-account-dropdown .fa {
    width: 16px;
    color: var(--ui-muted);
    text-align: center;
}

.app-account-divider {
    height: 1px;
    margin: var(--ui-space-6) var(--ui-space-4);
    background: var(--ui-neutral-tint);
}

.app-account-dropdown .app-account-logout {
    color: var(--ui-danger);
}

.app-login-context {
    display: flex;
    margin-left: auto;
    flex-direction: column;
    align-items: flex-end;
    color: var(--ui-label);
    font-weight: var(--ui-fw-bold);
}

.app-login-context small {
    margin-top: var(--ui-space-4);
    color: var(--ui-faint);
    font-weight: var(--ui-fw-regular);
}

.app-topbar .impersonation-banner {
    display: flex;
    min-height: 38px;
    align-items: center;
    justify-content: center;
    gap: var(--ui-space-18);
    margin: 0 calc(var(--ui-space-18) * -1);
    padding: var(--ui-space-8) var(--ui-space-18);
    border: 0;
    border-radius: 0;
    color: var(--ui-text-inverse);
    background: var(--ui-danger);
    font-size: var(--ui-fs-xs);
}

.app-topbar .impersonation-banner span,
.app-topbar .impersonation-banner form {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin: 0;
}

.bodybg {
    margin-top: 64px;
    min-height: calc(100vh - 110px);
    background: var(--ui-subtle);
}

.app-topbar.is-impersonating ~ .bodybg {
    margin-top: 102px;
}

.app-layout {
    margin-top: var(--ui-space-14);
}

#leftPanelTd {
    width: 248px !important;
    transition: width .18s ease;
}

#panelSpacerTd {
    width: 16px !important;
    transition: width .18s ease;
}

#navigation.app-sidebar {
    position: sticky;
    top: 80px;
    display: flex;
    width: 248px;
    max-height: calc(100vh - 96px);
    min-height: calc(100vh - 120px);
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    border: 1px solid var(--ui-ink);
    border-radius: var(--ui-radius-md);
    color: var(--ui-text-inverse);
    background: var(--ui-charcoal);
    box-shadow: var(--ui-shadow-md);
    transition: width .18s ease;
    scrollbar-color: #4a5056 transparent;
    scrollbar-width: thin;
}

#navigation.app-sidebar ul,
#navigation.app-sidebar li {
    position: static;
    display: block;
    width: auto;
    height: auto;
    min-height: 0;
    float: none;
    margin: 0;
    padding: 0;
    border: 0;
    list-style: none;
    line-height: normal;
    color: inherit;
    background: transparent;
}

#navigation.app-sidebar ul li:hover,
#navigation.app-sidebar ul ul li:hover,
#navigation.app-sidebar .sidebar-group:hover,
#navigation.app-sidebar .sidebar-submenu li:hover {
    position: static;
    color: inherit;
    background: transparent !important;
}

.sidebar-dashboard,
.sidebar-group-toggle {
    display: flex;
    width: calc(100% - 2 * var(--ui-space-8));
    min-height: 42px;
    align-items: center;
    gap: var(--ui-space-10);
    margin: var(--ui-space-8);
    padding: var(--ui-space-10) var(--ui-space-10);
    border: 0;
    border-radius: var(--ui-radius-md);
    color: #dce0e3;
    background: transparent;
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-tight);
    text-align: left;
    text-decoration: none;
    transition: background .15s ease, color .15s ease;
}

.sidebar-dashboard {
    margin-bottom: var(--ui-space-4);
}

.sidebar-top-row {
    display: flex;
    align-items: center;
    gap: var(--ui-space-4);
    margin: var(--ui-space-8);
}

.sidebar-top-row .sidebar-dashboard {
    flex: 1;
    width: auto;
    margin: 0;
}

.sidebar-inline-toggle {
    display: inline-flex;
    width: 38px;
    height: 42px;
    min-width: 38px;
    align-items: center;
    justify-content: center;
    border: 1px solid #3a4046;
    border-radius: var(--ui-radius-md);
    color: #aeb5bb;
    background: var(--ui-charcoal-soft);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.sidebar-inline-toggle:hover,
.sidebar-inline-toggle:focus {
    color: var(--ui-text-inverse);
    border-color: #596168;
    background: #30363c;
}

.sidebar-dashboard:hover,
.sidebar-dashboard:focus,
.sidebar-group-toggle:hover,
.sidebar-group-toggle:focus {
    color: var(--ui-text-inverse);
    background: var(--ui-charcoal-soft);
    text-decoration: none;
}

.sidebar-dashboard.is-active,
.sidebar-group.is-active > .sidebar-group-toggle {
    color: var(--ui-text-inverse);
    background: #30353b;
}

.sidebar-dashboard > .fa,
.sidebar-icon {
    display: inline-flex;
    width: 24px;
    min-width: 24px;
    justify-content: center;
    color: #aeb5bb;
    font-size: var(--ui-fs-base);
}

.sidebar-dashboard.is-active > .fa,
.sidebar-group.is-active .sidebar-icon {
    color: #ff555b;
}

.sidebar-section-label {
    margin: var(--ui-space-12) var(--ui-space-18) var(--ui-space-6);
    color: var(--ui-faint);
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-overline);
    text-transform: uppercase;
}

.sidebar-menu {
    flex: 0 0 auto;
}

.sidebar-group-toggle {
    margin-top: var(--ui-space-2);
    margin-bottom: var(--ui-space-2);
}

.sidebar-label {
    min-width: 0;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar-chevron {
    margin-left: auto;
    color: #727a81;
    font-size: var(--ui-icon-2xs);
    transition: transform .16s ease;
}

.sidebar-group.is-open > .sidebar-group-toggle .sidebar-chevron {
    transform: rotate(180deg);
}

#navigation.app-sidebar .sidebar-submenu {
    display: none !important;
    position: static;
    width: auto;
    margin: 0 var(--ui-space-8) var(--ui-space-6);
    background: transparent;
    box-shadow: none;
}

#navigation.app-sidebar .sidebar-group.is-open > .sidebar-submenu {
    display: block !important;
}

#navigation.app-sidebar .sidebar-submenu li {
    border: 0;
}

#navigation.app-sidebar .sidebar-submenu a {
    display: flex;
    width: auto;
    min-height: 32px;
    align-items: center;
    gap: var(--ui-space-12);
    padding: var(--ui-space-8) var(--ui-space-10);
    border-radius: var(--ui-radius-sm);
    color: #aeb5bb;
    font-size: var(--ui-fs-xs);
    line-height: var(--ui-lh-tight);
    text-decoration: none;
    float: none;
}

#navigation.app-sidebar .sidebar-submenu a:hover {
    color: var(--ui-text-inverse);
    background: var(--ui-charcoal-soft);
}

#navigation.app-sidebar .sidebar-submenu li.is-active > a {
    color: var(--ui-text-inverse);
    background: rgba(237, 28, 36, .16);
}

.sidebar-submenu-marker {
    display: inline-flex;
    width: 24px;
    height: 18px;
    min-width: 24px;
    align-items: center;
    justify-content: center;
    background: transparent;
}

.sidebar-submenu-marker::before {
    width: 5px;
    height: 5px;
    border-radius: var(--ui-radius-round);
    background: #626a71;
    content: "";
}

.sidebar-submenu li.is-active .sidebar-submenu-marker::before {
    box-shadow: 0 0 0 3px rgba(237, 28, 36, .14);
}

.sidebar-empty-state {
    padding: var(--ui-space-28) var(--ui-space-16);
    color: #90979d;
    text-align: center;
}

.sidebar-empty-state .fa,
.sidebar-empty-state span {
    display: block;
}

.sidebar-empty-state .fa {
    margin-bottom: var(--ui-space-8);
    font-size: var(--ui-fs-lg);
}

.sidebar-footer {
    display: flex;
    min-height: 44px;
    align-items: center;
    gap: var(--ui-space-10);
    margin-top: auto;
    padding: var(--ui-space-12) var(--ui-space-16);
    border-top: 1px solid var(--ui-ink);
    color: var(--ui-faint);
    font-size: var(--ui-fs-2xs);
    letter-spacing: 0;
    flex: 0 0 auto;
}

.sidebar-footer > .fa {
    color: #4f9b58;
    font-size: var(--ui-icon-2xs);
}

body.sidebar-collapsed #leftPanelTd {
    width: 68px !important;
}

body.sidebar-collapsed #panelSpacerTd {
    width: 12px !important;
}

body.sidebar-collapsed #navigation.app-sidebar {
    width: 68px;
}

body.sidebar-collapsed .app-sidebar .sidebar-label,
body.sidebar-collapsed .app-sidebar .sidebar-section-label,
body.sidebar-collapsed .app-sidebar .sidebar-chevron {
    display: none;
}

body.sidebar-collapsed .app-sidebar .sidebar-dashboard,
body.sidebar-collapsed .app-sidebar .sidebar-group-toggle {
    width: 48px;
    justify-content: center;
    margin-right: var(--ui-space-10);
    margin-left: var(--ui-space-10);
    padding-right: var(--ui-space-8);
    padding-left: var(--ui-space-8);
}

body.sidebar-collapsed .sidebar-top-row {
    justify-content: center;
    margin: var(--ui-space-8) var(--ui-space-10);
}

body.sidebar-collapsed .sidebar-top-row .sidebar-dashboard {
    display: none;
}

body.sidebar-collapsed .sidebar-inline-toggle {
    width: 48px;
    min-width: 48px;
    padding: 0 var(--ui-space-8);
}

body.sidebar-collapsed #navigation.app-sidebar .sidebar-group > .sidebar-submenu,
body.sidebar-collapsed #navigation.app-sidebar .sidebar-group.is-open > .sidebar-submenu {
    display: none !important;
}

body.sidebar-collapsed .sidebar-footer {
    justify-content: center;
    padding-right: var(--ui-space-8);
    padding-left: var(--ui-space-8);
}

.sidebar-backdrop {
    display: none;
}

.footer {
    color: #c9ced2 !important;
    background: var(--ui-charcoal);
}

@media (max-width: 1440px) {
    .app-brand {
        width: 146px;
        min-width: 146px;
    }

    .app-page-finder {
        width: min(28vw, 340px);
    }

    #leftPanelTd {
        width: 232px !important;
    }

    #navigation.app-sidebar {
        width: 232px;
    }
}

@media (max-width: 1120px) {
    .app-date,
    .app-account-copy small {
        display: none;
    }

    .app-account-menu > summary {
        min-width: 0;
    }

    .app-page-finder {
        flex: 1;
        width: auto;
    }
}

@media (max-width: 991px) {
    .app-topbar {
        position: fixed;
    }

    .app-mobile-nav-toggle {
        display: inline-flex;
    }

    .sidebar-inline-toggle {
        display: none;
    }

    body.sidebar-mobile-open {
        overflow: hidden;
    }

    .app-topbar-container {
        padding: 0 var(--ui-space-12);
    }

    .app-topbar-main {
        gap: var(--ui-space-10);
    }

    .app-brand {
        width: auto;
        min-width: 0;
    }

    .app-brand-logo img {
        width: 112px;
    }

    .app-location-badge,
    .app-page-finder,
    .app-date {
        display: none;
    }

    .app-topbar-actions {
        gap: var(--ui-space-8);
    }

    .app-account-copy {
        display: none;
    }

    .app-account-menu > summary {
        padding: var(--ui-space-4);
    }

    .app-quick-action span {
        display: none;
    }

    .app-quick-action {
        width: 38px;
        justify-content: center;
        padding: var(--ui-space-8);
    }

    .bodybg,
    .app-topbar.is-impersonating ~ .bodybg {
        margin-top: 64px;
    }

    .app-topbar.is-impersonating ~ .bodybg {
        margin-top: 102px;
    }

    #leftPanelTd,
    body.sidebar-collapsed #leftPanelTd {
        position: fixed;
        top: 64px;
        bottom: 0;
        left: -264px;
        z-index: var(--ui-z-drawer);
        display: block;
        width: 248px !important;
        transition: left .18s ease;
    }

    .app-topbar.is-impersonating ~ .sidebar-backdrop + .bodybg #leftPanelTd {
        top: 102px;
    }

    #panelSpacerTd {
        display: none;
    }

    /*
     * table-cell, NOT block. #contentTd is a <td> in the layout table
     * (admin.php:61 `<table class="topbuffer app-layout">`). Making it a block
     * box inside a table-row collapsed its height to its FIRST child - the
     * 56px .heading bar - and the unconditional `overflow: clip` on #contentTd
     * (line 153) then cut everything below it away. The result was a dashboard
     * that showed the "Welcome <name>" bar and nothing else on any screen
     * under 992px. Measured: display:block -> td 56px tall; table-cell -> 2659px.
     * The sidebar still goes off-canvas here - #leftPanelTd is position:fixed
     * and #panelSpacerTd is display:none above - so this cell is already the
     * full width of the row and does not need to stop being a cell to get there.
     */
    #contentTd {
        display: table-cell;
        width: 100% !important;
    }

    #navigation.app-sidebar,
    body.sidebar-collapsed #navigation.app-sidebar {
        position: static;
        width: 248px;
        max-height: 100%;
        min-height: 100%;
        border-radius: 0 var(--ui-radius-md) var(--ui-radius-md) 0;
    }

    body.sidebar-collapsed .app-sidebar .sidebar-label,
    body.sidebar-collapsed .app-sidebar .sidebar-section-label,
    body.sidebar-collapsed .app-sidebar .sidebar-chevron {
        display: initial;
    }

    body.sidebar-collapsed .app-sidebar .sidebar-dashboard,
    body.sidebar-collapsed .app-sidebar .sidebar-group-toggle {
        width: calc(100% - 2 * var(--ui-space-8));
        justify-content: flex-start;
        margin-right: var(--ui-space-8);
        margin-left: var(--ui-space-8);
        padding-right: var(--ui-space-10);
        padding-left: var(--ui-space-10);
    }

    body.sidebar-collapsed .sidebar-top-row {
        gap: var(--ui-space-4);
        margin: var(--ui-space-8);
    }

    body.sidebar-collapsed .sidebar-top-row .sidebar-dashboard {
        display: flex;
        flex: 1;
        width: auto;
        min-width: 0;
        margin: 0;
        padding-right: var(--ui-space-10);
        padding-left: var(--ui-space-10);
    }

    body.sidebar-collapsed .sidebar-inline-toggle {
        width: 38px;
        min-width: 38px;
    }

    body.sidebar-mobile-open #leftPanelTd {
        left: 0;
    }

    .sidebar-backdrop {
        position: fixed;
        inset: 64px 0 0;
        z-index: var(--ui-z-backdrop);
        width: 100%;
        height: auto;
        border: 0;
        background: rgba(19, 22, 25, .48);
    }

    body.sidebar-mobile-open .sidebar-backdrop {
        display: block;
    }

    .app-topbar.is-impersonating + .sidebar-backdrop {
        top: 102px;
    }
}

@media (max-width: 560px) {
    .app-brand-logo img {
        width: 96px;
    }

    .app-account-menu {
        margin-left: 0;
    }

    .app-avatar {
        width: 32px;
        height: 32px;
        min-width: 32px;
    }

    .app-login-context {
        font-size: var(--ui-fs-xs);
    }

    .app-login-context small {
        display: none;
    }
}

/* ------------------------------------------------- login password reveal */

.password-field {
    position: relative;
}

/* Room for the toggle, so a long password never runs underneath it. */
.password-field .form-control {
    padding-right: 46px;
}

.password-toggle {
    position: absolute;
    top: 0;
    right: 0;
    width: 46px;
    height: 100%;
    padding: 0;
    border: 0;
    color: #8a949a;
    background: none;
    line-height: var(--ui-lh-solid);
}

.password-toggle:hover,
.password-toggle:focus {
    color: #16211f;
}

.password-toggle:focus-visible {
    outline: 2px solid var(--ui-focus) !important;
    outline-offset: -2px;
}

/* ===========================================================================
   Legacy panels: .h_bg, .h_bg1, .h2, .h_bg2 / .h_bg3
   ---------------------------------------------------------------------------
   Credit notes, collection notes, discounted and pending-approval order
   details, purchase details, New Order / New Quotation and a dozen more
   screens still build their panels from style.css's old kit: a gradient
   background image, a grey #c1c1c1 frame, a 22px centred Segoe title and
   gradient label cells. On screen they now look like the rest of the admin:
   a white card, the admin's type, a quiet label column. The printed receipt
   is untouched - the print iframe loads only bootstrap.css and print.css.
   The modern order detail (.od-card, below) comes later and still wins.
   =========================================================================== */

.h_bg,
.h_bg1 {
    margin-bottom: var(--ui-card-gap);
    padding: var(--ui-space-16) var(--ui-card-inset);
}

.h2 {
    margin: 0 0 var(--ui-space-12);
    color: var(--ui-ink);
    font-family: var(--ui-font);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    text-align: left;
}

/* The label / value cells of the key-value grid. */
.h_bg2 {
    border-color: var(--ui-border);
    color: var(--ui-label);
    background: var(--ui-subtle);    /* was a gradient image */
    font-weight: var(--ui-fw-semibold);
}

.h_bg3 {
    border-color: var(--ui-border);
    color: var(--ui-ink);
}

/* A status badge in a value cell: the cell's 40px line-height (style.css)
   would stretch the pill to the cell's full height. */
.h_bg3 .ui-status {
    line-height: var(--ui-lh-tight);
    vertical-align: middle;
}

/* --- Legacy detail pages drawn as the order detail's cards ------------------
   Credit notes, collection notes, discounted, pending-approval, accounts and
   unpaid orders and Invoice Correction open a record as these panels inside
   #printArea. The same markup is the printed invoice, so it stays as it is;
   on screen (the print iframe never loads this file) each panel reads like an
   .od-card: its title is the card's grey head, and its label/value pairs form
   the .od-dl grid - small uppercase label over the value, four to a row -
   instead of a two-column table of boxed cells. The action panels under
   them (Approve, Cancel Order) get the same head. New Order and New
   Quotation use the same panels with no #printArea and keep their look. */

.detailfrm:has(#printArea) :is(.h_bg, .h_bg1) > .h2:first-child {
    float: none;
    width: auto;
    margin: calc(-1 * var(--ui-space-16)) calc(-1 * var(--ui-card-inset)) var(--ui-space-16) !important;
    padding: var(--ui-space-12) var(--ui-card-inset);
    border-bottom: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
    background: var(--ui-subtle);
    line-height: var(--ui-lh-snug);
}

/* The pairs are siblings, not wrapped, so each label and its value are sent
   to the same column (the Nth pair to column N mod 4) and dense placement
   drops the value into the row under its label. Anything else in the panel -
   the title, Cancellation Reason, a remark with no label - takes a whole row.
   A panel with a single pair is a form (Pending Approval's payment method)
   and keeps its layout. */
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-auto-flow: row dense;
    align-items: start;
    column-gap: var(--ui-space-24);
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > * {
    grid-column: 1 / -1;
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > :is(.h_bg2, .h_bg3) {
    float: none;
    width: auto;
    height: auto;
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    text-indent: 0;
    overflow-wrap: break-word;
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2 {
    margin-bottom: var(--ui-space-2);
    color: var(--ui-muted);
    font-size: var(--ui-fs-2xs);
    letter-spacing: var(--ui-tracking-overline);
    line-height: var(--ui-lh-snug);
    text-transform: uppercase;
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3 {
    padding-bottom: var(--ui-space-14);
    font-size: var(--ui-fs-base);
    line-height: var(--ui-lh-body);
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:empty::before {
    color: var(--ui-muted);
    content: "\2014";
}

#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(4n + 1 of .h_bg2),
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(4n + 1 of .h_bg3:not(.one)) { grid-column: 1; }
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(4n + 2 of .h_bg2),
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(4n + 2 of .h_bg3:not(.one)) { grid-column: 2; }
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(4n + 3 of .h_bg2),
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(4n + 3 of .h_bg3:not(.one)) { grid-column: 3; }
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(4n + 4 of .h_bg2),
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(4n + 4 of .h_bg3:not(.one)) { grid-column: 4; }

/* A value printed without a label (a collection note's remarks) is left out
   of the count above, so it takes a row of its own - or none, when empty. */
#printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3.one:empty { display: none; }

/* Cancellation Reason is an .h_bg1 inside the panel: a line, not a card in a
   card (the Cards component drops its paint). */
#printArea :is(.h_bg, .h_bg1) .h_bg1 {
    float: none;
    width: auto;
    margin: 0 0 var(--ui-space-14);
    padding: 0;
}

@media (max-width: 767px) {
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(2n + 1 of .h_bg2),
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(2n + 1 of .h_bg3:not(.one)) { grid-column: 1; }
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(2n + 2 of .h_bg2),
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(2n + 2 of .h_bg3:not(.one)) { grid-column: 2; }
}

@media (max-width: 479px) {
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) {
        grid-template-columns: minmax(0, 1fr);
    }

    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg2:nth-child(n of .h_bg2),
    #printArea :is(.h_bg, .h_bg1):has(> .h_bg2 ~ .h_bg2) > .h_bg3:nth-child(n of .h_bg3:not(.one)) { grid-column: 1; }
}

/* ==========================================================================
   Order detail (admin.php?act=orders&action=details)
   --------------------------------------------------------------------------
   Card treatment for every section, plus a right-hand rail that collects the
   order actions. Scoped to .order-detail so the legacy .h_bg* grid used by
   other screens is untouched.
   ========================================================================== */

.order-detail {
    margin-bottom: var(--ui-space-24);
}

.od-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ui-card-gap);
    align-items: start;
}

@media (min-width: 1200px) {
    .od-layout { grid-template-columns: minmax(0, 1fr) 330px; }
}

.od-main { min-width: 0; }

.od-rail {
    display: flex;
    flex-direction: column;
    gap: var(--ui-card-gap);
    min-width: 0;
}

/* The rail follows you down a long order. 72px topbar + a little breathing room. */
@media (min-width: 1200px) {
    .od-rail { position: sticky; top: 88px; }
}

/* ------------------------------------------------------------------- card */

.od-card {
    display: block;
    margin: 0 0 var(--ui-card-gap);
    /* .h_bg/.h_bg1 floated; these do not, so no clearfix is needed. */
    float: none;
    width: auto;
}

.od-rail .od-card { margin-bottom: 0; }
.od-main > .od-card:last-child { margin-bottom: 0; }

.od-card-head {
    display: flex;
    align-items: center;
    gap: var(--ui-space-10);
    padding: var(--ui-space-12) var(--ui-card-inset);
    border-bottom: 1px solid var(--ui-border);
    background: var(--ui-subtle);
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
}

.od-card-head h2 {
    margin: 0;
    color: var(--ui-ink);
    font-family: inherit;
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    letter-spacing: 0;
    text-align: left;
    width: auto;
}

.od-card-body { padding: var(--ui-space-16) var(--ui-card-inset); }

/* Legacy bodies still lay out with Bootstrap 3 floats (.row > .col-md-*). Without
   a clearfix the float escapes the card and the card collapses to zero height -
   which is what happened to Sales Return. */
.od-card-body::after {
    display: table;
    clear: both;
    content: "";
}
.od-card-body.od-flush { padding: 0; overflow-x: auto; }
.od-card-body.od-flush > .table { margin-bottom: 0; }
.od-card-body.od-flush > .table > tbody > tr:last-child > td { border-bottom: 0; }


.od-card-danger .od-card-head {
    border-bottom-color: var(--ui-danger-line);
    background: var(--ui-danger-wash);
}

.od-card-danger .od-card-head h2 { color: var(--ui-danger); }

.od-hint {
    margin: 0 0 var(--ui-space-12);
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    line-height: var(--ui-lh-body);
}

.od-card-body > .btn + .od-hint { margin: var(--ui-space-10) 0 0; }

/* -------------------------------------------------------------- label/value */

.od-dl {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--ui-space-14) var(--ui-space-24);
    margin: 0;
}

.od-dl > div { min-width: 0; }
.od-dl .od-span2 { grid-column: 1 / -1; }

.od-dl dt {
    margin: 0 0 var(--ui-space-2);
    color: var(--ui-muted);
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-overline);
    text-transform: uppercase;
}

.od-dl dd {
    margin: 0;
    font-size: var(--ui-fs-base);
    line-height: var(--ui-lh-body);
    overflow-wrap: break-word;
}

.od-dl dd.od-num { font-variant-numeric: tabular-nums; }
.od-dl dd.od-addr { line-height: var(--ui-lh-body); }

/* The order's cash-up remark, corrected in place: the text box, the ROUNDING
   note the save added, and the save icon common.js shows once the text changes. */
.od-remark-form {
    display: flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin: 0;
}

.od-remark-form .form-control { flex: 1 1 auto; min-width: 0; }
.od-remark-note { color: var(--ui-muted); font-size: var(--ui-fs-xs); white-space: nowrap; }
.od-remark-form .od-remark-save[hidden] { display: none; }
.od-empty { color: var(--ui-muted); font-style: italic; }

/* The cash-up difference (common.js CashupDiffBind): under the last payment
   line's amount box on Complete Order and Drivers Collections, the money typed
   less the ticked orders' worth, in the tone of what the handler will do. */
.cashup-diff {
    display: inline-block;
    margin-bottom: var(--ui-space-10);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
    font-variant-numeric: tabular-nums;
}

.cashup-diff:empty { display: none; }
.cashup-diff--ok { color: var(--ui-success-ink); }
.cashup-diff--muted { color: var(--ui-muted); }
.cashup-diff--warn { color: var(--ui-warning-ink); }
.cashup-diff--bad { color: var(--ui-danger-ink); }

/* ---------------------------------------------------------------- rail form */

.od-field { margin-bottom: var(--ui-space-12); }

.od-field > label {
    display: block;
    margin-bottom: var(--ui-space-4);
    color: var(--ui-ink);
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-semibold);
    letter-spacing: 0;
}

.od-field .form-control,
.od-field select { width: 100%; }

/* GetSelectDropdownList emits a bare <select> with no wrapper. */
.od-field > select.form-control { display: block; }

.od-rail .btn-block { margin-top: var(--ui-space-2); }

/* ---------------------------------------------------------------- timeline */

.od-timeline {
    margin: 0;
    padding: 0;
    list-style: none;
}

.od-timeline > li {
    position: relative;
    min-height: 30px;
    padding: 0 0 var(--ui-space-18) 34px;
    border-left: 2px solid var(--ui-border);
    margin-left: var(--ui-space-12);
}

.od-timeline > li:last-child { padding-bottom: 0; border-left-color: transparent; }

.od-tl-dot {
    position: absolute;
    top: -2px;
    left: -13px;
    display: inline-flex;
    width: 24px;
    height: 24px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-round);
    background: var(--ui-surface);
    color: var(--ui-muted);
    font-size: var(--ui-fs-2xs);
}

.od-timeline > li:first-child .od-tl-dot {
    border-color: #b9d0e0;
    background: var(--ui-canvas);
    color: var(--ui-link);
}

.od-timeline > li.is-failed .od-tl-dot {
    border-color: var(--ui-danger-line);
    background: var(--ui-danger-wash);
    color: var(--ui-danger);
}

.od-tl-what { font-size: var(--ui-fs-base); font-weight: var(--ui-fw-semibold); line-height: var(--ui-lh-snug); }

.od-tl-fail {
    display: inline-block;
    padding: 0 var(--ui-space-6) 1px;
    border-radius: var(--ui-radius-xs);
    background: var(--ui-danger);
    color: var(--ui-text-inverse);
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-bold);
    text-transform: uppercase;
}

.od-tl-detail { margin-top: 1px; color: var(--ui-ink); font-size: var(--ui-fs-sm); line-height: var(--ui-lh-snug); }

/* A run of near-identical events (50 lines added to one order) collapses to one entry
   with the individual lines behind this disclosure. Deliberately NOT .ui-disclosure -
   that is a full-width card, and this sits inside a timeline entry. */
.od-tl-group { margin-top: var(--ui-space-2); }

.od-tl-group > summary {
    display: inline-block;
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.od-tl-group > summary::-webkit-details-marker { display: none; }

.od-tl-group > summary::after {
    margin-left: var(--ui-space-6);
    font-family: var(--ui-font-icon);
    font-size: var(--ui-icon-xs);
    content: "\f078";
}

.od-tl-group[open] > summary::after { content: "\f077"; }

.od-tl-group > summary:hover { color: var(--ui-ink); text-decoration: underline; }

.od-tl-group[open] > summary { margin-bottom: var(--ui-space-2); }

.od-tl-group > .od-tl-detail:last-child { margin-bottom: var(--ui-space-2); }

.od-tl-meta {
    margin-top: var(--ui-space-2);
    color: var(--ui-muted);
    font-size: var(--ui-fs-xs);
    font-variant-numeric: tabular-nums;
}

/* "database" marker in the meta line. Muted on purpose - it says the row could not have
   been bypassed, which is worth being able to see, not worth a coloured badge. */
.od-tl-src { white-space: nowrap; }
.od-tl-src > .fa { margin-right: var(--ui-space-4); font-size: var(--ui-fs-2xs); }

/* ------------------------------------------------------------------- print */

@media print {
    .od-layout { display: block; }
    .od-rail { display: none; }
    .od-card { break-inside: avoid; }
    .od-card-head { background: none; }
}

.od-msg { margin-bottom: var(--ui-card-gap); }
.od-msg .bottombuffer { margin-bottom: 0; }

/* Flush tables sit edge-to-edge inside the card, so the outer columns carry the
   card's own 16px gutter instead of the table's tighter 8px cell padding.
   Without this the right-hand money column reads as touching the card border. */
.order-detail .od-flush > .table > thead > tr > th:first-child,
.order-detail .od-flush > .table > tbody > tr > td:first-child,
.order-detail .od-flush > .table > tbody > tr > th:first-child,
.order-detail .od-flush > .table > tfoot > tr > td:first-child,
.order-detail .od-flush > .table > tfoot > tr > th:first-child {
    padding-left: var(--ui-card-inset) !important;
}

.order-detail .od-flush > .table > thead > tr > th:last-child,
.order-detail .od-flush > .table > tbody > tr > td:last-child,
.order-detail .od-flush > .table > tbody > tr > th:last-child,
.order-detail .od-flush > .table > tfoot > tr > td:last-child,
.order-detail .od-flush > .table > tfoot > tr > th:last-child {
    padding-right: var(--ui-card-inset) !important;
}

/* Money columns line up digit for digit. */
.order-detail .od-flush > .table > tbody > tr > td,
.order-detail .od-flush > .table > tfoot > tr > td,
.order-detail .od-flush > .table > tbody > tr > th {
    font-variant-numeric: tabular-nums;
}

/* table-bordered draws a box round every cell; inside a card the outer ring is
   the card border, so drop the table's own outer edge. */
.order-detail .od-flush > .table-bordered {
    border-left: 0;
    border-right: 0;
    border-top: 0;
}

.order-detail .od-flush > .table-bordered > tbody > tr > td:first-child,
.order-detail .od-flush > .table-bordered > thead > tr > th:first-child,
.order-detail .od-flush > .table-bordered > tfoot > tr > td:first-child { border-left: 0; }

.order-detail .od-flush > .table-bordered > tbody > tr > td:last-child,
.order-detail .od-flush > .table-bordered > thead > tr > th:last-child,
.order-detail .od-flush > .table-bordered > tfoot > tr > td:last-child { border-right: 0; }

/* ------------------------------------------------ inline form inside a card */

.od-inline-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--ui-space-10);
}

.od-inline-grow {
    flex: 1 1 260px;
    min-width: 0;
    margin-bottom: 0;
}

.od-inline-action {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

/* ==========================================================================
   #printArea overrides
   --------------------------------------------------------------------------
   The upper half of the detail view sits inside #printArea, which six other
   screens also use, so its rules in style.css must not change. But those rules
   are ID-based and !important -- `#printArea .table th, #printArea .table td
   { padding: 2px !important }` (style.css:474) outranks every class selector,
   so the card gutters above lost and the money column rendered hard against
   the card edge. These selectors carry the same id plus more classes, so they
   win without touching the shared rule.

   Note the print iframe (js/common.js) loads only bootstrap.css and
   /css/print.css, never this file - so nothing here affects the printout.
   ========================================================================== */

/* Every other #printArea on screen gets the admin's type and table spacing.
   The driver-assignment lists (Pending Assign Drivers, Reassign Driver, the
   collection and credit-note assign lists) wrap their whole list in
   #printArea, as do the old-style detail pages. style.css's 2px cells and
   Arial made those tables squashed and in a different font next to every
   other list. These match the plain .table rules exactly; the order detail's
   own card spacing below is more specific and still wins. */
#printArea {
    font-family: var(--ui-font);
}

#printArea .table > thead > tr > th {
    padding: var(--ui-space-10) var(--ui-space-8) !important;
}

#printArea .table > tbody > tr > th,
#printArea .table > tbody > tr > td,
#printArea .table > tfoot > tr > td {
    padding: var(--ui-space-8) !important;
}

.order-detail #printArea .od-flush > .table > thead > tr > th,
.order-detail #printArea .od-flush > .table > tbody > tr > td,
.order-detail #printArea .od-flush > .table > tbody > tr > th {
    padding: var(--ui-space-10) var(--ui-space-8) !important;
}

.order-detail #printArea .od-flush > .table > thead > tr > th:first-child,
.order-detail #printArea .od-flush > .table > tbody > tr > td:first-child,
.order-detail #printArea .od-flush > .table > tbody > tr > th:first-child {
    padding-left: var(--ui-card-inset) !important;
}

.order-detail #printArea .od-flush > .table > thead > tr > th:last-child,
.order-detail #printArea .od-flush > .table > tbody > tr > td:last-child,
.order-detail #printArea .od-flush > .table > tbody > tr > th:last-child {
    padding-right: var(--ui-card-inset) !important;
}

/* #printArea sets font-family: arial, so the four card headings inside it drifted
   away from the six outside it. Pin every heading to the app face. */
.order-detail .od-card-head h2,
.order-detail .od-dl,
.order-detail .od-subhead {
    font-family: var(--ui-font);
}

/* ------------------------------------------------ sub-section inside a card */

.od-subsection {
    padding: var(--ui-space-16) var(--ui-card-inset);
    border-top: 1px solid var(--ui-border);
}

.od-subhead {
    margin: 0 0 var(--ui-space-10);
    font-size: var(--ui-fs-base);
    font-weight: var(--ui-fw-bold);
}

.order-detail #printArea .od-subsection .table th,
.order-detail #printArea .od-subsection .table td {
    padding: var(--ui-space-8) var(--ui-space-8) !important;
}

.od-summary-section {
    padding: var(--ui-space-16);
    border-top: 1px solid var(--ui-border);
}

.od-order-summary {
    max-width: 420px;
    margin-left: auto;
    padding: var(--ui-space-16);
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-sm);
    background: var(--ui-subtle);
    color: var(--ui-ink);
}

.od-order-summary dl { margin: 0; }

.od-order-summary dl > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ui-space-16);
    padding: var(--ui-space-6) 0;
}

.od-order-summary dt { font-weight: var(--ui-fw-regular); }

.od-order-summary dd {
    margin: 0;
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.od-order-summary .od-summary-total {
    margin-top: var(--ui-space-8);
    padding-top: var(--ui-space-12);
    border-top: 1px solid var(--ui-border);
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
}

.od-order-summary .od-summary-total dt { font-weight: var(--ui-fw-bold); }

/* ==========================================================================
   Order detail - card head actions, Sales Return toggle, collapsible rail
   ========================================================================== */

.order-detail .od-card-head {
    display: flex;
    align-items: center;
    gap: var(--ui-space-12);
}

.order-detail .od-card-head h2 { margin-right: auto; }

.od-head-action {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-8);
    padding: var(--ui-space-6) var(--ui-space-12);
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-sm);
    color: #35515f;
    background: var(--ui-surface);
    font-size: var(--ui-fs-sm);
    font-weight: var(--ui-fw-semibold);
    line-height: var(--ui-lh-snug);
    white-space: nowrap;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}

.od-head-action:hover,
.od-head-action:focus {
    border-color: #9fb0ba;
    color: var(--ui-ink);
    background: var(--ui-subtle);
}

/* Open state reads as "this is now active and clicking again backs out". */
.od-head-action.is-active {
    border-color: var(--ui-danger);
    color: var(--ui-danger);
    background: var(--ui-danger-wash);
}

.od-head-action .fa { font-size: var(--ui-fs-xs); }

.od-return-panel[hidden] { display: none !important; }

/* ------------------------------------------------------------ rail collapse */

.od-rail-head {
    display: flex;
    align-items: center;
    gap: var(--ui-space-8);
    margin-bottom: var(--ui-space-12);
    padding-bottom: var(--ui-space-10);
    border-bottom: 1px solid var(--ui-border);
}

.od-rail-title {
    color: var(--ui-muted);
    font-size: var(--ui-fs-2xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-overline);
    text-transform: uppercase;
}

.od-rail-toggle {
    display: inline-flex;
    width: 30px;
    height: 30px;
    margin-left: auto;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-sm);
    color: #35515f;
    background: var(--ui-surface);
    cursor: pointer;
}

.od-rail-toggle:hover,
.od-rail-toggle:focus {
    border-color: #9fb0ba;
    background: var(--ui-subtle);
}

/* Scoped to the two-column breakpoint. Unscoped this outranks the single-column
   base rule (higher specificity, later in the file) and would force a 42px
   second column onto narrow screens, where the rail stacks underneath. */
@media (min-width: 1200px) {
    .order-detail.od-rail-collapsed .od-layout {
        grid-template-columns: minmax(0, 1fr) 42px;
    }
}

.order-detail.od-rail-collapsed .od-rail > .od-card { display: none; }

.order-detail.od-rail-collapsed .od-rail-head {
    /*
     * column-reverse, so the toggle stays at the top of the rail in both states
     * rather than being pushed ~55px down the page by the vertical label. The
     * label is a non-focusable <span>, so reversing the visual order does not
     * affect tab order.
     */
    flex-direction: column-reverse;
    justify-content: flex-start;
    gap: var(--ui-space-12);
    margin-bottom: 0;
    /* Without this the vertical label sits flush against the page heading bar. */
    padding: var(--ui-space-14) 0 0;
    border-bottom: 0;
}

.order-detail.od-rail-collapsed .od-rail-toggle { margin-left: 0; }

/* Collapsed to a 42px strip, the label turns on its side so the rail still
   says what it is rather than being an unexplained chevron. */
.order-detail.od-rail-collapsed .od-rail-title {
    writing-mode: vertical-rl;
    letter-spacing: var(--ui-tracking-overline);
}

@media (max-width: 1199px) {
    /* Below the two-column breakpoint the rail is already full width, so the
       collapse control would only hide the actions with no way to see them. */
    .od-rail-head { display: none; }
    .order-detail.od-rail-collapsed .od-rail > .od-card { display: block; }
}

/* ---------------------------------------------------------------------------
   Branch identity
   ---------------------------------------------------------------------------
   Staff take orders across Birmingham / London / Manchester, and the three
   admins are otherwise pixel-identical. --ui-branch is set per request by
   administrator/include/header.php from config/sites/<site>.php.

   The colour is a peripheral cue only: the branch NAME is always rendered
   beside it in .app-branch-chip, so nothing here depends on distinguishing
   hues. That also keeps it usable with any colour vision deficiency.

   Fallback matters: if --ui-branch were ever unset, the rail would collapse to
   an invisible border and silently stop signalling, so it defaults to the
   muted grey rather than to transparent.
   --------------------------------------------------------------------------- */

/* One branch rule, at the top edge. The bottom edge was a second 3px accent
   line; it is now a hairline so the colour is not repeated on every edge. */
.app-topbar {
    border-top: 3px solid var(--ui-branch, var(--ui-muted));
    border-bottom: 1px solid var(--ui-border);
}

/* .app-brand is pinned to width:146px, sized for the logo alone. Let it grow to
   fit the chip while keeping 146px as the floor, so nothing else on the topbar
   shifts when the chip is absent. */
.app-brand {
    width: auto;
}

.app-branch-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-6);
    padding: var(--ui-space-4) var(--ui-space-10) var(--ui-space-4) var(--ui-space-8);
    border: 1px solid var(--ui-branch, var(--ui-muted));
    border-radius: var(--ui-radius-pill);
    background: color-mix(in srgb, var(--ui-branch, var(--ui-muted)) 8%, transparent);
    color: var(--ui-branch, var(--ui-muted));
    font-size: var(--ui-fs-xs);
    font-weight: var(--ui-fw-bold);
    letter-spacing: var(--ui-tracking-overline);
    text-transform: uppercase;
    white-space: nowrap;
    /* Never the thing that shrinks when the topbar is tight. */
    flex: 0 0 auto;
}

/* color-mix is unsupported on older Safari/Chrome; there the chip simply has no
   tint. Border, dot and text still carry it, so the cue degrades rather than
   disappears. */
@supports not (background: color-mix(in srgb, red 8%, transparent)) {
    .app-branch-chip { background: transparent; }
}

.app-branch-dot {
    width: 7px;
    height: 7px;
    border-radius: var(--ui-radius-round);
    background: var(--ui-branch, var(--ui-muted));
    flex: 0 0 auto;
}

@media (max-width: 767px) {
    /* Keep the dot and the rail; drop only the word, which no longer fits. */
    .app-branch-chip {
        padding: var(--ui-space-4) var(--ui-space-6);
        font-size: 0;
        gap: 0;
    }
    .app-branch-dot { width: 9px; height: 9px; }
}

/* ===========================================================================
   Branch identity - part 2: sidebar field + page-header bar
   ---------------------------------------------------------------------------
   Everything derives from --ui-branch (set per request by
   administrator/include/header.php from config/sites/<site>.php). No new
   per-branch config keys.

   WHY THIS SITS AT THE END OF THE FILE: .heading is declared twice at equal
   specificity (:212 and :2366, so :2366 already wins and an edit at :212 is
   dead code), and #navigation.app-sidebar is (0,1,1,0). A plain
   `.app-sidebar {}` rule loses on specificity wherever it is placed. Every
   selector below either copies the shipped selector shape exactly - so source
   order decides - or is strictly more specific.
   =========================================================================== */

/* The branch-derived tokens (--ui-branch-deep/-tint, the --sb-* sidebar ladder)
   live in css/tokens.css. */

/* --- sidebar field --------------------------------------------------------
   Must be #navigation.app-sidebar: the markup is
   <nav id="navigation" class="app-sidebar"> (include/menu/xmlparser.php:179)
   and the shipped rule at :2035 is (0,1,1,0). Also relights the border and
   scrollbar thumb - #2d3339 and #4a5056 are lighter than charcoal but darker
   than any accent, so their relationship to the ground inverts.             */
#navigation.app-sidebar {
    border-color: var(--sb-line);
    color: var(--ui-text-inverse);
    background: var(--ui-branch-deep);
    scrollbar-color: rgba(255, 255, 255, .55) transparent;
}

/* <button> does not inherit color, and :2092 sets #dce0e3 on both explicitly,
   so the root `color` above does not reach them.                            */
.sidebar-dashboard,
.sidebar-group-toggle {
    color: var(--ui-text-inverse);
}

.sidebar-dashboard > .fa,
.sidebar-icon                    { color: var(--sb-fg2); }   /* was #aeb5bb */
.sidebar-section-label           { color: var(--sb-fg3); }   /* was #747c83 - already fails AA today at 4.03:1 */
.sidebar-chevron                 { color: var(--sb-fg4); }   /* was #727a81 */
.sidebar-empty-state             { color: var(--sb-fg3); }   /* was #90979d */
.sidebar-footer                  { color: var(--sb-fg3); border-top-color: var(--sb-line); }
.sidebar-footer > .fa            { color: var(--sb-ok);  }   /* #4f9b58 drops to 2.53-3.15 on the deep grounds */
.sidebar-submenu-marker::before  { background: var(--sb-fg4); }  /* was #626a71 - 1.00:1 on raw teal */

/* --- hover ----------------------------------------------------------------
   The five shipped state fills (#2b3036, #30353b, #292e34, #252a30, #30363c)
   are fully OPAQUE near-blacks, not translucent tints. Left alone they punch
   charcoal holes through the accent on exactly the rows being touched.
   Do NOT put hover colour on the <li>: :2072-2079 sets
   `background: transparent !important` on every li:hover permutation (it
   neutralises menu.css:75's legacy #ffa943) and no plain rule beats it. The
   interactive element is the <a>/<button>.                                  */
.sidebar-dashboard:hover,
.sidebar-dashboard:focus,
.sidebar-group-toggle:hover,
.sidebar-group-toggle:focus {
    color: var(--ui-text-inverse);
    background: var(--ui-branch-tint);
}

/* --- active ("you are here") ----------------------------------------------
   All four shipped cues are Banoze red and fall to 1.15-1.53:1 on an accent -
   red and amber are near-neighbours in hue at matched luminance, so under any
   colour vision deficiency nothing is left. Carry the state on LUMINANCE
   (white) instead, and let the row itself be full-strength accent.          */
.sidebar-dashboard.is-active,
.sidebar-group.is-active > .sidebar-group-toggle {
    color: var(--ui-text-inverse);
    background: var(--ui-branch, var(--ui-muted));
    box-shadow: inset 3px 0 0 var(--ui-text-inverse);       /* was var(--ui-logo-red) */
}
.sidebar-dashboard.is-active > .fa,
.sidebar-group.is-active .sidebar-icon {
    color: var(--ui-text-inverse);                          /* was #ff555b */
}

/* --- submenu --------------------------------------------------------------
   These MUST keep the id+class prefix: the shipped rules are (0,1,2,1) and
   (0,1,3,2), so a plain `.sidebar-submenu a` loses.                         */
#navigation.app-sidebar .sidebar-submenu a {
    color: var(--sb-fg2);
}
/* :focus is genuinely missing from the shipped CSS - submenu links are the
   only sidebar focusables with no focus state beyond the outline.           */
#navigation.app-sidebar .sidebar-submenu a:hover,
#navigation.app-sidebar .sidebar-submenu a:focus {
    color: var(--ui-text-inverse);
    background: var(--ui-branch-tint);
}
#navigation.app-sidebar .sidebar-submenu li.is-active > a {
    color: var(--ui-text-inverse);
    background: var(--ui-branch, var(--ui-muted));   /* was rgba(237,28,36,.16) = 1.01:1 on amber */
    box-shadow: inset 3px 0 0 var(--ui-text-inverse);
}
.sidebar-submenu li.is-active .sidebar-submenu-marker::before {
    background: var(--ui-text-inverse);
    box-shadow: 0 0 0 3px rgba(255, 255, 255, .18);
}

/* --- collapse toggle ------------------------------------------------------
   Permanently visible, and the only control left in the 68px collapsed rail
   (.sidebar-dashboard is display:none at :2341). Its border is a UI component
   boundary needing 3:1 - the shipped #3a4046 gives 1.57-2.09 and the hover
   #596168 gives 1.06-1.25, i.e. invisible.                                  */
.sidebar-inline-toggle {
    color: var(--ui-text-inverse);
    border-color: var(--sb-edge);
    background: var(--ui-branch-tint);
}
.sidebar-inline-toggle:hover,
.sidebar-inline-toggle:focus {
    color: var(--ui-text-inverse);
    border-color: var(--ui-text-inverse);
    background: var(--ui-branch, var(--ui-muted));
}

/* --- focus ring -----------------------------------------------------------
   :49 forces `outline: 3px solid rgba(201,21,29,.22) !important` globally. At
   22% alpha that composites to 1.07-1.27:1 on any accent (and is already only
   1.13:1 on today's charcoal - a live WCAG 1.4.11 failure). !important at
   (0,0,1,1) means a plain override cannot win; repeat !important higher.    */
#navigation.app-sidebar a:focus-visible,
#navigation.app-sidebar button:focus-visible {
    outline: 3px solid var(--ui-text-inverse) !important;
    outline-offset: 2px;
}

/* ===========================================================================
   Page-header bar
   ---------------------------------------------------------------------------
   A light tint of the site colour with a 4px site-coloured top edge, and the
   title in the deep shade of the same colour (2026-09-24). Staff keep
   Birmingham / London / Manchester open side by side, so every page has to
   say which site it is at a glance - but a SOLID slab of the accent (as
   before) made every page open at full volume. The tint keeps the cue on the
   widest band of the page while the site-coloured primary buttons carry it
   into the content.

   The bar's paint, and how it joins the panel under it into one card, is in
   the Cards component. Without color-mix (see the @supports blocks in tokens.css) the tint and deep shade
   fall back to white and ink; the top edge still carries the colour.
   =========================================================================== */

/* style.css:157 sets the title white (it sat on the old solid bar). The deep
   shade is the sidebar's own field colour: on the wash it is 7.9:1
   (Manchester), 8.2:1 (Birmingham), 9.3:1 (London). */
.heading h1,
.heading h1 a {
    color: var(--ui-branch-deep, var(--ui-ink));
}


/* ===========================================================================
   Print
   ---------------------------------------------------------------------------
   The real invoice / packing-list path is already safe: js/common.js:755-814
   clones #printArea into an iframe loading only bootstrap.css and print.css,
   so neither surface exists there. This guard is for a staff Ctrl+P on a
   normal admin screen. bootstrap.css:190-197 already forces
   `* { background: transparent !important; color: #000 !important }`, so do
   NOT restate background/colour here - those would be inert. Borders are what
   bootstrap's reset misses, and the nav prints as a dead ~248px text column.
   =========================================================================== */
@media print {
    #leftPanelTd,
    #panelSpacerTd,
    #navigation.app-sidebar { display: none !important; }
}

/* ---------------------------------------------------------------------------
   Badges - semantic tier, fixed across all three branches
   ---------------------------------------------------------------------------
   These were 100% stock Bootstrap 3.2 (zero .label rules anywhere in our CSS),
   and five of six variants fail AA at 10.5px bold: warning 1.95, info 2.09,
   success 2.48, danger 3.96, default 4.48. Someone already hit this and
   hand-patched around it - bodies/registrations.php:144 carries an inline
   background-color:#204d74 on a .label-primary.

   Model reused from .exec-pill (exec-dashboard.css:547-551): dark ink on a pale
   tint of the same hue. No new hues introduced.

   "Active" must mean the same thing in Birmingham, London and Manchester, so
   these are branch-INVARIANT - never var(--ui-branch).

   (0,1,0), same as Bootstrap's, and ui-refresh.css loads last, so source order
   wins - no !important needed. Do NOT add .label{font-weight:700}:
   bootstrap.css:4445 already says bold.
   --------------------------------------------------------------------------- */
.label-success { color: var(--ui-success-ink); background: var(--ui-success-tint); }   /* 2.48 -> 6.99 */
.label-danger  { color: var(--ui-danger-ink); background: var(--ui-danger-tint); }   /* 3.96 -> 7.81 */
.label-warning { color: var(--ui-warning-ink); background: var(--ui-warning-tint); }   /* 1.95 -> 6.80 */
.label-default,
.label-primary,
.label-info    { color: var(--ui-ink-2); background: var(--ui-neutral-tint); }   /* 4.48 -> 6.27 */

/* ---------------------------------------------------------------------------
   Required fields - quiet affordance, error only after interaction
   ---------------------------------------------------------------------------
   Replaces six !important rules deleted from style.css:194-215, which painted
   every VALID required field pale yellow (#fcfcd2) and every INVALID one pink
   (#f1dddd).

   Two problems with that. The yellow was inverted feedback: it marked the
   normal state as noteworthy, so a correctly-filled form became a wall of
   yellow that said nothing. And :invalid matches an empty required field
   immediately on page LOAD - before the user has typed - so every fresh form
   opened as a wall of pink errors, which trains staff to ignore the error
   colour. bodies/size.php shipped an inline <style> undoing it; that
   workaround has been removed too.

   Semantic tier: required-ness and error state mean the same thing on all
   three branches, so nothing here uses var(--ui-branch).
   --------------------------------------------------------------------------- */

/* Required = an asterisk after the field's label, the convention staff know
   from every other form on the web. It replaced (2026-09-24) a 3px grey left
   border on the control, which nothing explained and which read as a
   rendering glitch on Add Customer.

   The label is found from the control: `label + [required]` covers the
   legacy markup (label directly before the control, including the
   .ui-field-label that Layout.FilterLabels inserts). Where the visible
   label's for= does not match the control's id (Add Customer's for="name"
   on every field), Layout.AccessibleControls slips a visually-hidden
   .ui-generated-label in between, hence the second selector. Readonly/disabled
   controls are not asked of the user, so they get no mark. The asterisk is
   the conventional dark red; it is one glyph, so it marks without shouting.
   :has() is Chrome 105+ / Safari 15.4+ / Firefox 121+; anything older keeps
   the left border instead. */
@supports selector(:has(*)) {
    label:not(.ui-generated-label):has(+ :is(input, select, textarea)[required]:not([readonly]):not([disabled]))::after,
    label:has(+ label.ui-generated-label + :is(input, select, textarea)[required]:not([readonly]):not([disabled]))::after {
        margin-left: var(--ui-space-4);
        color: var(--ui-danger-ink);
        font-weight: var(--ui-fw-bold);
        content: "*";
    }
}

@supports not selector(:has(*)) {
    input[required],
    select[required],
    textarea[required] {
        border-left-width: 3px;
        border-left-color: var(--ui-muted);
    }
}

/* :user-invalid fires only after the user has actually interacted with the
   field (or tried to submit), which is the whole point - unlike :invalid it
   does not match an untouched empty field on load.
   Baseline since late 2023 (Chrome 119 / Safari 16.5 / Firefox 88). On older
   browsers the selector simply never matches, so fields stay neutral and the
   browser's own submit-time validation still applies - it degrades to no
   styling, never to a false error. */
input[required]:user-invalid,
select[required]:user-invalid,
textarea[required]:user-invalid {
    border-color: var(--ui-danger);
    border-left-color: var(--ui-danger);
    background: var(--ui-danger-wash);
}

/* Zebra-stripe leftovers: 163 body files alternate class="warning"/class="active"
   on <tr> to fake striping (the $flg loop). :758-761 already flattens their
   RESTING colour to white, and the real stripe comes from tr:nth-child(even) -
   but bootstrap.css:2219-2225 also styles their HOVER, at (0,3,3):

       .table-hover > tbody > tr.warning:hover > td { background-color: #faf2cc }

   which outranks our (0,2,3) .table-hover > tbody > tr:hover > td. Result: the
   "warning" half of the stripe hovered YELLOW while the "active" half hovered
   neutral - i.e. alternating rows lit up differently.

   Matched at (0,3,3) so source order decides, and ui-refresh.css loads last.

   This block and the tr.warning/tr.active arms at :758-759 both exist only to
   neutralise that legacy striping. Both can be deleted once the $flg loop is
   gone from all 126 files - see UI_CLEANUP.md item 1. */
.table-hover > tbody > tr.warning:hover > td,
.table-hover > tbody > tr.active:hover > td,
.table-hover > tbody > tr.warning:hover > th,
.table-hover > tbody > tr.active:hover > th {
    background: var(--ui-neutral-tint);
}

/* Orders list: an order with a sale return (return_qty > 0, about 1 in 60).
   One flat tint whichever stripe the row lands on, and a darker left edge so
   the tint reads as deliberate rather than a striping glitch - and so the row
   is marked by shape, not by colour alone. Placed after the zebra arms and
   the (0,3,3) hover block above:
   same specificity, so source order is what makes these win. */
.table > tbody > tr.ui-row-returned > td {
    background: var(--ui-returned-tint);
}

.table > tbody > tr.ui-row-returned:hover > td {
    background: var(--ui-returned-tint-hover);
}

.table > tbody > tr.ui-row-returned > td:first-child {
    box-shadow: inset 3px 0 0 var(--ui-returned-line);
}

/* Manage Customers: an inactive customer (status I), beside its Inactive badge.
   The returned-order treatment - one flat tint over both stripes and a darker
   left edge, so the row is marked by shape as well as colour - in slate. */
.table > tbody > tr.ui-row-inactive > td {
    background: var(--ui-inactive-tint);
}

.table > tbody > tr.ui-row-inactive:hover > td {
    background: var(--ui-inactive-tint-hover);
}

.table > tbody > tr.ui-row-inactive > td:first-child {
    box-shadow: inset 3px 0 0 var(--ui-inactive-line);
}

/* Secondary text inside the page-header bar (act=activity_log shows it next
   to "User Activity"). On the white bar the muted ink is 4.9:1, so it can be
   dimmer than the title again. */
.heading h1 small,
.heading h1 .small,
.heading .text-muted {
    color: var(--ui-muted);
    font-weight: var(--ui-fw-regular);
    opacity: 1;
}

/* ---------------------------------------------------------------------------
   Long forms - titled sections and an action bar that stays in reach
   ---------------------------------------------------------------------------
   Add/Update Customer is ~25 fields; they were one ragged grid with the
   submit button below the fold. Sections group what belongs together (the
   fields, their names and their order within a group are unchanged), and the
   action bar sticks to the bottom of the window while the form is taller than
   the screen.

   .ui-form-card goes on the form's .contentdiv. .contentdiv is overflow-x:auto,
   which makes it a scroll container, and a scroll container captures
   position:sticky - the bar would stick to the card, i.e. never. The form
   has no wide table to scroll, so the card can be overflow:visible.
   --------------------------------------------------------------------------- */
.contentdiv.ui-form-card {
    overflow: visible;
}

.ui-form-section {
    margin: 0 0 var(--ui-space-8);
    padding: 0 0 var(--ui-space-6);
}

.ui-form-section + .ui-form-section {
    padding-top: var(--ui-space-18);
    border-top: 1px solid var(--ui-border);
}

.ui-form-section-title {
    margin: 0 0 var(--ui-space-14);
    color: var(--ui-ink);
    font-family: inherit;
    font-size: var(--ui-fs-md);
    font-weight: var(--ui-fw-bold);
    line-height: var(--ui-lh-snug);
}

/* Spans the form's own column (the bar lives inside the <form>, which sits
   in a centred column, not flush with the card). */
.ui-form-actions {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: var(--ui-space-10);
    margin: var(--ui-space-12) 0 0;
    padding: var(--ui-space-12) 0;
    border-top: 1px solid var(--ui-border);
    background: var(--ui-surface);
}

@media print {
    .ui-form-actions { display: none; }
}
