/* Project-specific overrides for the Tabler theme.
   Keep this file small — prefer Tabler's built-in classes over overrides. */

/* Self-hosted Inter variable font. Drop InterVariable.woff2 /
   InterVariable-Italic.woff2 into /static/fonts/inter/ — Tabler's default
   font stack ("Inter Var", Inter, ...) will pick this up automatically.
   If files are missing, the browser falls back to system fonts gracefully. */
@font-face {
    font-family: 'Inter Var';
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/inter/InterVariable-2bf3d951bf9d6109437ea0874bbf12fb.woff2') format('woff2-variations');
}
@font-face {
    font-family: 'Inter Var';
    font-weight: 100 900;
    font-style: italic;
    font-display: swap;
    src: url('/fonts/inter/InterVariable-Italic-8d1dc86da25893737b6bb8a9c64b790e.woff2') format('woff2-variations');
}

/* Sidebar collapse (toggled by #sidebarToggleBtn in topbar) — hides the
   vertical nav and lets .page-wrapper reclaim the full viewport width.
   Persisted via localStorage in scripts.js.
   Uses `margin-left` because Tabler's .page-wrapper rules set `margin-left: 15rem`
   in multiple media queries; `margin-inline-start` alone didn't beat them in LTR. */
body.sidebar-collapsed .navbar-vertical { display: none !important; }
body.sidebar-collapsed .page-wrapper {
    margin-left: 0 !important;
    margin-right: 0 !important;
}

/* --- DataTables footer (info + pagination) --- */
/* Kill default DataTables margin/padding so info+pagination sit cleanly in the flex footer */
.dt-footer .dataTables_info { padding: 0; }
.dt-footer .dataTables_paginate { padding: 0; margin: 0; }
.dt-footer .pagination { margin: 0; }
/* Small muted page-links matching Tabler's compact button sizing */
.dt-footer .page-link {
    padding: .25rem .5rem;
    font-size: .8125rem;
}
/* Hide the whole footer row when there's nothing to page (only 1 page of results).
   DataTables still renders the bar; this keeps cards tight on small tables. */
.dt-footer:has(.paginate_button.current:only-child) .dataTables_paginate { display: none; }

/* --- Accessibility: visible focus ring for keyboard users --- */
/* Browsers show :focus-visible only for keyboard navigation, not mouse clicks,
   so interactive targets stay clean when clicked but prominent when tabbed to. */
