/*
  Venturly design tokens + Bootstrap theme layer  (PRO-73, Phase 8.1 — UI modernization foundation).

  Loaded AFTER bootstrap.min.css and BEFORE site.css. Purely visual: no markup, routing or
  behaviour change. Themes key on the app's existing `data-bs-theme` mechanism (resolved pre-paint
  by the inline script in _Layout and toggled by wwwroot/js/site.js) — never prefers-color-scheme.

  Token values are the user-locked design direction (mockup on epic PRO-72). The sidebar app-shell
  (--side-*) tokens are defined now for Phase 8.2 but are unused by this slice.
*/

/* ============================================================
   1. Design tokens — base + LIGHT
   ============================================================ */
:root,
[data-bs-theme="light"] {
    /* Neutrals — cool, faint teal bias */
    --ground: #F6F8F8;
    --surface: #FFFFFF;
    --surface-2: #F0F4F3;
    --ink: #0E1D1C;
    --muted: #5A6B69;
    --faint: #8A9997;
    /* PRO-198: raised off the white surface — the old #E2E9E8 / #EDF1F0 were almost invisible on
       the invoice list and the Create line-item table. The colour does the work; thickness unchanged. */
    --line: #C6D0CE;
    --line-soft: #D7DFDD;

    /* Brand accent — jewel teal */
    --accent: #0E9384;
    --accent-strong: #0A6E64;
    --accent-ink: #FFFFFF;
    --accent-soft: #E4F3F0;

    /* Dark sidebar shell — defined now for Phase 8.2, unused in this slice */
    --side-bg: #0C1A19;
    --side-panel: #10201F;
    --side-ink: #B7C7C5;
    --side-ink-strong: #FFFFFF;
    --side-active: rgba(45, 212, 191, .14);
    --side-accent: #2DD4BF;
    --side-line: rgba(255, 255, 255, .08);

    /* Semantic status — separate from the accent hue */
    --paid: #0E8F63;   --paid-soft: #E1F4EB;
    --sent: #A96410;   --sent-soft: #FBEDD8;
    --draft: #5E7079;  --draft-soft: #ECF0F2;
    --void: #BC443C;   --void-soft: #FAE8E6;
    /* PRO-109: partly paid. Violet, deliberately outside the paid/sent/draft/void hue family — a partial
       payment is a state of its own, not a shade of "owed". No -rgb companion: those exist only to feed
       Bootstrap's --bs-* alpha compositing below, and this maps to no Bootstrap semantic. */
    --partial: #6D5BD0; --partial-soft: #ECE9FA;
    /* PRO-95: overpaid / in credit. Blue, again outside the paid/sent/draft/void family — a surplus is
       not a shade of "settled". It used to render as status-paid green, which said "nothing to do" to
       an owner carrying a liability and to a customer owed money. No -rgb companion, same reason as
       --partial. */
    --credit: #2563C9; --credit-soft: #E3ECFB;

    /* RGB companions — Bootstrap needs -rgb triplets for alpha compositing (badges, tints, tables) */
    --accent-rgb: 14, 147, 132;
    --paid-rgb: 14, 143, 99;
    --sent-rgb: 169, 100, 16;
    --draft-rgb: 94, 112, 121;
    --void-rgb: 188, 68, 60;
    --ground-rgb: 246, 248, 248;
    --ink-rgb: 14, 29, 28;

    /* Elevation + shape */
    --shadow: 0 1px 2px rgba(16, 33, 32, .04), 0 8px 24px -12px rgba(16, 33, 32, .12);
    --shadow-lg: 0 2px 6px rgba(16, 33, 32, .06), 0 24px 48px -20px rgba(16, 33, 32, .22);
    --radius: 12px;
    --radius-sm: 8px;

    /* Type stacks */
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas, monospace;

    /* Type scale — tokens for later slices (headings are left untouched in 8.1) */
    --text-xs: .75rem;
    --text-sm: .875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.375rem;
    --text-2xl: 1.75rem;
    --leading-tight: 1.25;
    --leading-normal: 1.5;
}

/* ============================================================
   2. Design tokens — DARK overrides
   ============================================================ */
