/* ============================================================================
 * SlowBooks Pro 2026 — UI Stylesheet
 * ============================================================================ */

/* Skip-to-content link — first tab stop on every page, hidden until focused.
   Lets keyboard / screen-reader users jump past the topbar + sidebar straight
   into the main content area. */
.skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    background: var(--qb-blue, #1e3a8a);
    color: #fff;
    padding: 8px 16px;
    z-index: 10000;
    text-decoration: none;
    font-weight: 600;
}
/* Shown, it draws the keyboard's ring and halo (below), 8px in from the
   corner: at the corner itself the ring's top and left lines fell off the
   window, and it kept the brand's paler gold, 1.95:1 against the toolbar
   it lands on (review of NEW-43, 2.22.0). It lands on the toolbar but is
   not inside it, so it takes the toolbar's own deeper gold (#topbar,
   below): the theme's #a37a29 is 2.51:1 against the bar's darker stop.
   Light only: dark.css puts the dark theme's gold back. */
.skip-link:focus {
    top: 8px;
    left: 8px;
    --focus-ring: #8f6a1e;
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 8px var(--focus-halo);
}
/* A form's or page's own skip link (#266): out of the way until the
   keyboard reaches it, then shown where it sits, above what it skips. */
.skip-link--inline {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    top: auto;
    left: auto;
    font-size: 12px;
}
.skip-link--inline:focus {
    position: static;
    display: inline-block;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    margin: 0 0 8px;
    padding: 6px 12px;
    box-shadow: 0 0 0 4px var(--focus-halo);
}

/* Focus is visible on every control (WCAG 2.4.7). Links and buttons had
   no focus style of their own and relied on the browser's ring, which
   WebKit, with the Mac's keyboard navigation off as it ships, draws
   faintly on buttons and not at all on links: Tab walked A/R Aging's
   customers with nothing to show where it was (macOS gate NEW-43, 2.22.0).
   Links, buttons, checkboxes, radios, a file's picker and whatever else
   the keyboard reaches draw the skip link's ring, in gold (--focus-ring,
   each theme's own), on :focus-visible only, so a mouse click on a button
   or a link draws none. A field keeps its own focus style (the blue border and pale
   ground, below), so a dense form gets no ring on a click into a box.
   The ring is two colours (WCAG 1.4.11: 3:1 against what it meets). The
   gold alone fell under 3:1 wherever the ground is neither light nor dark:
   the toolbar's grey gradient (2.54:1), a dialog's blue title bar (1.73),
   a table's header row, the grey edge of a field beside it. So it has a
   halo of the theme's own ground on both its sides (--focus-halo: white in
   light, 3.9:1 against the gold; the page's dark in dark, 8.5), whatever
   lies beyond: a box-shadow out to 8px, the ring drawn over it from 2px to
   4px, so the halo is also what lies 1-4px past the ring, where the macOS
   gate reads a ring's ground. A link in running text has it too: with 2px
   past its ring, the gate read the line or the table header beyond (the
   General Ledger's account names, 2.24:1, 1.02 in dark; Settings'
   developer.intuit.com, 1.44; a permit's lookup link, 1.36), so while it
   has focus its halo covers a little of the lines above and below it.
   The control is lifted (position: relative, no offset, so nothing moves)
   to paint after what follows it: a field just below a button painted
   over its halo.
   All in :where(), the rule counts for nothing against a selector of a
   control's own: the grid's and the search results' own rings win (bare,
   [tabindex]:not([tabindex="-1"]):focus-visible was (0,3,0) and beat
   their (0,2,0)), and so does any rule that places an element (the skip
   link stays absolute). */
:where(a:focus-visible,
       button:focus-visible,
       summary:focus-visible,
       [tabindex]:not([tabindex="-1"]):focus-visible,
       input[type="checkbox"]:focus-visible,
       input[type="radio"]:focus-visible,
       input[type="file"]:focus-visible) {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 8px var(--focus-halo);
    position: relative;
}
/* Tab scrolls a control into view with room for its ring and halo: at a
   scrolling dialog's edge (New Customer's Create Customer) their outer
   side was cut off. */
:where(a, button, summary, [tabindex]:not([tabindex="-1"]),
       input[type="checkbox"], input[type="radio"], input[type="file"]) {
    scroll-margin: 8px;
}
/* What takes focus by script alone — the dialog itself (#modal, when its
   first control is Void or Delete) and the skip link's target — is a place,
   not a control: never outlined. */
[tabindex="-1"]:focus {
    outline: none;
}

/* ============================================================================
 * BUILD NOTE: The yellow invoice texture (RT_BITMAP 0x012C) was a 4x4 px
 * tiled BMP in the original. We recreated it as a CSS gradient. Close enough.
 * ============================================================================ */

:root {
    /* -- the classic "Default Blue" palette -- */
    --qb-navy:        #003366;
    --qb-navy-dark:   #002244;
    --qb-navy-mid:    #004488;
    --qb-blue:        #336699;
    /* the top of the primary-button and dialog-title gradients, under white
       text: #4a7fb5 was 4.20:1, this is 4.56 */
    --qb-blue-light:  #4779ad;
    --qb-blue-accent: #5b9bd5;
    /* Link colour. There was no rule for a bare <a> at all, so the browser
       chose — and its default #0000EE sits at 1.73:1 on the dark panel,
       against a product documenting WCAG AA. Attachment filenames, the
       SimpleFIN bridge link and "Get a key" are all bare anchors.
       Measured: 12.6:1 here, 7.2:1 in dark. (macbase1, 2.10.3 gate) */
    --text-link:      #003366;
    --text-success:   #166534;   /* 7.13:1 on white */
    /* Chart colours: a chart's lines, bars and keys are graphics, 3:1 on the
       card (WCAG 1.4.11); every one here clears it on white. */
    --chart-green: #0a9a6c;
    --chart-red: #e5484d;
    --chart-orange: #d9730d;
    --chart-amber: #cc6a0a;
    --chart-crimson: #d63240;
    --chart-blue: #4c6ef5;
    --chart-purple: #8b5cf6;
    --chart-pink: #c026d3;
    --chart-sky: #0b8bc4;
    --chart-yellow: #b08900;
    /* The A/R aging bands, for the bar and the key beside each label, so
       the two stay matched. A key is a graphic: 3:1 on the card (WCAG
       1.4.11). Gold #cc9933 was 2.57 and orange #f97316 2.80 (macbase1
       NEW-11); these are 3.13 and 3.11. */
    --aging-current:  var(--success);   /* 3.57 */
    --aging-30:       #b88a2e;
    --aging-60:       #f36706;
    --aging-90:       var(--danger);    /* 5.14 */
    /* Red and amber for words, as --text-success is green for them: the
       pages wrote #a4242b, #c0392b, #a8761f and #b45309 in by hand, which
       the dark theme could not change (2.22:1 and 4.09 there) */
    --text-danger:    #a4242b;   /* 7.33:1 on white, 6.41 on a pale red note */
    --text-warning:   #92400e;   /* 7.09:1 on white */
    /* Only the sidebar's section headings use it, on the navy: #669999 was
       3.95:1 there, this is 4.57 (macOS gate's contrast sweep, 2.18.0) */
    --qb-teal:        #76a4a4;
    /* Green and orange are text (report amounts, import results) and the
       success toast's ground: #339966 was 3.57:1 on white and #cc6633 3.81.
       2.23.1 (#308's release): the green is a leaf green, no longer the
       teal-leaning #277650 the QuickBooks palette gave it, and carries
       SlowBooks' own name. It is 5.91:1 on white, 5.60 on a striped
       row, 5.30 on a totals row's tint and 4.86 on a hovered one
       (AA's 4.5 everywhere a figure sits), and white reads on it at
       5.91 for the toast. The orange is 4.82 on white. */
    --sb-green:       #257321;
    --sb-green-dark:  #21651d;
    --qb-gold:        #cc9933;
    /* The keyboard's ring (NEW-43): a deeper gold than the brand's, so it
       clears 3:1 against what it sits on (WCAG 1.4.11): 3.9:1 on white,
       3.2:1 on the navy sidebar. #cc9933 was 2.57:1 on white. Its halo,
       on both its sides, is white: 3.9:1 against it on any ground (the
       sidebar's is its own navy, 4.1:1: #sidebar below). */
    --focus-ring:     #a37a29;
    --focus-halo:     #ffffff;
    --qb-red:         #cc3333;
    --qb-orange:      #b2592d;

    /* -- Form backgrounds (the famous tinted papers) -- */
    --invoice-bg:     #fffef5;    /* yellow tint — RT_BITMAP 0x012C */
    --estimate-bg:    #f5fff8;    /* green tint — RT_BITMAP 0x012D */
    --form-bg:        #f7f8fa;

    /* -- Chrome colors -- */
    --toolbar-bg:     #d6dfe8;
    --toolbar-border: #a8b8c8;
    --content-bg:     #eef1f5;
    --panel-bg:       #ffffff;
    --panel-border:   #c0c8d0;
    --input-border:   #7f9db9;
    --input-focus:    #3366cc;

    /* -- Text -- */
    --text-primary:   #1a1a2e;
    --text-secondary: #4a4a6a;
    /* #7a7a9a was 4.14:1 on white, 3.65 on the page and 3.25 on a page
       header's gradient. 4.59 at that gradient's darkest, 5.85 on white */
    --text-muted:     #626282;
    --text-light:     #6a6a88;   /* was #9a9ab0, 2.75:1 on white; 5.21 */
    --text-on-dark:   #e8ecf0;
    --text-on-navy:   #c8d8e8;

    /* -- Functional -- */
    --success:        #339966;
    --warning:        #cc9933;
    --danger:         #cc3333;

    /* -- Gray scale (used by JS components and dark theme overrides) -- */
    --primary-light: #e8ecf2;
    --gray-50:  #f9fafb;
    --gray-100: #f3f4f6;
    --gray-200: #e5e7eb;
    --gray-300: #d1d5db;
    /* Text only. #6b7280 was 4.27:1 on the page, 3.79 on a page header and
       3.59 on a toolbar; 4.62 at the toolbar, 6.22 on white. (#9ca3af before
       it was 2.5:1 on white.) */
    --gray-400: #5b616d;
    --gray-500: #5b616d;
    --gray-600: #4b5563;

    --sidebar-width: 200px;
    --topbar-height: 40px;
    --statusbar-height: 24px;
    --font: Tahoma, 'Segoe UI', Geneva, Verdana, sans-serif;
    --font-mono: 'Consolas', 'Courier New', monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
    font-family: var(--font);
    background: var(--content-bg);
    color: var(--text-primary);
    font-size: 12px;
    line-height: 1.4;
    overflow: hidden;
}

