/* ============================================================
   Quantum Power Accounting System - Theme System
   ------------------------------------------------------------
   CSS custom property theming with light + dark modes.

   HOW IT WORKS
   - All colors are semantic tokens defined on :root (light) and
     .theme-dark (dark). Components only ever reference tokens,
     never raw hex, so a new theme = one new block of variables.
   - Toggle by putting a class on <html>:
       <html>              -> light (default, no JS needed)
       <html class="theme-dark">  -> dark
     Because light is the default on :root, the correct theme
     shows on first paint with zero JavaScript.
   - Optional: respect the OS setting automatically (see the
     prefers-color-scheme block at the bottom).

   ADDING A THEME LATER
     Copy the .theme-dark block, rename it (e.g. .theme-highcontrast),
     and override only the tokens you want. No component CSS changes.

   CONTRAST
     Foreground/background pairings below are chosen to meet or
     exceed WCAG AA (4.5:1 for body text, 3:1 for large text and
     UI borders) in both themes. Key ratios noted inline.
   ============================================================ */

/* ------------------------------------------------------------
   1. BRAND SCALE (raw values - not used by components directly)
   These are the palette primitives. Semantic tokens below point
   at them. Keeping brand and semantic layers separate is what
   makes re-theming clean.
   ------------------------------------------------------------ */
:root {
    /* Quantum Power orange — #ff5000, sampled from the logo (5.90% of its
       pixels). Every ratio below is measured. The brand orange itself is only
       3.28:1 on white, so it CANNOT carry body text; -600 is the darkened
       same-hue shade that can. */
    --brand-orange-400: #ff7c40;  /* light: for DARK surfaces. 5.87:1 on #23272d */
    --brand-orange-500: #ff5000;  /* THE logo orange. Fills only, never text on white */
    --brand-orange-600: #bf3c00;  /* text-on-light. 5.44:1 on #fff  AA */

    /* Quantum Power yellow — #ffcc00, also sampled from the logo (1.83%). */
    --brand-yellow-400: #ffd633;  /* fills and badges on dark */
    --brand-yellow-500: #ffcc00;  /* THE logo yellow. 1.51:1 on white — fills ONLY */
    --brand-yellow-600: #8c7000;  /* the only yellow safe as text: 4.74:1  AA */

    /* Warm dark neutrals — the DARK-MODE page/card/row surfaces.
       The grey-*  scale is cool (blue-leaning: B>G>R). Against the warm chrome
       band it read as two different products sharing a screen — a cool charcoal
       content area beside a warm brown sidebar. These are the same lightness
       steps, re-tinted warm (R>G>B) so the whole dark theme belongs to one
       family. Separation between them is 1.16 and 1.24, matching the cool
       scale's own 1.19 and 1.29, so surfaces stay as distinguishable as before. */
    --dark-900: #15110e;   /* page background */
    --dark-800: #25201a;   /* cards, tables, panels */
    --dark-700: #36302a;   /* zebra rows, subtle fills, borders */
    --dark-600: #4a423a;   /* stronger borders */

    /* Entry-page ground — the sign-in, MFA, password-reset and error pages.
       #0c1117 sampled from the Quantum Power dashboard mock-up. Deliberately
       NOT --brand-chrome-900: the chrome token paints the sidebar band beside
       white content, where a near-black reads as heavy, while a full-bleed
       sign-in page behind a single white card wants the deeper ground. */
    --brand-ink-900: #0c1117;

    /* Warm chrome (replaces Raven navy for the header/sidebar band). Warm
       rather than neutral so the dark band reads as part of an orange brand. */
    --brand-chrome-900: #241a15;  /* header/footer bg. white on it = 17:1  AAA */
    --brand-chrome-800: #2b1d16;
    --brand-chrome-700: #33241c;  /* hover state on chrome surfaces */
    --brand-chrome-600: #4a362a;  /* subtle dividers within the dark band */

    /* --- compatibility aliases ------------------------------------------
       The old Raven names are kept because they are referenced DIRECTLY in
       ~28 rules further down this file and in 10 templates — the header's
       claim that components only touch semantic tokens is aspirational.
       Renaming would mean editing all of them; aliasing costs nothing and
       keeps this change contained to the palette.
       --------------------------------------------------------------------- */
    --brand-gold-400: var(--brand-orange-400);
    --brand-gold-500: var(--brand-orange-500);
    --brand-gold-600: var(--brand-orange-600);
    --brand-navy-900: var(--brand-chrome-900);
    --brand-navy-800: var(--brand-chrome-800);
    --brand-navy-700: var(--brand-chrome-700);
    --brand-navy-600: var(--brand-chrome-600);

    /* Neutral grey scale (the workhorse for a data UI) */
    --grey-000: #ffffff;
    --grey-050: #f6f7f8;
    --grey-100: #eceef1;
    --grey-200: #dfe2e6;
    --grey-300: #c4c9d0;
    --grey-400: #9aa1ab;
    --grey-500: #6b727c;
    --grey-600: #4a4f57;
    --grey-700: #333940;
    --grey-800: #23272d;
    --grey-900: #15181c;

    /* Status hues (accounting needs clear paid/overdue/etc.) */
    --green-500: #2e7d46;
    --green-600: #1f6b39;
    --red-500:   #c0392b;
    --red-600:   #a02f23;
    --amber-500: #b9770e;
    --blue-500:  #2f6fb0;
}

/* ------------------------------------------------------------
   2. SEMANTIC TOKENS - LIGHT (default)
   Components reference ONLY these. Named by role, not by color.
   ------------------------------------------------------------ */
:root {
    color-scheme: light;

    /* Surfaces */
    --color-bg:            var(--grey-050);   /* app background - light grey for visibility */
    --color-surface:       var(--grey-000);   /* cards, tables, panels */
    --color-surface-alt:   var(--grey-100);   /* zebra rows, subtle fills */
    --color-overlay:       rgba(21, 24, 28, 0.45);

    /* Text  (on --color-surface unless noted) */
    --color-text:          var(--grey-900);   /* body: #15181c on #fff = ~16:1  AAA */
    --color-text-muted:    var(--grey-500);   /* secondary: #6b727c on #fff = ~5:1  AA */
    --color-text-inverse:  var(--grey-000);   /* text on dark/accent fills */

    /* Borders / lines */
    --color-border:        var(--grey-200);
    --color-border-strong: var(--grey-300);

    /* Brand accent */
    /* THE LOGO ORANGE. Used for fills, active states, focus rings and borders —
       everywhere the accent is a SHAPE rather than a letterform. #ff5000 is only
       3.28:1 on white, which is fine for a button body or a 4px rule but fails
       AA for body text, so text does not use this token. */
    --color-accent:        var(--brand-orange-500); /* #ff5000 — the logo orange */
    --color-accent-hover:  var(--brand-orange-600);
    /* The same orange darkened just enough to be legible as TEXT on white:
       #bf3c00 is 5.44:1 (AA). Links, small labels and figures use this. Same
       hue as the logo, so it reads as the brand rather than a second colour. */
    --color-accent-text:   var(--brand-orange-600);
    /* WHITE, not dark. This is the one token that had to flip when the brand
       moved from gold to orange: gold was light enough to carry dark text, the
       dark orange is not. On the #bf3c00 fill, white is 5.44:1 (AA) and
       #15181c is 3.29:1. But the accent is now the LOGO orange #ff5000, which is
       lighter — on it, a dark label is 5.43:1 and white is only 3.28:1. So the
       dark label wins in both themes now. */
    --color-accent-contrast: var(--grey-900);

    /* Brand navy - used for the header/nav band and brand headings */
    --color-brand:         var(--brand-chrome-900);
    --color-header-bg:     var(--brand-chrome-900);
    --color-header-text:   var(--grey-000);       /* #fff on #241a15 = 17:1  AAA */
    --color-header-border: var(--brand-navy-600);

    /* Interactive / focus */
    --color-focus-ring:    var(--blue-500);

    /* Status - text colors meet AA on --color-surface */
    --color-success:       var(--green-600);
    --color-danger:        var(--red-600);
    --color-warning:       var(--amber-500);
    --color-info:          var(--blue-500);

    /* Status - soft backgrounds (pair with the matching text token) */
    /* A pale wash of the brand orange, for surfaces that are branded rather than
       carrying a status meaning — the summary tiles above a list, for one. Sits in
       the same family as the status -bg tokens below so tiles look like one set. */
    --color-accent-bg:     #fff4ee;
    --color-success-bg:    #e7f3ec;
    --color-danger-bg:     #fbeae8;
    --color-warning-bg:    #fdf3e2;
    --color-info-bg:       #e9f1f9;

    /* Elevation */
    --shadow-sm: 0 1px 2px rgba(21,24,28,0.06), 0 1px 3px rgba(21,24,28,0.08);
    --shadow-md: 0 2px 6px rgba(21,24,28,0.08), 0 6px 16px rgba(21,24,28,0.10);

    /* Shape + rhythm (not colors, but part of the token system) */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --space-1: 4px;  --space-2: 8px;  --space-3: 12px;
    --space-4: 16px; --space-5: 24px; --space-6: 32px; --space-8: 48px;

    /* Type */
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace; /* for figures */
}

/* ------------------------------------------------------------
   3. SEMANTIC TOKENS - DARK
   Same names, dark values. Only overrides what changes.
   Applied via <html class="theme-dark">.
   ------------------------------------------------------------ */
.theme-dark {
    color-scheme: dark;

    --color-bg:            var(--dark-900);
    --color-surface:       var(--dark-800);
    --color-surface-alt:   var(--dark-700);
    --color-overlay:       rgba(0, 0, 0, 0.6);

    /* Warm off-white, not the blue-white #eef0f2 the cool scale used. */
    --color-text:          #f0ece9;           /* on #25201a = 13.8:1  AAA */
    --color-text-muted:    #b3a49b;           /* on #25201a =  6.7:1  AA  */
    --color-text-inverse:  var(--dark-900);

    --color-border:        var(--dark-700);
    --color-border-strong: var(--dark-600);

    --color-accent:        var(--brand-orange-400); /* #ff7c40 on #25201a = 6.31:1  AA */
    --color-accent-hover:  var(--brand-orange-500);
    /* On the dark ground the accent is already legible as text, so there is no
       need for a darkened variant — both tokens are the same colour here. */
    --color-accent-text:   var(--brand-orange-400);
    /* Dark on the light-orange fill: 6.96:1 AA. White here would be 1.93:1. */
    --color-accent-contrast: var(--grey-900);

    --color-focus-ring:    #6ea8e0;

    --color-success:       #5cc98a;
    --color-danger:        #e8776b;
    --color-warning:       #e0a944;
    --color-info:          #6ea8e0;

    --color-accent-bg:     #2e1d13;
    --color-success-bg:    #1c3327;
    --color-danger-bg:     #3a201d;
    --color-warning-bg:    #362a15;
    --color-info-bg:       #1d2c3b;

    --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
    --shadow-md: 0 2px 6px rgba(0,0,0,0.45), 0 6px 16px rgba(0,0,0,0.5);

    /* Header/sidebar chrome sits a shade lighter than the near-black page
       bg so it stays legible as a distinct band instead of blending in. */
    --color-header-bg:     var(--brand-navy-800);
    --color-header-border: var(--brand-navy-600);
}