[data-bs-theme="dark"] {
    --ground: #0A1211;
    --surface: #0F1817;
    --surface-2: #131E1D;
    --ink: #E6EDEC;
    --muted: #8DA19F;
    --faint: #62716F;
    --line: #1E2B2A;
    --line-soft: #182322;

    --accent: #2DD4BF;
    --accent-strong: #5EEAD4;
    --accent-ink: #04211E;
    --accent-soft: #123430;

    --side-bg: #08110F;
    --side-panel: #0C1615;

    --paid: #34D399;   --paid-soft: #102C22;
    --sent: #E0A44F;   --sent-soft: #2E2410;
    --draft: #93A4AC;  --draft-soft: #1A2529;
    --void: #F0776C;   --void-soft: #2F1A18;
    /* PRO-109 — the dark counterpart. Both blocks must define this pair: a token declared only in the
       light block resolves to nothing here, and the badge silently renders as unstyled text. */
    --partial: #A99BF0; --partial-soft: #241F3D;
    /* PRO-95 — the dark counterpart, for the reason stated one block up: declared only in light, this
       resolves to nothing here and the badge renders as unstyled text IN DARK MODE ONLY, which the
       stylesheet guard cannot see because it matches the light rule and passes. */
    --credit: #7BA7F0; --credit-soft: #16233B;

    --accent-rgb: 45, 212, 191;
    --paid-rgb: 52, 211, 153;
    --sent-rgb: 224, 164, 79;
    --draft-rgb: 147, 164, 172;
    --void-rgb: 240, 119, 108;
    --ground-rgb: 10, 18, 17;
    --ink-rgb: 230, 237, 236;

    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px -12px rgba(0, 0, 0, .6);
    --shadow-lg: 0 2px 6px rgba(0, 0, 0, .5), 0 24px 48px -20px rgba(0, 0, 0, .7);
}

/* ============================================================
   2b. Design tokens — DIM (PRO-199)
   A less-bright LIGHT theme: soft off-white surfaces, same dark ink — for users who find Light's
   stark white too bright but don't want Dark. `:root` already feeds this element the full §1 light
   palette and the §3 Bootstrap mapping; this block only dims the neutrals (surfaces/ink/lines) and
   their two -rgb companions, leaving accent, status, shadow, type and radius to inherit from light.
   color-scheme stays light so native controls/scrollbars render light. Not an Auto target — manual only.
   ============================================================ */
[data-bs-theme="dim"] {
    --ground: #E7ECEB;
    --surface: #F1F4F3;
    --surface-2: #E3E9E8;
    --ink: #17302D;
    --muted: #556664;
    --faint: #879694;
    --line: #B4C0BD;
    --line-soft: #C8D2D0;

    --ground-rgb: 231, 236, 235;
    --ink-rgb: 23, 48, 45;

    color-scheme: light;
}

/* ============================================================
   3. Map tokens -> Bootstrap variables
   The selector list keeps equal specificity with Bootstrap's own light/dark blocks; theme.css
   loads after bootstrap.min.css, so source order wins. The var()s resolve against whichever
   theme's tokens are active, so a single mapping block covers both themes.
   ============================================================ */
:root,
[data-bs-theme="dark"] {
    /* Surfaces + ink */
    --bs-body-bg: var(--ground);
    --bs-body-bg-rgb: var(--ground-rgb);
    --bs-body-color: var(--ink);
    --bs-body-color-rgb: var(--ink-rgb);
    --bs-emphasis-color: var(--ink);
    --bs-emphasis-color-rgb: var(--ink-rgb);
    --bs-secondary-color: var(--muted);
    --bs-tertiary-color: var(--faint);
    --bs-secondary-bg: var(--surface-2);
    --bs-tertiary-bg: var(--surface-2);
    --bs-border-color: var(--line);

    /* Brand + semantic scale */
    --bs-primary: var(--accent);
    --bs-primary-rgb: var(--accent-rgb);
    --bs-success: var(--paid);
    --bs-success-rgb: var(--paid-rgb);
    --bs-danger: var(--void);
    --bs-danger-rgb: var(--void-rgb);
    --bs-warning: var(--sent);
    --bs-warning-rgb: var(--sent-rgb);
    --bs-secondary: var(--draft);
    --bs-secondary-rgb: var(--draft-rgb);

    /* Links */
    --bs-link-color: var(--accent);
    --bs-link-color-rgb: var(--accent-rgb);
    --bs-link-hover-color: var(--accent-strong);
    --bs-link-hover-color-rgb: var(--accent-rgb);

    /* Shape */
    --bs-border-radius: var(--radius);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius);

    /* Type */
    --bs-body-font-family: var(--sans);
    --bs-font-monospace: var(--mono);

    /* Component surfaces read --surface, not the page --ground (else cards/modals vanish) */
    --bs-card-bg: var(--surface);
    --bs-modal-bg: var(--surface);
    --bs-dropdown-bg: var(--surface);
}

