/* ──────────────────────────────────────────────────────────────────────────
   SYSTEM TYPOGRAPHY — single source of truth
   ---------------------------------------------------------------------------
   One UI font (Inter) and one monospace (JetBrains Mono, for codes / reference
   numbers), both self-hosted from wwwroot/fonts so the app renders identically
   offline and on-prem with no Google Fonts round-trip. Every page should use
   the var(--tb-font-*) tokens or `inherit` — never a hardcoded family — so the
   whole system stays on one typeface. Sizes come from the --fs-* scale below.

   Inter and JetBrains Mono are variable fonts: one file spans weights 100–900,
   so `font-weight` works continuously without extra @font-face rules.
   ────────────────────────────────────────────────────────────────────────── */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/InterVariable.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: italic;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/InterVariable-Italic.woff2') format('woff2');
}
@font-face {
    font-family: 'JetBrains Mono';
    font-style: normal;
    font-weight: 100 800;
    font-display: swap;
    src: url('/fonts/JetBrainsMono.woff2') format('woff2');
}
/* Branding wordmark only (the "Truebooks" logo on login/landing). Self-hosted so
   those pages no longer reach out to Google Fonts. Not a UI font — never use it
   for body text. */
@font-face {
    font-family: 'Michroma';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/Michroma.woff2') format('woff2');
}

:root {
    /* The two families every page should reference. The fallbacks cover the
       brief moment before the woff2 loads and any glyph Inter lacks. */
    --tb-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    --tb-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* Type scale (all rem, so relative to the 16px root below). */
    --fs-xs:   0.8125rem; /* 13px — captions, meta, chips            */
    --fs-sm:   0.875rem;  /* 14px — dense table cells                */
    --fs-base: 1rem;      /* 16px — body, inputs, buttons (default)  */
    --fs-lg:   1.0625rem; /* 17px — section titles                   */
    --fs-h3:   1.1875rem; /* 19px                                    */
    --fs-h2:   1.3125rem; /* 21px                                    */
    --fs-h1:   1.5rem;    /* 24px — page headings                    */
}

/* Root font-size is the master density control. `rem` is relative to the ROOT,
   so scaling <html> proportionally resizes every rem-based dimension across the
   whole app — the MudBlazor type scale and our --fs-* tokens included. We run it
   at 93.75% = 15px (down from the 16px browser default) for a denser, less
   "Material-airy" layout without touching a single component.

   THE ONE RULE that keeps this safe and non-compounding: body must stay at
   EXACTLY 1rem. An earlier attempt set html to 0.875rem *and* left body on a
   sub-1rem --fs-base, so the two multiplied down to ~12px (the whole UI ~15% too
   small). As long as --fs-base is 1rem (above), body resolves to the root size
   with no compounding and the scale is a single clean multiplier.
   Adjust app density HERE and nowhere else. */
html { font-size: 93.75%; } /* 15px base (was 16px = 100%) */
body {
    font-family: var(--tb-font-sans);
    font-size: var(--fs-base); /* MUST remain 1rem — see note above */
}

/* Numbers in data tables line up in columns (tabular figures). Inter ships
   proportional figures by default, which makes amount columns look ragged. */