/* ------------------------------------------------------------
   3b. CONTENT BLACKOUT ("privacy screen") — <html class="content-blackout">
   Blacks out the working area on demand while keeping the left sidebar and the
   top ribbon usable. A full-viewport black layer is laid down, then the sidebar
   and ribbon are lifted ABOVE it — so the "L" of chrome shows through and only
   the content area goes black. Scroll-proof (the layer is fixed). Click the
   black area to dismiss. Momentary — it does not persist across page loads.
   ------------------------------------------------------------ */
#raven-blackout-layer {
    display: none;
    position: fixed;
    inset: 0;
    background: #000;
    z-index: 400;
    cursor: pointer;
}
html.content-blackout #raven-blackout-layer { display: block; }
html.content-blackout .sidebar,
html.content-blackout .site-ribbon {
    position: relative;
    z-index: 401;
}

/* ------------------------------------------------------------
   4. BASE ELEMENTS (consume tokens only)
   ------------------------------------------------------------ */
* { box-sizing: border-box; }

/* Always reserve room for the vertical scrollbar. Without this, a short page
   (no scrollbar) and a long one (scrollbar) have different viewport widths, so
   the sidebar and every left margin jump sideways as you navigate. */
html {
    /* Reserve the vertical scrollbar so a short page and a long page have the
       same viewport width — otherwise every left margin jumps as you navigate.
       overflow-y:scroll is the universally-supported fallback; scrollbar-gutter
       is the modern equivalent and wins where supported. */
    overflow-y: scroll;
    scrollbar-gutter: stable;
}

body {
    margin: 0;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--color-accent-text); text-decoration: none; }
a:hover { color: var(--color-accent-hover); text-decoration: underline; }

h1, h2, h3 { color: var(--color-text); line-height: 1.25; }

/* Visible keyboard focus everywhere (accessibility floor) */
:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* ------------------------------------------------------------
   5. COMPONENT PATTERNS (examples using the tokens)
   ------------------------------------------------------------ */

/* App shell: fixed navy sidebar + fluid main column.
   The sidebar always uses the header/brand tokens (not the
   light/dark surface tokens) so it reads as constant chrome,
   the same way the marketing site's nav stays navy in both modes. */
.app-shell {
    display: flex;
    min-height: 100vh;
}

.sidebar {
    width: 224px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: var(--color-header-bg);
    border-right: 1px solid var(--color-header-border);
    /* Pin to the viewport: the sidebar is its own full-height column that scrolls
       internally, so the footer (theme / blackout / sign-out) stays reachable at
       the bottom of the SCREEN no matter how long the page content is. */
    position: sticky;
    top: 0;
    align-self: flex-start;      /* don't stretch to the tall page's height */
    height: 100vh;
    overflow-y: auto;
    overflow-x: hidden;
    /* Keep the scrollbar out of the way — a full-width OS scrollbar down the navy
       reads as a seam. Thin, translucent, and only obvious on hover. */
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,.16) transparent;
}
.sidebar::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(255,255,255,.14); border-radius: 999px; }
.sidebar:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,.30); }
.sidebar::-webkit-scrollbar-button { display: none; }   /* no arrow buttons */

.sidebar-brand {
    /* logo-sidebar.png is cropped to the artwork; logo.png carries ~66% empty
       canvas and pushed everything down when used here. */
    padding: var(--space-3) var(--space-3) var(--space-2);
}
/* the logo links to the dashboard — keep it looking like a logo, not a link */
.sidebar-brand a { display: block; text-decoration: none; }
.sidebar-brand a:hover { text-decoration: none; opacity: .88; }
.sidebar-brand img { display: block; width: 100%; max-width: 178px; height: auto; margin: 0 auto; }
.sidebar-subtitle {
    text-align: center;
    margin-top: var(--space-2);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--brand-gold-400);
}

.sidebar-search {
    padding: 0 var(--space-3) var(--space-2);
}
.sidebar-search input {
    width: 100%;
    font: inherit;
    font-size: 13px;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255,255,255,0.15);
    background: rgba(255,255,255,0.08);
    color: #fff;
}
.sidebar-search input::placeholder { color: rgba(255,255,255,0.5); }
.sidebar-search input:focus { outline: 2px solid var(--brand-gold-400); outline-offset: 1px; }

/* ------------------------------------------------------------
   SIDEBAR SECTION HEADINGS — now a clickable module toggle.
   They used to be the same weight and grey as the links around them, so they
   read as noise rather than as structure. Bigger, a lighter blue-grey, more
   air above, and a hairline divider so each module is visibly its own block.
   ------------------------------------------------------------ */
.sidebar-section {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    width: calc(100% - var(--space-4));
    margin: var(--space-4) var(--space-2) 0;
    padding: var(--space-2) var(--space-3) 7px;
    font-size: 12px;                       /* was 11 */
    font-weight: 700;                      /* was 600 */
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #9fb4cc;                        /* light blue-grey, not flat grey */
    background: none;
    border: 0;
    border-bottom: 1px solid rgba(255,255,255,0.09);   /* the divider */
    border-radius: 0;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
}
.sidebar-section:hover { color: #cfe0f0; }
.sidebar-section .chev {
    width: 12px; height: 12px; flex: none;
    transition: transform .15s ease;
    opacity: .7;
}
.sidebar-section[aria-expanded="false"] .chev { transform: rotate(-90deg); }
.sidebar-section[aria-expanded="false"] { color: rgba(255,255,255,0.42); }

/* A collapsed module hides its links entirely. */
.sidebar-nav[hidden] { display: none; }

/* ---- Favorites: the pages you actually live in ---- */
.sidebar-fav a .star { margin-left: auto; opacity: .35; width: 14px; height: 14px; flex: none; }
.sidebar-fav a:hover .star { opacity: .8; }
.sidebar-nav a .star { margin-left: auto; opacity: 0; width: 14px; height: 14px; flex: none;
                       transition: opacity .12s; }
.sidebar-nav li:hover a .star { opacity: .45; }
.sidebar-nav a .star:hover, .sidebar-nav a .star.on { opacity: 1; color: var(--brand-gold-400); }
.star.on { color: var(--brand-gold-400); opacity: 1 !important; }

/* ---- + New ---- */
.sidebar-new { position: relative; margin: var(--space-3) var(--space-3) 0; }
.sidebar-new > button {
    width: 100%;
    display: flex; align-items: center; justify-content: center; gap: 6px;
    padding: 9px var(--space-3);
    border: 0; border-radius: var(--radius-sm);
    background: var(--brand-gold-400);
    color: #1a1a1a; font-weight: 700; font-size: 14px;
    cursor: pointer; font-family: inherit;
}
.sidebar-new > button:hover { filter: brightness(1.06); }
.sidebar-new-menu {
    display: none;
    position: absolute; left: 0; right: 0; top: calc(100% + 5px); z-index: 70;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 10px 28px rgba(0,0,0,.3);
    padding: 5px;
}
.sidebar-new-menu.open { display: block; }
.sidebar-new-menu a {
    display: block; padding: 8px 10px; font-size: 13px;
    color: var(--color-text); text-decoration: none; border-radius: var(--radius-sm);
}
.sidebar-new-menu a:hover { background: var(--color-surface-alt); text-decoration: none; }

/* NOT flex:1 — each nav group is its own <ul>, so flex:1 makes every group
   claim an equal share of the leftover height and the links drift apart. The
   gaps then change whenever a group appears or disappears (permissions, or a
   new section). The footer is pinned by margin-top:auto instead. */
.sidebar-nav { list-style: none; margin: 0; padding: 0 0 var(--space-3); flex: 0 0 auto; }
.sidebar-nav li { margin: 0; }
.sidebar-nav a {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    margin: 1px var(--space-2);
    border-radius: var(--radius-sm);
    color: rgba(255,255,255,0.75);
    font-size: 14px;
}
.sidebar-nav a:hover { background: rgba(255,255,255,0.07); color: #fff; text-decoration: none; }
.sidebar-nav a.active {
    background: rgba(255,255,255,0.1);
    color: #fff;
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--brand-gold-400);
}
.sidebar-nav .icon { width: 18px; height: 18px; flex-shrink: 0; }

/* ------------------------------------------------------------
   SIDEBAR USER — a menu, not a label.
   Hierarchy: the NAME is what you scan for, so it's white and bold; the ROLE is
   supporting detail, so it's smaller and gold (it also happens to be the most
   safety-relevant thing on screen — "God Mode" should register).
   ------------------------------------------------------------ */
.sidebar-user { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--color-header-border); }
.sidebar-user-name {
    color: #fff; font-size: 14px; font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-user-role {
    color: rgba(255,255,255,0.55); font-size: 12px;
    text-transform: uppercase; letter-spacing: 0.04em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.sidebar-footer {
    margin-top: auto;          /* push to the bottom when the nav is short */
    position: sticky;          /* and keep it there when the nav scrolls */
    bottom: 0;
    display: flex;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--color-header-border);
    background: var(--color-header-bg);   /* so scrolled nav doesn't show through */
}
.sidebar-footer button, .sidebar-footer a {
    display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    border-radius: var(--radius-sm);
    background: transparent; border: none;
    color: rgba(255,255,255,0.7); cursor: pointer;
}
.sidebar-footer button:hover, .sidebar-footer a:hover { background: rgba(255,255,255,0.1); color: #fff; }
.sidebar-footer .icon { width: 18px; height: 18px; }

.main { flex: 1; min-width: 0; }

/* Top page header: title + primary actions */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-6);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}
.page-header h1 { margin: 0; font-size: 22px; }
.page-header-actions { display: flex; gap: var(--space-2); align-items: center; }

/* Toolbar: search + sort controls above a data table */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-4) var(--space-6) 0;
}
.field-input, .field-select {
    font: inherit;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
}
.field-input { min-width: 240px; }

.content { padding: var(--space-5) var(--space-6) var(--space-6); }

