/* =============================================================================
   Relite Logistics — Design System : COMPONENTS                    (P1 Wave 0)
   =============================================================================
   Modern styling for the primitives every panel already uses, expressed purely
   in the tokens from tokens.css.

   RULES OF THE ROAD
   -----------------
   * Load AFTER bootstrap.css / style.css / custom.css / the member theme. These
     selectors deliberately match vendor specificity and win on source order, so
     no vendor file is ever edited and the whole layer reverts by removing one
     <link>.
   * Never reference a ramp step (--ds-brand-500) here — only semantic aliases
     (--ds-accent, --ds-text-primary). That is what makes the dark theme a
     one-line switch rather than a rewrite.
   * `!important` is avoided. Where a legacy rule outguns us we raise specificity
     instead, so panel views can still override locally.

   Covers the shared primitives: buttons, cards (Bootstrap .card AND Inspinia
   .ibox), forms, tables, badges/labels, alerts, modals, and the DevExtreme grid
   chrome — grids are the main surface of this product, so leaving them on the
   stock dx theme would undo the rest.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. Base
   -------------------------------------------------------------------------- */
body {
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-base);
    line-height: var(--ds-leading-normal);
    color: var(--ds-text-primary);
    background-color: var(--ds-bg-canvas);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* NOTE THE ABSENCE OF `color` — it is deliberate, and removing it fixed a recurring bug.
   -----------------------------------------------------------------------------
   This rule used to end `color: var(--ds-text-primary)`. Because it targets the heading
   ELEMENT, it beat the colour a heading would otherwise INHERIT from a coloured parent —
   and inheritance always loses to a direct rule, whatever the source order. Any heading
   placed on a coloured surface therefore rendered dark navy on that surface. It was fixed
   three separate times, in three places, before being fixed here:
       Phase 440  dashboard stat tiles      — figures dark on saturated tiles
       Phase 445  order-screen panel titles — "black text on coloured titles"
       Phase 456  the remaining panel headers, plus bg-info/bg-warning, which had been
                  wrong all along for the same reason
   Each fix re-asserted `color: inherit` for one more context, which is a losing game: the
   list only grows as new coloured surfaces appear.

   Dropping the declaration is safe HERE specifically, and that was verified rather than
   assumed:
     * body already sets `color: var(--ds-text-primary)`, so on any normal surface a
       heading inherits the SAME value it was being given explicitly — no visual change.
     * No vendor stylesheet in the Admin, Member or DirectUser stacks sets a heading
       colour (checked style.css, app.css and both bootstrap.css builds: zero rules), so
       nothing else takes over.
   Everything non-colour stays, so headings keep their family, weight, leading and
   tracking.

   ONE PANEL IS NOT COVERED: the guest site. Sigma's own style.css sets
   `h1..h6 { color: var(--thm-secondary) }`, and with our declaration gone THAT becomes the
   direct rule — so the same trap can still occur there. home-premium.css already handles
   the guest cases explicitly; anything new on a coloured guest surface must still set its
   own colour. Left alone on purpose: --thm-secondary is the guest theme's own heading
   colour and overriding it would restyle the public site, which is a separate decision.
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--ds-font-sans);
    font-weight: var(--ds-font-semibold);
    line-height: var(--ds-leading-tight);
    letter-spacing: var(--ds-tracking-tight);
}

h1, .h1 { font-size: var(--ds-text-2xl); }
h2, .h2 { font-size: var(--ds-text-xl); }
h3, .h3 { font-size: var(--ds-text-lg); }
h4, .h4 { font-size: var(--ds-text-md); }
h5, .h5 { font-size: var(--ds-text-base); }
h6, .h6 { font-size: var(--ds-text-sm); }

a {
    color: var(--ds-text-brand);
    text-decoration: none;
    transition: color var(--ds-transition);
}

a:hover,
a:focus {
    color: var(--ds-accent-active);
    text-decoration: none;
}

code, pre, kbd, samp { font-family: var(--ds-font-mono); }

hr { border-top-color: var(--ds-border-subtle); }

small, .small { font-size: var(--ds-text-xs); }

.text-muted { color: var(--ds-text-muted); }

/* A single visible focus ring for keyboard users across every control. Legacy
   Inspinia removed outlines outright, which left keyboard navigation invisible. */
:focus-visible {
    outline: none;
    box-shadow: var(--ds-focus-ring);
    border-radius: var(--ds-radius-sm);
}

/* -----------------------------------------------------------------------------
   2. Buttons
   -------------------------------------------------------------------------- */
.btn {
    font-family: var(--ds-font-sans);
    font-size: var(--ds-text-base);
    font-weight: var(--ds-font-medium);
    line-height: 1.4;
    padding: var(--ds-space-2) var(--ds-space-4);
    border: 1px solid transparent;
    border-radius: var(--ds-radius-sm);
    transition: background-color var(--ds-transition),
                border-color var(--ds-transition),
                box-shadow var(--ds-transition),
                color var(--ds-transition);
    cursor: pointer;
}

.btn:focus,
.btn.focus,
.btn:focus-visible {
    outline: none;
    box-shadow: var(--ds-focus-ring);
}

.btn:disabled,
.btn.disabled {
    opacity: 0.55;
    cursor: not-allowed;
    box-shadow: none;
}

/* Bootstrap ships a `.btn-*:disabled` fill for every variant at specificity (0,2,0),
   which outguns the plain `.btn-*` rules below at (0,1,0) — source order cannot save
   us. Without this block a disabled Primary rendered Bootstrap blue (#007bff) instead
   of brand green. Matching the disabled fill to the enabled one and letting the shared
   opacity above do the fading keeps every variant on-brand. */