table, .mud-table, .mud-table-cell, .eva-num, .apl-num, [class*="-num"] {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* ──────────────────────────────────────────────────────────────────────────
   DENSITY PASS (step 2) — trims MudBlazor's default Material spacing app-wide so
   forms, dialogs, grids and the nav rail read tighter. Pairs with the 15px root
   above. Presentation-only: no component/API changes, and reversible by deleting
   this block to restore stock MudBlazor spacing.

   Deliberately scoped: only VERTICAL padding is cut (row height is the "air"),
   horizontal padding is left intact so columns/labels don't cramp; and inputs
   are left alone, since squeezing their internals misaligns labels/adornments.
   ────────────────────────────────────────────────────────────────────────── */
/* Table rows — the single biggest source of Material airiness. */
.mud-table-cell { padding-top: 7px !important; padding-bottom: 7px !important; }
.mud-table-dense-row .mud-table-cell { padding-top: 4px !important; padding-bottom: 4px !important; }
/* Content cards / panels. */
.mud-card-content { padding: 12px 14px !important; }
.mud-expand-panel-text { padding: 10px 14px !important; }
/* Dropdown rows, menus and nav sub-lists. */
.mud-list-item { padding-top: 6px !important; padding-bottom: 6px !important; }
/* Left navigation rail rows. */
.mud-nav-link { padding-top: 7px !important; padding-bottom: 7px !important; }

/* Ensure MudSelect/MudAutocomplete popovers and date pickers always appear above MudDialog */
.mud-popover {
    z-index: 9999 !important;
}

/* GLOBAL POPUP RULE — a dialog must never push its action row off-screen.
   These used to be `overflow: visible !important` so dropdowns could escape the
   dialog's clipping context. Side effect: a dialog taller than the viewport could
   not scroll, so its Cancel/Save row spilled below the fold and became unreachable
   (reported as "the Cancel/Save buttons don't work"). Individual dialogs were being
   patched one at a time with an inner max-height wrapper; this fixes it for all of
   them at once.

   Cap the dialog to the viewport and scroll ONLY the content area, so the title and
   the DialogActions row (Cancel/Save) stay pinned and clickable at any screen size.
   Dropdowns/date-pickers are unaffected: MainLayout renders <MudPopoverProvider />,
   so MudBlazor v7 places popovers at the document root, not inside the dialog. */
.mud-dialog {
    max-height: calc(100vh - 48px);
    display: flex;
    flex-direction: column;
}

.mud-dialog-content {
    overflow-y: auto !important;
    flex: 1 1 auto;
    min-height: 0;
}

/* Title and action rows never shrink or scroll away. */
.mud-dialog > .mud-dialog-title,
.mud-dialog > .mud-dialog-actions {
    flex-shrink: 0;
}

.cursor-pointer {
    cursor: pointer;
}

.mud-nav-link.active {
    background-color: rgba(var(--mud-palette-primary-rgb), 0.12);
    border-left: 4px solid var(--mud-palette-primary);
}

.mud-drawer-content {
    overflow-y: auto;
    overflow-x: hidden;
    max-height: calc(100vh - 64px);
}

.mud-main-content {
    overflow-y: auto;
    height: calc(100vh - 64px);
    padding-bottom: 24px;
}

.mud-nav-menu {
    overflow-y: auto;
    overflow-x: hidden;
}

.action-icons-wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    max-width: 160px;
    align-items: center;
}

.action-icons-wrap .mud-icon-button {
    flex-shrink: 0;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="time"],
input[type="datetime-local"] {
    max-height: 30px;
    padding-top: 4px;
    padding-bottom: 4px;
}

input[type="number"] {
    text-align: right;
}

/* The "dd/mm/yyyy" hint in an empty date field is drawn by the BROWSER, not by
   us — a native date input has no placeholder attribute to set. text-transform
   does inherit into Chrome's internal date-field parts, so this is what turns
   the hint into DD/MM/YYYY. Cosmetic only: once a date is chosen the field
   shows digits and separators, which uppercase leaves untouched. */
input[type="date"],
input[type="time"],
input[type="datetime-local"] {
    text-transform: uppercase;
}

.btn-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 16px;
    background-color: var(--mud-palette-primary, #1976d2);
    color: var(--mud-palette-primary-text, #fff);
    border: none;
    border-radius: 6px;
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: opacity 0.2s ease;
    white-space: nowrap;
}

.btn-primary:hover:not(:disabled) {
    opacity: 0.88;
}

.btn-primary:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 16px;
    background-color: #6c757d;
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: opacity 0.2s ease;
    white-space: nowrap;
}

.btn-secondary:hover:not(:disabled) {
    opacity: 0.88;
}

.btn-secondary:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 4px;
    background: none;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    color: inherit;
    transition: background-color 0.2s ease;
}

.btn-icon:hover:not(:disabled) {
    background-color: rgba(0, 0, 0, 0.08);
}