/* Dashboard: KPI stat tiles */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}
.stat-tile {
    display: block;
    /* Grid items default to min-width:auto, which means they refuse to shrink
       below their content. A long currency figure then pushes straight out of
       the card instead of the text scaling down — that is what clipped
       "$9,933,921.41" mid-digit on a narrow screen. */
    min-width: 0;
    overflow: hidden;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4) var(--space-5);
    border-left: 3px solid var(--color-border-strong);
}
/* Tiles that are links: no underline, subtle lift on hover */
a.stat-tile { color: inherit; text-decoration: none; transition: box-shadow .12s ease, transform .12s ease; }
a.stat-tile:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); text-decoration: none; }
.stat-tile.is-accent  { border-left-color: var(--brand-gold-500); }
.stat-tile.is-danger  { border-left-color: var(--color-danger); }
.stat-tile.is-info    { border-left-color: var(--color-info); }
.stat-label {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    margin: 0 0 var(--space-2);
}
.stat-value {
    font-family: var(--font-mono);
    /* Scales with the tile instead of a fixed 26px, so eight-figure sums still
       fit on a tablet. Clamped at both ends: never smaller than readable, never
       larger than the original size on a desktop. */
    font-size: clamp(18px, 2.4vw, 26px);
    font-weight: 700;
    color: var(--color-text);
    line-height: 1.1;
    /* Last resort if a figure is still too wide — wrap rather than clip, because
       a truncated amount reads as a different number. */
    overflow-wrap: anywhere;
}
.stat-sub { font-size: 13px; color: var(--color-text-muted); margin-top: var(--space-1); }

/* Two-column dashboard layout for panels below the tiles */
.dash-cols {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: var(--space-4);
    align-items: start;
}
@media (max-width: 860px) { .dash-cols { grid-template-columns: 1fr; } }

.panel-title {
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin: 0 0 var(--space-3);
}
.card.flush { padding: 0; overflow: hidden; }
.card.flush .panel-title { padding: var(--space-4) var(--space-5) 0; }
.card.flush .table th:first-child,
.card.flush .table td:first-child { padding-left: var(--space-5); }
.card.flush .table th:last-child,
.card.flush .table td:last-child { padding-right: var(--space-5); }

/* Status breakdown list (sidebar panel on the dashboard) */
.status-list { list-style: none; margin: 0; padding: 0; }
.status-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border);
}
.status-list li:last-child { border-bottom: none; }
.status-list .count { font-family: var(--font-mono); font-weight: 700; }
.status-list a { display: inline-block; }

/* Active-filter chip shown on a filtered list page */
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}
.filter-chip a {
    color: var(--color-text-muted);
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
}
.filter-chip a:hover { color: var(--color-danger); }

/* Flash messages (redirect feedback: convert quote, etc.) */
.flash {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-4);
    font-size: 14px;
    font-weight: 500;
    border: 1px solid transparent;
}
.flash-success { background: var(--color-success-bg); color: var(--color-success); border-color: var(--color-success); }
.flash-error   { background: var(--color-danger-bg);  color: var(--color-danger);  border-color: var(--color-danger); }
.flash-info    { background: var(--color-info-bg);    color: var(--color-info);    border-color: var(--color-info); }

/* Forms (data entry -> MySQL) */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
}
.form-field { display: flex; flex-direction: column; gap: var(--space-1); }
.form-field.full { grid-column: 1 / -1; }
.form-field label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}
.form-field label .req { color: var(--color-danger); }
.form-field input, .form-field select, .form-field textarea {
    font: inherit;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
}
.form-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border);
}
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }

/* Segmented phone input: country + (area) - prefix - line */
.phone-input { display: flex; align-items: center; gap: var(--space-1); }
.phone-input select,
.phone-input input {
    font: inherit;
    padding: var(--space-2);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    text-align: center;
}
.phone-input .ph-country { width: 88px; text-align: left; }
.phone-input .ph-area    { width: 58px; }
.phone-input .ph-prefix  { width: 58px; }
.phone-input .ph-line    { width: 74px; }
.phone-input .ph-sep { color: var(--color-text-muted); font-weight: 600; }

/* Email form on invoice/quote detail (From / To / CC + actions) */
.email-form { margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--color-border); }
.email-fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
.email-fields .field-input { width: 100%; min-width: 0; }
.email-fields label { font-size: 13px; font-weight: 600; color: var(--color-text-muted); margin-bottom: var(--space-1); }
.email-actions { display: flex; gap: var(--space-2); margin-top: var(--space-3); }
@media (max-width: 700px) { .email-fields { grid-template-columns: 1fr; } }

/* "Emailed" tracking pill on quotes */
.emailed-pill {
    display: inline-flex; align-items: center; gap: var(--space-1);
    font-size: 12px; font-weight: 600;
    color: var(--color-success);
    background: var(--color-success-bg);
    padding: 2px var(--space-2); border-radius: 999px;
}
.not-emailed { color: var(--color-text-muted); font-size: 13px; }

/* Overdue emphasis: red due date + balance on past-due invoices */
.overdue { color: var(--color-danger); font-weight: 600; }

/* Totals summary at the top of financial list pages.
   Each stat gets its own tile; the container is now just the layout grid, so
   tiles share the row evenly and each number sits centred inside its box.
   Shared by invoices, quotes, bills, payments, credit memos and the profile
   pages — restyling here restyles all of them. */
.totals-bar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-5);
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
}
.totals-bar > div,
.totals-bar > a {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
    /* Branded, not informational: these tiles summarise the list below rather than
       flagging anything, so they take the accent wash. --color-accent-bg is defined
       for both themes. */
    background: var(--color-accent-bg);
    border: 1px solid var(--color-border);
    border-top: 3px solid var(--color-accent);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    transition: box-shadow .12s ease, transform .12s ease;
}
.totals-bar > div:hover,
.totals-bar > a:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
/* tiles that filter the list below are links; keep them looking like tiles */
.totals-bar > a { text-decoration: none; color: inherit; cursor: pointer; }
.totals-bar > a:hover { text-decoration: none; }
/* These are reachable by keyboard now that the payment header tiles are links,
   and a tile that can be focused must show that it is. */
.totals-bar > a:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
    text-decoration: none;
}
.totals-bar .tb-label {
    font-size: 12px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .06em; color: var(--color-text-muted);
}
.totals-bar .tb-value {
    font-family: var(--font-mono); font-size: 26px; font-weight: 700;
    line-height: 1.15; color: var(--color-text);
}
.totals-bar .tb-sub { font-size: 12px; color: var(--color-text-muted); }
.totals-bar .tb-value.danger { color: var(--color-danger); }

/* Colour-coded tiles. The top border + value carry the hue; the tinted
   background stays pale so the figures keep AA contrast in both themes. */
/* Scoped as `.totals-bar > a.tb-tile-*` rather than `.tb-tile-*` alone.
   The base rule `.totals-bar > a` is specificity 0,1,1 and sets both the
   background and the border-top shorthand; a bare `.tb-tile-danger` is 0,1,0
   and loses to it no matter where it sits in the file. The result was every
   tile drawing the same info-blue, so Overdue, Paid and Due Soon were
   indistinguishable — the colour was carrying no information at all.
   The .tb-value rules were already 0,2,0 and did win, which is why the FIGURE
   was the right colour above a border that was not. */
.totals-bar > a.tb-tile-success { background: var(--color-success-bg); border-top-color: var(--color-success); }
.totals-bar > a.tb-tile-danger  { background: var(--color-danger-bg);  border-top-color: var(--color-danger); }
.totals-bar > a.tb-tile-warning { background: var(--color-warning-bg); border-top-color: var(--color-warning); }
/* Non-link tiles use the same classes on a div. */
.totals-bar > div.tb-tile-success { background: var(--color-success-bg); border-top-color: var(--color-success); }
.totals-bar > div.tb-tile-danger  { background: var(--color-danger-bg);  border-top-color: var(--color-danger); }
.totals-bar > div.tb-tile-warning { background: var(--color-warning-bg); border-top-color: var(--color-warning); }
.tb-tile-success .tb-value { color: var(--color-success); }
.tb-tile-danger  .tb-value { color: var(--color-danger); }
.tb-tile-warning .tb-value { color: var(--color-warning); }

/* The tile matching the active filter. This used --color-focus-ring (blue), which
   was the loudest thing on a page that now has no other blue in it. It is the
   accent instead. It shares a colour with the :focus-visible rule above, but not a
   shape: selection sits INSIDE the tile (offset -2px), keyboard focus sits OUTSIDE
   it (offset +2px), so a focused tile that is also selected shows both rings. */
.totals-bar > a.tb-active {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

/* "Showing: Overdue invoices  ×"  chip above the table */
.filter-chip {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    margin-bottom: var(--space-3);
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    font-size: 13px;
}
.filter-chip a { text-decoration: none; font-weight: 700; color: var(--color-text-muted); }
.filter-chip a:hover { color: var(--color-danger); text-decoration: none; }

/* Reports */
.report-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); }
.report-card {
    display: block;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--brand-gold-500);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4) var(--space-5);
    color: inherit; text-decoration: none;
    transition: box-shadow .12s ease, transform .12s ease;
}
.report-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); text-decoration: none; }
.report-card h3 { margin: 0 0 var(--space-1); font-size: 16px; color: var(--color-text); }
.report-card p { margin: 0; font-size: 13px; color: var(--color-text-muted); }

/* Financial statement tables */
.stmt { width: 100%; border-collapse: collapse; }
.stmt td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-border); }
.stmt .amt { text-align: right; font-family: var(--font-mono); white-space: nowrap; }
.stmt .section-head td { font-weight: 700; color: var(--color-text); text-transform: uppercase; font-size: 12px; letter-spacing: .03em; background: var(--color-surface-alt); }
.stmt .subtotal td { font-weight: 700; border-top: 2px solid var(--color-border-strong); }
.stmt .grand td { font-weight: 700; color: var(--color-accent-text); border-top: 2px solid var(--color-border-strong); font-size: 15px; }
.stmt .indent { padding-left: var(--space-5); }
.stmt .indent2 { padding-left: calc(var(--space-5) + var(--space-5)); color: var(--color-text-muted); }
.stmt .rollup-sub td { font-weight: 600; border-bottom: 1px solid var(--color-border); }
.balanced-note { margin-top: var(--space-4); font-size: 13px; }
.balanced-ok { color: var(--color-success); font-weight: 600; }
.balanced-bad { color: var(--color-danger); font-weight: 600; }

/* Card / panel */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5);
}