:root {
    --app-focus-ring: var(--tblr-primary, #066fd1);
    /* The close "X" as a painted image, for .btn-close:focus-visible below — Tabler's own glyph fill. */
    --app-btn-close-glyph: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%231f2937'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");
}
/* Stock primary is 2.9–3.7:1 on dark surfaces, under the 3:1 a focus indicator needs; use the audited
   dark link blue (set in the WCAG block below). The sidebar is always dark, so it takes this too. */
[data-bs-theme="dark"] {
    --app-focus-ring: var(--tblr-link-color);
    /* Dark theme's body text colour (Tabler gray-200), which the masked glyph otherwise takes. */
    --app-btn-close-glyph: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23e5e7eb'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");
}
*:focus-visible {
    outline: 2px solid var(--app-focus-ring);
    outline-offset: 2px;
    border-radius: 2px;
}
/* Tabler's .btn:focus-visible / .btn-close:focus set outline:0 and outrank the rule above, leaving only a
   faint tinted shadow (WCAG 2.4.7). Same ring, keeping the button's own corner radius; the focus halo is
   dropped (back to the button's resting shadow) so it reads as one crisp ring. */
.btn:focus-visible {
    outline: 2px solid var(--app-focus-ring);
    outline-offset: 2px;
    border-radius: var(--tblr-btn-border-radius);
    box-shadow: var(--tblr-btn-box-shadow);
}
/* The mobile menu toggle and the sidebar / header links: Tabler gives them outline:0 and a faint 4px halo. */
.navbar-toggler:focus-visible {
    outline: 2px solid var(--app-focus-ring);
    outline-offset: 2px;
    box-shadow: none;
}
/* Inset: sidebar links span its full width and the sidebar scrolls, so an outside ring would be clipped. */
.nav-link:focus-visible {
    outline: 2px solid var(--app-focus-ring);
    outline-offset: -2px;
    border-radius: var(--tblr-border-radius);
    box-shadow: none;
}
/* Tabler draws the X as a mask over a coloured box, and a mask clips the element's own outline and shadow
   too — so no ring could ever show. While focused, drop the mask and paint the X as an image instead. */
.btn-close:focus-visible {
    -webkit-mask: none;
    mask: none;
    background: transparent var(--app-btn-close-glyph) no-repeat center / calc(var(--tblr-btn-close-size) * 0.75);
    outline: 2px solid var(--app-focus-ring);
    outline-offset: 2px;
    border-radius: var(--tblr-border-radius);
    box-shadow: none;
}
/* A modal's X sits flush against .modal-content, which clips overflow: draw its ring inside the button. */
.modal-header .btn-close:focus-visible {
    outline-offset: -4px;
}
/* Override inside form controls — BS5 already shows a box-shadow on :focus,
   the outline would duplicate. Use outline only on buttons and links. */
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible {
    outline: none;
}

/* Centred code fields (sign-in and step-up): the invalid state pads the right for its icon, which pushed the
   digits and placeholder off-centre. Pad the left to match. */
.form-control.text-center.is-invalid {
    padding-left: 2.375rem;
}

/* Date inputs: the browser draws its calendar button at the right edge, where Tabler's valid / invalid icon also
   sits, and the two overlapped. One icon only: the border colour and the feedback line carry the state here.
   The state also widens the right padding to make room for that icon, which slid the calendar button 22px left
   on blur; keep the resting padding (1rem, measured live) since there is no icon to make room for. */
.form-control[type="date"].is-valid,
.form-control[type="date"].is-invalid {
    background-image: none;
    padding-right: 1rem;
}

/* --- Required field indicator: red asterisk after the label --- */
/* Uses :has() to detect when the immediately-following input is required.
   Falls back silently on browsers without :has() support (Chrome 105+, Safari 15.4+, Firefox 121+). */
.form-label:has(+ input[required]),
.form-label:has(+ textarea[required]),
.form-label:has(+ select[required]) {
    position: relative;
}
.form-label:has(+ input[required])::after,
.form-label:has(+ textarea[required])::after,
.form-label:has(+ select[required])::after {
    content: " *";
    color: var(--tblr-danger, #d63939);
    font-weight: 600;
}

/* --- Card header dropdowns: let menus escape the card's overflow:hidden clipping.
   Targeted via :has() so only cards that actually have a header dropdown lose the clip. */
.card:has(> .card-header .dropdown),
.card:has(> .card-header .dropend),
.card:has(> .card-header .dropstart) {
    overflow: visible;
}

/* --- Row-level dropdowns inside DataTables: same clipping pattern, but inside
   .table-responsive (which has overflow-x:auto). Open the parent card while a
   menu is shown so it isn't cut by the row container. */
.card:has(.table-responsive .dropdown-menu.show) {
    overflow: visible;
}
.card:has(.table-responsive .dropdown-menu.show) .table-responsive {
    overflow: visible;
}

/* --- Linked primary cells in list tables.
   Click the name/account/reference to open the detail page. Hover affordance
   matches Tabler's link styles; truncation + native title attribute handles
   long values on narrow viewports without breaking row height.
   Hover styling is scoped to anchor variants only — a bare .row-link <div>
   is non-interactive and must not look clickable. */
.row-link {
    max-width: 100%;
}
a.row-link:hover .fw-medium,
a.row-link:focus-visible .fw-medium {
    color: var(--tblr-primary);
    text-decoration: underline;
}
.row-link .text-truncate {
    max-width: 28ch;
}
@media (min-width: 992px) {
    .row-link .text-truncate { max-width: 32ch; }
}

/* --- Sticky first column for tables that may overflow on narrow viewports.
   Add class="table-sticky-first" to the <table>. Keeps the primary identifier
   (name / reference) in view while the rest scrolls horizontally. */
.table-sticky-first > thead > tr > th:first-child,
.table-sticky-first > tbody > tr > td:first-child {
    position: sticky;
    left: 0;
    background-color: var(--tblr-bg-surface, #fff);
    z-index: 1;
}
.table-sticky-first > thead > tr > th:first-child {
    z-index: 2;
}
[data-bs-theme="dark"] .table-sticky-first > thead > tr > th:first-child,
[data-bs-theme="dark"] .table-sticky-first > tbody > tr > td:first-child {
    background-color: var(--tblr-bg-surface, #1a2234);
}
/* Striping must keep working under the sticky cell. */
.table-sticky-first.card-table > tbody > tr:nth-child(even) > td:first-child {
    background-color: var(--tblr-bg-surface-tertiary, rgba(0, 0, 0, .025));
}
[data-bs-theme="dark"] .table-sticky-first.card-table > tbody > tr:nth-child(even) > td:first-child {
    background-color: rgba(255, 255, 255, .03);
}

/* --- Skeleton row placeholders for server-side DataTables initial load --- */
/* Shimmer effect matches Bootstrap 5 .placeholder-glow. Rows are replaced
   automatically by DataTables on first draw. */
tr.dt-skeleton td .placeholder {
    height: 0.75rem;
    background-color: var(--tblr-bg-surface-secondary, rgba(0, 0, 0, .08));
}

/* --- Empty / load-failure state cell. List tables carry .text-nowrap so data cells
   stay on one line; the empty cell would inherit it, and its sentence would then set
   the table's width and push the header past the card. Let it wrap to the card.
   dataTables_empty is the DataTables 1.x class, dt-empty the 2.x one. */
td.dataTables_empty,
td.dt-empty {
    white-space: normal;
}

/* --- Alternating row colors for card-table data tables --- */
/* Global zebra striping — applies to every Tabler .card-table so list pages
   don't each need their own .table-striped class. Uses a subtle background
   that works in both light and dark themes via the --tblr-bg-surface-tertiary var. */
.table.card-table > tbody > tr:nth-child(even) > * {
    background-color: var(--tblr-bg-surface-tertiary, rgba(0, 0, 0, .025));
}
[data-bs-theme="dark"] .table.card-table > tbody > tr:nth-child(even) > * {
    background-color: rgba(255, 255, 255, .03);
}

/* Sidebar submenu — inline expansion (BS5 collapse) inside .navbar-vertical.
   Nested nav items use the same look as top-level but slightly muted so the
   hierarchy reads clearly. */
.navbar-vertical .nav-sidebar-submenu { margin: 0; padding-left: 0; list-style: none; }
.navbar-vertical .nav-sidebar-submenu .nav-link { padding-block: .35rem; opacity: .85; }
.navbar-vertical .nav-sidebar-submenu .nav-link:hover { opacity: 1; }

/* Tabler's .nav-link-toggle class draws the chevron via ::after
   and rotates it 180° on [aria-expanded=true] — no custom rules needed. */

/* The sidebar has a small fixed set of items — suppress the browser scrollbar
   and OS-level scroll arrow buttons that Tabler's default overflow: auto causes
   when content is near the viewport boundary. */
.navbar-vertical .navbar-collapse {
    overflow: visible !important;
}
.navbar-vertical {
    overflow-y: auto;
    scrollbar-width: none;               /* Firefox */
}
.navbar-vertical::-webkit-scrollbar {    /* Chrome, Safari, Edge */
    width: 0;
    height: 0;
}

/* --- Sidebar hover + active states (dark theme) --- */

/* Hover on any top-level or submenu nav link — subtle lighter tint. */
.navbar-vertical .nav-link:hover {
    background-color: rgba(255, 255, 255, 0.06);
    color: #fff;
}

/* Active (current page) — accented background + left border + white text. */
.navbar-vertical .nav-item.active > .nav-link,
.navbar-vertical .nav-sidebar-submenu .nav-item.active > .nav-link {
    background-color: rgba(var(--tblr-primary-rgb, 6, 111, 209), 0.15);
    color: #fff;
    border-left: 3px solid var(--tblr-primary, #066fd1);
    padding-left: calc(var(--tblr-navbar-nav-link-padding-x, .75rem) - 3px);
}

/* Submenu items — tighter left padding accounts for the active border. */
.navbar-vertical .nav-sidebar-submenu .nav-item.active > .nav-link {
    padding-left: calc(1rem - 3px);
    opacity: 1;
}

/* --- Acumatica: error message viewer inside the batch-detail modal.
     Acumatica's 422 bodies can be hundreds of lines, so we cap height and
     allow line wrapping rather than horizontal scroll. */
.acumatica-error-block {
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 320px;
    overflow: auto;
}

/* --- Acumatica: technical-details summary needs an obvious affordance.
     Default <summary> renders a tiny native marker that's easy to miss; we hide it
     and use a chevron that rotates 90deg when the accordion is open. */
.acumatica-tech-details summary {
    cursor: pointer;
    list-style: none;
}
.acumatica-tech-details summary::-webkit-details-marker {
    display: none;
}
.acumatica-tech-details summary .icon-chevron {
    transition: transform 0.15s ease;
}
.acumatica-tech-details[open] summary .icon-chevron {
    transform: rotate(90deg);
}

/* --- Acumatica: BR account chips on the error card.
     Yellow-on-yellow was overwhelming when there are dozens of unmapped accounts;
     this is a neutral chip with monospace digits and a per-chip copy button. */
.acumatica-account-chip {
    padding: 0.125rem 0.25rem 0.125rem 0.5rem;
    border: 1px solid var(--tblr-border-color, #dee2e6);
    border-radius: 0.25rem;
    background: var(--tblr-bg-surface-secondary, #f8f9fa);
    font-size: 0.8125rem;
    color: var(--tblr-body-color, #1f2937);
}

/* --- Acumatica: GL lines table can be 100+ rows; keep the header visible while scrolling. */
.acumatica-lines-table {
    max-height: 60vh;
    overflow: auto;
}
.acumatica-lines-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--tblr-bg-surface, #fff);
}
.acumatica-lines-table tfoot td {
    position: sticky;
    bottom: 0;
    z-index: 1;
    background: var(--tblr-bg-surface, #fff);
    box-shadow: inset 0 1px 0 var(--tblr-border-color, #dee2e6);
}

/* --- Readability: WCAG contrast fixes (audited 2026-06-11, 4.5:1 target).
     Dark mode: Tabler's stock primary (#066fd1) and muted gray (#6b7280) sit
     at 2.9–3.7:1 on the dark surfaces — lighten links, outline buttons and
     secondary text only there. Ghost-danger red was 2.9:1 dark / 4.46:1 on
     striped rows in light — nudged both ways. */
[data-bs-theme="dark"] {
    --tblr-link-color: #74b6f3;
    --tblr-link-hover-color: #a3cdf6;
    --tblr-secondary-color: #98a2b3;  /* .text-secondary, footer, inactive tabs */
    --tblr-muted: #98a2b3;
}
[data-bs-theme="dark"] .btn-outline-primary {
    --tblr-btn-color: #74b6f3;
    --tblr-btn-border-color: #74b6f3;
}
[data-bs-theme="dark"] .btn-ghost-danger {
    --tblr-btn-color: #f27d7d;
}
/* Ghost-secondary icon buttons (Toggle sidebar, row actions, copy): on hover, focus and press the fill turns
   gray-400 with a near-white icon — about 2.5:1. Keep the fill and draw the icon in the page's dark
   background colour instead (about 7:1). */
[data-bs-theme="dark"] .btn-ghost-secondary {
    --tblr-btn-hover-color: var(--tblr-body-bg);
    --tblr-btn-active-color: var(--tblr-body-bg);
}
/* These take a literal gray from the vendor build, not --tblr-secondary-color,
   so the variable override above can't reach them. */
[data-bs-theme="dark"] .footer {
    color: #98a2b3;
}
[data-bs-theme="dark"] .nav-tabs .nav-link:not(.active) {
    color: #98a2b3;
}
/* Row-link hover highlight uses --tblr-primary, which is too dim on dark rows. */
[data-bs-theme="dark"] a.row-link:hover .fw-medium,
[data-bs-theme="dark"] a.row-link:focus-visible .fw-medium {
    color: #74b6f3;
}
.btn-ghost-danger {
    --tblr-btn-color: #c92a2a;  /* stock #d63939 measured 4.46:1 on striped table rows */
}

/* Amber notes (stalled batches, stale balances): Tabler's .text-warning is
   2.1:1 on light surfaces — far too faint for text that carries information.
   Readable amber pair for both themes. */
.note-warning {
    color: #9a6700;
}
[data-bs-theme="dark"] .note-warning {
    color: #f0b357;
}

/* Soft amber badges ("Not set up", "Locked", "Setting up"): Tabler's bg-warning-lt paints its amber text
   at 2.1:1 on the tint. A shade darker than .note-warning's #9a6700, which measures 4.498:1 on this tint.
   The doubled class and !important are needed to beat Tabler's own !important utilities. */
.badge.bg-warning-lt {
    color: #946300 !important;
}
[data-bs-theme="dark"] .badge.bg-warning-lt {
    color: #f0b357 !important;
}
/* The other soft badges (getDisplayBadge, activity and status words) fall short the same way: Tabler's text on
   its own -lt tint measured 2.5:1 (success), 2.7:1 (orange, info), 4.0:1 (danger) in light and 2.9:1 (danger),
   3.3:1 (primary) in dark. Each pair below is >= 4.5:1 on its tint, measured in that theme, in a card and on a
   table row (whose dark background is lighter: the dark green, grey and red are a step lighter for it). */
.badge.bg-success-lt { color: #1e7b34 !important; }
.badge.bg-info-lt { color: #1f6fb2 !important; }
.badge.bg-orange-lt { color: #b54708 !important; }
.badge.bg-danger-lt { color: #c92a2a !important; }
.badge.bg-secondary-lt { color: #5f6773 !important; }
.badge.bg-primary-lt, .badge.bg-blue-lt { color: #0559a7 !important; }
[data-bs-theme="dark"] .badge.bg-success-lt { color: #4cc764 !important; }
[data-bs-theme="dark"] .badge.bg-info-lt { color: #74b6f3 !important; }
[data-bs-theme="dark"] .badge.bg-orange-lt { color: #f79552 !important; }
[data-bs-theme="dark"] .badge.bg-danger-lt { color: #f79a9a !important; }
[data-bs-theme="dark"] .badge.bg-secondary-lt { color: #a8b1c0 !important; }
[data-bs-theme="dark"] .badge.bg-primary-lt, [data-bs-theme="dark"] .badge.bg-blue-lt { color: #74b6f3 !important; }
/* The topbar initials sit on the same primary tint: 4.2:1 light, 3.3:1 dark with Tabler's blue. */
.avatar.bg-primary-lt { color: #0559a7 !important; }
[data-bs-theme="dark"] .avatar.bg-primary-lt { color: #74b6f3 !important; }

/* Tabler 1.4 defines every --tblr-*-darken as the colour at 80% opacity, so a solid button's hover and
   pressed states come out PALER than at rest — on white they read as disabled (a hovered "Finish set-up"
   looked switched off). Make them genuinely darker, following whatever the theme colour is. */
:root {
    --tblr-primary-darken: color-mix(in oklab, var(--tblr-primary), black 15%);
    --tblr-secondary-darken: color-mix(in oklab, var(--tblr-secondary), black 15%);
    --tblr-success-darken: color-mix(in oklab, var(--tblr-success), black 15%);
    --tblr-info-darken: color-mix(in oklab, var(--tblr-info), black 15%);
    --tblr-warning-darken: color-mix(in oklab, var(--tblr-warning), black 15%);
    --tblr-danger-darken: color-mix(in oklab, var(--tblr-danger), black 15%);
}

/* --- Dashboard: staggered card reveal. Each card animates in slightly after
     the previous one (driven by --dash-delay), so the page builds itself
     instead of popping in at once. Respects reduced-motion. */
.dash-reveal {
    opacity: 0;
    animation: dash-reveal 0.45s ease forwards;
    animation-delay: calc(var(--dash-delay, 0) * 70ms);
}
@keyframes dash-reveal {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .dash-reveal { animation: none; opacity: 1; }
}

/* --- Dashboard: pulse dot on the verdict strip. Breathing ring while healthy,
     solid + urgent ring when something needs attention. */
.dash-pulse-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    flex: 0 0 auto;
    position: relative;
    background: var(--tblr-secondary, #6c7a91);
}
.dash-pulse-dot::after {
    content: '';
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 2px solid currentColor;
    opacity: 0;
}
.dash-pulse-dot[data-tone="ok"]      { background: var(--tblr-success, #2fb344); color: var(--tblr-success, #2fb344); }
.dash-pulse-dot[data-tone="warning"] { background: var(--tblr-warning, #f59f00); color: var(--tblr-warning, #f59f00); }
.dash-pulse-dot[data-tone="danger"]  { background: var(--tblr-danger, #d63939);  color: var(--tblr-danger, #d63939); }
.dash-pulse-dot[data-tone="ok"]::after,
.dash-pulse-dot[data-tone="warning"]::after,
.dash-pulse-dot[data-tone="danger"]::after {
    animation: dash-pulse 2s ease-out infinite;
}
@keyframes dash-pulse {
    0%   { transform: scale(0.6); opacity: 0.8; }
    70%  { transform: scale(1.6); opacity: 0; }
    100% { transform: scale(1.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .dash-pulse-dot::after { animation: none !important; }
}

/* --- Dashboard: provider health pills — status-page style, one dot per provider. */
.dash-provider-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--tblr-border-color, #dee2e6);
    border-radius: 2rem;
    font-size: 0.8125rem;
    color: var(--tblr-body-color, #1f2937);
    background: var(--tblr-bg-surface, #fff);
}
.dash-provider-pill .dash-provider-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--tblr-success, #2fb344);
}
.dash-provider-pill.is-degraded {
    border-color: var(--tblr-danger, #d63939);
    color: var(--tblr-danger, #d63939);
}
.dash-provider-pill.is-degraded .dash-provider-dot {
    background: var(--tblr-danger, #d63939);
    animation: dash-pulse 1.2s ease-out infinite;
}

/* --- Dashboard: attention chips on the pulse strip — each is a deep link to
     the page where the problem gets fixed. */
.dash-attention-chip {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.75rem;
    border-radius: 2rem;
    font-size: 0.8125rem;
    font-weight: 500;
    text-decoration: none;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.dash-attention-chip:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
    text-decoration: none;
}
.dash-attention-chip.is-warning {
    background: var(--tblr-warning-lt, #fcf0db);
    /* NOT var(--tblr-warning): that amber is 2.1:1 on the -lt background. */
    color: #9a6700;
}
[data-bs-theme="dark"] .dash-attention-chip.is-warning {
    color: #f0b357;
}
.dash-attention-chip.is-danger {
    background: var(--tblr-danger-lt, #fbebeb);
    color: var(--tblr-danger, #d63939);
}

/* --- Soft action buttons (btn-soft-*) — the paved-path family for action
     buttons per CLAUDE.md. Tabler v1.4.0 ships no soft button variant, so the
     classes rendered as bare white .btn until defined here. Built on Tabler's
     --tblr-btn-* variable API: tinted -lt resting state (same pairing as the
     soft badges), solid fill on hover/active so the pressed affordance is
     unmistakable. The -lt tokens are theme-scoped, so dark mode tints follow
     automatically; text colors reuse the values from the 2026-06 contrast pass
     where the stock token fell below AA. */
.btn-soft-primary {
    /* #0559a7, not var(--tblr-primary): the stock blue measured 4.33:1 on its -lt tint at btn-sm size (below AA);
       this is 6.1:1. Dark mode keeps its own value below. */
    --tblr-btn-color: #0559a7;
    --tblr-btn-bg: var(--tblr-primary-lt);
    --tblr-btn-border-color: transparent;
    --tblr-btn-hover-color: var(--tblr-primary-fg, #fff);
    --tblr-btn-hover-bg: var(--tblr-primary);
    --tblr-btn-hover-border-color: transparent;
    --tblr-btn-active-color: var(--tblr-primary-fg, #fff);
    --tblr-btn-active-bg: var(--tblr-primary-darken, var(--tblr-primary));
    --tblr-btn-active-border-color: transparent;
    --tblr-btn-disabled-color: #0559a7;
    --tblr-btn-disabled-bg: var(--tblr-primary-lt);
}
.btn-soft-secondary {
    --tblr-btn-color: var(--tblr-secondary);
    --tblr-btn-bg: var(--tblr-secondary-lt);
    --tblr-btn-border-color: transparent;
    --tblr-btn-hover-color: var(--tblr-secondary-fg, #fff);
    --tblr-btn-hover-bg: var(--tblr-secondary);
    --tblr-btn-hover-border-color: transparent;
    --tblr-btn-active-color: var(--tblr-secondary-fg, #fff);
    --tblr-btn-active-bg: var(--tblr-secondary-darken, var(--tblr-secondary));
    --tblr-btn-active-border-color: transparent;
    --tblr-btn-disabled-color: var(--tblr-secondary);
    --tblr-btn-disabled-bg: var(--tblr-secondary-lt);
}
.btn-soft-success {
    --tblr-btn-color: var(--tblr-success);
    --tblr-btn-bg: var(--tblr-success-lt);
    --tblr-btn-border-color: transparent;
    --tblr-btn-hover-color: var(--tblr-success-fg, #fff);
    --tblr-btn-hover-bg: var(--tblr-success);
    --tblr-btn-hover-border-color: transparent;
    --tblr-btn-active-color: var(--tblr-success-fg, #fff);
    --tblr-btn-active-bg: var(--tblr-success-darken, var(--tblr-success));
    --tblr-btn-active-border-color: transparent;
    --tblr-btn-disabled-color: var(--tblr-success);
    --tblr-btn-disabled-bg: var(--tblr-success-lt);
}
.btn-soft-warning {
    /* NOT var(--tblr-warning) for text: that amber is 2.1:1 on the -lt tint
       (same finding as .note-warning / .dash-attention-chip). */
    --tblr-btn-color: #9a6700;
    --tblr-btn-bg: var(--tblr-warning-lt);
    --tblr-btn-border-color: transparent;
    --tblr-btn-hover-color: var(--tblr-warning-fg, #fff);
    --tblr-btn-hover-bg: var(--tblr-warning);
    --tblr-btn-hover-border-color: transparent;
    --tblr-btn-active-color: var(--tblr-warning-fg, #fff);
    --tblr-btn-active-bg: var(--tblr-warning-darken, var(--tblr-warning));
    --tblr-btn-active-border-color: transparent;
    --tblr-btn-disabled-color: #9a6700;
    --tblr-btn-disabled-bg: var(--tblr-warning-lt);
}
.btn-soft-danger {
    /* #c92a2a: stock danger red measured 4.46:1 on light tints (see .btn-ghost-danger). */
    --tblr-btn-color: #c92a2a;
    --tblr-btn-bg: var(--tblr-danger-lt);
    --tblr-btn-border-color: transparent;
    --tblr-btn-hover-color: var(--tblr-danger-fg, #fff);
    --tblr-btn-hover-bg: var(--tblr-danger);
    --tblr-btn-hover-border-color: transparent;
    --tblr-btn-active-color: var(--tblr-danger-fg, #fff);
    --tblr-btn-active-bg: var(--tblr-danger-darken, var(--tblr-danger));
    --tblr-btn-active-border-color: transparent;
    --tblr-btn-disabled-color: #c92a2a;
    --tblr-btn-disabled-bg: var(--tblr-danger-lt);
}
/* Dark-theme text legibility on the darker -lt tints — same audited values as
   the link/outline/ghost overrides above. */
[data-bs-theme="dark"] .btn-soft-primary {
    --tblr-btn-color: #74b6f3;
    --tblr-btn-disabled-color: #74b6f3;
}
[data-bs-theme="dark"] .btn-soft-secondary {
    --tblr-btn-color: #98a2b3;
    --tblr-btn-disabled-color: #98a2b3;
}
[data-bs-theme="dark"] .btn-soft-warning {
    --tblr-btn-color: #f0b357;
    --tblr-btn-disabled-color: #f0b357;
}
[data-bs-theme="dark"] .btn-soft-danger {
    --tblr-btn-color: #f27d7d;
    --tblr-btn-disabled-color: #f27d7d;
}

/* --- Bill payment provider form: product/package cards collapse to a one-line
     header so a provider with many products stays scannable. Chevron points
     right when collapsed, down when expanded. Direct-child selector so a
     product's chevron is not affected by its nested package cards. */
.js-toggle-card .icon-chevron {
    transition: transform 0.15s ease;
    transform: rotate(90deg);
}
.card-collapsed > .card-header .js-toggle-card .icon-chevron {
    transform: none;
}

/* Bootstrap sizes a scrollable modal's .modal-body as a flex child of .modal-content. Our modals
   wrap header, body and footer in a <form>, which breaks that chain: the body never shrinks, so
   long content (the role permission matrix) is clipped with no scrollbar. Restore the chain on
   the form so the body scrolls and the footer stays in view. */
.modal-dialog-scrollable .modal-content > form {
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: 100%;
    overflow: hidden;
}

/* --- KYC upgrade batch: the outcome bar is the page's summary, so it is taller than a plain
     progress bar; segments animate their width as the poll moves numbers out of PENDING. The
     legend below it is the table filter — the pressed button reads as the current view. */
.kyc-outcome-bar {
    height: 14px;
}
.kyc-outcome-bar .progress-bar {
    transition: width 0.4s ease;
}
.kyc-outcome-filter .btn.active {
    background-color: var(--tblr-primary-lt);
    border-color: var(--tblr-primary);
    color: var(--tblr-primary);
}
@media (prefers-reduced-motion: reduce) {
    .kyc-outcome-bar .progress-bar {
        transition: none;
    }
}

/* --- Access activity: the stat cards are filter buttons. A Tabler card on a <button> keeps its border and
     background but needs the button's centring and font undone; the pressed card carries the primary border
     (aria-pressed is the state, the colour only shows it), and keyboard focus is always visible. */
.access-activity-card {
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}
.access-activity-card[aria-pressed="true"] {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 1px var(--tblr-primary);
}
.access-activity-card:focus-visible {
    outline: 2px solid var(--tblr-primary);
    outline-offset: 2px;
}
/* Wide enough for the Mobile tab's "Search by name, wallet or operator number" placeholder. */
.access-activity-search {
    width: 24rem;
    max-width: 100%;
}
/* The name column holds a quarter of the table and truncates inside it (renderLinkedName adds the full name as a
   title); the other columns share the rest. Taking all the slack left a wide gap after short names at desktop width,
   and a fixed cap pushed the table past the card at tablet width. max-width: 0 lets the cell shrink below its text. */
.access-activity-table td:first-child {
    width: 25%;
    max-width: 0;
    min-width: 7rem;
}
/* Tabler keeps headers on one line, so "Last verified sign-in" alone held its column at 171px and pushed the table
   past the card beside the open sidebar (1024px). Two-line headers let the dates set the column widths. */
.access-activity-table thead th {
    white-space: normal;
}
/* The three date columns, last in every tab. A column of dashes (nobody signed in yet) would otherwise shrink until
   "Last verified sign-in" broke over three lines. */
.access-activity-table thead th:nth-last-child(-n+3) {
    min-width: 7rem;
}
/* A status may wrap ("Reset, waiting for a new PIN") but not one word a line; any wider and the Integrated tab no
   longer fits beside the sidebar at 1024px. */
.access-activity-status {
    min-width: 5.5rem;
}