.btn-primary:disabled,   .btn-primary.disabled   { background-color: var(--ds-accent);        border-color: var(--ds-accent);        color: var(--ds-text-on-brand); }
.btn-success:disabled,   .btn-success.disabled   { background-color: var(--ds-success-500);   border-color: var(--ds-success-500);   color: var(--ds-text-on-color); }
.btn-warning:disabled,   .btn-warning.disabled   { background-color: var(--ds-warning-500);   border-color: var(--ds-warning-500);   color: var(--ds-text-on-color); }
.btn-danger:disabled,    .btn-danger.disabled    { background-color: var(--ds-danger-500);    border-color: var(--ds-danger-500);    color: var(--ds-text-on-color); }
.btn-info:disabled,      .btn-info.disabled      { background-color: var(--ds-info-500);      border-color: var(--ds-info-500);      color: var(--ds-text-on-color); }
.btn-secondary:disabled, .btn-secondary.disabled,
.btn-white:disabled,     .btn-white.disabled,
.btn-default:disabled,   .btn-default.disabled   { background-color: var(--ds-bg-surface);    border-color: var(--ds-border-default); color: var(--ds-text-primary); }
.btn-outline-primary:disabled,
.btn-outline-primary.disabled { background-color: transparent; border-color: var(--ds-border-brand); color: var(--ds-text-brand); }

.btn-xs { font-size: var(--ds-text-xs); padding: 2px var(--ds-space-2); }
.btn-sm { font-size: var(--ds-text-sm); padding: var(--ds-space-1) var(--ds-space-3); }
.btn-lg { font-size: var(--ds-text-md); padding: var(--ds-space-3) var(--ds-space-6); }

.btn-primary {
    background-color: var(--ds-accent);
    border-color: var(--ds-accent);
    color: var(--ds-text-on-brand);
    box-shadow: var(--ds-shadow-xs);
}
.btn-primary:hover     { background-color: var(--ds-accent-hover);  border-color: var(--ds-accent-hover);  color: var(--ds-text-on-brand); }
.btn-primary:active,
.btn-primary.active,
.btn-primary:not(:disabled):not(.disabled):active {
    background-color: var(--ds-accent-active);
    border-color: var(--ds-accent-active);
    color: var(--ds-text-on-brand);
}

/* Inspinia's stock "white" button is the de-facto secondary across these panels */
.btn-white,
.btn-default,
.btn-secondary {
    background-color: var(--ds-bg-surface);
    border-color: var(--ds-border-default);
    color: var(--ds-text-primary);
    box-shadow: var(--ds-shadow-xs);
}
.btn-white:hover,
.btn-default:hover,
.btn-secondary:hover {
    background-color: var(--ds-bg-hover);
    border-color: var(--ds-border-strong);
    color: var(--ds-text-primary);
}

.btn-success { background-color: var(--ds-success-500); border-color: var(--ds-success-500); color: var(--ds-text-on-color); }
.btn-success:hover { background-color: var(--ds-success-600); border-color: var(--ds-success-600); color: var(--ds-text-on-color); }

.btn-warning { background-color: var(--ds-warning-500); border-color: var(--ds-warning-500); color: var(--ds-text-on-color); }
.btn-warning:hover { background-color: var(--ds-warning-600); border-color: var(--ds-warning-600); color: var(--ds-text-on-color); }

.btn-danger { background-color: var(--ds-danger-500); border-color: var(--ds-danger-500); color: var(--ds-text-on-color); }
.btn-danger:hover { background-color: var(--ds-danger-600); border-color: var(--ds-danger-600); color: var(--ds-text-on-color); }
.btn-danger:focus, .btn-danger:focus-visible { box-shadow: var(--ds-focus-danger); }

.btn-info { background-color: var(--ds-info-500); border-color: var(--ds-info-500); color: var(--ds-text-on-color); }
.btn-info:hover { background-color: var(--ds-info-600); border-color: var(--ds-info-600); color: var(--ds-text-on-color); }

.btn-link { color: var(--ds-text-brand); box-shadow: none; }
.btn-link:hover { color: var(--ds-accent-active); background: transparent; }

.btn-outline-primary {
    background-color: transparent;
    border-color: var(--ds-border-brand);
    color: var(--ds-text-brand);
    box-shadow: none;
}
.btn-outline-primary:hover {
    background-color: var(--ds-accent);
    border-color: var(--ds-accent);
    color: var(--ds-text-on-brand);
}

/* MUST stay scoped to `.btn.btn-rounded`. `.btn-rounded` means two different things in this app:
   Inspinia (Admin) puts it ON a button — class="btn btn-primary btn-rounded" — while the Sigma guest
   theme puts it on the BODY as a parent modifier (<body class="btn-style-1 btn-rounded ...">) that
   rounds the buttons inside it. An unscoped `.btn-rounded` therefore applied a 999px radius to <body>
   and pilled the entire home page into an ellipse, clipping the header and footer. Requiring `.btn`
   too excludes the body, which never carries that class. */
.btn.btn-rounded { border-radius: var(--ds-radius-pill); }

/* -----------------------------------------------------------------------------
   3. Surfaces — Bootstrap .card and Inspinia .ibox unified
   -------------------------------------------------------------------------- */
.card,
.ibox {
    background-color: var(--ds-bg-surface);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-lg);
    box-shadow: var(--ds-shadow-sm);
    margin-bottom: var(--ds-space-6);
    transition: box-shadow var(--ds-transition);
}

.card:hover,
.ibox:hover { box-shadow: var(--ds-shadow-md); }

