/* ==========================================================================
   Saraf2 design tokens
   --------------------------------------------------------------------------
   The single semantic layer for the web client. Before this file the client had
   three parallel colour systems that knew nothing about each other: Bootstrap's
   variables, the user-configurable accent, and ~172 hardcoded hex values spread
   through site.css plus 366 more inside 22 view-level <style> blocks. Measured
   before this file existed: 14 distinct border-radius values and ~10 distinct
   box-shadows all serving the same visual role. That is why the client read as
   assembled rather than designed -- not because the colours were wrong.

   Rules for this file:
     * It is the only place that introduces a new colour value.
     * It loads AFTER site.css, so its declarations win where they overlap.
     * Tokens are defined per theme; no component rule here hardcodes a colour.
     * Every text colour clears WCAG AA against every surface it can land on.
       e2e/tests/contrast.spec.ts is the gate that proves it.
   ========================================================================== */

/* --- Typography: a real Persian UI face ----------------------------------
   The client shipped with a system-font fallback, which is the single biggest
   reason it reads as dry. Vazirmatn is a Persian-first UI typeface, vendored
   locally (three weights, ~153 KB) so the client has no external dependency. */
@font-face {
  font-family: 'Vazirmatn';
  src: url('../lib/vazirmatn/Vazirmatn-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Vazirmatn';
  src: url('../lib/vazirmatn/Vazirmatn-Medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Vazirmatn';
  src: url('../lib/vazirmatn/Vazirmatn-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Type */
  --s2-font-ui: 'Vazirmatn', 'Segoe UI', Tahoma, system-ui, -apple-system, sans-serif;
  --s2-text-xs: 0.75rem;
  --s2-text-sm: 0.82rem;
  --s2-text-md: 0.92rem;
  --s2-text-lg: 1.05rem;
  --s2-text-xl: 1.3rem;
  --s2-text-2xl: 1.6rem;
  --s2-leading-tight: 1.35;
  --s2-leading-normal: 1.7;
  --s2-leading-loose: 2;

  /* Space */
  --s2-space-1: 0.25rem;
  --s2-space-2: 0.5rem;
  --s2-space-3: 0.75rem;
  --s2-space-4: 1rem;
  --s2-space-5: 1.5rem;
  --s2-space-6: 2rem;

  /* Radius -- five steps replacing fourteen ad-hoc values. Every legacy radius
     in site.css maps to the nearest step, so the migration moves no corner by
     more than ~2px while giving the whole client one scale. */
  --s2-radius-xs: 0.375rem;
  --s2-radius-sm: 0.5rem;
  --s2-radius-md: 0.75rem;
  --s2-radius-lg: 1rem;
  --s2-radius-xl: 1.25rem;
  --s2-radius-pill: 999px;

  /* Elevation: two stacked shadows (a tight contact shadow plus a soft ambient
     one) instead of a single heavy blur. That is what makes a surface read as
     layered rather than as a box with a drop shadow. */
  --s2-shadow-1: 0 1px 2px rgba(16, 24, 40, 0.04), 0 1px 3px rgba(16, 24, 40, 0.06);
  --s2-shadow-2: 0 2px 4px rgba(16, 24, 40, 0.04), 0 8px 20px rgba(16, 24, 40, 0.07);
  --s2-shadow-3: 0 4px 8px rgba(16, 24, 40, 0.05), 0 16px 32px rgba(16, 24, 40, 0.09);

  /* Motion: present but never decorative. */
  --s2-transition: 160ms ease;
}

/* --- Surfaces, text and the accent aliases, per theme --------------------
   The page is deliberately one step darker than the cards, so content lifts off
   the background instead of everything sitting on the same flat white.

   `--s2-accent-*` are aliases, not sources: the real values are derived from the
   user's chosen accent at runtime in _Layout.cshtml and written onto the root
   element. The fallbacks here keep every rule valid before that runs (or when no
   accent is configured at all). */
:root,
html[data-bs-theme='light'] {
  color-scheme: light;
  --s2-surface-0: #f5f6f8; /* page */
  --s2-surface-1: #ffffff; /* card / panel */
  --s2-surface-2: #eef0f3; /* inset well, table head */
  --s2-surface-3: #e6e8ec; /* pressed / deepest */
  --s2-text-1: #1c1f23;
  --s2-text-2: #5a6169;
  /* Tertiary ink. Only the chart canvas reads it (legend labels), which is why the 4.16:1 it
     used to sit at on white never showed up in a DOM contrast sweep: canvas pixels are not
     audited. #6b7280 keeps the step below --s2-text-2 while clearing AA at 4.9:1. */
  --s2-text-3: #6b7280;
  --s2-border-1: rgba(16, 24, 40, 0.12);
  --s2-border-2: rgba(16, 24, 40, 0.2);
  --s2-border-soft: rgba(16, 24, 40, 0.1);
  --s2-hover-toward: #000000;
  --s2-accent-text: var(--s2-accent-text-light, #6f5628);
  --s2-accent-text-strong: var(--s2-accent-text-strong-light, #6f5628);
  --s2-accent-fill: var(--s2-accent-fill-light, #6f5628);
  --s2-accent-on: var(--s2-accent-on-light, #ffffff);
  --s2-focus-ring: var(--s2-accent-text-light, #6f5628);

  /* The accent's source values and the tints derived from it. These used to be
     declared in site.css, which left the token layer reading colours it did not
     own; the runtime still writes the two source values onto <html> once an
     accent is configured, and every derived value follows. The accent is
     theme-independent -- only the tints mixed against the themed greys differ. */
  --s2-theme-accent: #2563eb;
  --s2-theme-accent-strong: #1d4ed8;
  --s2-theme-accent-soft: color-mix(in srgb, var(--s2-theme-accent) 12%, var(--bs-body-bg));
  --s2-theme-accent-border: color-mix(in srgb, var(--s2-theme-accent) 28%, var(--bs-border-color));
  /* The direction that deepens an accent fill, for the far end of an accent
     gradient. Stays towards black in both themes: the accent is a mid-tone
     surface, so its gradient end has to recede, not lift. */
  --s2-accent-deep: #000000;
}

html[data-bs-theme='dark'] {
  color-scheme: dark;
  --s2-surface-0: #16181b;
  --s2-surface-1: #1f2226;
  --s2-surface-2: #26292e;
  --s2-surface-3: #2d3136;
  --s2-text-1: #e6e8ea;
  --s2-text-2: #a9b0b7;
  --s2-text-3: #8a9199;
  --s2-border-1: rgba(255, 255, 255, 0.12);
  --s2-border-2: rgba(255, 255, 255, 0.2);
  --s2-border-soft: rgba(255, 255, 255, 0.12);
  --s2-hover-toward: #ffffff;
  --s2-accent-text: var(--s2-accent-text-dark, #b08a52);
  --s2-accent-text-strong: var(--s2-accent-text-strong-dark, #b08a52);
  --s2-accent-fill: var(--s2-accent-fill-dark, #b08a52);
  --s2-accent-on: var(--s2-accent-on-dark, #16181b);
  --s2-focus-ring: var(--s2-accent-text-dark, #b08a52);
  /* A dark surface needs a stronger accent tint before the same 12% reads as a
     tint at all, so both derived values step up in this theme. */
  --s2-theme-accent-soft: color-mix(in srgb, var(--s2-theme-accent) 24%, var(--bs-body-bg));
  --s2-theme-accent-border: color-mix(in srgb, var(--s2-theme-accent) 38%, var(--bs-border-color));
}

/* --- Semantic colour families --------------------------------------------
   Seven families, five roles each. Every value below was checked with WCAG
   contrast maths against all three surfaces before it was written down:
   `text` clears 4.5:1 on its own tint, on the card and on the page; `border`
   sits at 1.5-2.6:1 against its tint so a chip edge is visible without turning
   into a line; `strong`/`on-strong` clear 4.5:1 as a solid pair.

   This replaces a light-first palette where each page hand-wrote its own dark
   counterpart -- which is why dark mode had accumulated five different greens. */
:root,
html[data-bs-theme='light'] {
  --s2-success-text: #166534;
  --s2-success-bg: #dcfce7;
  --s2-success-border: #6ee7b7;
  --s2-success-strong: #15803d;
  --s2-success-on-strong: #ffffff;

  --s2-warning-text: #92400e;
  --s2-warning-bg: #fef3c7;
  --s2-warning-border: #f59e0b;
  --s2-warning-strong: #f59e0b;
  --s2-warning-on-strong: #16181b;

  --s2-danger-text: #b91c1c;
  --s2-danger-bg: #fee2e2;
  --s2-danger-border: #fca5a5;
  --s2-danger-strong: #dc2626;
  --s2-danger-on-strong: #ffffff;

  --s2-info-text: #075985;
  --s2-info-bg: #e0f2fe;
  --s2-info-border: #38bdf8;
  --s2-info-strong: #0369a1;
  --s2-info-on-strong: #ffffff;

  --s2-purple-text: #7e22ce;
  --s2-purple-bg: #f3e8ff;
  --s2-purple-border: #c084fc;
  --s2-purple-strong: #7e22ce;
  --s2-purple-on-strong: #ffffff;

  --s2-teal-text: #115e59;
  --s2-teal-bg: #ccfbf1;
  --s2-teal-border: #2dd4bf;
  --s2-teal-strong: #0f766e;
  --s2-teal-on-strong: #ffffff;

  /* The soft green-tinted surface at the top of a success card: a share of the
     success tint over the card. Derived, so it resolves per theme -- in dark it
     lands on a dark green-grey instead of the near-white the views used to
     hardcode, which is what made those gradients glow in dark mode. */
  --s2-success-surface: color-mix(in srgb, var(--s2-success-bg) 45%, var(--s2-surface-1));

  /* Canvas / chart palette. A 2D canvas cannot resolve var(), so scripts read
     these with `s2Tokens.get()` / `s2Tokens.chart(n)` (wwwroot/js/s2-tokens.js);
     every slot must therefore be a plain colour. Slots 1-10 are the categorical
     series ramp (in order, most used first); mint-deep is the deep stop of the
     share-card gradient, which the semantic success tokens cannot express. */
  --s2-chart-1: #0d6efd;
  --s2-chart-2: #dc3545;
  --s2-chart-3: #ffc107;
  --s2-chart-4: #0dcaf0;
  --s2-chart-5: #6610f2;
  --s2-chart-6: #198754;
  --s2-chart-7: #fd7e14;
  --s2-chart-8: #6f42c1;
  --s2-chart-9: #343a40;
  --s2-chart-10: #adb5bd;
  --s2-chart-mint-deep: #c8ecd8;

  --s2-pink-text: #be185d;
  --s2-pink-bg: #fce7f3;
  --s2-pink-border: #f9a8d4;
  --s2-pink-strong: #be185d;
  --s2-pink-on-strong: #ffffff;
}

html[data-bs-theme='dark'] {
  --s2-success-text: #86efac;
  --s2-success-bg: #14311f;
  --s2-success-border: #2c5c3f;
  --s2-success-strong: #22c55e;
  --s2-success-on-strong: #16181b;

  --s2-warning-text: #fcd34d;
  --s2-warning-bg: #3a2c0e;
  --s2-warning-border: #5f4a18;
  --s2-warning-strong: #fbbf24;
  --s2-warning-on-strong: #16181b;

  --s2-danger-text: #fca5a5;
  --s2-danger-bg: #3a1c1c;
  --s2-danger-border: #823a3a;
  --s2-danger-strong: #ef4444;
  --s2-danger-on-strong: #16181b;

  --s2-info-text: #7dd3fc;
  --s2-info-bg: #102b3f;
  --s2-info-border: #27506b;
  --s2-info-strong: #38bdf8;
  --s2-info-on-strong: #16181b;

  --s2-purple-text: #d8b4fe;
  --s2-purple-bg: #2b1b45;
  --s2-purple-border: #5a3d88;
  --s2-purple-strong: #a855f7;
  --s2-purple-on-strong: #16181b;

  --s2-teal-text: #5eead4;
  --s2-teal-bg: #0f2f2b;
  --s2-teal-border: #1f544c;
  --s2-teal-strong: #2dd4bf;
  --s2-teal-on-strong: #16181b;

  /* Canvas / chart palette, dark counterpart. The categorical ramp lifts to the
     light tints of the same hues: the light slots include #343a40, which is
     invisible on a dark surface, so the ramp cannot simply be shared. */
  --s2-chart-1: #6ea8fe;
  --s2-chart-2: #ea868f;
  --s2-chart-3: #ffda6a;
  --s2-chart-4: #6edff6;
  --s2-chart-5: #a370f7;
  --s2-chart-6: #75b798;
  --s2-chart-7: #feb272;
  --s2-chart-8: #b197fc;
  --s2-chart-9: #ced4da;
  --s2-chart-10: #adb5bd;
  --s2-chart-mint-deep: #1b4a33;

  --s2-pink-text: #f9a8d4;
  --s2-pink-bg: #3a1526;
  --s2-pink-border: #743250;
  --s2-pink-strong: #f472b6;
  --s2-pink-on-strong: #16181b;
}

/* --- Component accents: wallet metrics and transaction types --------------
   Both families used to be defined as component-local custom properties inside
   site.css, which made them the last colours the app owned outside this file.
   The values are unchanged; only the ownership moved -- and because they are
   per-theme here, the components no longer need a dark counterpart of their own. */
:root,
html[data-bs-theme='light'] {
  --s2-metric-teal-text: #0f766e;
  --s2-metric-teal-bg: #e6f7f4;
  --s2-metric-blue-text: #2563eb;
  --s2-metric-blue-bg: #eaf1ff;
  --s2-metric-amber-text: #8a4b00;
  --s2-metric-amber-bg: #fff4df;
  --s2-metric-purple-text: #6d28d9;
  --s2-metric-purple-bg: #f1ebff;

  --s2-txn-buy-text: var(--s2-success-text);
  --s2-txn-buy-bg: #e3f6eb;
  --s2-txn-sell-text: #9a3412;
  --s2-txn-sell-bg: #fff0e8;
  --s2-txn-exchange-text: #075985;
  --s2-txn-exchange-bg: #e4f4ff;
  --s2-txn-transfer-text: #5b21b6;
  --s2-txn-transfer-bg: #f0ebff;
  --s2-txn-request-text: #8a4b00;
  --s2-txn-request-bg: #fff3dc;
}

html[data-bs-theme='dark'] {
  /* The metric text colours are kept as they are: they sit on their own tint
     chip rather than on the page, so only the tints have to darken. */
  --s2-metric-teal-bg: #163f3a;
  --s2-metric-blue-bg: #1e3a66;
  --s2-metric-amber-bg: #4d3a18;
  --s2-metric-purple-bg: #39275e;
  /* The ink has to move with the tint: the light values are dark colours (#0f766e, #2563eb,
     #8a4b00, #6d28d9) and disappear on these backgrounds -- the icon measured 1.7-2.9:1.
     Aliased to the matching family so the hue stays one source. */
  --s2-metric-teal-text: var(--s2-teal-text);
  --s2-metric-blue-text: var(--s2-info-text);
  --s2-metric-amber-text: var(--s2-warning-text);
  --s2-metric-purple-text: #c9b4ff;

  /* Transaction types flip completely: on a dark page the label has to be the
     light element and the tint the dark one. */
  --s2-txn-buy-text: #8ce5b3;
  --s2-txn-buy-bg: #1a4734;
  --s2-txn-sell-text: #ffc0a3;
  --s2-txn-sell-bg: #512b1e;
  --s2-txn-exchange-text: #8fd3ff;
  --s2-txn-exchange-bg: #183d5c;
  --s2-txn-transfer-text: #c9b4ff;
  --s2-txn-transfer-bg: #3a285e;
  --s2-txn-request-text: #ffd188;
  --s2-txn-request-bg: #4f3b1b;
}

/* --- Map the tokens onto Bootstrap ---------------------------------------
   Bootstrap's own components (cards, tables, form controls, dropdowns) then
   follow the token layer without any per-component work.

   Deliberately NOT remapped: `--bs-<family>-rgb`. Bootstrap uses that one value
   for `.text-*` AND for the solid `.btn-*` background, while hardcoding black
   text on `.btn-warning` -- so pointing it at a readable text colour would put
   black text on a dark amber button. The text utilities are redirected
   explicitly below instead, and the solid buttons get real variants. */
:root {
  --bs-body-font-family: var(--s2-font-ui);
  --bs-border-radius-sm: var(--s2-radius-sm);
  --bs-border-radius: var(--s2-radius-md);
  --bs-border-radius-lg: var(--s2-radius-lg);
  --bs-border-radius-xl: var(--s2-radius-xl);
  --bs-border-radius-xxl: var(--s2-radius-xl);
  --bs-border-radius-pill: var(--s2-radius-pill);
  --bs-secondary-color: var(--s2-text-2);
  --bs-emphasis-color: var(--s2-text-1);
  --bs-body-font-size: var(--s2-text-md);
  --bs-body-line-height: var(--s2-leading-normal);
}

/* Bootstrap 5.3's subtle/emphasis system is the supported hook behind alerts,
   badges and `.text-bg-*-subtle`, and it splits roles exactly the way this token
   set already does. Wiring it here fixes every `.alert-*` (129 uses across 31
   views) in both themes at once, rather than one hand-written override per view. */
:root,
html[data-bs-theme='light'] {
  --bs-success-text-emphasis: var(--s2-success-text);
  --bs-success-bg-subtle: var(--s2-success-bg);
  --bs-success-border-subtle: var(--s2-success-border);
  --bs-warning-text-emphasis: var(--s2-warning-text);
  --bs-warning-bg-subtle: var(--s2-warning-bg);
  --bs-warning-border-subtle: var(--s2-warning-border);
  --bs-danger-text-emphasis: var(--s2-danger-text);
  --bs-danger-bg-subtle: var(--s2-danger-bg);
  --bs-danger-border-subtle: var(--s2-danger-border);
  --bs-info-text-emphasis: var(--s2-info-text);
  --bs-info-bg-subtle: var(--s2-info-bg);
  --bs-info-border-subtle: var(--s2-info-border);
  --bs-primary-text-emphasis: var(--s2-accent-text-light, #6f5628);
  --bs-primary-bg-subtle: var(--s2-theme-accent-soft);
  --bs-primary-border-subtle: var(--s2-theme-accent-border, var(--s2-border-1));
}

/* Bootstrap's code colour is tuned for body copy on a white page; on this app's
   layered greys the light value measures 4.02:1, so light alone darkens it to the
   nearest value that clears AA (4.74:1) while staying recognisably pink. Dark keeps
   Bootstrap's own #e685b5, which is already AA on these surfaces. */
html[data-bs-theme='light'] {
  --bs-code-color: #c03179;
}

html[data-bs-theme='dark'] {
  --bs-success-text-emphasis: var(--s2-success-text);
  --bs-success-bg-subtle: var(--s2-success-bg);
  --bs-success-border-subtle: var(--s2-success-border);
  --bs-warning-text-emphasis: var(--s2-warning-text);
  --bs-warning-bg-subtle: var(--s2-warning-bg);
  --bs-warning-border-subtle: var(--s2-warning-border);
  --bs-danger-text-emphasis: var(--s2-danger-text);
  --bs-danger-bg-subtle: var(--s2-danger-bg);
  --bs-danger-border-subtle: var(--s2-danger-border);
  --bs-info-text-emphasis: var(--s2-info-text);
  --bs-info-bg-subtle: var(--s2-info-bg);
  --bs-info-border-subtle: var(--s2-info-border);
  --bs-primary-text-emphasis: var(--s2-accent-text-dark, #b08a52);
  --bs-primary-bg-subtle: var(--s2-theme-accent-soft);
  --bs-primary-border-subtle: var(--s2-theme-accent-border, var(--s2-border-1));
}

/* Bootstrap's `.text-*` utilities paint from the raw `--bs-<family>-rgb` -- the
   palette that scored 1.63:1 in the contrast audit. They are redirected to the
   emphasis tokens. Text only, so solid buttons are untouched. */
.text-success { color: var(--s2-success-text) !important; }
.text-warning { color: var(--s2-warning-text) !important; }
.text-danger  { color: var(--s2-danger-text)  !important; }
.text-info    { color: var(--s2-info-text)    !important; }
.text-primary { color: var(--s2-accent-text-light, #6f5628) !important; }

html[data-bs-theme='dark'] .text-primary { color: var(--s2-accent-text-dark, #b08a52) !important; }

/* Bootstrap paints bare links from the same raw `--bs-link-color-rgb` (#0d6efd):
   measured 4.16:1 on the footer strip in light theme, under AA, and the blue tells
   the user nothing about this app's accent. Redirect the paint to the accent ink
   tokens the way `.text-*` is redirected above; Bootstrap's own link variants
   (.btn, .nav-link, .dropdown-item, .page-link, .list-group-item, .navbar-brand,
   .alert-link) are excluded so they keep their designed colours. */
a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.list-group-item):not(.navbar-brand):not(.alert-link) {
  color: var(--s2-accent-text);
}

a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.list-group-item):not(.navbar-brand):not(.alert-link):hover,
a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.list-group-item):not(.navbar-brand):not(.alert-link):focus {
  color: var(--s2-accent-text-strong);
}

/* --- Page surface --------------------------------------------------------
   The themed selectors are not decoration: site.css carries its own
   `html[data-bs-theme="dark"] body { background-color:#212529 }`, which outranks
   a bare `body` selector. Matching that specificity here (and loading later) is
   what lets the token layer own the page surface while site.css keeps its other
   dark rules. */
body,
html[data-bs-theme='light'] body,
html[data-bs-theme='dark'] body {
  background-color: var(--s2-surface-0);
  color: var(--s2-text-1);
  font-family: var(--s2-font-ui);
  -webkit-font-smoothing: antialiased;
}

/* --- Panels --------------------------------------------------------------
   Every page had grown its own panel class, so the same visual object carried
   41 different names and fourteen different corner radii. Rather than editing
   41 rules, one grouped rule normalises them all. The selector list was
   generated from site.css, and this file stays the single place that owns how a
   panel looks. */
.s2-page-header,
.s2-surface-form,
.ops-summary-card,
.ops-check-card,
.ops-history,
.currency-form-card,
.currency-list-card,
.customer-profile-form,
.customer-form-subsection,
.customer-form-readonly-note,
.wallet-dashboard-stat,
.wallets-index-hero,
.wallets-index-summary,
.wallets-index-stat,
.wallets-currency-panel,
.currency-index-stat,
.currency-dashboard-rates,
.currency-dashboard-stat,
.currency-dashboard-panel,
.transactions-hub-card,
.transactions-hub-card-icon,
.viewer-portal-hero,
.viewer-balance-card,
.viewer-content-card,
.viewer-table-shell,
.viewer-portal-request-form,
.portal-queue-filter,
.portal-queue-card,
.home-dashboard-hero,
.home-dashboard-panel,
.home-dashboard-section,
.customers-index-hero,
.customers-index-filter-panel,
.customers-index-table-panel,
.customers-index-card,
.customers-index-stat,
.customers-index-dashboard-btn,
.home-dashboard-role,
.home-stat-card,
.home-quick-action,
.home-attention-item,
.financial-operation-header,
.financial-operation-actions,
.auth-shell,
.management-dashboard-hero,
.management-dashboard-range,
.management-kpi,
.management-panel,
.management-report-hub,
.management-report-icon,
.data-quality-hero,
.data-quality-checked-at,
.data-quality-card {
  border: 1px solid var(--s2-border-soft);
  border-radius: var(--s2-radius-lg);
  box-shadow: var(--s2-shadow-1);
}

/* Bigger surfaces take the larger radius and the deeper shadow, so the corner
   reads as a family rather than a uniform stamp. */
.s2-page-header,
.ops-history,
.wallets-currency-panel,
.currency-dashboard-panel,
.customers-index-table-panel,
.customers-index-filter-panel,
.viewer-content-card,
.viewer-table-shell,
.home-dashboard-hero,
.home-dashboard-panel,
.home-dashboard-section,
.management-panel,
.management-dashboard-hero,
.data-quality-card {
  border-radius: var(--s2-radius-xl);
  box-shadow: var(--s2-shadow-2);
}

/* --- Generic cards -------------------------------------------------------
   The group above normalises the named panel classes page by page, but the plain
   Bootstrap `.card` -- the object every remaining page builds its blocks from --
   was still painted by site.css with the old look: a hard accent border and the
   smallest radius in the scale. Driving Bootstrap's own card variables from the
   token layer is what generalises the dashboard's treatment to every page at
   once, so no page can drift back to the previous shape.

   Border width is 0 on purpose: a surface is separated from the page by the
   layered shadow, not by an outline. `--bs-card-bg` is pinned because
   Bootstrap's default (`--bs-body-bg`) is the dark theme's own page colour. */
:root {
  --bs-card-border-width: 0;
  --bs-card-border-color: var(--s2-border-soft);
  --bs-card-border-radius: var(--s2-radius-lg);
  --bs-card-inner-border-radius: calc(var(--s2-radius-lg) - 1px);
  --bs-card-box-shadow: var(--s2-shadow-1);
  --bs-card-bg: var(--s2-surface-1);
}

/* The header's gradient has to stop at the inner radius, or it overshoots the
   card's own corner now that the radius grew. */
.card-header {
  border-top-left-radius: var(--bs-card-inner-border-radius);
  border-top-right-radius: var(--bs-card-inner-border-radius);
}

/* --- Edge-flush table shells ---------------------------------------------
   A panel's table wrapper reaches the panel's left, right and bottom edges, and
   it carries its own outline at the shell radius (6px) while the panel's corner
   is 16/20px. The result is two nested outlines with different radii: at the
   bottom the inner line hugs the panel's curve instead of matching it, which
   reads as one broken corner. The surface already draws that boundary, so where
   the shell is flush the surface keeps the outline and the shell contributes
   only content -- its own line and corner radius are dropped. `:is()` is used
   so the child side does not have to be repeated for every parent. */
:is(
  .s2-page-header,
  .s2-surface-form,
  .ops-summary-card,
  .ops-check-card,
  .ops-history,
  .currency-form-card,
  .currency-list-card,
  .customer-profile-form,
  .customer-form-subsection,
  .customer-form-readonly-note,
  .wallet-dashboard-stat,
  .wallets-index-hero,
  .wallets-index-summary,
  .wallets-index-stat,
  .wallets-currency-panel,
  .currency-index-stat,
  .currency-dashboard-rates,
  .currency-dashboard-stat,
  .currency-dashboard-panel,
  .transactions-hub-card,
  .transactions-hub-card-icon,
  .viewer-portal-hero,
  .viewer-balance-card,
  .viewer-content-card,
  .viewer-table-shell,
  .viewer-portal-request-form,
  .portal-queue-filter,
  .portal-queue-card,
  .home-dashboard-hero,
  .home-dashboard-panel,
  .home-dashboard-section,
  .customers-index-hero,
  .customers-index-filter-panel,
  .customers-index-table-panel,
  .customers-index-card,
  .customers-index-stat,
  .customers-index-dashboard-btn,
  .home-dashboard-role,
  .home-stat-card,
  .home-quick-action,
  .home-attention-item,
  .financial-operation-header,
  .financial-operation-actions,
  .auth-shell,
  .management-dashboard-hero,
  .management-dashboard-range,
  .management-kpi,
  .management-panel,
  .management-report-hub,
  .management-report-icon,
  .data-quality-hero,
  .data-quality-checked-at,
  .data-quality-card,
  .card
) > :is(.s2-table-shell, .table-responsive):not(.viewer-table-shell):last-child {
  border: 0;
  border-radius: 0;
}

/* Interactive panels lift on hover; static ones must not, or the interface lies
   about what is clickable. */
.home-stat-card,
.home-quick-action,
.customers-index-card,
.management-kpi,
.management-report-hub {
  transition: box-shadow var(--s2-transition), transform var(--s2-transition);
}

.home-stat-card:hover,
.home-quick-action:hover,
.customers-index-card:hover,
.management-kpi:hover,
.management-report-hub:hover {
  box-shadow: var(--s2-shadow-2);
  transform: translateY(-2px);
}

/* Panel typography: the label/kicker pair that repeats on every dashboard. */
.home-stat-label,
.home-dashboard-kicker,
.management-kpi-label {
  color: var(--s2-text-2);
  font-size: var(--s2-text-sm);
  letter-spacing: 0.01em;
}

.home-stat-value,
.management-kpi-value {
  font-size: var(--s2-text-xl);
  line-height: var(--s2-leading-tight);
}

/* --- Buttons -------------------------------------------------------------
   The client had no button system: stock Bootstrap plus a handful of outline
   overrides. `.btn-primary` alone appears 81 times across 34 views, so its look
   is most of what the app feels like.

   The solid variants are driven entirely through Bootstrap 5.3's own `--bs-btn-*`
   hooks -- no markup changes and no `!important`. The `.btn-primary` fill is
   derived from the user's accent at runtime (see _Layout.cshtml): the accent is a
   preference, so it cannot be baked in, and the text colour on it has to be
   computed rather than assumed. */
.btn {
  --s2-btn-radius: var(--s2-radius-md);
  border-radius: var(--s2-btn-radius);
  font-weight: 500;
  letter-spacing: 0;
  transition: background-color var(--s2-transition), border-color var(--s2-transition),
    box-shadow var(--s2-transition), color var(--s2-transition);
}

.btn:focus-visible {
  outline: 2px solid var(--s2-focus-ring);
  outline-offset: 2px;
  box-shadow: none;
}

.btn-sm { --s2-btn-radius: var(--s2-radius-sm); }
.btn-lg { --s2-btn-radius: var(--s2-radius-lg); }

/* Primary: the brand accent, filled and readable whatever accent is chosen. */
.btn-primary {
  --bs-btn-bg: var(--s2-accent-fill);
  --bs-btn-border-color: var(--s2-accent-fill);
  --bs-btn-color: var(--s2-accent-on);
  --bs-btn-hover-bg: color-mix(in srgb, var(--s2-accent-fill) 88%, var(--s2-hover-toward));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--s2-accent-fill) 88%, var(--s2-hover-toward));
  --bs-btn-hover-color: var(--s2-accent-on);
  --bs-btn-active-bg: color-mix(in srgb, var(--s2-accent-fill) 78%, var(--s2-hover-toward));
  --bs-btn-active-border-color: color-mix(in srgb, var(--s2-accent-fill) 78%, var(--s2-hover-toward));
  --bs-btn-active-color: var(--s2-accent-on);
  --bs-btn-disabled-bg: color-mix(in srgb, var(--s2-accent-fill) 55%, var(--s2-surface-1));
  --bs-btn-disabled-border-color: transparent;
  --bs-btn-disabled-color: var(--s2-accent-on);
}

/* Semantic solids. Bootstrap's own `.btn-warning` hardcodes black text on a
   yellow fill; these carry an explicit `on-strong` that was contrast-checked. */
.btn-success {
  --bs-btn-bg: var(--s2-success-strong);
  --bs-btn-border-color: var(--s2-success-strong);
  --bs-btn-color: var(--s2-success-on-strong);
  --bs-btn-hover-bg: color-mix(in srgb, var(--s2-success-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--s2-success-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-color: var(--s2-success-on-strong);
  --bs-btn-active-bg: color-mix(in srgb, var(--s2-success-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-border-color: color-mix(in srgb, var(--s2-success-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-color: var(--s2-success-on-strong);
}

.btn-danger {
  --bs-btn-bg: var(--s2-danger-strong);
  --bs-btn-border-color: var(--s2-danger-strong);
  --bs-btn-color: var(--s2-danger-on-strong);
  --bs-btn-hover-bg: color-mix(in srgb, var(--s2-danger-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--s2-danger-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-color: var(--s2-danger-on-strong);
  --bs-btn-active-bg: color-mix(in srgb, var(--s2-danger-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-border-color: color-mix(in srgb, var(--s2-danger-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-color: var(--s2-danger-on-strong);
}

.btn-info {
  --bs-btn-bg: var(--s2-info-strong);
  --bs-btn-border-color: var(--s2-info-strong);
  --bs-btn-color: var(--s2-info-on-strong);
  --bs-btn-hover-bg: color-mix(in srgb, var(--s2-info-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--s2-info-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-color: var(--s2-info-on-strong);
  --bs-btn-active-bg: color-mix(in srgb, var(--s2-info-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-border-color: color-mix(in srgb, var(--s2-info-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-color: var(--s2-info-on-strong);
}

.btn-warning {
  --bs-btn-bg: var(--s2-warning-strong);
  --bs-btn-border-color: var(--s2-warning-strong);
  --bs-btn-color: var(--s2-warning-on-strong);
  --bs-btn-hover-bg: color-mix(in srgb, var(--s2-warning-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--s2-warning-strong) 88%, var(--s2-hover-toward));
  --bs-btn-hover-color: var(--s2-warning-on-strong);
  --bs-btn-active-bg: color-mix(in srgb, var(--s2-warning-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-border-color: color-mix(in srgb, var(--s2-warning-strong) 78%, var(--s2-hover-toward));
  --bs-btn-active-color: var(--s2-warning-on-strong);
}

/* Soft variants: a tinted fill with the family text colour. These need new
   markup, so they are offered rather than applied -- the recommended choice for
   secondary actions INSIDE a panel, where a row of outlined buttons competes
   with the panel's own border. */
.btn-soft-primary,
.btn-soft-success,
.btn-soft-danger,
.btn-soft-warning,
.btn-soft-info {
  border: 1px solid transparent;
}

.btn-soft-primary { background-color: var(--s2-theme-accent-soft); color: var(--s2-accent-text); }
.btn-soft-success { background-color: var(--s2-success-bg); color: var(--s2-success-text); }
.btn-soft-danger  { background-color: var(--s2-danger-bg);  color: var(--s2-danger-text); }
.btn-soft-warning { background-color: var(--s2-warning-bg); color: var(--s2-warning-text); }
.btn-soft-info    { background-color: var(--s2-info-bg);    color: var(--s2-info-text); }

.btn-soft-primary:hover { background-color: color-mix(in srgb, var(--s2-theme-accent) 22%, var(--s2-surface-1)); }
.btn-soft-success:hover { background-color: var(--s2-success-border); }
.btn-soft-danger:hover  { background-color: var(--s2-danger-border); }
.btn-soft-warning:hover { background-color: var(--s2-warning-border); }
.btn-soft-info:hover    { background-color: var(--s2-info-border); }

/* ── Page header ───────────────────────────────────────────────────────────────────────────
   One shape for every page in the client. wwwroot/js/s2-page-shell.js marks the element that
   carries the page title as .s2-page-header (creating one when a page has no header band), marks
   the title .s2-page-title and gathers the page's action buttons into .s2-page-header-actions.
   The selectors start with `html` on purpose: page-local rules like
   `html[data-bs-theme="light"] .some-hero h2` otherwise outrank a plain class rule and the pages
   drift apart again -- that is how five different header shapes accumulated. */
html .s2-page-header {
  align-items: center;
  background:linear-gradient(135deg, var(--s2-theme-accent) 0%, var(--s2-theme-accent-strong) 100%);
  border: 0;
  border-radius: var(--s2-radius-lg);
  box-shadow: var(--s2-shadow-1);
  color: var(--s2-accent-on);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: 1.25rem;
  padding: 1.4rem 1.5rem;
}

html .s2-page-header .s2-page-title {
  background: none;
  border: 0;
  box-shadow: none;
  color: var(--s2-accent-on);
  display: block;
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.45;
  margin: 0;
  min-height: 0;
  padding: 0;
}

/* Secondary text that sits on the accent panel follows the panel's ink; badges and buttons keep
   their own colours so their semantics survive. */
html .s2-page-header :is(p, small, .text-muted, .text-body-secondary, [class*="kicker"], [class*="subtitle"], [class*="eyebrow"], [class*="description"], [class*="note"]) {
  color:color-mix(in srgb, var(--s2-accent-on) 82%, transparent);
}

/* Buttons and badges keep their own colours: forcing the panel's ink onto them painted light
   buttons white-on-white (the portal-requests page measured 1.05:1). Only plain text follows the
   panel. */

html .s2-page-header-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  justify-content: flex-end;
}

html .s2-page-header-actions .btn { border-radius: var(--s2-radius-xs); }

/* Respect a user who has asked the system for less motion. */
@media (prefers-reduced-motion: reduce) {
  .btn,
  .home-stat-card,
  .home-quick-action,
  .customers-index-card,
  .management-kpi,
  .management-report-hub {
    transition: none;
  }

  .home-stat-card:hover,
  .home-quick-action:hover,
  .customers-index-card:hover,
  .management-kpi:hover,
  .management-report-hub:hover {
    transform: none;
  }
}