#app {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

/* ========================================================================
 * TOP BAR — menu row with 24x24 toolbar buttons
 * ======================================================================== */

#topbar {
    height: var(--topbar-height);
    background: linear-gradient(180deg, #e8edf2 0%, #c8d0dc 100%);
    border-bottom: 1px solid #a0a8b8;
    display: flex;
    align-items: center;
    padding: 0 12px;
    gap: 2px;
    flex-shrink: 0;
    /* No z-index of its own: as a flex item that made the bar a stacking
       context, which kept its Back button under a dialog's overlay (200).
       The search dropdown carries its own z-index. */
    /* The keyboard's ring on the bar is the Back border's deeper gold:
       3.2:1 against the bar's darker stop, 4.2 against its lighter one and
       4.9 against the white halo, so the gold clears the bar itself as well
       as its halo (#a37a29 is 2.5 against the darker stop). Light only:
       dark.css puts the dark theme's gold back. */
    --focus-ring: #8f6a1e;
}

.topbar-company {
    font-weight: 700;
    font-size: 12px;
    padding: 0 10px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 260px;
}
.topbar-company:empty { display: none; }
.topbar-company:empty + .tb-sep { display: none; }

.topbar-brand {
    font-size: 13px;
    font-weight: 700;
    color: var(--qb-navy);
    margin-right: 16px;
    letter-spacing: -0.02em;
    white-space: nowrap;
    user-select: none;
}

/* The brand's year, the clock and the status bar sit on the toolbar and
   status bar gradients, which are darker than the page: muted text there
   read 2.57-2.66:1. They take the secondary text colour, as they already
   do in dark theme (macOS gate's contrast sweep, 2.18.0). */
.topbar-brand span {
    font-weight: 400;
    color: var(--text-secondary);
    font-size: 10px;
    margin-left: 4px;
}

.tb-sep {
    width: 1px;
    height: 22px;
    background: #b0b8c4;
    margin: 0 4px;
}

.tb-btn {
    padding: 3px 10px;
    font-size: 11px;
    font-family: var(--font);
    color: var(--text-secondary);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 3px;
    cursor: pointer;
    white-space: nowrap;
}

.tb-btn:hover {
    background: rgba(255,255,255,0.6);
    border-color: #b0b8c8;
}

.tb-btn:active {
    background: rgba(0,0,0,0.05);
}

/* Back (NEW-30, NEW-42): a bordered gold button reading "← Back" while
   there is somewhere to go. It was a bare grey glyph that got its border on
   hover alone, so with a card open it read as an arrow pointing at the
   brand, unusable until the pointer passed over it (macOS gate, 2.22.0
   round 2): never grey while it works, a gold border, and the navy for its
   words (11.4:1 on the pale gold tint). The border is the button's edge
   against the bar (WCAG 1.4.11, 3:1): a deep gold, 3.2:1 against the bar's
   darker stop (#c8d0dc) and 4.9:1 on white — the brand's #cc9933 is 1.9:1
   there, and the ring's #a37a29 2.5:1. Dimmed, with no border, while there
   is nowhere to go, as a browser's is; an inactive control is exempt from
   contrast (WCAG 1.4.3).
   It stands above a dialog's overlay (200), under toasts (300): a report,
   a customer's page and a drill-down are dialogs, and that is where Back
   is wanted; the rest of the bar stays under the overlay. The dialog
   itself starts below the bar (the overlay's 40px padding). */
.tb-back {
    position: relative;
    z-index: 210;
    font-weight: 700;
    line-height: 1;
    padding: 3px 10px;
    color: var(--qb-navy);
    background: #fdf3dc;
    border: 2px solid #8f6a1e;
}
/* The pointer over it: deeper still (4.4:1 on the bar's darker stop), not
   the other buttons' grey border */
.tb-back:not(:disabled):hover {
    color: var(--qb-navy);
    background: #fbe9bf;
    border-color: #75561a;
}
.tb-back:not(:disabled):active {
    background: #f3dfa8;
}
/* Under a form's overlay, not above it: a Back that discarded a half-typed
   form would be worse than none (app.js App.editingDialog). */
.tb-back.tb-back--under {
    z-index: auto;
}
.tb-btn:disabled {
    opacity: 0.45;
    cursor: default;
}
.tb-btn:disabled:hover {
    background: transparent;
    border-color: transparent;
}
/* Nowhere to go: muted and borderless, plainly not the lit button; still
   named, still in its place, so nothing shifts when it lights. */
.tb-back:disabled {
    color: var(--text-muted);
    background: transparent;
    border-color: transparent;
    opacity: 0.6;
}

/* Theme toggle — moon (light mode) and sun (dark mode) get a colored glow
   so the click target is obvious instead of disappearing into the toolbar. */
#theme-toggle {
    font-size: 15px;
    line-height: 1;
    padding: 2px 12px;
    color: #3b5987;       /* cool blue moon; #4a6fa8 was 3.27:1 on the toolbar */
    text-shadow: 0 0 6px rgba(74, 111, 168, 0.45);
    transition: color 0.15s, text-shadow 0.15s;
}
#theme-toggle:hover {
    color: #2e4d80;
    text-shadow: 0 0 10px rgba(74, 111, 168, 0.7);
    background: rgba(74, 111, 168, 0.08);
    border-color: #b0c0d8;
}

.topbar-right {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
}

.topbar-clock {
    font-size: 10px;
    font-family: var(--font-mono);
    color: var(--text-secondary);
}

/* ========================================================================
 * SIDEBAR / NAVIGATOR — left-docked panel with 32x32 icons
 * ======================================================================== */

#main-area {
    display: flex;
    flex: 1;
    overflow: hidden;
}

#sidebar {
    width: var(--sidebar-width);
    background: linear-gradient(180deg, var(--qb-navy) 0%, var(--qb-navy-dark) 100%);
    color: white;
    flex-shrink: 0;
    overflow-y: auto;
    border-right: 1px solid #001a33;
    display: flex;
    flex-direction: column;
    /* the keyboard's ring meets the sidebar's own navy here, not white:
       4.1:1 against the gold, and no white box on the navy */
    --focus-halo: var(--qb-navy-dark);
}

.sidebar-header {
    padding: 14px 14px 10px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    background: rgba(0,0,0,0.15);
}

.sidebar-header h1 {
    font-size: 15px;
    font-weight: 700;
    color: #ffffff;
    letter-spacing: -0.03em;
    text-shadow: 0 1px 2px rgba(0,0,0,0.3);
}

.sidebar-edition {
    font-size: 9px;
    font-weight: 700;
    color: var(--qb-gold);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-top: 2px;
}

.nav-links {
    list-style: none;
    padding: 4px 0;
    flex: 1;
}

.nav-section {
    padding: 10px 14px 3px;
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--qb-teal);
    font-weight: 700;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 14px;
    color: var(--text-on-navy);
    text-decoration: none;
    font-size: 11px;
    transition: background 0.1s;
    border-left: 3px solid transparent;
}

.nav-link:hover {
    background: rgba(255,255,255,0.08);
    color: #ffffff;
}

.nav-link.active {
    background: rgba(255,255,255,0.12);
    color: #ffffff;
    border-left-color: var(--qb-gold);
    font-weight: 700;
}

/* The sidebar scrolls (overflow-y), which clipped the ring at +2px to its
   top and bottom lines: a link spans the sidebar, so its sides fell
   outside. On a link here the ring and its halo are drawn inside its
   edge (the ring 4px in, 4px of halo outside it and 2px inside, under the
   words), so all four sides paint, and the gold meets the halo even where
   the link above or below is the open page's lighter one (2.35:1 against
   that). The left border, the open page's gold marker included, takes the
   halo's colour while the link is focused: the ring is the gold there. */
#sidebar .nav-link:focus-visible {
    outline-offset: -6px;
    box-shadow: inset 0 0 0 8px var(--focus-halo);
    border-left-color: var(--focus-halo);
}

.nav-icon {
    width: 16px;
    text-align: center;
    font-size: 11px;
    opacity: 0.7;
}

.nav-link.active .nav-icon { opacity: 1; }

.sidebar-footer {
    padding: 8px 14px;
    border-top: 1px solid rgba(255,255,255,0.08);
    font-size: 9px;
    /* 0.25 alpha measured 2.53:1 against the sidebar, and 0.50 is 4.28 at
       the top of the sidebar's navy (5.06 where the footer sits): 0.60 is
       5.47 at the top. Still clearly secondary. */
    color: rgba(255,255,255,0.60);
    text-align: center;
    user-select: none;
}

.sidebar-footer .footer-link {
    display: block;
    margin-bottom: 4px;
    font-size: 10px;
    /* 0.35 was 3.13:1 (skytech R3-1); 0.55 is 4.84 at the navy's top */
    color: rgba(255,255,255,0.55);
    text-decoration: none;
}