/* `color` intentionally absent — same reasoning as the heading rule above. Asserting it
   here broke inheritance for anything nested on a coloured card: .ds-tile-* sets its
   colour on the CARD, and this rule then repainted the header inside it dark. On a normal
   card the header inherits --ds-text-primary from body, exactly what this used to state. */
.card-header,
.ibox-title {
    background-color: transparent;
    border-bottom: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-lg) var(--ds-radius-lg) 0 0;
    padding: var(--ds-space-4) var(--ds-space-5);
    font-weight: var(--ds-font-semibold);
    /* Inspinia draws a 2px coloured cap on .ibox-title; the token system uses a
       hairline instead so cards read as one surface rather than stacked strips. */
    border-top: none;
    min-height: 0;
}

.card-header h5, .ibox-title h5 {
    font-size: var(--ds-text-md);
    font-weight: var(--ds-font-semibold);
    margin: 0;
}

/* `color` intentionally absent — see .card-header above. */
.card-body,
.ibox-content {
    background-color: transparent;
    border: none;
    padding: var(--ds-space-5);
}

.card-footer {
    background-color: var(--ds-bg-sunken);
    border-top: 1px solid var(--ds-border-subtle);
    border-radius: 0 0 var(--ds-radius-lg) var(--ds-radius-lg);
    padding: var(--ds-space-4) var(--ds-space-5);
}

/* -----------------------------------------------------------------------------
   4. Forms
   -------------------------------------------------------------------------- */
label,
.control-label {
    font-size: var(--ds-text-sm);
    font-weight: var(--ds-font-medium);
    color: var(--ds-text-secondary);
    margin-bottom: var(--ds-space-2);
}

/* Appearance applies to EVERY size. Note what is NOT here: height and padding. */
.form-control,
.custom-select {
    font-family: var(--ds-font-sans);
    /* 16px: anything smaller makes iOS Safari zoom on focus */
    font-size: var(--ds-text-md);
    color: var(--ds-text-primary);
    background-color: var(--ds-bg-surface);
    border: 1px solid var(--ds-border-default);
    border-radius: var(--ds-radius-sm);
    box-shadow: none;
    transition: border-color var(--ds-transition), box-shadow var(--ds-transition);
}

/* -----------------------------------------------------------------------------
   Default-size METRICS only — the size variants keep the vendor's own
   -----------------------------------------------------------------------------
   `height: auto` is deliberate: it releases the fixed height Bootstrap puts on
   .form-control so that the padding below governs the control instead. But height and
   padding were previously declared in the rule above, which matches .form-control-sm and
   .form-control-lg too — and, loading after the vendor at the SAME specificity, silently
   defeated their sizing. A .form-control-sm then sized to its content and rendered 42px:
   LARGER than the default control, which is nonsense for a "small" variant, and it
   affected every panel that loads this file.
   Scoping the two METRIC properties to non-variant controls keeps the original intent for
   the default size while letting -sm/-lg size themselves. Appearance stays universal
   above, so variants still get the same font, colours, border and focus treatment — the
   16px font-size in particular still applies to every size, because the iOS-zoom problem
   it guards against is not size-specific.
   -------------------------------------------------------------------------- */
.form-control:not(.form-control-sm):not(.form-control-lg):not(.input-sm):not(.input-lg),
.custom-select:not(.custom-select-sm):not(.custom-select-lg):not(.input-sm):not(.input-lg) {
    padding: var(--ds-space-2) var(--ds-space-3);
    height: auto;
}

/* .input-sm / .input-lg are Bootstrap THREE names, and Bootstrap 4 dropped them — but they
   are NOT dead here: Inspinia's style.css deliberately aliases them, `.input-sm,
   .form-control-sm { height: 31px }`, and 34 Admin views, 3 Member and 2 DirectUser still
   use them. Excluding only the BS4 names left `height:auto` matching those elements and
   overriding the theme's alias, so a BS3-named small input measured 42px while its BS4
   twin measured 31.
   Excluded here rather than renamed in ~40 views across four panels: the theme already
   supports the alias, so a rename would be pure markup churn with real regression risk and
   no behavioural gain. */

.form-control:focus,
.custom-select:focus {
    border-color: var(--ds-accent);
    box-shadow: var(--ds-focus-ring);
    outline: none;
}

.form-control::placeholder { color: var(--ds-text-disabled); }

.form-control:disabled,
.form-control[readonly] {
    background-color: var(--ds-bg-sunken);
    color: var(--ds-text-muted);
    cursor: not-allowed;
}

.form-control.is-invalid,
.form-control.input-validation-error {
    border-color: var(--ds-danger-500);
}
.form-control.is-invalid:focus,
.form-control.input-validation-error:focus {
    box-shadow: var(--ds-focus-danger);
}

/* ASP.NET validation output */
.field-validation-error,
.invalid-feedback,
.text-danger {
    color: var(--ds-danger-600);
    font-size: var(--ds-text-xs);
}

.validation-summary-errors {
    background-color: var(--ds-danger-50);
    border: 1px solid var(--ds-danger-100);
    border-radius: var(--ds-radius-md);
    color: var(--ds-danger-700);
    padding: var(--ds-space-3) var(--ds-space-4);
    font-size: var(--ds-text-sm);
}
.validation-summary-errors ul { margin: 0; padding-left: var(--ds-space-4); }

.input-group-text {
    background-color: var(--ds-bg-sunken);
    border: 1px solid var(--ds-border-default);
    border-radius: var(--ds-radius-sm);
    color: var(--ds-text-muted);
    font-size: var(--ds-text-sm);
}