/* ============================================================
   4. Component reskins
   Bootstrap 5.3 compiles button / pagination / nav-pills / dropdown "active" colours to literal
   hex, NOT var(--bs-primary), so remapping the scale (section 3) does not reach them. Re-point the
   component-level variables instead of setting background-color directly, so hover/active/disabled
   states keep working. Solid buttons use --accent-ink for text: it is white in light and near-black
   in dark, which mirrors how every colour token flips dark->light per theme (legible on all four).
   ============================================================ */

/* --- Solid buttons --- */
.btn-primary {
    --bs-btn-bg: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-color: var(--accent-ink);
    --bs-btn-hover-bg: var(--accent-strong);
    --bs-btn-hover-border-color: var(--accent-strong);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: var(--accent-strong);
    --bs-btn-active-border-color: var(--accent-strong);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-bg: var(--accent);
    --bs-btn-disabled-border-color: var(--accent);
    --bs-btn-disabled-color: var(--accent-ink);
}

.btn-success {
    --bs-btn-bg: var(--paid);
    --bs-btn-border-color: var(--paid);
    --bs-btn-color: var(--accent-ink);
    --bs-btn-hover-bg: color-mix(in srgb, var(--paid) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--paid) 88%, #000);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: color-mix(in srgb, var(--paid) 82%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--paid) 82%, #000);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-bg: var(--paid);
    --bs-btn-disabled-border-color: var(--paid);
    --bs-btn-disabled-color: var(--accent-ink);
}

.btn-danger {
    --bs-btn-bg: var(--void);
    --bs-btn-border-color: var(--void);
    --bs-btn-color: var(--accent-ink);
    --bs-btn-hover-bg: color-mix(in srgb, var(--void) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--void) 88%, #000);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: color-mix(in srgb, var(--void) 82%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--void) 82%, #000);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-bg: var(--void);
    --bs-btn-disabled-border-color: var(--void);
    --bs-btn-disabled-color: var(--accent-ink);
}

.btn-secondary {
    --bs-btn-bg: var(--draft);
    --bs-btn-border-color: var(--draft);
    --bs-btn-color: var(--accent-ink);
    --bs-btn-hover-bg: color-mix(in srgb, var(--draft) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--draft) 88%, #000);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: color-mix(in srgb, var(--draft) 82%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--draft) 82%, #000);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-bg: var(--draft);
    --bs-btn-disabled-border-color: var(--draft);
    --bs-btn-disabled-color: var(--accent-ink);
}

.btn-link {
    --bs-btn-color: var(--accent);
    --bs-btn-hover-color: var(--accent-strong);
    --bs-btn-active-color: var(--accent-strong);
}

/* --- Outline buttons (primary / secondary / danger / success are the variants in use) --- */
.btn-outline-primary {
    --bs-btn-color: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-hover-bg: var(--accent);
    --bs-btn-hover-border-color: var(--accent);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: var(--accent);
    --bs-btn-active-border-color: var(--accent);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-color: var(--accent);
    --bs-btn-disabled-border-color: var(--accent);
}

.btn-outline-success {
    --bs-btn-color: var(--paid);
    --bs-btn-border-color: var(--paid);
    --bs-btn-hover-bg: var(--paid);
    --bs-btn-hover-border-color: var(--paid);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: var(--paid);
    --bs-btn-active-border-color: var(--paid);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-color: var(--paid);
    --bs-btn-disabled-border-color: var(--paid);
}

.btn-outline-danger {
    --bs-btn-color: var(--void);
    --bs-btn-border-color: var(--void);
    --bs-btn-hover-bg: var(--void);
    --bs-btn-hover-border-color: var(--void);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: var(--void);
    --bs-btn-active-border-color: var(--void);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-color: var(--void);
    --bs-btn-disabled-border-color: var(--void);
}

/* Ghost / neutral secondary action (Edit, Back, Clear filters, Cancel-as-button): a quiet outlined
   button — hairline border, muted ink, subtle surface fill on hover (PRO-75 refinement of 8.1's
   slate-fill outline). The accent stays reserved for the single primary action per view. */
.btn-outline-secondary {
    --bs-btn-color: var(--muted);
    --bs-btn-border-color: var(--line);
    --bs-btn-hover-bg: var(--surface-2);
    --bs-btn-hover-border-color: var(--line);
    --bs-btn-hover-color: var(--ink);
    --bs-btn-active-bg: var(--surface-2);
    --bs-btn-active-border-color: var(--line);
    --bs-btn-active-color: var(--ink);
    --bs-btn-disabled-color: var(--faint);
    --bs-btn-disabled-border-color: var(--line-soft);
}

/* --- Nav pills / pagination / dropdown active states --- */
.nav-pills {
    --bs-nav-pills-link-active-bg: var(--accent);
}