.sidebar-footer .footer-link:hover {
    color: rgba(255,255,255,0.75);
    text-decoration: underline;
}

/* Desktop-install update notice (fed by /api/system/update-check) */
/* The update notice sits at the TOP of the sidebar. In the footer it was
   only seen by someone who scrolled the whole menu, so people stayed on old
   versions without knowing there was one. Deliberately a quiet banner and
   not a dialog: apparent the moment the app opens, never blocking, and it
   occupies no space at all when you are up to date. */
#sidebar-update:empty { display: none; }

.update-badge {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 10px 0 2px;
    padding: 7px 9px;
    border-radius: 4px;
    background: rgba(224, 158, 36, 0.16);
    border: 1px solid rgba(224, 158, 36, 0.45);
    border-left: 3px solid #e09e24;
    color: #e8b64c;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.35;
    text-decoration: none;
    user-select: auto;
    transition: background 120ms ease, border-color 120ms ease;
}

.update-badge:hover {
    background: rgba(224, 158, 36, 0.30);
    border-color: rgba(224, 158, 36, 0.85);
}

.update-badge__arrow {
    flex: 0 0 auto;
    font-size: 13px;
    line-height: 1;
}

.update-badge__text { display: block; }

/* The call to action is the quiet half — present, not shouting. */
.update-badge__cta {
    display: block;
    margin-top: 1px;
    font-weight: 500;
    font-size: 10px;
    opacity: 0.85;
}

/* Respect a reduced-motion preference by having no motion to begin with —
   this notice never animates, pulses or slides. */

/* ========================================================================
 * CONTENT AREA
 * ======================================================================== */

#content {
    flex: 1;
    overflow-y: auto;
    padding: 16px 20px;
    background: var(--content-bg);
}

#page-content {
    max-width: 1100px;
}

/* ========================================================================
 * STATUS BAR — "Ready" | company file path | current date
 * ======================================================================== */

#statusbar {
    height: var(--statusbar-height);
    background: linear-gradient(180deg, #e0e4ea 0%, #c8ccd4 100%);
    border-top: 1px solid #a8acb4;
    display: flex;
    align-items: center;
    padding: 0 12px;
    font-size: 10px;
    color: var(--text-secondary);   /* see .topbar-brand span */
    flex-shrink: 0;
    gap: 16px;
}

.status-section {
    display: flex;
    align-items: center;
    gap: 4px;
}

.status-sep {
    width: 1px;
    height: 14px;
    background: #b0b4bc;
}

.status-right {
    margin-left: auto;
}

/* ========================================================================
 * PAGE HEADERS — QuickBooks used a colored bar at the top of each view
 * ======================================================================== */

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
    padding: 8px 12px;
    background: linear-gradient(180deg, #f0f3f7 0%, #e0e4ec 100%);
    border: 1px solid var(--panel-border);
    border-radius: 3px;
}

.page-header h2 {
    font-size: 14px;
    font-weight: 700;
    color: var(--qb-navy);
}

/* ========================================================================
 * BUTTONS — subtle 3D bevel, in the Windows Classic spirit
 * ======================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 12px;
    border: 1px solid #7f9db9;
    border-radius: 3px;
    font-size: 11px;
    font-weight: 400;
    font-family: var(--font);
    cursor: pointer;
    transition: all 0.1s;
    background: linear-gradient(180deg, #ffffff 0%, #e8ecf0 100%);
    color: var(--text-primary);
}

.btn:hover:not(:disabled) {
    background: linear-gradient(180deg, #f0f4f8 0%, #d8dce4 100%);
    border-color: #5f7d99;
}

.btn:active:not(:disabled) {
    background: linear-gradient(180deg, #d0d4d8 0%, #e0e4e8 100%);
}

.btn-primary {
    background: linear-gradient(180deg, var(--qb-blue-light) 0%, var(--qb-blue) 100%);
    color: white;
    border-color: var(--qb-navy);
    font-weight: 700;
}

.btn-primary:hover:not(:disabled) {
    background: linear-gradient(180deg, var(--qb-blue) 0%, var(--qb-navy-mid) 100%);
}

.btn-secondary {
    background: linear-gradient(180deg, #ffffff 0%, #e8ecf0 100%);
    color: var(--text-primary);
    border-color: #7f9db9;
}

.btn-secondary:hover:not(:disabled) {
    background: linear-gradient(180deg, #f0f4f8 0%, #d8dce4 100%);
}

.btn-danger {
    /* white on the old top stop, #e05555, was 3.75:1 */
    background: linear-gradient(180deg, #d34242 0%, var(--qb-red) 100%);
    color: white;
    border-color: #992222;
}