.btn-icon:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* ── MudBlazor outlined input label — white patch fix ───────────────────────
   The floating label on Variant.Outlined MudTextField/MudSelect needs a
   background colour to cover the fieldset border behind it. Without an
   explicit value the browser falls back to white, which shows as a bright
   patch on any non-white surface (dialogs, cards, coloured panels, etc.).
   Using --mud-palette-surface (= #fff in the default light theme) keeps the
   label invisible on white backgrounds and lets per-page overrides work via
   a single CSS variable.                                                     */
.mud-input-label-outlined {
    background-color: var(--mud-palette-surface, #fff);
    padding: 0 4px;
}

/* The <legend> element inside the outlined <fieldset> creates the notch gap
   in the border. It must stay transparent — it is never directly visible.   */
.mud-input-outlined-border legend {
    background: transparent !important;
}

/* Keyboard shortcut hint badge — shown inside action buttons to surface
   discoverable hotkeys (e.g. Alt+S, Alt+N, Alt+L).                          */
kbd.kb-hint {
    display: inline-block;
    font-family: var(--tb-font-mono);
    font-size: 0.60rem;
    line-height: 1;
    padding: 2px 5px;
    border: none;
    border-radius: 3px;
    background: rgba(0, 0, 0, 0.28);
    color: #fff;
    opacity: 1;
    vertical-align: middle;
    margin-left: 6px;
    letter-spacing: 0.05em;
    font-weight: 700;
    white-space: nowrap;
}

/* =====================================================
   Suppress Blazor's built-in reconnect overlay entirely.
   We use our own #tb-reconnect-banner instead.
   ===================================================== */
#components-reconnect-modal {
    display: none !important;
}

/* =====================================================
   Truebooks custom reconnect banner - non-intrusive
   slim bar at the top, fully under our control.
   ===================================================== */
#tb-reconnect-banner {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    background: #1565c0;
    color: #fff;
    font-size: 13px;
    text-align: center;
    padding: 6px 16px;
    box-shadow: 0 2px 6px rgba(0,0,0,0.3);
    align-items: center;
    justify-content: center;
    gap: 8px;
}
#tb-reconnect-banner.tb-reconnect-show {
    display: flex;
}
#tb-reconnect-banner.tb-reconnect-failed {
    display: flex;
    background: #b71c1c;
}

@keyframes tb-spin {
    to { transform: rotate(360deg); }
}
.tb-reconnect-spinner {
    display: inline-block;
    width: 13px;
    height: 13px;
    border: 2px solid rgba(255,255,255,0.4);
    border-top-color: #fff;
    border-radius: 50%;
    animation: tb-spin 0.8s linear infinite;
    flex-shrink: 0;
}
#tb-reconnect-banner.tb-reconnect-failed .tb-reconnect-spinner {
    display: none;
}

/* =====================================================
   Truebooks Standard Design System (Task #1024)
   Tokens — keep in sync with TbTheme.cs
   ===================================================== */
:root {
    --tb-color-create:        #007BFF;
    --tb-color-save:          #28A745;
    --tb-color-update:        #90EE90;
    --tb-color-cancel:        #000000;
    --tb-color-void:          #FFC0CB;
    --tb-color-delete:        #DC3545;
    --tb-color-on-action:     #FFFFFF;

    --tb-border:              #E2E8F0;
    --tb-border-input:        #CBD5E1;
    --tb-shadow-sm:           0 1px 2px rgba(15,23,42,0.06);
    --tb-shadow-md:           0 2px 8px rgba(15,23,42,0.08);
    --tb-hover:               #F1F5F9;

    --tb-btn-h:               36px;
    --tb-btn-px:              18px;
    --tb-btn-radius:          6px;
    --tb-btn-font:            14px;
    --tb-btn-icon-gap:        8px;

    --tb-grid-btn-h:          30px;
    --tb-grid-btn-font:       13px;
}

/* StandardActionButton */
.tb-std-btn .mud-button-root,
.tb-std-btn {
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-weight: 500;
}
.tb-std-btn .mud-button-icon-start { margin-right: var(--tb-btn-icon-gap); }
.tb-std-btn:hover { filter: brightness(0.94); }
.tb-std-btn:disabled, .tb-std-btn.mud-disabled { opacity: 0.55; }