.pagination {
    --bs-pagination-color: var(--accent);
    --bs-pagination-hover-color: var(--accent-strong);
    --bs-pagination-active-bg: var(--accent);
    --bs-pagination-active-border-color: var(--accent);
    --bs-pagination-active-color: var(--accent-ink);
}

.dropdown-menu {
    --bs-dropdown-link-active-bg: var(--accent);
    --bs-dropdown-link-active-color: var(--accent-ink);
}

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

.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--accent);
}

/* --- Alerts (only the variants used across the app) --- */
.alert-primary {
    --bs-alert-bg: var(--accent-soft);
    --bs-alert-border-color: color-mix(in srgb, var(--accent) 25%, var(--line));
    --bs-alert-color: var(--accent-strong);
    --bs-alert-link-color: var(--accent-strong);
}

.alert-success {
    --bs-alert-bg: var(--paid-soft);
    --bs-alert-border-color: color-mix(in srgb, var(--paid) 25%, var(--line));
    --bs-alert-color: var(--paid);
    --bs-alert-link-color: var(--paid);
}

.alert-warning {
    --bs-alert-bg: var(--sent-soft);
    --bs-alert-border-color: color-mix(in srgb, var(--sent) 25%, var(--line));
    --bs-alert-color: var(--sent);
    --bs-alert-link-color: var(--sent);
}

.alert-danger {
    --bs-alert-bg: var(--void-soft);
    --bs-alert-border-color: color-mix(in srgb, var(--void) 25%, var(--line));
    --bs-alert-color: var(--void);
    --bs-alert-link-color: var(--void);
}

.alert-secondary {
    --bs-alert-bg: var(--draft-soft);
    --bs-alert-border-color: color-mix(in srgb, var(--draft) 25%, var(--line));
    --bs-alert-color: var(--draft);
    --bs-alert-link-color: var(--draft);
}

/* --- Badges: shared pill shape (PRO-75, Phase 8.3) ---
   Every .badge gets the pill shape (rounded, inline-flex so a leading dot can sit beside the text)
   for a consistent language across the app. Invoice-status badges additionally get a soft tint +
   a leading dot (below); non-status badges (Deactivated / Primary / Linked / "via customer portal" /
   client Type) keep their Bootstrap semantic colour and simply inherit this shape (user decision
   2026-07-11: shape only, semantics unchanged). gap only consumes space when there's a dot, so
   dot-less badges get no phantom leading space. */
.badge {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    padding: .34em .72em;
    border-radius: 999px;
    font-weight: 600;
    font-size: .72rem;
    letter-spacing: .01em;
    line-height: 1.2;
    vertical-align: baseline;
}

/* --- Invoice status pills: soft tint + semantic text + leading dot (PRO-75) ---
   Replaces 8.1's solid fills. The text colour carries the semantic hue on a soft-tint background
   (legible in both themes — every token flips per theme); the dot is a ::before pseudo-element using
   currentColor, so the badge partial needs no markup of its own for the dot.

   PRO-109 UPDATE: the old note here claimed _InvoiceStatusBadge.cshtml "stays byte-for-byte" and that its
   view test was untouched. Both are now false — the partial takes the derived InvoicePaymentState and
   delegates to InvoiceStatusBadge, and the source-scraping view test was replaced by InvoiceStatusBadgeTests
   against the helper. The CLASSES below are what stayed stable, which is the part that actually mattered.

   The portal reuses these same classes (PortalInvoiceStatus.BadgeClass), and the COMPANY list reuses
   status-paid/status-draft for Active/Deactivated — so none of these names may be renamed, however
   tempting status-sent looks next to an enum member called Unpaid. */
.badge.status-paid    { color: var(--paid);    background-color: var(--paid-soft); }
.badge.status-sent    { color: var(--sent);    background-color: var(--sent-soft); }
.badge.status-draft   { color: var(--draft);   background-color: var(--draft-soft); }
.badge.status-void    { color: var(--void);    background-color: var(--void-soft); }
/* PRO-109: unreachable until PRO-95 makes partial amounts recordable — no browser test can see it yet,
   so InvoiceStatusBadgeTests asserts a rule exists for every state the helper can return. */
.badge.status-partial { color: var(--partial); background-color: var(--partial-soft); }
/* PRO-95: overpaid. Unreachable until the record-payment form ships in a later child of PRO-95, so the
   same arrangement applies — InvoiceStatusBadgeTests asserts a rule exists for every state the helper
   can return, which is what catches a missing rule before a browser ever could. */
.badge.status-credit  { color: var(--credit);  background-color: var(--credit-soft); }