.btn-danger:hover:not(:disabled) {
    background: linear-gradient(180deg, var(--qb-red) 0%, #aa2222 100%);
}

.btn-sm {
    padding: 2px 8px;
    font-size: 10px;
}

/* A disabled button looks it: dimmed, a not-allowed cursor, and no hover
   (the hover rules above skip :disabled). Disabled buttons looked enabled
   and did nothing when clicked: an Import with no connection,
   Restore before its tick (macbase1 NEW-15, 2.18.0 gate). WCAG exempts an
   inactive control from contrast. */
.btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn-group {
    display: flex;
    gap: 4px;
}

/* ========================================================================
 * TABLES — report view: alternating row colors, thin column separators
 * ======================================================================== */

.table-container {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 0;
    overflow-x: auto;
    overflow-y: hidden;
}
/* A table wider than its box scrolls sideways in it: the box clipped it,
   its last column cut off even at 900px, the desktop app's least (Items'
   History, Benefits' Retire, Fixed Assets' Dispose at 1024), out of reach
   for a mouse too; where the table fits nothing changes. Tab brings a stop
   in whole (App.revealInTable). Up and down the box clips what overflows
   it (there since the first release), which cut the keyboard's halo below
   a last row's buttons and links (Adjust, History, View, Void met the page
   beyond at 2.2:1): the room for it is inside, below the table, 8px, the
   halo's reach (on the last row's cells it set a one-row table's words
   high in their row). Letting the overflow out while focus was inside
   spilled a wide table past its side (Jobs, Settings, Fixed Assets at 1024
   and 900). */
.table-container:not(.table-container--scroll) {
    padding-bottom: 8px;
}

table {
    width: 100%;
    border-collapse: collapse;
}

thead th {
    background: linear-gradient(180deg, #e8ecf2 0%, #d0d8e0 100%);
    padding: 4px 10px;
    text-align: left;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--panel-border);
    border-right: 1px solid #d8dce4;
    white-space: nowrap;
}

thead th:last-child { border-right: none; }

thead th.sortable {
    cursor: pointer;
    user-select: none;
}
thead th.sortable:hover {
    background: linear-gradient(180deg, #d8dee8 0%, #c0cad4 100%);
}
thead th.sort-active {
    color: var(--text-primary);
    font-weight: 800;
    background: linear-gradient(180deg, #d4dde6 0%, #b8c4d0 100%);
}
thead th .sort-arrow {
    font-size: 9px;
    margin-left: 2px;
    /* --qb-blue was 3.38:1 on the sorted column's header, 3.23 in dark */
    color: var(--qb-navy);
}

tbody td {
    padding: 3px 10px;
    border-bottom: 1px solid #e8ecf0;
    font-size: 11px;
    border-right: 1px solid #f0f2f4;
}

tbody td:last-child { border-right: none; }

tbody tr:nth-child(even) { background: #f7f9fb; }
tbody tr:hover { background: #e8f0f8; }
tbody tr:last-child td { border-bottom: none; }

td.actions {
    text-align: right;
    white-space: nowrap;
}

td.amount, th.amount {
    text-align: right;
    font-family: var(--font-mono);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

/* ========================================================================
 * CARDS — used on Dashboard and Bank Accounts
 * Styled to match QB2003's "Company Snapshot" panel
 * ======================================================================== */

.card {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 0;
    padding: 12px 14px;
    box-shadow: 1px 1px 2px rgba(0,0,0,0.06);
}

.card-header {
    font-size: 10px;
    font-weight: 700;
    color: var(--qb-navy);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 4px;
    padding-bottom: 4px;
    border-bottom: 1px solid #e0e4ec;
}

.card-value {
    font-size: 22px;
    font-weight: 700;
    color: var(--qb-navy);
    font-family: var(--font-mono);
}

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 10px;
    margin-bottom: 16px;
}
/* A card that opens something is a link to its address, looking as the
   card did: no link colour or underline, a hand over it — the Report
   Center's reports (ReportsPage._card), Banking's accounts and the
   dashboard's bank cards (each to its register). Its focus draws the
   keyboard's ring and halo in place of the card's own shadow (a.: over the
   dark theme's .card). */
.card-link {
    display: block;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}
.card-link:hover { text-decoration: none; }
a.card-link:focus-visible { box-shadow: 0 0 0 8px var(--focus-halo); }

/* ========================================================================
 * FORMS — Modal forms styled like QB2003 data entry windows
 * The original forms had a light blue/gray gradient background with
 * sunken 3D edit controls (WS_EX_CLIENTEDGE)
 * ======================================================================== */

/* minmax(0, 1fr), not 1fr: a column may be narrower than what is in it. A
   select is as wide as its longest option, so one customer with a
   120-character name widened the first column until the second (Date, Due
   Date, Class, Exchange Rate) sat past the Edit Invoice dialog's edge,
   behind a sideways scrollbar (macbase1, F22, still there at the 2.18.0
   gate). Each field now fills its own column; the open list still shows
   every name in full. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px 12px;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.form-group.full-width {
    grid-column: 1 / -1;
}

.form-group label {
    font-size: 10px;
    font-weight: 700;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

/* A field's look: a form's fields, and every other field on a page or in
   a dialog — a list's search box and filters, a line item, a type-ahead
   box, Budgets' cells, Settings' lists, the chart's filter, a report's
   pickers and dates, a page's Notes box. Those kept the browser's own
   look, white in the dark theme, and its ring on focus (review). Their
   rule counts for nothing (:where), so a look of a field's own still
   wins: a toolbar's or a line item's narrower padding, an AI panel's
   rounded box. Their focus style is at the end of the sheet. */
.form-group input,
.form-group select,
.form-group textarea,
:where(#page-content, #modal) :where(input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"]), select, textarea) {
    padding: 4px 6px;
    border: 1px solid var(--input-border);
    border-radius: 0;
    font-size: 11px;
    font-family: var(--font);
    color: var(--text-primary);
    background: #ffffff;
    /* WS_EX_CLIENTEDGE sunken effect */
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.12);
}

/* A checkbox and a radio are not fields: the border and ground here do not
   show on one, so with the outline gone it showed nothing at all (the P&L
   by Class chooser's classes, "Show classes with no activity"). They keep
   the keyboard's ring (top of the sheet), and so does a file's picker, a
   button as the keyboard has it (the chart's import, an invoice's
   attachment). */
.form-group input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;   /* a field's own focus style, not the keyboard's ring */
    border-color: var(--input-focus);
    background: #fffff8;
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.08);
}
/* and the ring's halo, out to 8px as every control's, over the sunken shadow
   every input is given above (and in the line-item tables and the
   toolbars): a white box below, in the dark theme, would otherwise be the
   ground 4-7px out */
input:is([type="checkbox"], [type="radio"], [type="file"]):focus-visible {
    box-shadow: 0 0 0 8px var(--focus-halo);
}

.form-group textarea {
    min-height: 50px;
    resize: vertical;
}

.form-group select,
:where(#page-content, #modal) :where(select) {
    box-shadow: none;
    background: linear-gradient(180deg, #ffffff 0%, #f0f2f4 100%);
}

.form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid #d0d4dc;
}

/* ========================================================================
 * BADGES — Status indicators
 * Colored text, no pill shapes
 * We kept the pills because they look better, but matched the colors
 * ======================================================================== */

.badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 2px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.badge-draft   { background: #e8ecf0; color: #4a5568; border: 1px solid #c8ccd4; }
.badge-sent    { background: #dbeafe; color: #1e40af; border: 1px solid #93c5fd; }
.badge-partial { background: #fef3c7; color: #92400e; border: 1px solid #fbbf24; }
.badge-paid    { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-recorded { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-void    { background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
/* issued / applied: the credit-document vocabulary. These existed in the
   status enum from the day credit memos shipped and had no rule, so every
   credit memo has been rendering an unstyled badge. Vendor credits use the
   same words, so both are styled here. */
.badge-issued  { background: #dbeafe; color: #1e40af; border: 1px solid #93c5fd; }
.badge-applied { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-pending { background: #fef3c7; color: #92400e; border: 1px solid #fbbf24; }
.badge-accepted{ background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-rejected{ background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
/* PTO request vocabulary — approved / denied had no rule either */
.badge-approved{ background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-denied  { background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
.badge-cancelled{background: #e8ecf0; color: #4a5568; border: 1px solid #c8ccd4; }
/* The dashboard aging legend's swatches: a coloured square, not a "■"
   character (text at 3.1:1, read aloud as "black square" — NEW-62) */
.aging-swatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px; vertical-align: -1px; }
/* Analytics' expenses table is the doughnut's legend: a swatch per row */
.chart-swatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px; vertical-align: -1px; margin-right: 2px; }
/* Form history: Check's answer carries a mark and the time (NEW-67) */
.audit-status--ok { color: var(--text-success); }
.audit-status--changed { color: var(--text-danger); }
.audit-status .audit-mark { font-weight: 700; }
.audit-status .audit-checked-at { color: var(--text-muted); font-size: 11px; }
/* a header kept for the accessibility tree only (the portal has its own copy) */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Form history: the row a footer hash was found on */
#audit-rows tr[data-found] td { background: #fef3c7; }
.badge-converted{background: #dbeafe; color: #1e40af; border: 1px solid #93c5fd; }
.badge-product { background: #dbeafe; color: #1e40af; border: 1px solid #93c5fd; }
.badge-service { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.badge-material{ background: #fef3c7; color: #92400e; border: 1px solid #fbbf24; }
.badge-labor   { background: #ede9fe; color: #5b21b6; border: 1px solid #c4b5fd; }

/* ========================================================================
 * MODAL — in the spirit of QB2003's popup data entry windows
 * ======================================================================== */

.modal-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0,20,50,0.45);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 40px;
    z-index: 200;
}


/* A bare <a> had no rule anywhere, so the user agent picked the colour and
   the dark theme had no way to change it — 1.73:1 (macbase1's sweep, which
   found 58 elements below AA from three causes; this was the worst).
   Components with their own link styling are unaffected: they set `color`
   at higher specificity. */
a {
    color: var(--text-link);
}

a:hover { text-decoration: underline; }

/* ------------------------------------------------------------------------
   Utility: .hidden
   ------------------------------------------------------------------------
   The JS hides elements by adding this class — ten call sites across three
   files — but there was no generic rule, only `.modal-overlay.hidden` and
   `.splash-overlay.hidden`. Everything else kept the class and stayed on
   screen. The global search dropdown was the visible casualty: it never
   closed on click-away, on clearing the box, or on navigating, and at rest
   it painted a 2px white sliver under the toolbar in every session, which
   is the grey edge visible along the top of desktop screenshots for
   releases. Found by the marketing agent building the training videos.

   `!important` because the elements that carry this class set `display`
   through their own rules at equal or higher specificity; without it the
   trap is merely reset for the next component.

   Note this is the .hidden CLASS. The HTML `hidden` ATTRIBUTE is separate
   and untouched (`[hidden]` is handled by the shell's own reset).
   ------------------------------------------------------------------------ */
.hidden { display: none !important; }

.modal-overlay.hidden { display: none; }

.modal {
    background: var(--form-bg);
    border: 2px solid var(--qb-navy);
    border-radius: 0;
    width: 90%;
    max-width: 700px;
    max-height: calc(100vh - 80px);
    overflow-y: auto;
    box-shadow: 4px 4px 12px rgba(0,0,0,0.3);
}

/* A dialog for a form with a wide line-item table: as wide as the window
   allows, up to 1280px. Opened with openModal(title, html, { wide: true }). */
.modal.modal--wide {
    max-width: min(1280px, 96vw);
}

/* A line-item table with more columns than the dialog is wide scrolls
   sideways inside its container instead of losing its last columns behind
   overflow: hidden (#174: the job cost entry's Bill? column and the remove
   button were cut off, with no way to reach them). It has the keyboard's
   room inside too, 8px below its table and at its right end (a line's ✕
   and the pay run's last Stub PDF sat 3-5px from its edges, their halo
   cut, and the ring on the scrollbar once it scrolled); not at its left,
   where the pay run's Employee column is held. */
.table-container--scroll {
    overflow-x: auto;
    padding-right: 8px;
    padding-bottom: 8px;
    /* Room under the box for a classic horizontal bar (17px on macOS and
       Windows), whether or not the engine counted the bar in the box's
       height: WebKit drew the bill form's and the job cost entry's bar
       over the top half of "+ Add Line" below (2.23.1 gate, NEW-104). The
       button's own 8px above it make 20 together. */
    margin-bottom: 12px;
}
/* Only the job cost entry's grid is wider than the dialog (its Bill? and
   remove columns); the estimate's fits, and a floor on every scrolling grid
   pushed its Amount column past the dialog's edge at 1280 wide (2.18.0 gate,
   macbase1 NEW-12). */
.table-container--scroll .line-items-table--wide {
    min-width: 1180px;
}

/* The pay-run view: when the table is wider than the window, the Employee
   column stays put while the rest scrolls, so each row still says whose
   pay it is. The dialog used to open scrolled to the first Stub PDF, with
   the names out of sight (2.18.0 gate, macbase1 NEW-3). The cell needs a
   ground of its own, or the scrolled columns show through it. */
.pay-run-table th:first-child,
.pay-run-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    white-space: nowrap;
    box-shadow: inset -1px 0 0 var(--panel-border);
}
.pay-run-table td:first-child { background: var(--panel-bg); }
.pay-run-table tbody tr:nth-child(even) td:first-child { background: #f7f9fb; }
.pay-run-table tbody tr:hover td:first-child { background: #e8f0f8; }

/* The P&L by Class / by Job grid (R1, #231): a column per class or job
   inside the wide dialog. The container scrolls both ways and has a height
   of its own, so the header row (sticky top) and the Account column
   (sticky left) have a scroll root to hold to; table{width:100%} would
   squeeze twelve columns into the dialog, so the table takes the width its
   columns need. The corner cell sits above both. */
.grid-scroll {
    overflow: auto;
    max-height: min(68vh, 720px);
    overscroll-behavior: contain;
    /* a cell Tab moves to is scrolled out from under the frozen header row
       and Account column (their height and width, ReportsPage._gridFit),
       and its own scroll-margin (above) keeps its ring and halo, 8px,
       clear of them and of the grid's edges */
    scroll-padding: var(--grid-head, 0px) 0 0 var(--grid-frozen, 0px);
    padding-bottom: 8px;
}
/* the grid's own ring, not the keyboard's gold ring and halo; on its edge,
   not inside it, where the frozen header row and Account column painted
   over half of it */
.grid-scroll:focus-visible { outline: 2px solid var(--qb-navy); outline-offset: 0; box-shadow: none; }
.pivot-grid {
    width: max-content;
    min-width: 100%;
}
.pivot-grid th, .pivot-grid td { white-space: nowrap; }
.pivot-grid td.amount, .pivot-grid th.amount { min-width: 96px; }
.pivot-grid thead th { position: sticky; top: 0; z-index: 2; }
/* The keyboard's ring and halo reach 8px out from a link, and no scroll
   gives them room where the grid ends: above a heading, held to the
   grid's top, and below the last row (the grid's own padding, as a plain
   table's container has, above) */
.pivot-grid thead th { padding-top: 8px; }
.pivot-grid tbody th {
    font-weight: inherit;
    text-align: left;
    padding: 3px 10px;
    border-bottom: 1px solid #e8ecf0;
    font-size: 11px;
}
.pivot-grid th:first-child, .pivot-grid td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    min-width: 180px;
    max-width: 280px;
    overflow: hidden;
    text-overflow: ellipsis;
    box-shadow: inset -1px 0 0 var(--panel-border);
}
.pivot-grid thead th:first-child { z-index: 3; }
.pivot-grid tbody th:first-child, .pivot-grid tbody td:first-child { background: var(--panel-bg); }
.pivot-grid tbody tr:nth-child(even) th:first-child,
.pivot-grid tbody tr:nth-child(even) td:first-child { background: #f7f9fb; }
.pivot-grid tbody tr:hover th:first-child,
.pivot-grid tbody tr:hover td:first-child { background: #e8f0f8; }
/* a subtotal row's own ground (set inline) carries into its frozen cell */
.pivot-grid tbody tr[style*="background"] th:first-child { background: inherit; }
.pivot-grid tbody tr.grid-section th:first-child { font-weight: 700; }
/* the column gone to: the header and every cell under it */
.pivot-grid .is-current { background: #dbe7f6; }
.pivot-grid thead th.is-current { box-shadow: inset 0 -3px 0 var(--qb-navy); }
.pivot-grid tbody tr[style*="background"] td.is-current { background: #dbe7f6; }

/* Go to <class>, ‹ Prev / Next ›, and where the grid is now. */
.grid-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
    margin-bottom: 8px;
    font-size: 11px;
}
.grid-toolbar label { font-weight: 700; color: var(--text-secondary); }
.grid-toolbar select, .grid-toolbar .cbx { min-width: 180px; }
.grid-live { color: var(--text-secondary); font-size: 11px; }
.grid-step__buttons { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.grid-step { justify-content: flex-end; }
/* A view that shows part of the company: said above the table. */
.grid-filtered {
    margin: 0 0 8px;
    padding: 6px 10px;
    font-size: 12px;
    background: #fffbe6;
    border: 1px solid #f5a623;
    border-left: 4px solid #d97706;
    color: #78350f;
}
.grid-chooser {
    margin: 0 0 8px;
    padding: 8px 10px;
    border: 1px solid var(--panel-border);
    background: var(--panel-bg);
}
.grid-chooser legend { font-weight: 700; font-size: 11px; }
.grid-chooser__list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 2px 12px;
    max-height: 160px;
    overflow: auto;
    margin: 4px 0 8px;
    /* the list scrolls: room inside it for a checkbox's ring and halo
       (8px), which its edge cut off on the first row and column */
    padding: 8px;
    font-size: 11px;
}
.grid-chooser__list label { display: flex; gap: 6px; align-items: center; font-weight: normal; }
/* "Show classes with no activity": 6px between the box and its words, as
   the chooser's classes have: the ring and its halo end before the words */
#grid-empty { margin-right: 5px; }
.grid-chooser__actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* On a phone the wide dialog is the screen's width, the grid swipes
   sideways with the Account column frozen, and Prev / Next lead. */
@media (max-width: 700px) {
    .modal.modal--wide { width: 96vw; }
    .pivot-grid th:first-child, .pivot-grid td:first-child { min-width: 120px; max-width: 150px; }
    .grid-toolbar label { flex-basis: 100%; }
    .grid-toolbar select, .grid-toolbar .cbx { flex: 1 1 100%; }
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 10px;
    background: linear-gradient(180deg, var(--qb-blue-light) 0%, var(--qb-navy) 100%);
    color: white;
}

.modal-header h2 {
    font-size: 12px;
    font-weight: 700;
    text-shadow: 0 1px 1px rgba(0,0,0,0.3);
}

.modal-close {
    background: none;
    border: none;
    font-size: 16px;
    cursor: pointer;
    /* white: the dialog header's gradient starts at --qb-blue-light, where
       0.7 white was 3.11:1 and white is 4.56 */
    color: #ffffff;
    padding: 2px 4px;
    line-height: 1;
    /* room above and below for the keyboard's ring and its halo (8px):
       the dialog scrolls, which cut them at the title bar's top */
    margin: 2px 0;
}
.modal-close:hover { color: #ffffff; background: rgba(255, 255, 255, 0.15); }

#modal-body {
    padding: 14px 16px;
}

/* ========================================================================
 * TOAST NOTIFICATIONS
 * ======================================================================== */

#toast-container {
    position: fixed;
    bottom: calc(var(--statusbar-height) + 8px);
    right: 12px;
    z-index: 300;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.toast {
    padding: 6px 14px;
    border-radius: 2px;
    font-size: 11px;
    font-weight: 700;
    color: white;
    box-shadow: 2px 2px 6px rgba(0,0,0,0.2);
    animation: toast-in 0.2s ease;
    border: 1px solid rgba(0,0,0,0.2);
}

.toast-success { background: var(--sb-green); }
.toast-error   { background: var(--qb-red); }
.toast-info    { background: var(--qb-blue); }

@keyframes toast-in {
    from { transform: translateY(20px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

/* ========================================================================
 * DASHBOARD — "Company Snapshot" style
 * ======================================================================== */

.dashboard-section {
    margin-bottom: 16px;
}

.dashboard-section h3 {
    font-size: 11px;
    font-weight: 700;
    color: var(--qb-navy);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 6px;
    padding-bottom: 4px;
    border-bottom: 2px solid var(--qb-gold);
}

/* ========================================================================
 * LINE ITEMS TABLE — the invoice/estimate grid
 * ======================================================================== */

.line-items-table {
    width: 100%;
    margin-top: 8px;
    border: 1px solid var(--panel-border);
}

.line-items-table th {
    font-size: 9px;
    padding: 3px 6px;
    background: linear-gradient(180deg, #e8ecf2 0%, #d0d8e0 100%);
    border-right: 1px solid #d8dce4;
}

.line-items-table th:last-child { border-right: none; }

.line-items-table td {
    padding: 2px 4px;
    border-right: 1px solid #e8ecf0;
    border-bottom: 1px solid #e8ecf0;
}

.line-items-table td:last-child { border-right: none; }

.line-items-table input,
.line-items-table select {
    width: 100%;
    padding: 2px 4px;
    border: 1px solid var(--input-border);
    border-radius: 0;
    font-size: 11px;
    font-family: var(--font);
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.08);
}

.line-items-table .col-qty    { width: 60px; }
.line-items-table .col-rate   { width: 80px; }
.line-items-table .col-amount { width: 80px; text-align: right; white-space: nowrap; }
.line-items-table .col-actions{ width: 30px; text-align: center; }

.invoice-logo-option {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    padding: 10px;
    margin-bottom: 12px;
    border: 1px solid var(--panel-border);
    background: var(--form-bg);
}

.invoice-logo-preview {
    max-width: 140px;
    max-height: 56px;
    object-fit: contain;
    padding: 4px;
    background: white;
}

.invoice-logo-option label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 700;
}

.invoice-logo-option input {
    width: auto;
}

.invoice-logo-help,
.invoice-logo-status {
    font-size: 10px;
    color: var(--text-secondary);
    margin-top: 3px;
}

.invoice-logo-status.invoice-logo-error {
    color: var(--danger);
}

.invoice-totals {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid var(--panel-border);
}

.total-row {
    display: flex;
    gap: 12px;
    font-size: 11px;
}

.total-row .label {
    color: var(--text-secondary);
    min-width: 80px;
    text-align: right;
    font-weight: 700;
}

.total-row .value {
    min-width: 80px;
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

.total-row.grand-total {
    font-weight: 700;
    font-size: 13px;
    padding-top: 4px;
    margin-top: 2px;
    border-top: 2px solid var(--qb-navy);
    color: var(--qb-navy);
}

/* ========================================================================
 * EMPTY STATE
 * ======================================================================== */

.empty-state {
    text-align: center;
    padding: 30px 16px;
    color: var(--text-muted);
}

.empty-state p {
    margin-bottom: 10px;
    font-size: 12px;
}

/* ========================================================================
 * TOOLBAR / FILTERS
 * ======================================================================== */

.toolbar {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-bottom: 10px;
    padding: 4px 8px;
    background: var(--toolbar-bg);
    border: 1px solid var(--toolbar-border);
}

.toolbar input,
.toolbar select {
    padding: 3px 6px;
    border: 1px solid var(--input-border);
    border-radius: 0;
    font-size: 11px;
    font-family: var(--font);
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.08);
}

/* Show: Active / Inactive / All on a list page (#210) */
.toolbar .list-show-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-secondary);
}

/* Clickable row */
tr.clickable { cursor: pointer; }

/* ========================================================================
 * SPLASH / ABOUT DIALOG
 * ======================================================================== */

.splash-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0,10,30,0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 500;
    animation: splash-fade-in 0.3s ease;
}

.splash-overlay.hidden { display: none; }

@keyframes splash-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.splash-box {
    width: 420px;
    background: linear-gradient(180deg, var(--qb-navy) 0%, #001a33 100%);
    border: 3px solid var(--qb-gold);
    box-shadow: 0 0 40px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.1);
    text-align: center;
    padding: 30px 24px 20px;
    position: relative;
    overflow: hidden;
}

.splash-box::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--qb-gold), #ffcc66, var(--qb-gold));
}

.splash-title {
    font-size: 28px;
    font-weight: 700;
    color: #ffffff;
    letter-spacing: -0.03em;
    text-shadow: 0 2px 4px rgba(0,0,0,0.4);
    margin-bottom: 2px;
}

.splash-subtitle {
    font-size: 11px;
    font-weight: 700;
    color: var(--qb-gold);
    letter-spacing: 0.15em;
    text-transform: uppercase;
    margin-bottom: 20px;
}

.splash-divider {
    width: 60%;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--qb-gold), transparent);
    margin: 0 auto 16px;
}

.splash-info {
    font-size: 10px;
    /* 0.5 was 4.28:1 at the top of the navy (4.48 as skytech measured it
       with the terms showing, R3-1); 0.6 is 5.47. Its bold stays 0.7. */
    color: rgba(255,255,255,0.6);
    line-height: 1.6;
    margin-bottom: 16px;
}

.splash-info strong {
    color: rgba(255,255,255,0.7);
}

.splash-whatsnew {
    text-align: left;
    margin: 10px 0 4px;
    padding: 8px 10px;
    border: 1px solid var(--gray-300, #d1d5db);
    background: rgba(255, 255, 255, 0.55);
    font-size: 11px;
    line-height: 1.35;
    max-height: 170px;
    overflow-y: auto;
}
.splash-whatsnew-title { font-weight: 700; margin-bottom: 4px; }
.splash-whatsnew ul { margin: 0; padding-left: 16px; }
.splash-whatsnew li { margin: 2px 0; }

.splash-terms {
    text-align: left;
    margin: 12px 0 4px;
    padding: 10px 14px;
    border: 1px solid var(--gray-300, #d0d4dc);
    border-radius: 6px;
    font-size: 12px;
    line-height: 1.45;
    max-height: 260px;
    overflow-y: auto;
    /* the sibling .splash-whatsnew's panel; without it the text sits at 1.16:1 on the
       splash navy (macbase1, 2.14.0 gate) */
    background: rgba(255, 255, 255, 0.55);
}
.splash-terms-title { font-weight: 700; margin-bottom: 6px; }
.splash-terms-list { margin: 0; padding-left: 18px; }
.splash-terms-list li { margin-bottom: 6px; }
/* the link to LICENSE sat at 4.29:1 on the light panel (measured in headless
   Chromium, 2.16.2 gate); the navy the button uses reads at AA there. The dark
   theme overrides it in dark.css. */
.splash-terms a { color: #002244; font-weight: 600; }

.splash-legal {
    font-size: 8px;
    /* 0.25 was 2.22:1 (skytech R3-1); 0.55 is 4.84, still the faintest line */
    color: rgba(255,255,255,0.55);
    line-height: 1.5;
    margin-bottom: 16px;
    padding: 0 16px;
}

.splash-btn {
    padding: 6px 28px;
    background: linear-gradient(180deg, var(--qb-gold) 0%, #b8862e 100%);
    border: 1px solid #8a6420;
    color: var(--qb-navy-dark);
    font-size: 11px;
    font-weight: 700;
    font-family: var(--font);
    cursor: pointer;
    border-radius: 2px;
    text-shadow: 0 1px 0 rgba(255,255,255,0.2);
}

.splash-btn:hover {
    background: linear-gradient(180deg, #ddaa44 0%, var(--qb-gold) 100%);
}

/* ========================================================================
 * SCROLLBAR STYLING — to match the Windows XP / Classic theme
 * ======================================================================== */

::-webkit-scrollbar { width: 14px; }
::-webkit-scrollbar-track { background: #e8ecf0; }
::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, #c0c8d0 0%, #a0a8b0 100%);
    border: 1px solid #8890a0;
}
::-webkit-scrollbar-thumb:hover { background: #a0a8b4; }

/* ========================================================================
 * PRINT STYLES
 * ======================================================================== */

@media print {
    #sidebar, #topbar, #statusbar, .btn, .modal-overlay, #toast-container { display: none !important; }
    #content { margin: 0; padding: 0; }
    body { background: white; }
}

/* ========================================================================
 * GLOBAL SEARCH
 * ======================================================================== */

.tb-search {
    width: 180px;
    padding: 3px 8px;
    font-size: 11px;
    font-family: var(--font);
    border: 1px solid var(--input-border);
    background: white;
    border-radius: 2px;
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.08);
}

.tb-search:focus {
    outline: none;   /* a field's own focus style, not the keyboard's ring */
    border-color: var(--input-focus);
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.08), 0 0 0 2px rgba(51,102,204,0.2);
}

.search-dropdown {
    position: absolute;
    top: var(--topbar-height);
    right: 120px;
    width: 300px;
    max-height: 300px;
    overflow-y: auto;
    background: white;
    border: 1px solid var(--panel-border);
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    z-index: 200;
}

.search-section {
    padding: 4px 10px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--content-bg);
    border-bottom: 1px solid var(--panel-border);
}

.search-item {
    padding: 6px 10px;
    font-size: 11px;
    cursor: pointer;
    border-bottom: 1px solid #f0f0f0;
}

.search-item:hover,
.search-item:focus-visible {
    background: var(--primary-light, #e8f0f8);
}

/* its own ring, inside the result (the list scrolls), not the keyboard's
   gold one and its halo */
.search-item:focus-visible {
    outline: 2px solid var(--qb-blue, #2a6fb4);
    outline-offset: -2px;
    box-shadow: none;
}

/* ========================================================================
 * SETTINGS PAGE — CPreferencesDialog
 * ======================================================================== */

.settings-section {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    padding: 16px;
    margin-bottom: 16px;
}

.settings-section h3 {
    font-size: 13px;
    font-weight: 700;
    color: var(--qb-navy);
    margin-bottom: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--panel-border);
}

/* ========================================================================
 * QUICK ENTRY — batch invoice entry
 * ======================================================================== */

.quick-entry-info {
    font-size: 11px;
}

#qe-log {
    max-height: 200px;
    overflow-y: auto;
    border: 1px solid var(--panel-border);
    /* Was a hardcoded `white`, which the dark theme could not override — the
       log text is near-white there, so the running confirmation of what you
       just saved was invisible at about 1.1:1 contrast. Quick Entry exists
       to batch entries and read that log back, and the product documents
       WCAG AA. Token, so both themes follow. */
    background: var(--panel-bg);
    color: var(--text-primary);
    padding: 4px 8px;
}

/* ========================================================================
 * IIF IMPORT/EXPORT — QuickBooks 2003 Pro Interoperability
 * File > Utilities > Import/Export IIF Files
 * ======================================================================== */

.iif-sections {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-top: 12px;
}

.iif-section {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    padding: 16px;
}

.iif-section h3 {
    font-size: 13px;
    font-weight: 700;
    color: var(--qb-navy);
    margin-bottom: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--panel-border);
}

.iif-export-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
}

.iif-export-grid .btn {
    justify-content: center;
    text-align: center;
    padding: 6px 10px;
}

.iif-date-range {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 10px;
    font-size: 11px;
}

.iif-date-range label {
    font-size: 10px;
    font-weight: 700;
    color: var(--text-secondary);
    text-transform: uppercase;
}

.iif-date-range input {
    padding: 3px 6px;
    border: 1px solid var(--input-border);
    border-radius: 0;
    font-size: 11px;
    font-family: var(--font);
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.08);
}

.iif-dropzone {
    border: 2px dashed var(--input-border);
    border-radius: 3px;
    padding: 24px 16px;
    text-align: center;
    cursor: pointer;
    transition: all 0.15s;
    background: #fafbfc;
    margin-bottom: 12px;
}

.iif-dropzone:hover,
.iif-dropzone.dragover {
    border-color: var(--qb-blue);
    background: #f0f4fa;
}

.iif-dropzone-icon {
    font-size: 28px;
    color: var(--qb-blue);
    margin-bottom: 6px;
}

.iif-dropzone-text {
    font-size: 11px;
    color: var(--text-secondary);
}

.iif-dropzone-text strong {
    color: var(--qb-blue);
}

/* The per-line Class box on every form (Nonprofit.classCellHtml): the bill
   form's cell squeezed it to 73 px, so "North LLC" read "North LL"; the
   journal form's was 124 (2.23.1 gate, NEW-99). */
td > select[class$="-fund"] { min-width: 124px; }
/* The bill form's line table has the most columns (Item, Account, Description,
   Cost Code, Class, Function, Qty, Rate, Amount, ✕): the 124 px Class box took
   its room from Account (77 → 58 px, "6000 - A…" → "600…") and Item (84 → 51)
   on a company with cost codes (2.23.1 gate, NEW-103). The table scrolls
   sideways in its container, as the job cost entry's does, and each box keeps
   a width that shows its name. The widths sit on the visible type-ahead
   picker (cbx--<class>, mirrored from the select by combobox.js), not on the
   1px select behind it, and on the plain Description, Qty and Rate boxes,
   which the wider pickers squeezed next (round 3's W-5 / NEW-103). */
.bill-lines-table .cbx--line-item { min-width: 110px; }
.bill-lines-table .cbx--line-account { min-width: 200px; }
.bill-lines-table .cbx--line-cost-code { min-width: 110px; }
.bill-lines-table .line-desc { min-width: 140px; }
.bill-lines-table .line-qty { min-width: 64px; }
.bill-lines-table .line-rate { min-width: 84px; }

.iif-file-info {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: #f0f4fa;
    border: 1px solid var(--qb-blue);
    margin-bottom: 12px;
    font-size: 11px;
}

.iif-file-info .filename {
    font-weight: 700;
    color: var(--qb-navy);
    flex: 1;
}

.iif-file-info .filesize {
    color: var(--text-muted);
    font-size: 10px;
}

.iif-import-actions {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
}

.iif-results {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    padding: 12px;
    font-size: 11px;
}

.iif-results h4 {
    font-size: 12px;
    font-weight: 700;
    color: var(--qb-navy);
    margin-bottom: 8px;
}

.iif-results .result-row {
    display: flex;
    justify-content: space-between;
    padding: 3px 0;
    border-bottom: 1px solid #e8ecf0;
}

.iif-results .result-row:last-child {
    border-bottom: none;
}

/* The file's transaction blocks, accounted for in one sentence on a line of
   its own: as a label and a count across the row it wrapped, the count's
   "3:" beside "the" reading as a superscript (2.23.1 gate, NEW-106). */
.iif-results .result-row--line {
    display: block;
}

.iif-results .result-count {
    font-weight: 700;
    font-family: var(--font-mono);
}

.iif-validation-ok {
    color: var(--sb-green);
    font-weight: 700;
}

.iif-validation-err {
    color: var(--qb-red);
    font-weight: 700;
}

.iif-validation-warn {
    color: var(--qb-orange);
}

.iif-errors {
    margin-top: 8px;
    max-height: 150px;
    overflow-y: auto;
    font-size: 10px;
    font-family: var(--font-mono);
    background: #fff8f8;
    border: 1px solid #fca5a5;
    padding: 6px 8px;
    color: var(--qb-red);
}

.iif-warnings {
    margin-top: 8px;
    max-height: 150px;
    overflow-y: auto;
    font-size: 10px;
    font-family: var(--font-mono);
    background: #fffbf0;
    border: 1px solid #fbbf24;
    padding: 6px 8px;
    color: var(--qb-orange);
}

/* "Change ALL-CAPS names" on the IIF import (#195): the box, and a few of
   the file's own names as it would import them */
.iif-retitle {
    margin-top: 8px;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    padding: 8px 10px;
    font-size: 11px;
    color: var(--text-primary);
}

.iif-retitle label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 700;
}

.iif-retitle ul {
    margin: 6px 0 6px 20px;
    font-family: var(--font-mono);
}

.iif-retitle p {
    margin: 0;
    color: var(--text-secondary);
}

/* ===========================================================================
   Phase 9: Analytics page (#/analytics)
   Inline SPA page matching the QB2003 blue skin with light/dark support.
   =========================================================================== */

.analytics-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
}

.analytics-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.analytics-controls label {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-right: 2px;
}

.analytics-controls select,
.analytics-controls .btn-sm {
    padding: 4px 10px;
    font-size: 12px;
    border-radius: 4px;
}

.analytics-controls select {
    background: var(--panel-bg);
    color: var(--text-muted);
    border: 1px solid var(--panel-border);
}

.analytics-meta {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 12px;
    letter-spacing: 0.5px;
}

.analytics-kpi-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
    margin-bottom: 16px;
}

@media (max-width: 900px) {
    .analytics-kpi-grid { grid-template-columns: repeat(2, 1fr); }
}

.analytics-kpi {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    padding: 14px 16px;
}

.analytics-kpi-label {
    font-size: 9px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 6px;
}

.analytics-kpi-value {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.1;
}

.analytics-kpi-value.kpi-green  { color: var(--sb-green); }
.analytics-kpi-value.kpi-red    { color: var(--qb-red); }
.analytics-kpi-value.kpi-blue   { color: var(--qb-blue); }
.analytics-kpi-value.kpi-purple { color: #a855f7; }

.analytics-section-title {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 1.5px;
    margin: 20px 0 8px;
    font-weight: 600;
}

.analytics-card {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    padding: 14px 16px;
    margin-bottom: 8px;
}

.analytics-two-col {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); /* a card never grows past its column */
    gap: 12px;
}

@media (max-width: 900px) {
    .analytics-two-col { grid-template-columns: 1fr; }
}

.chart-wrap {
    position: relative;
    width: 100%;
    height: 280px;
    margin-bottom: 8px;
}

.chart-wrap-sm {
    height: 220px;
}

.analytics-empty {
    padding: 14px;
    text-align: center;
    color: var(--text-muted);
    font-size: 11px;
}

.analytics-error {
    padding: 14px 16px;
    background: #fff4f4;
    border: 1px solid var(--qb-red);
    border-radius: 4px;
    color: var(--qb-red);
    font-size: 12px;
}

.analytics-error strong { display: block; margin-bottom: 4px; }

.amount.green { color: var(--sb-green); }
.amount.red   { color: var(--qb-red); }
.amount.amber { color: var(--qb-orange); }

.totals-row td {
    border-top: 2px solid var(--panel-border);
    background: rgba(51, 102, 153, 0.04);
}

/* ================================================================
 * Phase 9.5 — AI Insights panel + settings modal
 * ================================================================ */
.ai-insights-card {
    border-left: 3px solid #a855f7;
}

.ai-insights-title {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #9b3cf6;   /* #a855f7, the card's border, is 3.96:1 as text on white */
}

.ai-insights-body {
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-muted);
}

.ai-insights-body h3,
.ai-insights-body h4 {
    margin: 10px 0 4px;
    color: var(--text-heading, var(--text-primary));
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

.ai-insights-body ul {
    margin: 4px 0 8px 18px;
    padding: 0;
}

.ai-insights-body li {
    margin-bottom: 3px;
}

.ai-insights-body p {
    margin: 6px 0;
}

.ai-insights-empty {
    color: var(--text-muted);
    font-style: italic;
    padding: 8px 0;
}

.ai-insights-busy {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted);
    padding: 10px 0;
}

.ai-insights-meta {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.ai-cache-badge {
    background: rgba(168, 85, 247, 0.15);
    color: #a855f7;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 9px;
    font-weight: 600;
}

/* AI Predefined Analyses panel — same visual family as .ai-insights-card */
.ai-actions-card {}

.ai-actions-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}

.ai-actions-form {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

.ai-actions-form select {
    flex: 1;
    min-width: 220px;
    padding: 6px 10px;
    font-size: 13px;
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    background: var(--panel-bg);
    color: var(--text-primary);
}

.ai-actions-form select:focus {
    outline: 2px solid #a855f7;
    outline-offset: -1px;
}

.ai-actions-meta {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.ai-actions-meta strong {
    color: var(--text-primary);
    text-transform: none;
    letter-spacing: 0;
    font-size: 12px;
}

.ai-actions-body {
    font-size: 13px;
    color: var(--text-primary);
    line-height: 1.6;
}

.ai-actions-body h3,
.ai-actions-body h4 {
    margin: 10px 0 4px;
    color: var(--text-primary);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

.ai-actions-body ul {
    margin: 4px 0 8px 18px;
    padding: 0;
}

.ai-actions-body li { margin: 2px 0; }
.ai-actions-body p { margin: 4px 0; }

.ai-actions-empty,
.ai-actions-busy {
    color: var(--text-muted);
    font-size: 12px;
    padding: 8px 0;
}

.ai-settings-form .form-field {
    display: flex;
    flex-direction: column;
    margin-bottom: 10px;
}

.ai-settings-form .form-field > span {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin-bottom: 4px;
}

.ai-settings-form input,
.ai-settings-form select {
    padding: 6px 10px;
    font-size: 12px;
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    background: var(--panel-bg);
    color: var(--text-primary);
}

.ai-settings-form input:focus,
.ai-settings-form select:focus {
    outline: 2px solid #a855f7;
    outline-offset: -1px;
}

.ai-settings-hint {
    font-size: 10px;
    color: var(--text-muted);
    margin-bottom: 10px;
    padding-left: 2px;
}

.ai-key-saved {
    color: var(--text-success);   /* #00c48f was 2.26:1 as text on white */
    font-style: normal;
    font-weight: 600;
    margin-left: 4px;
}

.ai-settings-buttons {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--panel-border);
}

.ai-settings-spacer { flex: 1; }

.ai-settings-test-result {
    font-size: 11px;
    color: var(--text-muted);
}

.ai-test-ok { color: var(--text-success); }   /* #00c48f was 2.26:1 on white */
.ai-test-fail { color: var(--text-danger); }

/* ----------------------------------------------------------------
 * Cloudflare Worker Gateway section (appears when provider is
 * `cloudflare_worker`). Called out visually so it's obvious this is
 * the self-hosted, credentials-stay-in-CF option.
 * ---------------------------------------------------------------- */
.ai-worker-section {
    margin: 10px 0 4px 0;
    padding: 10px 12px 6px 12px;
    border: 1px solid #f38020;  /* Cloudflare orange */
    border-left-width: 3px;
    border-radius: 4px;
    background: rgba(243, 128, 32, 0.06);
}

.ai-worker-section > legend {
    padding: 0 6px;
    font-size: 11px;
    font-weight: 700;
    color: #f38020;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.ai-worker-help,
.ai-worker-security {
    font-size: 11px;
    color: var(--text-muted);
    margin: 4px 0 6px 0;
    line-height: 1.4;
}

.ai-worker-help code,
.ai-worker-security code {
    background: rgba(0, 0, 0, 0.15);
    padding: 1px 4px;
    border-radius: 2px;
    font-size: 10px;
}

.ai-worker-security {
    color: var(--text-success);  /* green — safety callout (#00c48f was 2.26:1 on white) */
    border-top: 1px dashed rgba(243, 128, 32, 0.3);
    padding-top: 6px;
    margin-top: 6px;
}

.ai-worker-required {
    color: #f38020;
    font-style: normal;
    font-size: 10px;
    font-weight: 600;
    margin-left: 4px;
}

/* ================================================================
 * Phase 9.5b — AI Chat Q&A panel
 * ================================================================ */
.ai-chat-card {
    border-left: 3px solid #38bdf8;
}

.ai-chat-title {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #38bdf8;
}

.ai-chat-messages {
    max-height: 400px;
    overflow-y: auto;
    padding: 10px 0;
    font-size: 12px;
}

.ai-chat-empty {
    color: var(--text-muted);
    padding: 8px 4px;
}

.ai-chat-empty p {
    margin-bottom: 6px;
    font-style: italic;
}

.ai-chat-examples {
    margin: 6px 0 0 18px;
    padding: 0;
    font-size: 11px;
}

.ai-chat-examples li {
    margin-bottom: 3px;
    color: var(--text-muted);
}

.ai-chat-message {
    display: flex;
    gap: 8px;
    margin-bottom: 10px;
    padding: 0 4px;
}

.ai-chat-avatar {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    border-radius: 50%;
    background: var(--panel-border);
}

.ai-chat-user .ai-chat-avatar {
    background: rgba(56, 189, 248, 0.2);
}

.ai-chat-assistant .ai-chat-avatar {
    background: rgba(168, 85, 247, 0.2);
}

.ai-chat-bubble {
    flex: 1;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 8px 12px;
    line-height: 1.5;
}

.ai-chat-user .ai-chat-bubble {
    background: rgba(56, 189, 248, 0.05);
}

.ai-chat-bubble p {
    margin: 4px 0;
}

.ai-chat-bubble h3,
.ai-chat-bubble h4 {
    margin: 6px 0 3px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

.ai-chat-bubble ul {
    margin: 4px 0 4px 16px;
    padding: 0;
}

.ai-chat-tools {
    margin-top: 6px;
    font-size: 10px;
    color: var(--text-muted);
}

.ai-chat-tools summary {
    cursor: pointer;
    padding: 2px 0;
}

.ai-chat-tools ul {
    margin-top: 4px;
    padding-left: 16px;
}

.ai-chat-tools code {
    background: rgba(0, 0, 0, 0.05);
    padding: 1px 4px;
    border-radius: 3px;
    font-family: var(--mono-font, monospace);
}

.ai-chat-form {
    display: flex;
    gap: 6px;
    padding-top: 10px;
    border-top: 1px solid var(--panel-border);
    margin-top: 8px;
}

.ai-chat-form input {
    flex: 1;
    padding: 6px 10px;
    font-size: 12px;
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    background: var(--panel-bg);
    color: var(--text-primary);
}

.ai-chat-form input:focus {
    outline: 2px solid #38bdf8;
    outline-offset: -1px;
}

.ai-chat-form input:disabled {
    opacity: 0.5;
}

.spinner {
    display: inline-block;
    width: 10px;
    height: 10px;
    border: 2px solid var(--text-muted);
    border-top-color: transparent;
    border-radius: 50%;
    animation: ai-chat-spin 0.8s linear infinite;
    vertical-align: middle;
}

@keyframes ai-chat-spin {
    to { transform: rotate(360deg); }
}

/* Scan Receipt canvas — field picker buttons. Each carries its field
   color as custom properties (set by OcrCanvas.panelHtml). */
.btn.ocr-pick, .btn.ocr-pick:hover, .btn.ocr-pick:active {
    border: 2px solid var(--pick-stroke);
    background: var(--pick-fill);
    color: var(--pick-ink);
}
.btn.ocr-pick:hover { filter: brightness(0.96); }


/* Customizable dashboard: stat tiles share a row, panels take half or all */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}
.dash-card--stat { grid-column: span 1; }
.dash-card--half { grid-column: span 2; }
.dash-card--full { grid-column: span 4; }
.dash-card__body { margin-top: 6px; }
.dash-card__controls { display: inline-flex; gap: 2px; }
.dash-card__controls .btn { padding: 0 6px; line-height: 18px; font-size: 10px; }
@media (max-width: 900px) {
    .dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dash-card--full { grid-column: span 2; }
}
@media (max-width: 560px) {
    .dash-grid { grid-template-columns: 1fr; }
    .dash-card--stat, .dash-card--half, .dash-card--full { grid-column: span 1; }
}

/* Control accounts (issue #122) — the posting code finds these by number, so
   the Chart of Accounts marks them and the edit form locks the number and the
   type. The row still gets an Edit button: renaming is allowed, and the API
   is the authority on what is refused. */
.badge-control {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 2px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fbbf24;
    vertical-align: middle;
    cursor: help;
}

.hint--locked {
    background: #fffbeb;
    border: 1px solid #fbbf24;
    border-left: 3px solid #d97706;
    border-radius: 3px;
    padding: 8px 10px;
    font-size: 12px;
    line-height: 1.5;
    color: #78350f;
}

.hint--locked em { color: #92400e; font-style: normal; font-weight: 600; }

/* Notes drawn as a tinted box: all clear, needs attention, a caution, a
   plain step. They were inline backgrounds, which stayed pale in the dark
   theme under its light text (1.14:1); dark.css gives each a dark tint. */
.card.note--ok,
.note--ok      { background: #eaf5ec; border-left: 4px solid #1f7a36; }
.card.note--alert,
.note--alert   { background: #fdecea; border-left: 4px solid #a4242b; }
.card.note--caution,
.note--caution { background: #fffbe6; border-left: 4px solid #f5a623; }
.note--plain   { background: #f4f6f9; }

/* A void or inactive row reads as set aside: its text takes the muted
   colour. It used to fade the whole row (opacity .55-.6), which took the
   text below AA: muted text at 2.4:1 in dark. Badges keep their own. */
tbody tr.row--dim > td,
tbody tr.row--dim > td strong,
tbody tr.row--dim > td.amount { color: var(--text-muted); }

.form-group input[disabled],
.form-group select[disabled],
:where(#page-content, #modal) :where(input, select, textarea):disabled {
    background: #f1f3f6;
    color: var(--text-muted, #6b7090);
    cursor: not-allowed;
}

/* ========================================================================
 * TYPE-AHEAD PICKERS (combobox.js)
 * A picker is a box you type into; its <select> stays, out of sight, as
 * the answer. The list hangs from the window, over dialogs and tables.
 * ======================================================================== */

.cbx {
    position: relative;
    display: inline-block;
    vertical-align: middle;
    max-width: 100%;
}
.cbx.cbx--fill { display: block; width: 100%; }
.cbx[hidden] { display: none !important; }

/* The select is still there for the page's code (and a test's), but not
   for the eye, the mouse or Tab. */
.cbx > select.cbx-select {
    position: absolute !important;
    left: 0;
    bottom: 0;
    width: 1px !important;
    height: 1px !important;
    min-width: 0 !important;
    max-width: 1px !important;
    padding: 0 !important;
    margin: 0 !important;
    border: 0 !important;
    opacity: 0 !important;
    pointer-events: none !important;
    clip-path: inset(50%);
}

/* The box: like the app's other fields where no form styles it, and
   everywhere a picker's arrow, so it still reads as a list. */
.cbx-input {
    padding: 3px 6px;
    border: 1px solid var(--input-border);
    border-radius: 0;
    font-size: 11px;
    font-family: var(--font);
    color: var(--text-primary);
    background-color: #ffffff;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 0h8L4 5z' fill='%235b616d'/%3E%3C/svg%3E") !important;
    background-repeat: no-repeat !important;
    background-position: right 6px center !important;
    background-size: 8px 5px !important;
    padding-right: 20px !important;
}
.cbx > .cbx-input {
    width: 100%;
    box-sizing: border-box;
    text-overflow: ellipsis;
}

.cbx-popup {
    position: fixed;
    z-index: 450;   /* over a dialog (200) and a toast (300), under the splash */
    display: flex;
    flex-direction: column;
    background: #ffffff;
    color: var(--text-primary);
    border: 1px solid var(--input-border);
    box-shadow: 0 4px 14px rgba(0, 20, 50, 0.22);
    font-family: var(--font);
    font-size: 11px;
}
.cbx-popup[hidden] { display: none; }
.cbx-popup > ul {
    list-style: none;
    margin: 0;
    padding: 2px 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    min-height: 0;
}
.cbx-popup ul ul { list-style: none; margin: 0; padding: 0; }
.cbx-opt {
    padding: 3px 8px;
    cursor: default;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.cbx-opt.cbx-current { font-weight: 700; }
.cbx-opt mark { background: none; color: inherit; font-weight: 700; }
.cbx-action { color: var(--text-link); }
.cbx-in { color: var(--text-muted); font-size: 10px; }
.cbx-opt[aria-selected="true"] { background: var(--qb-navy); color: #ffffff; }
.cbx-opt[aria-selected="true"] .cbx-in { color: var(--text-on-navy); }
.cbx-group {
    display: block;
    padding: 4px 6px 2px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--text-secondary);
}
.cbx-note {
    padding: 3px 8px 4px;
    color: var(--text-muted);
    font-style: italic;
    border-top: 1px solid var(--panel-border);
}
.cbx-note[hidden] { display: none; }
.cbx-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
@media (forced-colors: active) {
    .cbx-opt[aria-selected="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
}

/* ============================================================================
 * A FIELD'S FOCUS, everywhere on a page and in a dialog
 * The blue border and the pale ground, never the browser's ring or the
 * keyboard's gold: the form's own, given to every field outside a form too
 * (see the field's look, above). Last in the sheet and (0,2,1), so it is
 * over every look of a field's own, an AI panel's coloured outline among
 * them; a checkbox, a radio and the like are not fields and keep the
 * keyboard's ring.
 * ============================================================================ */
:where(#page-content, #modal) :is(input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"]), select, textarea):focus {
    outline: none;
    border-color: var(--input-focus);
    background: #fffff8;
    box-shadow: inset 1px 1px 2px rgba(0,0,0,0.08);
}
/* With the system's high-contrast colours on, a border takes the system's
   colour and the blue border shows nothing: there a focused field draws the
   system's highlight as its ring. */
@media (forced-colors: active) {
    .form-group input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus,
    .form-group select:focus,
    .form-group textarea:focus,
    :where(#page-content, #modal) :is(input:not([type="checkbox"], [type="radio"], [type="file"], [type="range"], [type="color"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"]), select, textarea):focus {
        outline: 2px solid Highlight;
    }
}