/* Data table - the core of an accounting UI */
.table {
    width: 100%;
    border-collapse: collapse;
    background: var(--color-surface);
}
.table th, .table td {
    padding: var(--space-3) var(--space-4);
    text-align: left;
    border-bottom: 1px solid var(--color-border);
}
.table th {
    color: var(--color-text-muted);
    font-weight: 600;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: var(--color-surface-alt);
}
.table tbody tr:nth-child(even) { background: var(--color-surface-alt); }
/* Money columns: monospace + right-aligned reads cleanly */
.table .num { font-family: var(--font-mono); text-align: right; }
/* Right-aligned header cells (e.g. ID) line up over their right-aligned values */
.table th.num { text-align: right; }
/* Click-to-sort headers */
.table.sortable th:not(.no-sort) { cursor: pointer; user-select: none; }
.table.sortable th:not(.no-sort):hover { color: var(--color-text); }
.sort-ind { font-size: 11px; color: var(--color-accent-text); }

/* Buttons */
.btn {
    display: inline-block;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font: inherit; font-weight: 600;
    cursor: pointer;
}
.btn-primary {
    background: var(--color-accent);
    color: var(--color-accent-contrast);
}
.btn-primary:hover { background: var(--color-accent-hover); text-decoration: none; }
/* .btn-navy was used by seven buttons on four pages (late fees, recurring,
   job detail, settings) and defined nowhere, so every one of them rendered as
   a plain grey button. It follows the brand navy -- the header colour -- so a
   custom navy in Settings > Colours, or another deployment's palette, carries
   through. */
.btn-navy {
    background: var(--brand-navy-800, #10233f);
    color: #fff;
}
.btn-navy:hover { background: var(--brand-navy-700, #17304f); color: #fff; text-decoration: none; }
/* Lighter gold variant - e.g. the "+ New Customer" action */
.btn-gold-light {
    background: var(--brand-gold-400);
    color: var(--grey-900);
}
.btn-gold-light:hover { background: var(--brand-gold-500); text-decoration: none; }
.btn-secondary {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-border-strong);
}
.btn-sm { padding: 6px var(--space-3); font-size: 13px; }

/* Activity / notes timeline on profiles */
.timeline { display: flex; flex-direction: column; gap: var(--space-3); }
.tl-item { padding-left: var(--space-4); border-left: 2px solid var(--color-border-strong); }
.tl-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.tl-meta { font-size: 12px; color: var(--color-text-muted); }
.tl-body { margin-top: 4px; font-size: 14px; color: var(--color-text); white-space: pre-wrap; }
.btn-danger {
    background: var(--color-danger);
    color: #fff;
    border-color: var(--color-danger);
}
.btn-danger:hover { filter: brightness(1.08); text-decoration: none; }

/* Buttons are frequently <a> elements, so the global `a:hover` (gold text +
   underline) would otherwise override a button's own text colour — most visibly
   turning the gold "+ New …" buttons gold-on-gold and unreadable. Re-assert each
   variant's colour and kill the underline on both hover and keyboard focus. */
.btn:hover, .btn:focus { text-decoration: none; }
.btn-primary:hover,    .btn-primary:focus    { color: var(--color-accent-contrast); }
.btn-gold-light:hover, .btn-gold-light:focus { color: var(--grey-900); }
.btn-secondary:hover,  .btn-secondary:focus  { color: var(--color-text); background: var(--color-surface-alt); }
.btn-danger:hover,     .btn-danger:focus     { color: #fff; }
.btn-ribbon:hover,     .btn-ribbon:focus     { color: #fff; }

/* Client-side pagination control (bottom-right of list tables) */
.pager {
    display: flex; align-items: center; justify-content: flex-end;
    gap: var(--space-4); flex-wrap: wrap;
    margin: var(--space-3) 0 var(--space-2);
}
.pager-info { color: var(--color-text-muted); font-size: 13px; }
.pager-ctrls { display: flex; align-items: center; gap: var(--space-3); }
.pager label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--color-text-muted); }
.pager-per {
    padding: 6px var(--space-2);
    border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm);
    background: var(--color-surface); color: var(--color-text); font: inherit;
}
.pager .btn[disabled] { opacity: .45; cursor: default; }
/* Translucent button that sits on the navy dashboard ribbon */
.btn-ribbon {
    background: rgba(255,255,255,.14);
    color: #fff;
    border-color: rgba(255,255,255,.35);
}
.btn-ribbon:hover { background: rgba(255,255,255,.24); text-decoration: none; }

/* Site-wide top ribbon (navy → blue brand bar on every page) */
.site-ribbon {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-5);
    flex-wrap: wrap;
    padding: var(--space-3) var(--space-6);
    background: linear-gradient(120deg, var(--brand-chrome-900) 0%, var(--brand-chrome-700) 55%, var(--brand-orange-500) 150%);
    color: #fff;
    border-bottom: 3px solid var(--brand-gold-400);
}
.site-ribbon-brand { display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; flex-shrink: 0; }
.srb-name { font-size: 17px; font-weight: 700; color: #fff; letter-spacing: .01em; }
.srb-sub {
    font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
    color: var(--brand-gold-400);
}
/* Global search, centered in the ribbon */
.site-ribbon-search { flex: 1 1 auto; display: flex; justify-content: center; min-width: 0; }
.site-ribbon-search input {
    width: 100%; max-width: 480px;
    padding: 8px var(--space-4);
    border-radius: 999px;
    border: 1px solid rgba(255,255,255,.28);
    background: rgba(255,255,255,.12);
    color: #fff; font: inherit; font-size: 14px;
}
.site-ribbon-search input::placeholder { color: rgba(255,255,255,.7); }
.site-ribbon-search input:focus {
    outline: none; background: rgba(255,255,255,.18);
    border-color: var(--brand-gold-400);
}
.site-ribbon-right { display: flex; align-items: center; gap: var(--space-5); flex-shrink: 0; }
.srb-date { font-size: 13px; color: rgba(255,255,255,.82); white-space: nowrap; }
/* Top-right user menu — the conventional place for account actions. */
.srb-user-wrap { position: relative; }
.srb-user {
    display: flex; align-items: center; gap: 9px;
    line-height: 1.25; max-width: 240px;
    padding: 4px 8px 4px 4px; border-radius: var(--radius-sm);
    background: none; border: 0; cursor: pointer; font-family: inherit;
    text-decoration: none; transition: background .12s;
}
.srb-user:hover, .srb-user[aria-expanded="true"] { background: rgba(255,255,255,.14); }
.srb-user-avatar {
    width: 30px; height: 30px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255,255,255,.18); color: #fff; font-size: 13px; font-weight: 700;
}
.srb-user-text { min-width: 0; text-align: left; overflow: hidden; }
.srb-user-name { display: block; font-size: 13px; font-weight: 700; color: #fff;
                 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.srb-user-role { display: block; font-size: 11px; color: var(--brand-gold-400);
                 text-transform: uppercase; letter-spacing: .04em; font-weight: 600;
                 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.srb-user-chev { width: 12px; height: 12px; flex: none; color: rgba(255,255,255,.6);
                 transition: transform .15s ease; }
.srb-user[aria-expanded="true"] .srb-user-chev { transform: rotate(180deg); }

/* The dropdown panel (shared markup, .user-menu). */
.user-menu {
    display: none;
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 90;
    min-width: 240px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 12px 32px rgba(0,0,0,.32);
    padding: 5px;
}
.user-menu.open { display: block; }
.user-menu-head { display: flex; align-items: center; gap: 9px; padding: 8px 10px 4px; }
.user-menu-avatar { width: 34px; height: 34px; border-radius: 50%; flex: none;
                    display: flex; align-items: center; justify-content: center;
                    background: var(--color-surface-alt); color: var(--color-text);
                    font-size: 14px; font-weight: 700; }
.user-menu-name { display: block; font-size: 14px; font-weight: 700; color: var(--color-text); }
.user-menu-role { display: block; font-size: 11px; font-weight: 600; color: var(--brand-gold-500);
                  text-transform: uppercase; letter-spacing: .04em; }
.user-menu .sep { height: 1px; background: var(--color-border); margin: 5px 2px; }
.user-menu a, .user-menu button, .user-menu form > button {
    display: block; width: 100%; text-align: left;
    padding: 8px 10px; font-size: 13px; font-family: inherit;
    background: none; border: 0; cursor: pointer;
    color: var(--color-text); text-decoration: none; border-radius: var(--radius-sm);
}
.user-menu a:hover, .user-menu button:hover { background: var(--color-surface-alt); text-decoration: none; }
.user-menu .danger { color: var(--color-danger); }
.user-menu form { margin: 0; }
@media (max-width: 860px) {
    .site-ribbon-search { order: 3; flex-basis: 100%; justify-content: flex-start; }
    .site-ribbon-search input { max-width: none; }
}

/* Dashboard: the top bar + welcome hero are combined into one navy block,
   with the gold accent bar only at the very bottom of the pair. */
.ribbon-stack {
    background: linear-gradient(125deg, var(--brand-chrome-900) 0%, var(--brand-chrome-700) 60%, var(--brand-orange-500) 150%);
    border-bottom: 3px solid var(--brand-gold-400);
}
.ribbon-stack .site-ribbon {
    background: transparent;
    border-bottom: 1px solid rgba(255,255,255,.10);   /* subtle divider, not the gold bar */
}
.ribbon-stack .dash-ribbon { background: transparent; }

/* Dashboard welcome ribbon (a richer variant used only on the dashboard) */
.dash-ribbon {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-6);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-6) var(--space-5);
    color: #fff;
}
.dash-ribbon-eyebrow {
    margin: 0 0 4px; font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
    color: var(--brand-gold-400);
}
.dash-ribbon-title { margin: 0; font-size: 24px; font-weight: 700; color: #fff; }
.dash-ribbon-sub { margin: 4px 0 0; font-size: 14px; color: rgba(255,255,255,.78); }
.dash-ribbon-right { display: flex; align-items: center; gap: var(--space-5); }
.dash-ribbon-date {
    text-align: right; font-weight: 700; font-size: 15px; line-height: 1.3; color: #fff;
}
.dash-ribbon-date span { display: block; font-weight: 400; font-size: 13px; color: rgba(255,255,255,.72); }
.dash-ribbon-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }
@media (max-width: 720px) {
    .dash-ribbon-right { width: 100%; justify-content: space-between; }
}

/* ------------------------------------------------------------
   PHONE LAYOUT (<= 760px)
   The sidebar is a fixed 224px column on desktop, which would swallow most of a
   phone screen. Below this width the shell stacks: the sidebar becomes a compact
   horizontally-scrolling icon bar across the top and the content takes the full
   width. Needed so receipts can actually be scanned/uploaded from a phone.
   ------------------------------------------------------------ */