.badge.status-paid::before,
.badge.status-sent::before,
.badge.status-draft::before,
.badge.status-void::before,
.badge.status-partial::before,
.badge.status-credit::before {
    content: "";
    flex: 0 0 auto;
    width: .5em;
    height: .5em;
    border-radius: 50%;
    background-color: currentColor;
}

/* ============================================================
   5. Utilities
   ============================================================ */
/* Monospace + tabular figures for invoice numbers and money (a ledger feel). Available now;
   applied to markup in later slices. */
.mono {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
}

/* ============================================================
   6. Data tables — modern treatment (PRO-75, Phase 8.3)
   Global restyle of .table so every list gets it without markup churn: uppercase micro-label column
   heads, comfortable padding, and a row-hover cue. Monospace/right-aligned money & dates are applied
   per numeric cell in the views (CSS can't know which <td> is money). The sortable header renders a
   .btn-link inside <th> with .text-reset, so it inherits the head colour/transform; only its
   font-size needs pinning back to the head size.
   ============================================================ */
.table {
    --bs-table-border-color: var(--line-soft);
}

.table > thead > tr > th {
    text-transform: uppercase;
    letter-spacing: .04em;
    font-size: .72rem;
    font-weight: 600;
    color: var(--muted);
    padding-top: .75rem;
    padding-bottom: .75rem;
    border-bottom: 1px solid var(--line);
}

.table > tbody > tr > td {
    padding-top: .7rem;
    padding-bottom: .7rem;
}

/* Row hover — the app's tables don't carry .table-hover, so cue hover directly (skip it on the
   line-item editor rows inside a form, which aren't a navigable list). */
.table > tbody > tr:hover > td,
.table > tbody > tr:hover > th {
    background-color: var(--surface-2);
}

/* The sortable-header button inherits transform/letter-spacing/colour from the <th> (text-reset),
   but .btn hard-sets font-size — pin it back to the micro-label size. */
.table > thead > tr > th .btn-link {
    font-size: inherit;
    letter-spacing: inherit;
    vertical-align: baseline;
}

/* ============================================================
   7. Forms + buttons (PRO-75, Phase 8.3)
   Focus ring already lives in site.css (teal). This adds: the required-field "*" weight (PRO-42
   convention), a styled validation summary + inline field errors, and a compact square icon-button.
   ============================================================ */

/* Required-field marker (the inline <span class="text-danger">*</span>). Most forms render it as a
   SIBLING right after the label (`</label> <span>*</span>`); a few put it INSIDE the label
   (`<label>Companies <span>*</span></label>`). Cover both, but never the validation-message spans
   (those follow a .form-control, not a .form-label, so they stay normal weight). */
.form-label + .text-danger,
.form-label .text-danger,
label .text-danger {
    font-weight: 700;
    margin-left: .1rem;
}

/* Inline field error (asp-validation-for): a touch smaller, with a little breathing room. */
.field-validation-error {
    display: inline-block;
    margin-top: .2rem;
    font-size: .85rem;
}

/* Model-level validation summary: a soft danger block instead of a bare red list. Only rendered
   with the .validation-summary-errors class present (ASP.NET adds it when there are errors). */
.validation-summary-errors {
    background-color: var(--void-soft);
    border: 1px solid color-mix(in srgb, var(--void) 25%, var(--line));
    border-radius: var(--radius-sm);
    padding: .6rem .85rem;
    margin-bottom: 1rem;
    font-size: .9rem;
}
.validation-summary-errors ul {
    margin: 0;
    padding-left: 1.15rem;
}
/* An empty summary (valid state) must not paint a block. */
.validation-summary-errors:empty,
.validation-summary-valid {
    display: none;
}

/* Compact square icon button (e.g. the Team "revoke ×" action) — consistent hit area, no ad-hoc
   padding. Pairs with any .btn-outline-* / .btn-* variant for colour. */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    line-height: 1;
}

/* ============================================================
   8. Confirm modal + flash/alerts + pagination (PRO-75, Phase 8.3)
   The colour tokens already flow through Bootstrap's --bs-* vars (§3/§4); this adds the shape
   refinements the mockup calls for. No markup change — reaches every alert/modal/pager.
   ============================================================ */

/* Flash / alerts: a colour-matched left stripe + the app radius. The 1px border and background
   tint are already token-driven per variant (§4), so the stripe just thickens the left edge. */
.alert {
    border-left-width: 4px;
    border-radius: var(--radius-sm);
}

/* Confirm modal (and the quick-add modal): surface already reads --surface (§3); add the token
   border, radius and elevated shadow, and soften the header/footer rules. */
.modal-content {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
}
.modal-header,
.modal-footer {
    border-color: var(--line-soft);
}