/* GridActionButton */
.tb-grid-btn { color: inherit; }

/* StandardPageHeader */
.tb-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0 16px 0;
    border-bottom: 1px solid var(--tb-border);
    margin-bottom: 16px;
}
.tb-page-header-left {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.tb-page-title {
    font-weight: 600;
    color: #1f2937;
    line-height: 1.2;
    margin: 0;
}
.tb-page-subtitle {
    color: #64748b;
    margin: 0;
}
.tb-page-header-right {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: flex-end;
}
.tb-page-search {
    display: flex;
    align-items: center;
    background: #fff;
    border: 1px solid var(--tb-border-input);
    border-radius: var(--tb-btn-radius);
    padding: 0 10px;
    height: var(--tb-btn-h);
    min-width: 220px;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.tb-page-search:focus-within {
    border-color: var(--tb-color-create);
    box-shadow: 0 0 0 2px rgba(0,123,255,0.15);
}
.tb-page-search-icon { color: #94a3b8; margin-right: 6px; }
.tb-page-search-input {
    border: none;
    outline: none;
    background: transparent;
    font-size: var(--tb-btn-font);
    flex: 1;
    min-width: 100px;
    height: 100%;
    color: #1f2937;
}
.tb-page-search-input::placeholder { color: #94a3b8; }
.tb-page-filter-strip {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* ── Journal Vouchers single-row filter bar ─────────────────────────────── */
.jv-filter-row {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 16px;
}

/* Search box: fixed height so it bottom-aligns with the MudBlazor inputs */
.jv-filter-search {
    min-width: 200px;
    height: 40px;          /* matches MudBlazor Dense+Outlined input height   */
    align-self: flex-end;
    margin-bottom: 1px;    /* visual nudge to sit flush with input underlines  */
}

/* Status autocomplete: constrain width */
.jv-filter-select {
    min-width: 140px;
    max-width: 180px;
}

/* Date pickers: consistent width */
.jv-filter-date {
    min-width: 148px;
    max-width: 168px;
}

/* Apply + Clear buttons sit side-by-side, bottom-aligned */
.jv-filter-actions {
    display: flex;
    flex-direction: row;
    gap: 8px;
    align-self: flex-end;
    margin-bottom: 1px;
}

/* =====================================================================
   Mobile / small-screen layer (global — app.css loads on every page).
   Keeps dashboards and data-dense pages usable on phones & tablets:
   charts never exceed the screen, and dense tables scroll inside their
   own card instead of widening the whole page. Desktop (>960px) is
   untouched. MudTables that opt into Breakpoint stacking render as
   labelled cards and are unaffected by the table-scroll rule below.
   ===================================================================== */
@media (max-width: 960px) {
    .mud-chart,
    .mud-chart svg,
    .apexcharts-canvas,
    canvas {
        max-width: 100% !important;
    }

    .mud-table-container,
    .mud-simple-table,
    .mud-data-grid {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}

/* Include Voided checkbox — nudge down to align with button midpoints */
.jv-filter-checkbox-wrap {
    align-self: flex-end;
    margin-bottom: 2px;
}
.jv-filter-checkbox-wrap .mud-checkbox {
    margin: 0;
    padding: 0;
}
.jv-filter-checkbox-wrap .mud-form-control {
    margin: 0;
}
.tb-page-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* IconFilter */
.tb-icon-filter {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: var(--tb-btn-h);
    width: var(--tb-btn-h);
    border: 1px solid var(--tb-border-input);
    border-radius: var(--tb-btn-radius);
    background: #fff;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.tb-icon-filter:hover { background: var(--tb-hover); border-color: #94a3b8; }
.tb-icon-filter-icon { color: #475569; }
.tb-icon-filter-content {
    position: absolute;
    inset: 0;
    opacity: 0;
}
.tb-icon-filter-content > * {
    width: 100%;
    height: 100%;
    cursor: pointer;
}
.tb-icon-filter-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--tb-color-create);
}

/* Keyboard-highlighted row in the lightweight autocomplete dropdowns
   (autocompleteKeys.js — ArrowUp/Down move it, Enter selects it). Slightly
   stronger than the mouse :hover shade so the keyboard position stays visible. */
.autocomplete-item.active {
    background: #dbeafe;
}

/* ──────────────────────────────────────────────────────────────────────────
   GLOBAL PRINT / PDF-EXPORT RULES  (applies to EVERY page's browser print +
   "Save/Print to PDF", so every exported document is clean and system-wide)

   Fixes three things seen on exported PDFs (e.g. Proforma Invoice):
     1. The floating AI-help chat FAB ("two circles") printed on the document.
     2. A scrollbar strip captured down the right edge.
     3. Blurry, non-selectable text — the chat FAB is position:fixed with an
        INFINITE animation (pulse-glow), whose GPU compositing layer forces the
        print engine ("Microsoft Print to PDF" / Chromium) to rasterize the
        entire page to JPEG. Hiding it lets the page print as crisp vector text.
   Only affects @media print — screen rendering is untouched.
   ────────────────────────────────────────────────────────────────────────── */
@media print {
    /* Floating / fixed app chrome that must never appear on a printed document */
    .truebooks-chat-wrapper,
    .wa-wrapper,
    .mud-appbar,
    .mud-drawer,
    .mud-drawer-mini,
    .mud-nav-menu,
    .mud-overlay,
    .mud-snackbar-provider,
    .mud-snackbar,
    .mud-fab,
    .mobile-search-overlay,
    .mobile-search-results-panel,
    .mobile-search-btn,
    .no-print,
    nav {
        display: none !important;
    }

    /* No scrollbars, no clipping — let the document flow to its natural height so
       no scroll strip is captured and nothing is cut off across pages. */
    html, body,
    #app,
    .mud-layout,
    .mud-main-content,
    .mud-content,
    .page,
    .main {
        overflow: visible !important;
        height: auto !important;
        max-height: none !important;
        background: #fff !important;
    }
    .mud-main-content { padding: 0 !important; margin: 0 !important; }

    /* Hard-hide any residual scrollbars (WebKit/Chromium + Firefox). */
    ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }
    * { scrollbar-width: none !important; }

    /* Print background colours / accent bands faithfully. */
    * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
}

/* ──────────────────────────────────────────────────────────────────────────
   AP / AR registry list pages — shared "Customer Ledger" report look (apl- prefix).
   Header eyebrow/title/subtitle, accent-bar KPI cards, a "Filter Parameters"
   card, and a table/grid reskin — used by the Purchase Bill, Debit Note,
   Supplier Invoice and Supplier Payment registries.
   ────────────────────────────────────────────────────────────────────────── */
.apl-wrap { max-width:1480px; margin:12px auto 36px; padding:0 18px; color:#0f172a;
    font-family:var(--tb-font-sans); }
.apl-pghead { display:flex; justify-content:space-between; align-items:flex-start; gap:14px; flex-wrap:wrap; margin-bottom:16px; }
.apl-eyebrow { font-size:0.7rem; font-weight:600; color:#2563eb; letter-spacing:0.08em; text-transform:uppercase; }
.apl-title { font-size:1.4rem; font-weight:600; letter-spacing:-0.01em; margin-top:2px; color:#0f172a; }
.apl-sub { font-size:0.78rem; color:#64748b; margin-top:2px; }
.apl-headbtns { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
/* The register pages' primary action (New Proforma Invoice / Enter Supplier Bill /
   New Payment / Create Receipt / New Debit Note …) rendered at MudBlazor's default
   button size, which is oversized for a page-header action. Sized down here once so
   every apl- register page matches, rather than per page. */
.apl-headbtns .mud-button-root {
    height: 32px;
    min-height: 32px;
    padding: 0 14px;
    font-size: 0.74rem;
    letter-spacing: 0.02em;
}
.apl-headbtns .mud-button-root .mud-icon-root { font-size: 1rem; }
.apl-headbtns .mud-button-root .mud-button-icon-start { margin-right: 6px; }

.apl-card { background:#fff; border:1px solid #e2e8f0; border-radius:16px; box-shadow:0 1px 2px rgba(0,0,0,0.04); }

.apl-kpis { display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-bottom:12px; }
@media (max-width:760px){ .apl-kpis{ grid-template-columns:repeat(2,1fr);} }
.apl-kpi { position:relative; background:#fff; border:1px solid #e2e8f0; border-radius:12px;
    padding:8px 12px 9px 15px; box-shadow:0 1px 2px rgba(0,0,0,0.04); overflow:hidden; }
.apl-kpi::before { content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:#94a3b8; }
.apl-kpi .apl-k-l { font-size:0.6rem; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:#64748b; }
.apl-kpi .apl-k-v { font-size:1.08rem; font-weight:600; line-height:1.1; margin-top:4px; color:#0f172a; font-variant-numeric:tabular-nums; }
.apl-kpi .apl-k-s { font-size:0.58rem; color:#94a3b8; margin-top:4px; }
.apl-k-blue::before{ background:#6366f1;} .apl-k-blue .apl-k-l{ color:#4f46e5;}
.apl-k-amber::before{ background:#f59e0b;} .apl-k-amber .apl-k-l{ color:#b45309;}
.apl-k-rose::before{ background:#ef4444;} .apl-k-rose .apl-k-l{ color:#be123c;}
.apl-k-green::before{ background:#10b981;} .apl-k-green .apl-k-l{ color:#15803d;}
.apl-k-gray::before{ background:#94a3b8;} .apl-k-gray .apl-k-l{ color:#64748b;}

.apl-filter { margin-bottom:16px; overflow:hidden; }
.apl-fhead { display:flex; align-items:center; gap:11px; padding:10px 16px; }
.apl-fac { width:4px; height:20px; border-radius:2px; background:#2563eb; }
.apl-ftitle { font-size:0.82rem; font-weight:600; color:#0f172a; }
.apl-fbody { padding:10px 16px 12px; border-top:1px solid #f1f5f9; }
.apl-fbody .mud-input-control, .apl-fbody .mud-form-control { margin:0 !important; }
.apl-fbody .mud-input-outlined fieldset { border-color:#e2e8f0 !important; border-radius:10px !important; }
.apl-fbody .mud-input-outlined:focus-within fieldset { border-color:#2563eb !important; }

.apl-tcap { padding:10px 16px; border-bottom:1px solid #f1f5f9; display:flex; align-items:center; gap:9px; }
.apl-dot { width:9px; height:9px; border-radius:50%; background:#2563eb; }
.apl-tcap h2 { font-size:0.78rem; font-weight:600; text-transform:uppercase; letter-spacing:0.03em; margin:0; color:#0f172a; }
.apl-count { margin-left:auto; font-size:0.62rem; font-weight:600; color:#64748b; background:#f1f5f9; border-radius:9999px; padding:2px 10px; }

.apl-grid { overflow:hidden; }
.apl-grid table { border-collapse:separate; border-spacing:0; }
.apl-grid thead th, .apl-grid .mud-table-head th {
    background:#f8fafc !important; color:#64748b !important; font-size:0.6rem !important; font-weight:600 !important;
    text-transform:uppercase; letter-spacing:0.04em; border-bottom:1px solid #e2e8f0 !important;
    border-left:none !important; border-right:none !important; padding:7px 12px !important; }
.apl-grid tbody td, .apl-grid .mud-table-row td {
    border-left:none !important; border-right:none !important; border-bottom:1px solid #f1f5f9 !important;
    padding:7px 12px !important; font-size:0.78rem; color:#0f172a; }
.apl-grid tbody tr:hover td, .apl-grid .mud-table-row:hover td { background:#f8fafc !important; }
.apl-money { text-align:right; font-variant-numeric:tabular-nums; font-weight:600; }
.apl-link { color:#2563eb; font-weight:600; cursor:pointer; text-decoration:none; }
.apl-link:hover { text-decoration:underline; }

.apl-foot { padding:12px 20px; background:#f8fafc; border-top:1px solid #f1f5f9; display:flex;
    justify-content:space-between; gap:10px; font-size:0.72rem; color:#94a3b8; flex-wrap:wrap; }

/* ── Keyboard focus ────────────────────────────────────────────────────────────
   Until this existed, :focus-visible was defined in four files in the whole app, so
   focus landed with nothing to show for it. Every "Tab doesn't go to this button"
   report on 2026-09-04 — the page tabs, the Category toggle, Create Location,
   Template / Import, Add Item, Post Comment — turned out to be a control that Tab
   ALREADY reached. Nothing marked it, so it read as skipped.

   :focus-visible, not :focus — the browser only matches it for keyboard interaction,
   so a mouse click looks exactly as it did before. That is the whole reason this can
   be applied globally without changing how the app looks in normal use.

   Deliberately last in the file so it wins over the many controls that set
   `border: none` or their own background, which is what made the default ring so easy
   to miss in the first place. */
:where(a[href], button, input, select, textarea, summary, [role="button"], [role="tab"],
       [role="menuitem"], [tabindex]:not([tabindex="-1"])):focus-visible {
    /* A TWO-TONE ring, because one colour cannot be seen everywhere. The dark outline
       reads against white and against the pale cards; the white box-shadow fills the
       2px offset gap between the control and that outline, which is what makes the
       outline readable when the control itself is dark or strongly coloured — the
       purple Upload button, the blue UPDATE, the green SAVE. With a single blue ring
       those buttons showed a blue line on a blue-purple fill and read as "Tab skipped
       it", which is how this was reported on 2026-09-05. */
    outline: 2px solid #1d4ed8;
    outline-offset: 2px;
    box-shadow: 0 0 0 2px #ffffff;
    border-radius: 3px;
}

/* Controls sitting on the dark app bar need the two tones the other way round. */
.tb-appbar :where(a[href], button, input, [role="button"]):focus-visible,
.mud-appbar :where(a[href], button, input, [role="button"]):focus-visible {
    outline-color: #ffffff;
    box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.55);
}

/* The two top-bar search inputs are TRANSPARENT children of a rounded box that
   already reacts to focus. Ringing the input as well drew a dark rectangle inside
   that box, which is what was reported on 2026-09-07 as a black background on the
   menu and document search. Leave the input unmarked and ring the BOX instead, so
   keyboard focus is still obvious without painting over the control. */
.tb-appbar :where(.cmd-search-input, .doc-search-input):focus-visible,
.mud-appbar :where(.cmd-search-input, .doc-search-input):focus-visible {
    outline: none;
    box-shadow: none;
}
:where(.cmd-search-box, .doc-search-box):has(:focus-visible) {
    outline: 2px solid #ffffff;
    outline-offset: 1px;
}

/* SOLID-FILLED buttons — Upload, UPDATE, SAVE, CANCEL, and every coloured action
   button. On a 30px button an outline 2px away is a thin line the eye slides over,
   which is how the Upload button read as "focus is there but I cannot see it".
   A white ring painted INSIDE the button's own fill cannot be missed and cannot be
   clipped by anything, because it is drawn on the button itself rather than in the
   space around it. Buttons only — an inset ring on a white input would paint white
   on white and do nothing useful. */
:where(button, a[href], [role="button"]):focus-visible {
    box-shadow: 0 0 0 2px #ffffff, inset 0 0 0 2px #ffffff;
}

/* Borderless icon buttons — the row actions in every CRM grid (download / print /
   delete / edit). They have no background and no border, so a ring alone is a thin
   outline around a 20px glyph. Filling them on focus is what makes the position
   obvious at a glance, and it matches what hover already does. */
.doc-action-btn:focus-visible {
    background: #eff6ff;
    color: #1d4ed8;
}

/* A control whose focus is drawn by its own wrapper (a file input hidden inside its
   label) hands the ring to the wrapper instead of painting an invisible box. */
label:has(> input[type="file"]:focus-visible) {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}