@media (max-width: 760px) {
    .app-shell { flex-direction: column; }
    .sidebar {
        width: 100%;
        flex-direction: row;
        align-items: center;
        gap: 4px;
        padding: 6px 8px;
        overflow-x: auto;
        border-right: 0;
        border-bottom: 1px solid var(--color-header-border);
        /* Undo the desktop viewport-pinning — on a phone it's a top strip. */
        position: static;
        height: auto;
        overflow-y: visible;
    }
    .sidebar-footer { position: static; background: none; }
    .sidebar-brand { flex: 0 0 auto; padding: 0 6px 0 0; }
    .sidebar-brand img { max-width: 104px; }
    /* Group headings and the + New button are noise on a phone. The USER BLOCK
       stays — it now holds Sign Out and Lock Session, so hiding it would strip the
       only way to sign out on a phone. It collapses to just the avatar. */
    .sidebar-section, .sidebar-new { display: none; }
    .sidebar-user { margin-top: 0; margin-left: auto; flex: 0 0 auto; border-top: 0; }
    .sidebar-user-btn { padding: 4px; }
    .sidebar-user-text, .sidebar-user-chev { display: none; }
    .sidebar-user-menu { left: auto; right: 0; bottom: auto; top: calc(100% + 6px); min-width: 210px; }
    /* The headings are the ONLY way to expand a module — with them hidden, a
       collapsed module would strand its links with no way to reach them. So on a
       phone every module is forced open and the strip just scrolls. */
    .sidebar-nav[hidden] { display: flex !important; }
    .sidebar-nav .star { display: none; }   /* pinning is a desktop affordance */
    .sidebar-nav {
        display: flex; flex-direction: row; flex-wrap: nowrap;
        gap: 4px; padding: 0; flex: 0 0 auto;
    }
    .sidebar-nav a { white-space: nowrap; padding: 7px 10px; }
    .sidebar-footer { margin-top: 0; margin-left: auto; flex: 0 0 auto; border-top: 0; padding: 0; }

    .content { padding: var(--space-4) var(--space-3) var(--space-5); }
    .page-header { flex-wrap: wrap; gap: var(--space-2); padding: var(--space-4) var(--space-3); }
    .page-header h1 { font-size: 19px; }
    .page-header-actions { flex-wrap: wrap; }
    .totals-bar { grid-template-columns: 1fr 1fr; }
    .table { font-size: 13px; }
    /* Wide tables scroll inside their card instead of breaking the page. */
    .card { overflow-x: auto; }

    /* Dashboard scan button: full-width thumb target, first thing under the title. */
    .page-header-actions .scan-btn {
        width: 100%;
        justify-content: center;
        min-height: 50px;
        font-size: 16px;
        order: -1;              /* keep it first even though the row wraps */
    }

    /* Scan Inbox: stack the actions full-width so each is a proper thumb target,
       with Take Photo first — it's what you're there to do. */
    .scan-actions { flex-direction: column; align-items: stretch; }
    .scan-actions > .btn,
    .scan-actions > .file-picker {
        width: 100%;
        justify-content: center;
        min-height: 50px;
        font-size: 16px;   /* < 16px makes iOS zoom the whole page on focus */
    }
    .scan-actions #takePhoto { order: -1; }
    .scan-actions .file-name { text-align: center; }
}

/* ------------------------------------------------------------
   DASHBOARD SCAN BUTTON — top of the dashboard only, not site-wide
   ------------------------------------------------------------ */
.scan-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}
.scan-btn svg { width: 17px; height: 17px; flex: none; }

/* Status badges - map to your ENUM statuses */
.badge {
    display: inline-block;
    padding: 2px var(--space-2);
    border-radius: 999px;
    font-size: 12px; font-weight: 600;
}
.badge-paid     { background: var(--color-success-bg); color: var(--color-success); }
.badge-overdue  { background: var(--color-danger-bg);  color: var(--color-danger); }
.badge-partial  { background: var(--color-warning-bg); color: var(--color-warning); }
.badge-open     { background: var(--color-info-bg);    color: var(--color-info); }
.badge-void     { background: var(--color-surface-alt); color: var(--color-text-muted); }

/* ------------------------------------------------------------
   6. OPTIONAL: follow the OS setting automatically
   Only applies when the user hasn't explicitly chosen a theme
   (i.e. no theme-dark/theme-light class on <html>). Remove this
   block if you want light to always be the default.
   ------------------------------------------------------------ */
@media (prefers-color-scheme: dark) {
    /* Must carry EVERY token .theme-dark sets, not most of them.
       Four were missing — header background, overlay, and both shadows — so a
       visitor whose system is dark but who never touched the toggle got a dark
       ground with light-mode shadows painted on it: shadows tuned for dark text
       on white are nearly invisible against #15181c, and the modal overlay was
       the pale one, so a dialog floated with no separation from the page behind
       it.
       The explicit toggle was always right, which is why this survived: anyone
       testing dark mode clicks the button, and clicking the button takes the
       other path entirely. */
    :root:not(.theme-light):not(.theme-dark) {
        color-scheme: dark;
        --color-header-bg:     var(--brand-navy-800);
        --color-header-border: var(--brand-navy-600);
        --color-overlay:       rgba(0, 0, 0, 0.6);
        --shadow-sm:           0 1px 2px rgba(0,0,0,0.4);
        --shadow-md:           0 2px 6px rgba(0,0,0,0.45), 0 6px 16px rgba(0,0,0,0.5);
        --color-bg:            var(--dark-900);
        --color-surface:       var(--dark-800);
        --color-surface-alt:   var(--dark-700);
        --color-text:          #f0ece9;
        --color-text-muted:    #b3a49b;
        --color-text-inverse:  var(--dark-900);
        --color-border:        var(--dark-700);
        --color-border-strong: var(--dark-600);
        --color-accent:        var(--brand-orange-400);
        --color-accent-hover:  var(--brand-orange-500);
        --color-accent-text:   var(--brand-orange-400);
        /* MUST be restated here. This block overrides --color-accent but is a
           separate cascade from .theme-dark, so without this line it inherits
           :root's white — which is 1.93:1 on #ff7c40 and unreadable. The old
           gold palette hid this: its light and dark accents both took dark
           text, so the omission never showed. */
        --color-accent-contrast: var(--grey-900);
        --color-focus-ring:    #6ea8e0;
        --color-success: #5cc98a; --color-danger: #e8776b;
        --color-warning: #e0a944; --color-info: #6ea8e0;
        --color-accent-bg:#2e1d13;
        --color-success-bg:#1c3327; --color-danger-bg:#3a201d;
        --color-warning-bg:#362a15; --color-info-bg:#1d2c3b;
    }
}

/* Respect reduced motion (accessibility floor) */
@media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
}

/* Toasts for in-place form actions (see static/asyncform.js).
   Fixed to the viewport so the confirmation appears on whatever screen the
   action was fired from, instead of navigating to another page. */
.toast-host {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    z-index: 1000;
}
.toast {
    min-width: 260px;
    max-width: 420px;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    font-size: 14px;
    color: var(--color-header-text);
    background: var(--color-header-bg);
    border-left: 4px solid var(--color-accent);
    opacity: 1;
    transform: translateY(0);
    transition: opacity .35s ease, transform .35s ease;
}
.toast-success { border-left-color: var(--color-success); }
.toast-error   { border-left-color: var(--color-danger); }
.toast-out     { opacity: 0; transform: translateY(8px); }

/* Sticky column headers on long lists.

   This used to be paired with a horizontal-overflow rule scoped to
   max-width:1100px, on the reasoning that "on a wide screen the page scrolls".
   The page does scroll — and that is the problem. Measured on /admin/users at
   a 1300px viewport: the table is 1607px inside a 995px card, so the document
   grows to 1888px and the WHOLE page scrolls sideways, carrying the sidebar
   and the page header off-screen with it. It still overflows at 1600px. What
   you see is the last column cut off at the window edge.

   So the card now scrolls at every width. The cost is real and worth stating:
   overflow-x on an element makes overflow-y compute to auto, so the header
   sticks to the CARD rather than the viewport. That was already the accepted
   behaviour below 1100px; it is better than losing the sidebar. */
.table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--color-surface);
    box-shadow: inset 0 -1px 0 var(--color-border);
}
.card > .table { min-width: 100%; }
/* At EVERY width, not only below 1100px.
   This used to live inside `@media (max-width: 1100px)`, so a card holding a
   wide table had no scroll container on a large screen — and a table wider than
   its card then pushed the whole page sideways, which is the one thing a
   horizontal scrollbar on the body should never do. A table narrower than its
   card does not scroll, so applying this unconditionally costs nothing at any
   size; the media query was making the desktop the unhandled case. */
.card:has(> .table) { overflow-x: auto; }

/* Filter dropdown on list pages (see templates/_filter_bar.html) */
.filterbar { position: relative; }
.filterbar-toggle { display: inline-flex; align-items: center; gap: var(--space-2); }
.filter-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px;
    background: var(--color-accent); color: var(--color-accent-contrast);
    font-size: 11px; font-weight: 700;
}
.filter-menu {
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    z-index: 20;
    min-width: 320px;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}
.filter-menu[hidden] { display: none; }
.filter-menu label { display: flex; flex-direction: column; gap: var(--space-1); font-size: 13px; font-weight: 600; color: var(--color-text-muted); }
.filter-menu input, .filter-menu select {
    font: inherit; font-weight: 400; color: var(--color-text);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}
.filter-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.filter-actions { display: flex; gap: var(--space-2); margin-top: var(--space-1); }
.filter-actions .btn { flex: 1; justify-content: center; }
@media (max-width: 640px) {
    .filter-menu { min-width: 260px; }
    .filter-row { grid-template-columns: 1fr; }
}

/* Tab strip under a page header (Invoices / Recurring). Mirrors the QuickBooks
   pattern: sibling views of one thing, one sidebar entry. */
.tabs {
    display: flex;
    gap: var(--space-5);
    padding: 0 var(--space-6);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
}
.tabs a {
    position: relative;
    padding: var(--space-3) 0 var(--space-3);
    font-size: 14px;
    font-weight: 600;
    color: var(--color-text-muted);
    text-decoration: none;
    border-bottom: 3px solid transparent;
    margin-bottom: -1px;          /* sit the underline on the container border */
}
.tabs a:hover { color: var(--color-text); text-decoration: none; }
.tabs a.active {
    color: var(--color-text);
    border-bottom-color: var(--color-accent);
}

/* Attachments panel (templates/_attachments.html) ------------------------- */
.attach-empty {
    display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
    padding: var(--space-6) var(--space-4);
    color: var(--color-text-muted);
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-md);
    background: var(--color-surface-alt);
}
.attach-empty svg { opacity: .55; }
.attach-empty p { margin: 0; font-size: 14px; }

/* the upload strip: a styled label drives a visually-hidden file input, so the
   control matches the rest of the UI instead of the browser's default widget */