/* Pagination: separate the page links into spaced pills with token borders (both the GET pager
   _PaginationLinks and the POST pager _Pagination use .pagination / .btn-outline-secondary). */
.pagination {
    --bs-pagination-border-color: var(--line);
    --bs-pagination-bg: var(--surface);
    --bs-pagination-hover-bg: var(--surface-2);
    --bs-pagination-disabled-bg: transparent;
    --bs-pagination-disabled-border-color: var(--line-soft);
    gap: .3rem;
}
.pagination .page-link {
    border-radius: var(--radius-sm);
}

/* ============================================================
   9. Empty states (PRO-75, Phase 8.3)
   Rendered by the shared _EmptyState partial: a muted sprite icon + one line of copy + an optional
   CTA, centered — replacing bare <p class="text-muted"> list-empty messages.
   ============================================================ */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: .85rem;
    padding: 2.75rem 1rem;
    color: var(--muted);
}
.empty-state-icon {
    width: 44px;
    height: 44px;
    color: var(--faint);
}
.empty-state-msg {
    margin: 0;
    max-width: 34rem;
    font-size: .95rem;
}
.empty-state .btn {
    margin-top: .25rem;
}

/* ============================================================
   10. KPI dashboard tiles + sparklines (PRO-76, Phase 8.4)
   The owner invoice-home leads with a row of stat tiles (Outstanding / Paid this month / Overdue /
   Drafts), each with a small inline-SVG sparkline. Colour lives ONLY on the sparkline mark, keyed off the
   tile's --kpi-rgb (set inline to one of the status token triplets); label/value/caption wear text tokens
   per the dataviz design system. auto-fit grid → 4 across on wide, reflowing to 2 / 1 on narrow.
   ============================================================ */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: .85rem;
    margin-bottom: .5rem;
}
.kpi-tile {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .85rem 1rem 1rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.kpi-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
}
.kpi-label {
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--muted);
}
/* Big standalone figure: proportional (NOT tabular) numerals, per the stat-tile spec. */
.kpi-value {
    font-size: 1.6rem;
    font-weight: 600;
    line-height: 1.1;
    color: var(--ink);
}
.kpi-caption {
    font-size: .74rem;
    color: var(--muted);
}
.kpi-scope {
    margin: 0 0 1rem;
    font-size: .74rem;
    color: var(--muted);
}
/* The sparkline: fixed viewBox (112×34) scaled here at its own ratio so the endpoint dot stays round. */
.kpi-spark {
    width: 6rem;
    height: 1.82rem;
    flex: none;
    overflow: visible;
}
.kpi-spark-line {
    fill: none;
    stroke: rgb(var(--kpi-rgb));
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}
.kpi-spark-area {
    fill: rgba(var(--kpi-rgb), .12);
    stroke: none;
}
/* Emphasized endpoint: filled dot + a 2px surface ring so it stays legible over the line. */
.kpi-spark-dot {
    fill: rgb(var(--kpi-rgb));
    stroke: var(--surface);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
}
/* A flat (all-equal / single-point) series reads as a quiet muted rule, not a coloured trend. */
.kpi-spark.is-flat .kpi-spark-line {
    stroke: var(--line);
}
.kpi-spark.is-flat .kpi-spark-area {
    fill: none;
}

/* Free-text addresses are stored with real newlines; without this they collapse into one run-on line.
   Used by the customer business page's profile block (PRO-87). */
.address-lines {
    white-space: pre-line;
}

/* ============================================================
   11. List views — card surface, row identity, action de-clutter (PRO-83)
   Wraps each list in a bordered card with a toolbar (count + filter + optional switch), gives rows a
   thumbnail + two-line identity, and collapses the per-row button row to one hover quick-action + a
   kebab menu. Everything is scoped under .card-list / .list-toolbar to avoid the app-shell.css name
   clashes on .icon-btn (renamed here to .row-action) and .spacer (scoped to .list-toolbar). The table
   keeps its Bootstrap .table base (section 6) with card-scoped overrides — no forked table class.
   ============================================================ */

/* --- Card surface. No `overflow:hidden` (it would clip the kebab dropdown on the bottom rows — the
   R1 risk); corners are kept clean by rounding the toolbar top and the last row's outer cells. --- */