/* -----------------------------------------------------------------------------
   5. Tables
   -------------------------------------------------------------------------- */
.table {
    color: var(--ds-text-primary);
    font-size: var(--ds-text-base);
    margin-bottom: 0;
}

.table thead th {
    background-color: var(--ds-bg-sunken);
    border-bottom: 1px solid var(--ds-border-default);
    border-top: none;
    color: var(--ds-text-secondary);
    /* -sm, not -xs: uppercase + letter-spacing already costs legibility, and at -xs (12px) this was
       the one element the design system made SMALLER than Inspinia's stock 13px header. */
    font-size: var(--ds-text-sm);
    font-weight: var(--ds-font-semibold);
    letter-spacing: var(--ds-tracking-wide);
    text-transform: uppercase;
    padding: var(--ds-space-3) var(--ds-space-4);
    white-space: nowrap;
}

.table td {
    border-top: 1px solid var(--ds-border-subtle);
    padding: var(--ds-space-3) var(--ds-space-4);
    vertical-align: middle;
}

.table-hover tbody tr {
    transition: background-color var(--ds-duration-fast) var(--ds-ease);
}
.table-hover tbody tr:hover { background-color: var(--ds-bg-hover); }

.table-striped tbody tr:nth-of-type(odd) { background-color: var(--ds-bg-sunken); }

.table-bordered,
.table-bordered td,
.table-bordered th { border-color: var(--ds-border-subtle); }

/* -----------------------------------------------------------------------------
   6. Badges / labels  (Bootstrap 4 uses .badge; Inspinia markup still uses .label)
   -------------------------------------------------------------------------- */
.badge,
.label {
    display: inline-block;
    font-size: var(--ds-text-xs);
    font-weight: var(--ds-font-medium);
    line-height: 1.4;
    padding: 3px var(--ds-space-2);
    border-radius: var(--ds-radius-pill);
    letter-spacing: 0;
}

/* SOLID, high-contrast badges. The earlier soft style (X-50 bg + X-700 text) read as
   faint/near-invisible for users on some themes and repeatedly came back as "badge
   colour not showing" (Billing Type/Status, etc.). Solid fills with white text (dark
   text on the light amber warning, per WCAG) are unambiguous everywhere. Hex fallbacks
   so a missing token can't drop the background. Bootstrap 5's own .text-bg-* utilities
   are equivalent, so views using either class render the same solid badge. */