.attach-upload {
    display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border);
}
.attach-upload input[type=file],
.visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.file-picker {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    font-size: 14px; font-weight: 600; cursor: pointer;
}
.file-picker:hover { border-color: var(--color-accent); }
.file-name {
    font-size: 13px; color: var(--color-text-muted);
    max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.attach-hint { margin-left: auto; font-size: 12px; color: var(--color-text-muted); }
.attach-size { font-family: var(--font-mono); font-size: 13px; color: var(--color-text-muted); }

/* ============================================================
   PROFILE HEADER — shared by the customer, vendor and employee profiles.
   One navy identity band + one summary strip, so every record type reads the
   same way. Lives here rather than duplicated in three templates.
   ============================================================ */
.pf-head { background: var(--color-surface); border: 1px solid var(--color-border);
           border-radius: var(--radius-md); margin-bottom: var(--space-3); overflow: hidden; }
.pf-head-main { display: flex; gap: var(--space-4); align-items: center; flex-wrap: wrap;
                padding: var(--space-3) var(--space-4); background: #2b3f5c; }
.pf-avatar { width: 68px; height: 68px; border-radius: 50%; object-fit: cover; flex: none;
             border: 2px solid rgba(255,255,255,.25); background: rgba(255,255,255,.12);
             display: flex; align-items: center; justify-content: center;
             font-size: 22px; font-weight: 700; color: #fff; letter-spacing: .02em; }
.pf-id { flex: 1; min-width: 220px; }
.pf-id h1 { margin: 0 0 1px; font-size: 22px; color: #fff; }
.pf-id .sub { color: rgba(255,255,255,.68); font-size: 13px; margin: 0; line-height: 1.5; }
.pf-id .sub a { color: var(--brand-gold-400); }
.pf-id .sub a:hover { color: var(--brand-gold-400); text-decoration: underline; }
/* Right-hand kind/status block inside the navy band. */
.pf-kind { text-align: right; color: rgba(255,255,255,.68); font-size: 12px; line-height: 1.7; }
.pf-kind .k { color: #fff; font-weight: 700; font-size: 13px; }

.pf-strip { display: flex; flex-wrap: wrap; padding: 10px var(--space-4);
            border-top: 1px solid var(--color-border); background: var(--color-surface-alt); }
.pf-stat { flex: 1 1 96px; text-align: center; padding: 0 var(--space-2);
           border-left: 1px solid var(--color-border); }
.pf-stat:first-child { border-left: 0; text-align: left; padding-left: 0; }
.pf-stat .l { display: block; font-size: 10px; text-transform: uppercase;
              letter-spacing: .04em; color: var(--color-text-muted); }
.pf-stat .v { display: block; font-weight: 700; font-size: 14px; margin-top: 1px;
              font-family: var(--font-mono); }
.pf-stat .v.plain { font-family: inherit; }

/* Two-column card grid used by the profile Overviews. Cards stand on their own —
   no outer wrapper — so there's no grey-inside-white nesting. */
.pf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);
           margin-bottom: var(--space-3); }
@media (max-width: 860px) { .pf-grid { grid-template-columns: 1fr; } }
.pf-grid > .card { margin: 0; display: flex; flex-direction: column; }

/* Key/value rows inside those cards. */
.kv-list { margin: 0; }
.kv-list > div { display: flex; justify-content: space-between; align-items: baseline;
                 gap: var(--space-3); padding: 8px 0;
                 border-bottom: 1px solid var(--color-border); font-size: 14px; }
.kv-list > div:last-child { border-bottom: 0; }
.kv-list dt { color: var(--color-text-muted); flex: none; }
.kv-list dd { margin: 0; font-weight: 600; text-align: right; min-width: 0; word-break: break-word; }
.card-foot { margin-top: auto; padding-top: var(--space-3); text-align: right; }

/* Actions ▾ dropdown in a page header. */
.actions-wrap { position: relative; display: inline-block; }
.actions-menu { display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
                min-width: 230px; background: var(--color-surface);
                border: 1px solid var(--color-border); border-radius: var(--radius-sm);
                box-shadow: 0 10px 28px rgba(0,0,0,.22); padding: 5px; text-align: left; }
.actions-menu.open { display: block; }
.actions-menu a, .actions-menu button {
    display: block; width: 100%; text-align: left; padding: 8px 10px;
    background: none; border: 0; cursor: pointer; font-size: 13px; font-family: inherit;
    color: var(--color-text); text-decoration: none; border-radius: var(--radius-sm); }
.actions-menu a:hover, .actions-menu button:hover { background: var(--color-surface-alt); text-decoration: none; }
.actions-menu .sep { height: 1px; background: var(--color-border); margin: 5px 2px; }
.actions-menu .danger { color: var(--color-danger); }
.actions-menu form { margin: 0; }

/* "View as" preview banner — a persistent, unmistakable strip so you always know
   you're seeing the app through another user's permissions (read-only). */
.viewas-bar {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
    flex-wrap: wrap;
    background: var(--brand-gold-500); color: #1a1a1a;
    padding: 7px var(--space-4); font-size: 13px; font-weight: 600;
}
.viewas-bar a {
    color: #1a1a1a; text-decoration: underline; font-weight: 700; white-space: nowrap;
}

/* Workspace chooser link in the sidebar. Deliberately styled unlike the nav items
   below it — it leaves the current workspace rather than navigating within it. */
.sidebar-workspaces{
    display:flex; align-items:center; gap:9px;
    margin:0 12px 10px; padding:9px 12px;
    border:1px solid rgba(255,124,64,.40); border-radius:9px;
    color:var(--brand-orange-400); text-decoration:none; font-size:13.5px; font-weight:600;
    letter-spacing:.02em;
}
.sidebar-workspaces:hover{ background:rgba(255,124,64,.12); border-color:rgba(255,124,64,.75); }

/* Invoice line tax: now shown as text, since the rate is chosen once for the
   whole invoice rather than per line. */
.tax-label { display:inline-block; padding-top:9px; font-size:13px; color:var(--color-text-muted); }

/* Jobs "To do — not scheduled yet". Amber rather than red: these are outstanding,
   not wrong. Hidden entirely when empty (see jobs.html) so it stays a real list. */
.todo-card { border-left: 3px solid var(--color-warning); margin-bottom: var(--space-4); }
.todo-head { display:flex; align-items:baseline; gap:var(--space-3); flex-wrap:wrap;
             margin-bottom: var(--space-2); }
.todo-head h2 { font-size:17px; margin:0; }
.todo-count { display:inline-block; min-width:22px; padding:1px 7px; border-radius:999px;
              background:var(--color-warning); color:#fff; font-size:12.5px;
              font-weight:700; text-align:center; margin-left:6px; }
.todo-sub { color: var(--color-text-muted); font-size:13px; }

/* Chart of accounts — search across all types */
.acct-search { display:flex; align-items:center; gap:var(--space-2);
               padding: var(--space-2) var(--space-3);
               border-bottom: 1px solid var(--color-border); background: var(--color-surface); }
.acct-search input { flex:1; min-width:0; font: inherit; font-size:13.5px;
                     padding: var(--space-2) var(--space-3);
                     border: 1px solid var(--color-border-strong);
                     border-radius: var(--radius-sm);
                     background: var(--color-bg); color: var(--color-text); }
.acct-search input:focus { outline:none; border-color: var(--color-accent); }
.acct-search-count { flex:none; font-size:12px; color: var(--color-text-muted); white-space:nowrap; }

/* --- Role badges -------------------------------------------------------
   Colour carries meaning here rather than being decorative: the warmer the
   badge, the more the role can do. God Mode is red because it should catch the
   eye in a list, Manager amber, the money-handling roles blue, ordinary staff
   neutral, and Intern the quietest.

   Built from rgba tints over whatever surface is behind them, so one definition
   works in both light and dark mode — a fixed pastel would be unreadable on a
   dark table row.

   role-unknown is the fallback for any role added later, so a new role looks
   deliberate rather than unstyled. */
.badge-role {
    display: inline-block;
    font-size: 12px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 999px;
    white-space: nowrap;
    border: 1px solid transparent;
}
.role-god          { background: rgba(220,38,38,.16);  color: #dc2626; border-color: rgba(220,38,38,.35); }
.role-manager      { background: rgba(217,119,6,.16);  color: #b45309; border-color: rgba(217,119,6,.32); }
.role-supervisor   { background: rgba(202,138,4,.16);  color: #a16207; border-color: rgba(202,138,4,.30); }
.role-accountant   { background: rgba(37,99,235,.15);  color: #2563eb; border-color: rgba(37,99,235,.30); }
.role-ar_clerk     { background: rgba(13,148,136,.16); color: #0d9488; border-color: rgba(13,148,136,.30); }
.role-ap_clerk     { background: rgba(3,105,161,.15);  color: #0369a1; border-color: rgba(3,105,161,.30); }
.role-hr_payroll   { background: rgba(147,51,234,.15); color: #9333ea; border-color: rgba(147,51,234,.30); }
.role-salesperson  { background: rgba(22,163,74,.16);  color: #15803d; border-color: rgba(22,163,74,.30); }
.role-employee     { background: rgba(100,116,139,.18); color: #475569; border-color: rgba(100,116,139,.30); }
.role-employee_ess { background: rgba(100,116,139,.13); color: #64748b; border-color: rgba(100,116,139,.24); }
.role-intern       { background: rgba(148,163,184,.16); color: #64748b; border-color: rgba(148,163,184,.28); }
.role-unknown      { background: rgba(100,116,139,.16); color: #64748b; border-color: rgba(100,116,139,.28); }

/* Dark mode: the tints hold up over a dark ground, but the text colours are
   picked for a light background and go muddy, so lift them.

   Follows the same selector pattern as the rest of the file: the explicit class
   first, then the prefers-color-scheme block excluding BOTH explicit classes, so
   someone who has deliberately chosen a theme never gets this applied twice. */
.theme-dark .badge-role { filter: brightness(1.45); }
@media (prefers-color-scheme: dark) {
    :root:not(.theme-light):not(.theme-dark) .badge-role { filter: brightness(1.45); }
}

/* --- Activity-log event dots -------------------------------------------
   A small marker beside each event so a lockout or a failed sign-in is
   findable by eye in a long list, without colouring the whole row (which
   would make an ordinary log look alarming). Neutral events get a muted dot
   so every row still lines up on the same left edge. */
.ev-dot {
    display: inline-block;
    width: 7px; height: 7px;
    border-radius: 50%;
    margin-right: 8px;
    vertical-align: middle;
    background: var(--color-border);
}
.ev-dot.warn { background: #d97706; }
.ev-dot.bad  { background: #dc2626; }

/* --- Jobs to-do reasons -------------------------------------------------
   The to-do list holds every unfinished job, so the reason a job is there is
   the column that makes it useful. Colour ranks urgency rather than decorating:
   red for past due, amber for never scheduled, muted for work simply under way.

   rgba tints over the surface behind them, so one definition holds in light and
   dark alike. */
.todo-why {
    display: inline-block;
    font-size: 12px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}
.todo-why.overdue     { background: rgba(220,38,38,.15);   color: #dc2626; }
.todo-why.unscheduled { background: rgba(217,119,6,.15);   color: #b45309; }
.todo-why.on-hold     { background: rgba(100,116,139,.18); color: #475569; }
.todo-why.in-progress { background: rgba(37,99,235,.13);   color: #2563eb; }

.theme-dark .todo-why { filter: brightness(1.45); }
@media (prefers-color-scheme: dark) {
    :root:not(.theme-light):not(.theme-dark) .todo-why { filter: brightness(1.45); }
}

/* --- Search field -------------------------------------------------------
   A reusable search input: icon inside on the left, a clear button that only
   appears once there is something to clear, and a focus ring that uses the
   accent rather than the browser's default outline.

   Built from tokens so it follows the brand colour and both themes. Sized to
   match .btn-sm so it sits level with the filter buttons beside it instead of
   floating a couple of pixels off. */
.search-field {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex: 1;
    min-width: 200px;
    max-width: 380px;
}
.search-field input {
    width: 100%;
    padding: 8px 34px 8px 36px;
    font: inherit;
    font-size: 14px;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 9px;
    transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
}
.search-field input::placeholder { color: var(--color-text-muted); }
.search-field input:hover { border-color: var(--color-text-muted); }
.search-field input:focus {
    outline: none;
    border-color: var(--color-accent);
    /* A tinted ring rather than a hard outline — colour-mix keeps it in step
       with whatever accent is set under Settings -> Branding. */
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
/* The magnifier is decoration, so it must never swallow a click meant for the
   field behind it. */
.search-field .sf-icon {
    position: absolute; left: 11px; top: 50%;
    transform: translateY(-50%);
    width: 15px; height: 15px;
    color: var(--color-text-muted);
    pointer-events: none;
}
.search-field .sf-clear {
    position: absolute; right: 6px; top: 50%;
    transform: translateY(-50%);
    display: none;
    border: 0; background: transparent; cursor: pointer;
    padding: 4px 6px; line-height: 1; border-radius: 6px;
    color: var(--color-text-muted); font-size: 15px;
}
.search-field .sf-clear:hover { background: var(--color-surface-alt); color: var(--color-text); }
.search-field .sf-clear:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.search-field.has-value .sf-clear { display: block; }
/* Chrome draws its own clear button, which would sit beside ours. */
.search-field input::-webkit-search-cancel-button { display: none; }

/* --- Account team picker ------------------------------------------------
   A card per employee — tick, avatar, name, job title, and what they do on
   THIS account. Grouped by department, because "who from Field Services is on
   this account?" is the question people actually arrive with, and a flat
   alphabetical list of two dozen names cannot answer it.

   Built on tokens so it follows the brand accent and both themes. */
.team-picker-head {
    display: flex; align-items: center; gap: var(--space-3);
    flex-wrap: wrap; margin-bottom: var(--space-4);
}
.team-dept {
    margin: var(--space-5) 0 var(--space-2);
    display: flex; align-items: baseline; gap: 10px;
}
.team-dept:first-of-type { margin-top: var(--space-3); }
.team-dept h4 {
    margin: 0; font-size: 12px; font-weight: 700;
    letter-spacing: .09em; text-transform: uppercase;
    color: var(--color-text-muted);
}
.team-dept .n {
    font-size: 11px; font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface-alt);
    padding: 1px 7px; border-radius: 999px;
}
.team-dept .rule { flex: 1; height: 1px; background: var(--color-border); }

.team-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
    gap: 10px;
}
.team-card {
    display: flex; align-items: center; gap: 11px;
    padding: 11px 13px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    transition: border-color .12s ease, background .12s ease;
}
.team-card:hover { border-color: var(--color-text-muted); }
/* Selected reads as selected without shouting — a tinted border and ground
   rather than a saturated fill, since half the list may be ticked. */
.team-card.on {
    border-color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 8%, var(--color-surface));
}
.team-card input[type="checkbox"] { flex: none; width: 16px; height: 16px; cursor: pointer; }
.team-card .who { flex: 1; min-width: 0; }
.team-card .nm {
    font-weight: 600; font-size: 14px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.team-card .ti {
    font-size: 12px; color: var(--color-text-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.team-card select {
    flex: none; width: 132px; font-size: 12.5px;
    padding: 6px 8px; border-radius: 8px;
    border: 1px solid var(--color-border);
    background: var(--color-surface-alt); color: var(--color-text);
}
/* The role only matters for somebody who is ON the team, so it stays quiet
   until they are ticked — still reachable, just not competing for attention. */
.team-card:not(.on) select { opacity: .55; }

.team-avatar {
    flex: none; width: 30px; height: 30px; border-radius: 50%;
    display: grid; place-items: center;
    font-size: 11px; font-weight: 700; letter-spacing: .02em;
    color: #fff;
}

/* Selection summary — sticks to the bottom of the panel so the count and Save
   stay reachable however far down the list you have scrolled. */
.team-bar {
    position: sticky; bottom: 0; z-index: 2;
    display: flex; align-items: center; gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-4); padding: 13px 15px;
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: 10px;
}
.team-bar .count { font-weight: 600; }
.team-bar .sub { font-size: 12.5px; color: var(--color-text-muted); }
.team-bar .spacer { flex: 1; }
.team-empty { color: var(--color-text-muted); padding: var(--space-4) 0; }

/* --- Sidebar attention dot ----------------------------------------------
   Marks a nav item that has something waiting. Pushed to the right edge so it
   reads as a status on the row rather than punctuation after the label, and
   sized to be noticeable without competing with the active-page highlight.

   A fixed red rather than a token: this means "look here" in both themes, and
   should not shift when the brand accent is changed under Settings. */
.sidebar-nav a .nav-dot,
.rt-nav a .nav-dot,
nav a .nav-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #dc2626;
    margin-left: auto;
    flex: none;
    /* A ring so it stays visible against the dark sidebar and against the
       lighter active-row background. */
    box-shadow: 0 0 0 2px rgba(220, 38, 38, .25);
}


/* ============================================================
   Manage inventory (product page)
   Raven's own tokens throughout, so it sits in this application rather than
   looking like the one it borrows its shape from.
   ============================================================ */
.inv-panel { margin-top: var(--space-5); padding-top: var(--space-4);
             border-top: 1px solid var(--color-border); }
.inv-head { display: flex; justify-content: space-between; align-items: baseline;
            margin-bottom: var(--space-3); }
.inv-hist { font-size: 13px; font-weight: 600; color: var(--color-info); text-decoration: none; }
.inv-hist:hover { text-decoration: underline; }
.inv-track { max-width: 460px; margin-bottom: var(--space-4); }
.inv-track label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.inv-hint { font-size: 12px; color: var(--color-text-muted); margin: 6px 0 0; line-height: 1.5; }
.inv-table { margin-bottom: var(--space-4); }
.inv-table th { font-size: 12px; }
.inv-qty { width: 100%; text-align: right; font-family: var(--font-mono);
           padding: 6px 8px; border: 1px solid var(--color-border);
           border-radius: var(--radius-sm); background: var(--color-surface);
           color: var(--color-text); }
.inv-qty:focus { outline: none; border-color: var(--color-accent); }
.inv-drv { display: block; font-size: 12px; color: var(--color-text-muted); }
/* Available can legitimately go negative — more promised than is on the shelf.
   That is worth seeing, not hiding. */
.inv-neg { color: var(--color-danger); font-weight: 700; }
.inv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
            gap: var(--space-3); margin-bottom: var(--space-3); }
.inv-opt { font-weight: 400; color: var(--color-text-muted); font-size: 12px; }
.inv-note { font-size: 12px; color: var(--color-text-muted); }

/* ---- Inventory section ---- */
.inv-filters { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap;
               margin-bottom: var(--space-3); }
.inv-search { flex: 1 1 260px; min-width: 200px; padding: 8px 12px;
              border: 1px solid var(--color-border); border-radius: var(--radius-sm);
              background: var(--color-surface); color: var(--color-text); font: inherit; }
.inv-search:focus { outline: none; border-color: var(--color-accent); }
.inv-lowbox { display: flex; align-items: center; gap: 6px; font-size: 13px;
              white-space: nowrap; cursor: pointer; }
.inv-lowbox input { width: auto; margin: 0; }
.inv-badge { display: inline-block; min-width: 18px; padding: 1px 6px; border-radius: 999px;
             font-size: 11px; font-weight: 700; text-align: center;
             background: var(--color-warning); color: var(--color-text-inverse); }
/* A low row is tinted rather than recoloured, so the numbers stay readable and
   the whole table does not turn into a warning. */
.inv-row-low { background: color-mix(in srgb, var(--color-warning) 10%, transparent) !important; }
.inv-low { font-size: 11px; font-weight: 700; color: var(--color-warning); }
.inv-kind { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px;
            background: var(--color-surface-alt); color: var(--color-text-muted); }
.inv-kind.k-consumable { background: color-mix(in srgb, var(--color-info) 16%, transparent);
                         color: var(--color-info); }
.inv-kind.k-serialised { background: color-mix(in srgb, var(--color-accent) 16%, transparent);
                         color: var(--color-accent-text); }
.inv-kind.k-asset { background: color-mix(in srgb, var(--color-success) 16%, transparent);
                    color: var(--color-success); }

/* ---- Item image + colour tile ---- */
.img-row { display: flex; gap: var(--space-4); align-items: flex-start; flex-wrap: wrap; }
.img-ctl { flex: 1 1 320px; min-width: 260px; }
.img-lab { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
/* The preview is the size it will be in a list, so what you pick is what you get. */
.img-prev { width: 96px; height: 96px; flex: none; border-radius: var(--radius-sm);
            border: 1px solid var(--color-border); overflow: hidden;
            display: flex; align-items: center; justify-content: center; }
.img-prev img { width: 100%; height: 100%; object-fit: cover; }
.img-prev span { font-size: 28px; font-weight: 800; color: #fff;
                 text-shadow: 0 1px 2px rgba(0,0,0,.35); letter-spacing: .02em; }
.tiles { display: flex; flex-wrap: wrap; gap: 6px; }
.tile { width: 30px; height: 30px; border-radius: 6px; cursor: pointer; position: relative;
        border: 2px solid transparent; }
.tile input { position: absolute; opacity: 0; width: 0; height: 0; }
/* The chosen swatch is ringed rather than ticked: a tick disappears on the pale
   colours and the ring reads on all of them, on both themes. */
.tile.on { border-color: var(--color-text); box-shadow: 0 0 0 2px var(--color-surface) inset; }
.tile:focus-within { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.tile-none { background: var(--color-surface-alt); display: flex; align-items: center;
             justify-content: center; color: var(--color-text-muted); font-weight: 700; }
/* The same tile, list-sized, for the item library and the stock list. */
.item-tile { width: 34px; height: 34px; border-radius: 5px; flex: none; overflow: hidden;
             display: inline-flex; align-items: center; justify-content: center;
             border: 1px solid var(--color-border); vertical-align: middle; }
.item-tile img { width: 100%; height: 100%; object-fit: cover; }
.item-tile span { font-size: 12px; font-weight: 800; color: #fff;
                  text-shadow: 0 1px 2px rgba(0,0,0,.35); }

/* ---- Inventory tab bar ---- */
.inv-tabs { display: flex; gap: var(--space-4); align-items: center; flex-wrap: wrap;
            border-bottom: 1px solid var(--color-border); margin-bottom: var(--space-4); }
.inv-tabs a { padding: 10px 2px; font-size: 14px; font-weight: 600; text-decoration: none;
              color: var(--color-text-muted); border-bottom: 2px solid transparent;
              margin-bottom: -1px; white-space: nowrap; }
.inv-tabs a:hover { color: var(--color-text); }
/* --color-accent IS the brand orange — orange-600 on light, 400 on dark — so the
   underline follows the theme instead of the blue the mock borrowed. */
.inv-tabs a.on { color: var(--color-text); border-bottom-color: var(--color-accent); }

/* ---- Receiving / scanning ---- */
.scan-head { display: flex; justify-content: space-between; align-items: flex-start;
             gap: var(--space-3); margin-bottom: var(--space-3); flex-wrap: wrap; }
.scan-form { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
/* Deliberately large. This is used standing over a box with a scanner in one
   hand, and the value it holds has to be readable at arm's length. */
.scan-box { flex: 1 1 320px; min-width: 220px; font-size: 20px; padding: 12px 14px;
            font-family: var(--font-mono); letter-spacing: .04em;
            border: 2px solid var(--color-accent); border-radius: var(--radius-sm);
            background: var(--color-surface); color: var(--color-text); }
.scan-box:focus { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 30%, transparent); }
.scan-qty { width: 90px; font-size: 20px; padding: 12px 10px; text-align: right;
            font-family: var(--font-mono); border: 1px solid var(--color-border);
            border-radius: var(--radius-sm); background: var(--color-surface);
            color: var(--color-text); }

.pick-item { flex: 1 1 320px; min-width: 220px; padding: 10px 12px; font: inherit;
             border: 1px solid var(--color-border); border-radius: var(--radius-sm);
             background: var(--color-surface); color: var(--color-text); }

/* ---- Camera scanning ---- */
.scan-cam-btn.scan-cam-off { opacity: .45; cursor: not-allowed; }
.scan-cam { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.86);
            display: flex; align-items: center; justify-content: center; padding: 16px; }
.scan-cam-box { width: min(560px, 100%); text-align: center; }
.scan-cam video { width: 100%; max-height: 70vh; border-radius: var(--radius-md);
                  background: #000; object-fit: cover; }
/* A frame to aim with. Nothing reads it — it just stops people holding the phone
   at arm's length and wondering why nothing happens. */
.scan-cam-reticle { position: relative; margin: -28% auto 0; width: 68%; height: 22%;
                    border: 2px solid var(--brand-gold-400); border-radius: 8px;
                    box-shadow: 0 0 0 100vmax rgba(0,0,0,.28); pointer-events: none; }
.scan-cam-msg { color: #fff; margin: 22% 0 14px; font-size: 14px; }
.scan-cam-err { color: var(--brand-gold-400); font-weight: 600; }
.scan-cam-close { min-width: 120px; }

/* ---- Products list: codes, variations and stock levels -------------------
   A SKU and a variation are read character by character, not as words — one
   wrong digit is a different item. Monospace in a bordered chip is what makes
   that legible, and it separates the identifiers from the prose columns beside
   them at a glance. Both use the theme's own tokens, so they follow dark mode
   without a second rule. */
.table .sku,
.table .variation {
    font-family: var(--font-mono);
    font-size: 12.5px;
    padding: 2px 7px;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-surface-alt);
    white-space: nowrap;
}
/* The variation is a name, not a code, so it keeps the body face — it is boxed
   for grouping, not for character-by-character reading. */
.table .variation {
    font-family: inherit;
    font-size: 12.5px;
    color: var(--color-text-muted);
}
/* At or below the alert level. Stated in colour AND weight: colour alone is not
   a signal everyone receives. */
.table .stock-low {
    color: var(--color-danger);
    font-weight: 700;
}
/* An inactive item stays visible but recedes — it is still in the catalogue and
   still searchable, it just is not something you would sell today. */
.table tr.row-inactive td { opacity: 0.55; }

/* ---- Inventory overview: the numbers, then the list ---- */
/* Colours come from the theme tokens rather than the mock's own palette, so
   these follow light/dark and the brand the way the rest of the app does. */
.inv-stats { display: grid; gap: var(--space-3); margin-bottom: var(--space-4);
             grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.inv-stat { display: flex; gap: var(--space-3); align-items: flex-start;
            background: var(--color-surface); border: 1px solid var(--color-border);
            border-radius: var(--radius-lg, 12px); padding: var(--space-4); }
.inv-stat-ic { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 10px;
               display: grid; place-items: center; font-size: 17px; font-weight: 700;
               background: var(--color-surface-alt); color: var(--color-text-muted); }
.inv-stat-ic.ic-items { background: color-mix(in srgb, var(--color-info) 16%, transparent);
                        color: var(--color-info); }
.inv-stat-ic.ic-low   { background: color-mix(in srgb, var(--color-warning) 18%, transparent);
                        color: var(--color-warning); }
.inv-stat-ic.ic-out   { background: color-mix(in srgb, var(--color-danger) 16%, transparent);
                        color: var(--color-danger); }
.inv-stat-ic.ic-val   { background: color-mix(in srgb, var(--color-success) 16%, transparent);
                        color: var(--color-success); }
.inv-stat-k { margin: 0; font-size: 12.5px; color: var(--color-text-muted); font-weight: 600; }
.inv-stat-v { margin: 2px 0 0; font-size: 26px; font-weight: 700; line-height: 1.1;
              font-variant-numeric: tabular-nums; }
.inv-stat-s { margin: 2px 0 0; font-size: 12px; color: var(--color-text-muted); }

/* The primary action sits at the end of the filter row, away from Apply and
   Clear: creating an item is a different kind of act from narrowing a list. */
.inv-new { margin-left: auto; }

/* Eleven columns will not fit a laptop at every zoom. The TABLE scrolls inside
   its own box rather than the page scrolling sideways, which is what makes a
   sticky sidebar and a wide table unusable together. */
.inv-scroll { overflow-x: auto; }
.inv-table { min-width: 940px; }
.inv-mono { font-family: var(--font-mono); font-size: 12.5px; }
.inv-count { margin: 0; padding: 10px var(--space-4); font-size: 12.5px;
             color: var(--color-text-muted); text-align: right;
             border-top: 1px solid var(--color-border); }
.inv-act-head { display: flex; align-items: center; justify-content: space-between;
                gap: var(--space-3); margin-bottom: var(--space-3); flex-wrap: wrap; }

/* Status. Four states, and the reason each earns a colour:
     In Stock     nothing to do
     Low Stock    order it before it runs out
     Out of Stock somebody cannot do their job today
     Allocated    there IS stock and every unit of it is spoken for — the state
                  that looks fine in a count and is not. */
.inv-pill { display: inline-block; font-size: 11.5px; font-weight: 700;
            padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
.inv-pill.p-in        { background: color-mix(in srgb, var(--color-success) 16%, transparent);
                        color: var(--color-success); }
.inv-pill.p-low       { background: color-mix(in srgb, var(--color-warning) 20%, transparent);
                        color: var(--color-warning); }
.inv-pill.p-out       { background: color-mix(in srgb, var(--color-danger) 16%, transparent);
                        color: var(--color-danger); }
.inv-pill.p-allocated { background: color-mix(in srgb, var(--color-info) 16%, transparent);
                        color: var(--color-info); }

/* ---- Inventory: the section rail ---- */
/* minmax(0, 1fr) on the content column, NOT 1fr. A grid item's minimum width is
   its content by default, so a wide table would push the column wider than the
   page instead of scrolling inside .inv-scroll — the whole layout blows out and
   the page scrolls sideways. This one keyword is what makes the table box work. */
.inv-layout { display: grid; grid-template-columns: 216px minmax(0, 1fr);
              gap: var(--space-5); align-items: start; }
.inv-rail h1 { margin: 0 0 4px; font-size: 30px; line-height: 1.15; }
.inv-rail .sub { margin: 0 0 var(--space-4); color: var(--color-text-muted);
                 font-size: 14px; line-height: 1.45; }
.inv-railnav { display: flex; flex-direction: column; gap: 8px; }
.inv-railnav a { display: flex; align-items: center; gap: 10px;
                 padding: 11px 13px; border-radius: 10px; font-size: 13.5px;
                 font-weight: 600; text-decoration: none;
                 border: 1px solid var(--color-border);
                 background: var(--color-surface); color: var(--color-text); }
.inv-railnav a:hover { border-color: var(--color-accent); }
.inv-railnav a .ic { font-size: 15px; color: var(--color-text-muted); }
/* The page you are on, in Raven's gold rather than the mock's blue. */
.inv-railnav a.on { background: color-mix(in srgb, var(--color-accent) 12%, transparent);
                    border-color: color-mix(in srgb, var(--color-accent) 34%, transparent);
                    color: var(--color-accent); }
.inv-railnav a.on .ic { color: var(--color-accent); }

/* The app's own sidebar is 224px and always there, so this rail is a THIRD
   column. That is fine on a wide monitor and not on a laptop: 224 + 216 leaves
   under 950px for an eleven-column table. Below 1100px the rail becomes a row
   above the content and gives its width back — the rail is the part that can
   move, and the table is the part that cannot. */
@media (max-width: 1100px) {
    .inv-layout { grid-template-columns: 1fr; gap: var(--space-4); }
    .inv-railnav { flex-direction: row; flex-wrap: wrap; }
    .inv-rail h1 { font-size: 24px; }
}