.card-list {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* --- Toolbar: count + filter + optional switch (never the page title — that lives in the topbar). --- */
.list-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding: .85rem 1.25rem;
    border-bottom: 1px solid var(--line-soft);
}
/* Scoped so it never fights `.topbar .spacer` in app-shell.css. */
.list-toolbar .spacer {
    flex: 1 1 auto;
}
.list-count {
    font-size: .8rem;
    font-weight: 600;
    color: var(--muted);
}
.list-filter {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
    padding: .35rem .65rem;
    background: var(--surface-2);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    color: var(--faint);
    min-width: 12rem;
}
.list-filter:focus-within {
    border-color: var(--accent);
}
.list-filter-icon {
    flex: none;
    color: var(--faint);
}
.list-filter-input {
    border: 0;
    padding: 0;
    background: transparent;
    color: var(--ink);
    font-size: .85rem;
    width: 100%;
    outline: none;
}
.list-filter-input::placeholder {
    color: var(--faint);
}
.list-toolbar-switch {
    margin: 0;
    padding-left: 2.4rem;
    font-size: .85rem;
    color: var(--muted);
    white-space: nowrap;
}

/* --- Table inside the card: the card owns the outer border, so the table is flush + borderless at
   its edges; keep the section-6 head/hover treatment and add the mockup's roomier cell padding. --- */
.lt-scroll {
    overflow: visible;
}

/* --- PRO-109: a list wide enough to scroll on DESKTOP too, not just on narrow screens ---
   Opt-in, because the sibling rule below deliberately leaves desktop overflow visible so a row's kebab
   dropdown is never clipped. The invoice list is the one list with no kebab (ListViewShapeTests pins
   that), so it can scroll safely; do NOT put this class on a list that has row actions.

   Adding a sixth column (Balance) pushed the invoice table past its container, and a table with no
   min-width squeezes instead of overflowing: every Issued date broke as "2026-08-" / "14" and most money
   read "USD" above the amount. Rather than pick a magic min-width that guesses at content, the cells that
   must never wrap say so — the table then takes its own natural width and this wrapper scrolls only when
   that genuinely exceeds the space. .mono marks exactly those cells here: number, date, total, balance.

   ⚠️ DESKTOP ONLY, and that bound is load-bearing. Below 768px the sibling media query already solves
   this its own way (overflow-x on .lt-scroll plus a modest min-width, letting cells wrap), and applying
   nowrap there instead makes the table's natural width ~961px — far past a 500px viewport — which pushed
   the horizontal scroll onto the BODY rather than the wrapper. Two existing browser tests assert there is
   no horizontal page scroll at 500px, and both went red. Mobile keeps the answer it already had.

   position:relative is NOT cosmetic and must not be removed. The Balance cell renders a Bootstrap
   .visually-hidden label ("Nothing due") for the em dash, and .visually-hidden is position:absolute.
   Its containing block would otherwise be the initial containing block (site.css makes html
   position:relative), and an absolutely positioned box whose containing block is an ANCESTOR of a scroll
   container is not clipped by it — so that 1px span kept its viewport x-position and pushed
   documentElement.scrollWidth to 581 at a 500px viewport while every visible cell was clipped correctly.
   Two existing tests assert no horizontal PAGE scroll at 500px and both went red. Making this wrapper the
   containing block lets its own overflow-x absorb the span. Applies at ALL widths, deliberately: the
   escape happened at 500px, below the desktop rules below. Measured: 500 -> 500, 768 -> 768, 1280 -> 1280.
   Safe here only because this list has no row kebab anchored inside it. */
.card-list .lt-scroll-wide {
    position: relative;
}

@media (min-width: 768px) {
    .card-list .lt-scroll-wide {
        overflow-x: auto;
    }
    .card-list .lt-scroll-wide .mono {
        white-space: nowrap;
    }
}

/* A non-sortable header renders its label directly in the <th>, where the uppercase transform above
   applies. A SORTABLE one wraps its label in a .btn-link, and Bootstrap's .btn hard-sets
   text-transform:none — so every sortable header has always rendered title case and the uppercase rule
   has only ever been dormant. PRO-109's plain Balance header was the first to receive it, and stood out
   as the sole SHOUTING column. Match the five neighbours rather than change all of them, which would be
   a visible change to every list in the app for no reason connected to this work.

   Only the transform is reset — NOT letter-spacing. The sortable headers keep the .04em tracking (.btn
   overrides text-transform but inherits letter-spacing via the .btn-link rule above), so zeroing it here
   would trade one mismatch for a subtler one. */