.badge-primary,   .label-primary   { background-color: var(--ds-accent, #f05050);        color: #fff; }
.badge-success,   .label-success   { background-color: var(--ds-success-600, #039855);    color: #fff; }
.badge-warning,   .label-warning   { background-color: var(--ds-warning-500, #f79009);    color: #1c1917; }
.badge-danger,    .label-danger    { background-color: var(--ds-danger-600, #d92d20);     color: #fff; }
.badge-info,      .label-info      { background-color: var(--ds-info-600, #1570cd);       color: #fff; }
.badge-secondary, .label-default   { background-color: var(--ds-neutral-500, #64748b);    color: #fff; }
.badge-light                       { background-color: var(--ds-neutral-500, #64748b);    color: #fff; }

/* -----------------------------------------------------------------------------
   7. Alerts
   -------------------------------------------------------------------------- */
.alert {
    border: 1px solid transparent;
    border-radius: var(--ds-radius-md);
    padding: var(--ds-space-3) var(--ds-space-4);
    font-size: var(--ds-text-base);
    box-shadow: var(--ds-shadow-xs);
}

.alert h5 {
    font-size: var(--ds-text-sm);
    font-weight: var(--ds-font-semibold);
    margin-bottom: var(--ds-space-1);
}

.alert-success { background-color: var(--ds-success-50); border-color: var(--ds-success-100); color: var(--ds-success-700); }
.alert-warning { background-color: var(--ds-warning-50); border-color: var(--ds-warning-100); color: var(--ds-warning-700); }
.alert-danger  { background-color: var(--ds-danger-50);  border-color: var(--ds-danger-100);  color: var(--ds-danger-700); }
.alert-info    { background-color: var(--ds-info-50);    border-color: var(--ds-info-100);    color: var(--ds-info-700); }

.alert .close { color: inherit; opacity: 0.6; text-shadow: none; }
.alert .close:hover { opacity: 1; }

/* -----------------------------------------------------------------------------
   8. Modals / dropdowns
   -------------------------------------------------------------------------- */
.modal-content {
    background-color: var(--ds-bg-raised);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-lg);
    box-shadow: var(--ds-shadow-xl);
}

.modal-header {
    border-bottom: 1px solid var(--ds-border-subtle);
    padding: var(--ds-space-4) var(--ds-space-5);
}

.modal-title { font-size: var(--ds-text-lg); font-weight: var(--ds-font-semibold); }

.modal-body   { padding: var(--ds-space-5); }
.modal-footer { border-top: 1px solid var(--ds-border-subtle); padding: var(--ds-space-4) var(--ds-space-5); }
.modal-backdrop.show { opacity: 0.5; }

.dropdown-menu {
    background-color: var(--ds-bg-raised);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-md);
    box-shadow: var(--ds-shadow-lg);
    padding: var(--ds-space-1);
    font-size: var(--ds-text-base);
    color: var(--ds-text-primary);
}

.dropdown-item {
    border-radius: var(--ds-radius-xs);
    color: var(--ds-text-primary);
    padding: var(--ds-space-2) var(--ds-space-3);
    transition: background-color var(--ds-duration-fast) var(--ds-ease);
}
.dropdown-item:hover,
.dropdown-item:focus { background-color: var(--ds-bg-hover); color: var(--ds-text-primary); }
.dropdown-item.active,
.dropdown-item:active { background-color: var(--ds-accent-subtle); color: var(--ds-text-brand); }

.dropdown-divider { border-top-color: var(--ds-border-subtle); }

/* Tooltips/popovers */
.tooltip-inner {
    background-color: var(--ds-neutral-900);
    border-radius: var(--ds-radius-sm);
    font-size: var(--ds-text-xs);
    padding: var(--ds-space-1) var(--ds-space-2);
}

.popover {
    background-color: var(--ds-bg-raised);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-md);
    box-shadow: var(--ds-shadow-lg);
}

/* -----------------------------------------------------------------------------
   9. Pagination / tabs / progress
   -------------------------------------------------------------------------- */
.page-link {
    border-color: var(--ds-border-subtle);
    color: var(--ds-text-secondary);
    font-size: var(--ds-text-sm);
}
.page-link:hover { background-color: var(--ds-bg-hover); color: var(--ds-text-primary); }
.page-item.active .page-link {
    background-color: var(--ds-accent);
    border-color: var(--ds-accent);
    color: var(--ds-text-on-brand);
}

.nav-tabs { border-bottom: 1px solid var(--ds-border-subtle); }
.nav-tabs .nav-link {
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--ds-text-secondary);
    font-weight: var(--ds-font-medium);
    padding: var(--ds-space-3) var(--ds-space-4);
    transition: color var(--ds-transition), border-color var(--ds-transition);
}
.nav-tabs .nav-link:hover { border-bottom-color: var(--ds-border-default); color: var(--ds-text-primary); }
.nav-tabs .nav-link.active {
    background-color: transparent;
    border-bottom-color: var(--ds-accent);
    color: var(--ds-text-brand);
}

.progress {
    background-color: var(--ds-bg-active);
    border-radius: var(--ds-radius-pill);
    height: 8px;
}
.progress-bar { background-color: var(--ds-accent); border-radius: var(--ds-radius-pill); }

/* -----------------------------------------------------------------------------
   10. DevExtreme grid chrome
   -------------------------------------------------------------------------- */
.dx-widget,
.dx-datagrid { font-family: var(--ds-font-sans); color: var(--ds-text-primary); }

.dx-datagrid { background-color: transparent; }

.dx-datagrid-headers {
    background-color: var(--ds-bg-sunken);
    border-bottom: 1px solid var(--ds-border-default);
    color: var(--ds-text-secondary);
    /* -sm to match .table thead th — see the note there. */
    font-size: var(--ds-text-sm);
    font-weight: var(--ds-font-semibold);
    letter-spacing: var(--ds-tracking-wide);
    text-transform: uppercase;
}

.dx-datagrid-rowsview .dx-row > td { border-bottom: 1px solid var(--ds-border-subtle); }

.dx-datagrid-rowsview .dx-row-alt > td { background-color: var(--ds-bg-sunken); }

.dx-datagrid-rowsview .dx-data-row:hover > td { background-color: var(--ds-bg-hover); }

.dx-datagrid-rowsview .dx-selection > td,
.dx-datagrid-rowsview .dx-selection:hover > td { background-color: var(--ds-accent-subtle); }

.dx-datagrid-pager { border-top: 1px solid var(--ds-border-subtle); }

.dx-button.dx-button-default {
    background-color: var(--ds-accent);
    border-color: var(--ds-accent);
    border-radius: var(--ds-radius-sm);
}

/* The grid's own "no data" text — now actually reachable on the Member panel */
.dx-datagrid-nodata {
    color: var(--ds-text-muted);
    font-size: var(--ds-text-base);
}

/* -----------------------------------------------------------------------------
   11. Utilities
   -------------------------------------------------------------------------- */
.ds-surface   { background-color: var(--ds-bg-surface); border: 1px solid var(--ds-border-subtle); border-radius: var(--ds-radius-lg); box-shadow: var(--ds-shadow-sm); }
.ds-elevated  { box-shadow: var(--ds-shadow-lg); }
.ds-muted     { color: var(--ds-text-muted); }
.ds-mono      { font-family: var(--ds-font-mono); }

/* -----------------------------------------------------------------------------
   Dashboard tiles
   -----------------------------------------------------------------------------
   Replaces Angle's fixed palette utilities (.bg-primary/.bg-purple/.bg-green/
   .bg-info/.bg-warning and their -dark pairs). Those hard-code six colours with
   `!important`, so a dashboard built from them renders the same rainbow whatever
   the panel's theme is — a red top bar over blue/purple/green/teal/orange tiles.
   They also carry no meaning: "Today Shipment" being purple says nothing.

   Two families, and the split is the point:

     ds-tile-accent / -muted   DECORATIVE. Follows the theme. Use for metrics whose
                               colour is not information (totals, pipeline stages).
                               -accent for the headline figure, -muted for the rest.

     ds-tile-success/-danger/  SEMANTIC. Fixed on purpose. Use ONLY where the colour
     -warning                  IS the information (Delivered, Un-Delivered, RTO). A
                               delivered parcel must stay green under a red theme.

   Each has a `-dark` companion for the icon half of Angle's two-tone card.
   -------------------------------------------------------------------------- */
.ds-tile-accent      { background-color: var(--ds-accent);        color: var(--ds-text-on-color); }
.ds-tile-accent-dark { background-color: var(--ds-accent-active); color: var(--ds-text-on-color); }

.ds-tile-muted       { background-color: var(--ds-bg-surface);    color: var(--ds-text-primary); }
/* bg-active, not bg-sunken: sunken (#f8fafc) is so close to the surface and to the page canvas that
   the icon half vanished and the tile read as an empty white rectangle. */
.ds-tile-muted-dark  { background-color: var(--ds-bg-active);     color: var(--ds-text-secondary); }

/* -----------------------------------------------------------------------------
   Decorative hues — the rest of the palette (Phase 410)
   -----------------------------------------------------------------------------
   Still DECORATIVE: these carry no meaning, they exist so a dashboard of eighteen
   tiles is not eighteen copies of one colour. A single --ds-accent for every tile
   reads as a monotone wall; the original Angle dashboard varied the hue per tile
   and that is what made it legible at a glance.

   These are the Angle-family colours asked for (purple #7266ba included), taken
   from tokens rather than Angle's own bg-* utilities so they stay one source.
   Unlike bg-purple etc. they are NOT !important, so a call site can still win.

   They do NOT follow --ds-accent, by design: the tile grid keeps its variety under
   every preset, while the chrome around it re-colours. Semantic tiles are a
   separate family below and are unaffected by any of this.
   -------------------------------------------------------------------------- */
.ds-tile-purple      { background-color: var(--ds-purple-500); color: var(--ds-text-on-color); }
.ds-tile-purple-dark { background-color: var(--ds-purple-700); color: var(--ds-text-on-color); }

.ds-tile-info        { background-color: var(--ds-info-500);   color: var(--ds-text-on-color); }
.ds-tile-info-dark   { background-color: var(--ds-info-700);   color: var(--ds-text-on-color); }

/* Teal is Angle's bg-green (#37bc9b), NOT --ds-success-*. Decorative money figures used it
   ("Deduction"), and a deduction is not a success — see the token note. */
.ds-tile-teal        { background-color: var(--ds-teal-500);   color: var(--ds-text-on-color); }
.ds-tile-teal-dark   { background-color: var(--ds-teal-700);   color: var(--ds-text-on-color); }

/* Cyan is Angle's bg-info (#23b7e5). Decorative, like teal — it exists so the dashboard has a
   fourth cool hue and stops repeating --ds-info-500 and --ds-purple-500 three times each.
   Never use it for a status; see the token note in tokens.css.                    Phase 596 */
.ds-tile-cyan        { background-color: var(--ds-cyan-500);   color: var(--ds-text-on-color); }
.ds-tile-cyan-dark   { background-color: var(--ds-cyan-700);   color: var(--ds-text-on-color); }

/* Blue is Angle's bg-primary (#5d9cec) as a DECORATIVE tile, deliberately separate from
   .ds-tile-accent. Use this when a single tile should be blue; use .ds-tile-accent only when
   it should follow whatever primary colour the panel's theme preset sets.        Phase 598 */
.ds-tile-blue        { background-color: var(--ds-blue-500);   color: var(--ds-text-on-color); }
.ds-tile-blue-dark   { background-color: var(--ds-blue-700);   color: var(--ds-text-on-color); }

/* A blue button that is NOT the primary action colour. Same reasoning as .ds-tile-blue:
   restyling .btn-primary would have moved every primary button in the panel.      Phase 598 */
.btn-blue {
    background-color: var(--ds-blue-500);
    border-color: var(--ds-blue-500);
    color: var(--ds-text-on-color);
}

.btn-blue:hover,
.btn-blue:focus,
.btn-blue:active,
.btn-blue.active {
    background-color: var(--ds-blue-700);
    border-color: var(--ds-blue-700);
    color: var(--ds-text-on-color);
}

.btn-blue:disabled,
.btn-blue.disabled {
    background-color: var(--ds-blue-500);
    border-color: var(--ds-blue-500);
    color: var(--ds-text-on-color);
}

/* -----------------------------------------------------------------------------
   Admin's "colored cards" vs "normal cards" switch (Phase 410)
   -----------------------------------------------------------------------------
   data-ds-cards on <html>, chosen per panel in Admin > Preferences > Appearance,
   exactly like data-ds-theme. Default is "colored": that is what the dashboard
   looked like before, so the default is not itself a restyle.

   "normal" flattens the DECORATIVE families to plain neutral surfaces. It must not
   touch the semantic ones — a Delivered tile is green because green is the
   information, and an Admin choosing calmer cards is not asking to delete meaning.
   That is why this lists the decorative classes explicitly rather than matching
   [class*="ds-tile-"].

   (0,1,1) here beats the (0,1,0) rules above without !important, so the plain
   classes stay overridable at a call site.
   -------------------------------------------------------------------------- */
[data-ds-cards="normal"] :is(.ds-tile-accent, .ds-tile-purple, .ds-tile-info, .ds-tile-teal,
                             .ds-tile-cyan, .ds-tile-blue) {
    background-color: var(--ds-bg-surface);
    color: var(--ds-text-primary);
}

[data-ds-cards="normal"] :is(.ds-tile-accent-dark, .ds-tile-purple-dark, .ds-tile-info-dark, .ds-tile-teal-dark,
                             .ds-tile-cyan-dark, .ds-tile-blue-dark) {
    background-color: var(--ds-bg-active);
    color: var(--ds-text-secondary);
}

/* Neutralised tiles need the same treatment the muted family already gets: the icon
   half and its washed-out label classes are drawn for a DARK tile, so on a light
   surface they would be white-on-white. Mirrors the .ds-tile-muted block below. */
[data-ds-cards="normal"] :is(.ds-tile-accent, .ds-tile-accent-dark, .ds-tile-purple,
                             .ds-tile-purple-dark, .ds-tile-info, .ds-tile-info-dark,
                             .ds-tile-teal, .ds-tile-teal-dark,
                             .ds-tile-cyan, .ds-tile-cyan-dark,
                             .ds-tile-blue, .ds-tile-blue-dark)
    :is(.fa, .em, .text-white-50, .text-gray-300) {
    color: var(--ds-text-muted) !important;
}

.ds-tile-success      { background-color: var(--ds-success-500); color: var(--ds-text-on-color); }
.ds-tile-success-dark { background-color: var(--ds-success-700); color: var(--ds-text-on-color); }

.ds-tile-danger       { background-color: var(--ds-danger-500);  color: var(--ds-text-on-color); }
.ds-tile-danger-dark  { background-color: var(--ds-danger-700);  color: var(--ds-text-on-color); }

.ds-tile-warning      { background-color: var(--ds-warning-500); color: var(--ds-text-on-color); }
.ds-tile-warning-dark { background-color: var(--ds-warning-700); color: var(--ds-text-on-color); }

/* A muted tile sits on a light surface, so its footer link must not be white-on-white.
   The coloured tiles keep a translucent dark footer, which works over any hue. */
.ds-tile-footer       { background-color: rgba(0, 0, 0, 0.16); color: var(--ds-text-on-color); }
.ds-tile-muted .ds-tile-footer,
.ds-tile-footer.ds-tile-footer-muted {
    background-color: var(--ds-bg-sunken);
    color: var(--ds-text-secondary);
    border-top: 1px solid var(--ds-border-subtle);
}

/* Angle drew these icons for DARK tiles: it marks them .text-white-50 (defined as
   rgba(255,255,255,0.5) !important) or .text-gray-300 (never defined at all, so it just inherits).
   Both vanish on a light muted tile — the Remittance icons rendered white-on-white. text-white-50
   carries !important, so winning it needs !important plus the descendant selector. */
.ds-tile-muted .fa,
.ds-tile-muted .em,
.ds-tile-muted-dark .fa,
.ds-tile-muted-dark .em,
.ds-tile-muted .text-white-50,
.ds-tile-muted-dark .text-white-50,
.ds-tile-muted .text-gray-300,
.ds-tile-muted-dark .text-gray-300 {
    color: var(--ds-text-muted) !important;
}

/* The tiles are markup like <div class="card border-0"><div class="ds-tile-muted-dark">icon</div>
   <div class="ds-tile-muted">figure</div></div>. Bootstrap's .border-0 is !important, so a muted
   tile had no border and dissolved into the page canvas. Restore an outline on cards that contain
   a muted half; the coloured tiles define themselves and are left alone. */
.card:has(.ds-tile-muted),
.card:has(.ds-tile-muted-dark) {
    border: 1px solid var(--ds-border-subtle) !important;
}

/* Nothing inside a tile may re-assert a foreground colour: the tile owns it.
   Two rules of this file fight the tile and both had to be neutralised here —
     * `h1..h6, .h1..h6 { color: --ds-text-primary }`  — Angle writes the stat-card figure as .h2
     * `.card-header / .card-body { color: --ds-text-primary }` — the shipment tiles wrap their
       .text-lg figure in a .card-header, so the number went dark navy on green/red/amber
   In both cases the figure — the entire point of the tile — became its least readable element.
   Anything that only inherits (plain divs, <p>) was already correct, which is exactly why the
   LABELS looked right while the NUMBERS did not.
   :is() takes its most specific argument, so this lands at (0,2,0) and outranks those (0,1,0)
   rules without !important.

   BOTH lists below must name EVERY tile hue. Phase 439 put the Member shipment cards on
   info / teal / purple and RTO Delivered on warning-dark; those were missing here, so their
   figures went dark navy on a saturated tile — the same bug this block exists to prevent,
   reintroduced by adding hues and not extending the enumeration. If a new ds-tile-* hue is
   ever added, it has to be added here too. */
:is(.ds-tile-accent, .ds-tile-accent-dark,
    .ds-tile-muted, .ds-tile-muted-dark,
    .ds-tile-success, .ds-tile-success-dark,
    .ds-tile-danger, .ds-tile-danger-dark,
    .ds-tile-warning, .ds-tile-warning-dark,
    .ds-tile-info, .ds-tile-info-dark,
    .ds-tile-purple, .ds-tile-purple-dark,
    .ds-tile-teal, .ds-tile-teal-dark,
    .ds-tile-cyan, .ds-tile-cyan-dark,
    .ds-tile-blue, .ds-tile-blue-dark)
  :is(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6,
      .card-header, .card-body, .card-title, .card-subtitle, .text-lg, p) {
    color: inherit;
}

/* Same for a tile applied directly to the .card: its header/body must not repaint the text.
   The -dark variants belong here too: the shipment RTO Delivered card is .card.ds-tile-
   warning-dark, and the remittance cards put -dark tiles on the .card itself. */
:is(.card.ds-tile-accent, .card.ds-tile-accent-dark,
    .card.ds-tile-success, .card.ds-tile-success-dark,
    .card.ds-tile-danger, .card.ds-tile-danger-dark,
    .card.ds-tile-warning, .card.ds-tile-warning-dark,
    .card.ds-tile-info, .card.ds-tile-info-dark,
    .card.ds-tile-purple, .card.ds-tile-purple-dark,
    .card.ds-tile-teal, .card.ds-tile-teal-dark,
    .card.ds-tile-cyan, .card.ds-tile-cyan-dark,
    .card.ds-tile-blue, .card.ds-tile-blue-dark,
    .card.ds-tile-muted, .card.ds-tile-muted-dark)
  :is(.card-header, .card-body) {
    background-color: transparent;
    color: inherit;
}

/* KPI figure — dashboards across all five panels show these */
.ds-metric {
    font-size: var(--ds-text-2xl);
    font-weight: var(--ds-font-bold);
    letter-spacing: var(--ds-tracking-tight);
    line-height: var(--ds-leading-tight);
    color: var(--ds-text-primary);
}
.ds-metric-label {
    font-size: var(--ds-text-xs);
    font-weight: var(--ds-font-medium);
    letter-spacing: var(--ds-tracking-wide);
    text-transform: uppercase;
    color: var(--ds-text-muted);
}

/* Skeleton placeholder for async widgets (wallet balance, aggregator lookups) */
.ds-skeleton {
    background: linear-gradient(90deg,
        var(--ds-bg-active) 25%,
        var(--ds-bg-hover) 37%,
        var(--ds-bg-active) 63%);
    background-size: 400% 100%;
    animation: ds-shimmer 1.4s ease infinite;
    border-radius: var(--ds-radius-sm);
}

@keyframes ds-shimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
    .ds-skeleton { animation: none; }
}

/* =============================================================================
   Bootstrap 5's OWN colour variables, remapped to the panel accent  (Phase 583)
   =============================================================================
   Symptom: clicking a settings card flashed a BLUE background on a red panel, and
   the same stock blue turned up on focus rings, links and checkboxes elsewhere.

   This is structural to the Bootstrap 4 -> 5 move rather than a one-page bug.
   Bootstrap 4 COMPILED its colours into each rule, so overriding the handful of
   components this design system cared about was enough. Bootstrap 5 resolves them
   at RUNTIME from --bs-* custom properties, so every component the design system
   did NOT explicitly restyle silently falls back to #0d6efd — focus rings, link
   colour, .form-check, pagination, nav-pills, progress bars, spinners, and the
   :active/:focus states of buttons that were only themed for their resting look.

   Remapping the variables fixes all of them at the source, and does so per panel:
   these read --ds-accent, which themes.css sets from the Admin-chosen preset, so
   each panel's blue becomes ITS accent rather than one hard-coded colour.

   --ds-accent-rgb exists precisely because a hex token cannot go inside rgba();
   see the note at tokens.css ~L131.
   ============================================================================= */
:root {
    --bs-primary: var(--ds-accent);
    --bs-primary-rgb: var(--ds-accent-rgb);
    --bs-link-color: var(--ds-accent);
    --bs-link-color-rgb: var(--ds-accent-rgb);
    --bs-link-hover-color: var(--ds-accent-active);
    --bs-focus-ring-color: rgba(var(--ds-accent-rgb), .25);
}

/* Component-scoped in Bootstrap 5, so :root alone does not reach them. */
.btn {
    --bs-btn-focus-shadow-rgb: var(--ds-accent-rgb);
}

.form-control,
.form-select,
.form-check-input {
    --bs-focus-ring-color: rgba(var(--ds-accent-rgb), .25);
}

.form-check-input:checked {
    background-color: var(--ds-accent);
    border-color: var(--ds-accent);
}

/* =============================================================================
   Outline button: remap its OWN state variables                    (Phase 584)
   =============================================================================
   The settings cards still flashed blue when held down, because Phase 582
   overrode `.btn-outline-primary:active { background-color: ... }` — the wrong
   layer, and it loses anyway.

   Bootstrap 5.3 gives every button a private set of custom properties and then
   applies them from GENERIC selectors:

       .btn-outline-primary { --bs-btn-active-bg: #0d6efd; ... }
       :not(.btn-check) + .btn:active,
       .btn:first-child:active,
       .btn.active { background-color: var(--bs-btn-active-bg); ... }

   Those generic selectors are (0,3,0); `.btn-outline-primary:active` is (0,2,0),
   so Bootstrap won and the stock blue came back on mousedown. Setting the
   VARIABLES feeds Bootstrap's own rules instead of fighting them, so every state
   it derives — active, hover, focus, disabled, dropdown-open, .btn-check — follows
   the panel accent with no specificity contest at all.
   ============================================================================= */
.btn-outline-primary {
    --bs-btn-color: var(--ds-text-brand);
    --bs-btn-border-color: var(--ds-border-brand);
    --bs-btn-hover-bg: var(--ds-accent);
    --bs-btn-hover-border-color: var(--ds-accent);
    --bs-btn-hover-color: var(--ds-text-on-brand, #fff);
    --bs-btn-active-bg: var(--ds-accent);
    --bs-btn-active-border-color: var(--ds-accent);
    --bs-btn-active-color: var(--ds-text-on-brand, #fff);
    --bs-btn-disabled-color: var(--ds-text-brand);
    --bs-btn-disabled-border-color: var(--ds-border-brand);
}

/* =============================================================================
   Settings tiles: centre the content                               (Phase 584)
   =============================================================================
   .filter-link is a 193px-tall inline-block, so its icon, heading and caption
   stacked from the TOP and left a growing gap underneath. site.css additionally
   pins the caption with position:absolute + translateX(-50%), which keeps it out
   of the flow and prevents any centring of the group as a whole.

   Making the tile a centred flex column and returning the caption to normal flow
   centres all three as one unit. The absolute positioning is undone explicitly
   rather than by editing site.css, which is vendor-ish and shared.
   ============================================================================= */
.filter-link {
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: .35rem;
}

    .filter-link > span {
        position: static;
        left: auto;
        transform: none;
        margin-top: 0;
        width: auto;
    }

    .filter-link h3 {
        margin-top: 0;
    }