.card-list .table > thead > tr > th.th-plain {
    text-transform: none;
}
.card-list .table {
    margin-bottom: 0;
}
.card-list .table > thead > tr > th,
.card-list .table > tbody > tr > td {
    padding-left: 1.25rem;
    padding-right: 1.25rem;
}
.card-list .table > thead > tr > th {
    border-bottom-color: var(--line);
}
.card-list .table > tbody > tr {
    border-bottom: 1px solid var(--line-soft);
}
.card-list .table > tbody > tr:last-child {
    border-bottom: 0;
}
/* Round the last row's outer corners so hover fill never pokes past the card's rounded border. */
.card-list .table > tbody > tr:last-child > td:first-child {
    border-bottom-left-radius: var(--radius);
}
.card-list .table > tbody > tr:last-child > td:last-child {
    border-bottom-right-radius: var(--radius);
}
/* Rows that navigate on click read as clickable; the section-6 hover cue does the rest. */
.card-list .table > tbody > tr[data-href] {
    cursor: pointer;
}
/* Deactivated / inactive rows read quieter but stay legible on hover. */
.card-list .table > tbody > tr.inactive {
    opacity: .55;
}
.card-list .table > tbody > tr.inactive:hover {
    opacity: .85;
}
.num {
    text-align: right;
}

/* --- Entity cell: thumbnail + two-line stack --- */
.entity {
    display: flex;
    align-items: center;
    gap: .8rem;
    min-width: 0;
}
.thumb {
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: 9px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: .95rem;
    line-height: 1;
    /* Base variant tracks the accent, whose ink flips correctly per theme. */
    background: var(--accent);
    color: var(--accent-ink);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .05);
    overflow: hidden;
}
.thumb.sm {
    width: 30px;
    height: 30px;
    border-radius: 7px;
    font-size: .8rem;
}
.thumb-img {
    object-fit: cover;
    background: var(--surface-2);
}
/* Fixed mid-tone monogram colours (they do NOT flip per theme), so they carry a fixed light ink —
   NOT var(--accent-ink), which would flip to near-black in dark and fail on these backgrounds (R5). */
.thumb.t2 { background: #6D5BD0; color: #FFFFFF; }
.thumb.t3 { background: #B4690E; color: #FFFFFF; }
.thumb.t4 { background: #2A8FA8; color: #FFFFFF; }
.thumb.muted {
    background: var(--surface-2);
    color: var(--faint);
    box-shadow: inset 0 0 0 1px var(--line);
}
.entity .lines {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    min-width: 0;
}
.entity .primary {
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
a.primary:hover {
    color: var(--accent);
}
.entity .secondary {
    font-size: .78rem;
    color: var(--muted);
}

/* --- Row actions: one hover-revealed quick action + a kebab overflow menu (Bootstrap dropdown).
   `.row-action` replaces the mockup's `.icon-btn` name, which already means a 38px topbar button in
   app-shell.css (R2). --- */
.row-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .25rem;
}
.row-action {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 7px;
    background: transparent;
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: opacity .12s ease, background .12s ease, color .12s ease, border-color .12s ease;
}
.row-action svg {
    width: 16px;
    height: 16px;
}
.row-action:hover,
.row-action:focus-visible {
    background: var(--surface-2);
    border-color: var(--line);
    color: var(--accent);
    outline: none;
}
/* The pencil quick-action is revealed on row hover / keyboard focus; the kebab stays faintly visible
   (it may be the only action) and solidifies on hover. */
.row-action.quick {
    opacity: 0;
}
.card-list .table > tbody > tr:hover .row-action.quick,
.row-action.quick:focus-visible {
    opacity: 1;
}
.row-action.kebab {
    opacity: .55;
}
.card-list .table > tbody > tr:hover .row-action.kebab,
.row-action.kebab:focus-visible,
.row-action.kebab[aria-expanded="true"] {
    opacity: 1;
}
/* The kebab menu reuses Bootstrap .dropdown-menu; its destructive item wears the void token. */
.card-list .dropdown-item.text-danger {
    color: var(--void);
}
/* Trailing chevron: a quiet "row is clickable" affordance, not a pressable control — no hover box. */
.row-action.chevron {
    opacity: 1;
    border: 0;
    color: var(--faint);
    cursor: pointer;
}
.row-action.chevron:hover {
    background: transparent;
    border: 0;
}
.card-list .table > tbody > tr:hover .row-action.chevron {
    color: var(--accent);
}

/* --- No-match state (client-side filter emptied the list) --- */
.list-no-match {
    padding: 2.5rem 1.25rem;
    text-align: center;
    color: var(--muted);
}
.list-no-match .empty-state-icon {
    width: 2rem;
    height: 2rem;
    color: var(--faint);
    margin-bottom: .5rem;
}

/* --- Narrow screens: contain a wide table inside the card (switches on the horizontal scroll only
   here, so the kebab dropdown is never clipped on the roomy desktop layout — R1). --- */
@media (max-width: 767.98px) {
    .card-list .lt-scroll {
        overflow-x: auto;
    }
    .card-list .table {
        min-width: 34rem;
    }
    .list-filter {
        min-width: 0;
        flex: 1 1 auto;
    }
}
