/* The hidden attribute must beat every utility class that sets a display value.
   .btn{display:inline-flex}, .alert{display:flex}, .form-grid{display:grid} and friends
   all outrank the browser default [hidden]{display:none}, so markup that relies on the
   attribute rendered anyway - including Finance-only export buttons on an Employee's
   screen and the Admin-only "Reopen month" control. One global rule instead of a
   per-component patch each time it is noticed. */
[hidden]{display:none !important}

/* ============================================================
   MICO360 Finance - Design System (theme.css)
   Brand: MICO Maroon #8B1B1B + Charcoal ink on white.
   Static prototype styles. Single source of truth for all pages.
   ============================================================ */

/* ------------------------------------------------------------
   Self-hosted webfonts (Inter + Montserrat, SIL Open Font Licence 1.1).
   Previously both families were pulled from fonts.googleapis.com with a
   render-blocking <link> that sat BEFORE this stylesheet. For an on-premise
   finance product that meant (a) first paint waited on a third-party CDN and
   broke entirely on an air-gapped install, and (b) every user's IP was sent to
   Google on each page load. The files below are the same latin / latin-ext
   woff2 subsets Google serves, copied into assets/fonts/ - see fonts/OFL.txt.

   Both are VARIABLE fonts: one file covers the whole weight range, so the
   four files here replace what used to be twelve @font-face downloads.
   unicode-range keeps latin-ext lazy - it is only fetched if a page actually
   contains those codepoints. ------------------------------------------------ */
@font-face{
  font-family:'Inter';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('fonts/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,
    U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,
    U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'Montserrat';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('fonts/montserrat-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Montserrat';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('fonts/montserrat-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,
    U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,
    U+2113,U+2C60-2C7F,U+A720-A7FF;
}

:root{
  /* Brand */
  --maroon:#8B1B1B;          /* fills: buttons, header bars (white text on top) */
  --maroon-fg:#8B1B1B;       /* brand as TEXT/icon colour - brightened in dark theme */
  --maroon-dark:#6E1414;
  --maroon-tint:#F5E7E7;
  --maroon-tint-2:#FBF4F4;
  /* Sidebar — LIGHT in the light theme (matches the dashboard reference), dark in dark mode.
     A full token set so the shell is theme-aware instead of hard-coded white-on-dark. */
  --sidebar-bg:#FFFFFF;
  --sidebar-fg:#3F3F46;             /* nav item label */
  --sidebar-muted:#6B7280;          /* section headings + footer text — 4.83:1 on the white sidebar (AA) */
  --sidebar-hover-bg:#F4F5F7;
  --sidebar-border:#ECEDF0;
  --sidebar-active-bg:var(--maroon-tint);
  --sidebar-active-fg:var(--maroon);
  --sidebar-active-badge-bg:var(--maroon);
  --sidebar-brand-fg:#1C1C1C;
  --chart-grid:#EEEEF0;
  --chart-label:#5F6673;

  /* Neutrals */
  --ink:#1C1C1C;
  --ink-2:#3f3f46;
  /* was #6B7280 - only 4.40:1 on --bg #F4F4F5, i.e. below AA for the many
     muted captions that sit on the page background rather than on a card.
     #5F6673 keeps the same cool-grey character and clears AA on every
     surface we actually paint it on (5.26 on --bg, 5.78 on --surface). */
  --muted:#5F6673;
  --line:#E4E4E7;
  --line-2:#EEEEF0;
  --surface:#FFFFFF;
  --bg:#F4F4F5;
  --bg-2:#FAFAFB;

  /* Semantic.
     --warning was #B45309 (4.39 on --warning-bg) and --info was #2563EB
     (4.44 on --info-bg): both fell short of AA as badge/pill text, which is
     exactly how they are used most (Returned / Pending, Submitted). Darkened
     just enough to clear 4.5 while staying the same recognisable amber/blue. */
  --success:#1E7D46; --success-bg:#E7F3ED;
  --warning:#9A4A07; --warning-bg:#FBEEDD;
  --danger:#C62828;  --danger-bg:#FBE9E9;
  --info:#1D4FD8;    --info-bg:#E7EEFD;
  --violet:#6D4AAE;  --violet-bg:#F0EBFB;   /* the 5th KPI chip (reconciliation variance) */

  /* Type - self-hosted first, then a real local stack so an air-gapped or
     still-loading install lands on a near-metric match rather than Times. */
  --font-head:'Montserrat','Poppins','Segoe UI',system-ui,-apple-system,'Helvetica Neue',Arial,sans-serif;
  --font-body:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;

  /* Type scale (1.125-ish, tuned for a dense finance UI) */
  --fs-xs:11.5px; --fs-sm:12.5px; --fs-base:13.5px; --fs-md:15px;
  --fs-lg:17px;   --fs-xl:20px;   --fs-2xl:23px;
  --lh-tight:1.3; --lh-base:1.5;

  /* Spacing scale - 4px base. Replaces the ad-hoc 9/11/14/18/22px values that
     had accumulated, so page-head / card-head / card-pad share one rhythm. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px;
  --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px;

  /* Metrics */
  --radius:14px; --radius-sm:10px; --radius-lg:18px;
  --shadow:0 1px 2px rgba(0,0,0,.04),0 1px 3px rgba(0,0,0,.06);
  --shadow-lg:0 4px 12px rgba(0,0,0,.08),0 2px 4px rgba(0,0,0,.04);
  --sidebar-w:236px; --topbar-h:60px;
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  font-family:var(--font-body);
  color:var(--ink);
  background:var(--bg);
  font-size:14px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--maroon-fg);text-decoration:none}
a:hover{text-decoration:underline}
h1,h2,h3,h4,h5{font-family:var(--font-head);font-weight:800;color:var(--ink);letter-spacing:.01em;line-height:1.2}
.mono{font-variant-numeric:tabular-nums}
/* Every money / quantity / date figure in the product renders with tabular
   figures so decimal points line up down a column and a changing value never
   reflows its neighbours. Inter's default figures are proportional. */
.omr,.big-amt,.amt-in,.amt-out,.kpi .val,.num,.mono,
input[type=number],input[type=date],input[type=month],
td.num,th.num,.sc-amt,.pager .pinfo{font-variant-numeric:tabular-nums}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}

/* ---------- App shell ---------- */
.app{display:grid;grid-template-columns:var(--sidebar-w) 1fr;min-height:100vh}

.sidebar{
  background:var(--sidebar-bg);color:var(--sidebar-fg);
  display:flex;flex-direction:column;position:sticky;top:0;height:100vh;
  /* a clean seam against the topbar — a hairline in light mode, a dark edge in dark mode */
  border-right:1px solid var(--sidebar-border);
}
.sidebar .brand{
  display:flex;align-items:center;gap:9px;padding:16px 18px;border-bottom:1px solid var(--sidebar-border);
}
.sidebar .brand img{height:30px;width:auto;border-radius:6px}
.sidebar .brand .txt{font-family:var(--font-head);font-weight:800;font-size:15px;color:var(--sidebar-brand-fg);letter-spacing:.02em}
.sidebar .brand .txt .d{color:var(--maroon-fg)}
.sidebar .brand .sup{font-size:8px;vertical-align:super}

.nav{padding:10px 10px;overflow-y:auto;flex:1}
.nav .sec{font-size:11.5px;text-transform:uppercase;letter-spacing:.09em;color:var(--sidebar-muted);padding:14px 12px 6px}
.nav a{
  display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:var(--radius-sm);
  color:var(--sidebar-fg);font-weight:600;font-size:13.5px;margin-bottom:2px;transition:background .12s,color .12s;
}
.nav a:hover{background:var(--sidebar-hover-bg);color:var(--sidebar-active-fg);text-decoration:none}
.nav a.active{background:var(--sidebar-active-bg);color:var(--sidebar-active-fg);font-weight:700}
.nav a .ic{width:18px;text-align:center;font-size:15px;opacity:.95}
.nav a .badge{margin-left:auto;background:var(--maroon);color:#fff;font-size:10.5px;font-weight:700;
  padding:1px 7px;border-radius:999px}
.nav a.active .badge{background:var(--sidebar-active-badge-bg)}
/* eCheques section sub-menu: sections listed as indented sidebar items under the "eCheques" label */
.nav a.ec-navsub{padding-left:20px}

/* Primary quick-action pinned at the top of the sidebar: filing an expense is the most-used
   task, so it gets a permanently-filled button (accent follows the active company theme via
   --maroon, exactly like .btn.primary) rather than living only under the Expenses hub. */
.nav .nav-cta{
  display:flex;align-items:center;gap:10px;padding:11px 12px;margin-bottom:10px;border-radius:8px;
  background:var(--maroon);color:#fff;font-weight:700;font-size:13.5px;letter-spacing:.01em;
  box-shadow:0 1px 2px rgba(0,0,0,.28);
}
.nav .nav-cta:hover{background:var(--maroon-dark);color:#fff;text-decoration:none}
.nav .nav-cta:focus-visible{outline:2px solid var(--sidebar-active-fg);outline-offset:2px}
.nav .nav-cta .ic{width:18px;text-align:center;font-size:17px;font-weight:800;line-height:1;opacity:1}

.sidebar .foot{padding:12px 16px;border-top:1px solid var(--sidebar-border);font-size:12px;color:var(--sidebar-muted)}
.sidebar .foot-links{padding:4px 12px 10px;font-size:12px;color:var(--sidebar-muted);display:flex;gap:4px;flex-wrap:wrap}
/* generous hit area: these were 17px tall, well under a comfortable touch target */
.sidebar .foot-links a{color:var(--sidebar-muted);display:inline-flex;align-items:center;min-height:34px;padding:0 8px;border-radius:6px}
.sidebar .foot-links a:hover{color:var(--sidebar-active-fg);text-decoration:none;background:var(--sidebar-hover-bg)}
.sidebar .foot-links a:focus-visible{outline:2px solid var(--sidebar-active-fg);outline-offset:1px}

/* sortable table headers - wired by app.js on <table data-sortable>. A neutral glyph hints
   the column can be sorted; it flips to a filled arrow for the active sort direction. */
.tbl thead th.th-sort{cursor:pointer;user-select:none;white-space:nowrap}
.tbl thead th.th-sort::after{content:"\2195";opacity:.3;margin-left:6px;font-size:.85em;font-weight:400}
.tbl thead th.th-sort[data-sort-dir="asc"]::after{content:"\25B2";opacity:.85}
.tbl thead th.th-sort[data-sort-dir="desc"]::after{content:"\25BC";opacity:.85}
.tbl thead th.th-sort:hover{color:var(--maroon-fg)}
.tbl thead th.th-sort:focus-visible{outline:2px solid var(--maroon-fg);outline-offset:-2px}

/* ---------- Topbar ---------- */
.main{display:flex;flex-direction:column;min-width:0}
.topbar{
  height:var(--topbar-h);background:var(--surface);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:16px;padding:0 22px;position:sticky;top:0;z-index:20;min-width:0;
}
.topbar .page-title{font-family:var(--font-head);font-weight:800;font-size:16px}
.topbar .crumb{color:var(--muted);font-size:12.5px}
.topbar .spacer{flex:1}
/* Quick-action buttons (Snap a bill / Add Expense / Top-Up / Transfer) injected into every
   topbar by app.js. Icon-only by default: the header already carries the search box, company
   chip, bell and clock, and four LABELLED buttons on top of that overflowed the bar (and pushed
   the page into a horizontal scroll) at ordinary desktop widths. The full label lives in each
   button's tooltip, and returns inline only on a genuinely wide viewport where it demonstrably
   fits. Never wraps, so the header stays one row high. */
.topbar-qa{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}
.topbar-qa .btn{white-space:nowrap}
.topbar-qa .qa-lbl{display:none}
@media (min-width:1600px){ .topbar-qa .qa-lbl{display:inline} }
@media (max-width:640px){ .topbar-qa{gap:6px} }
/* Below 600px the bar drops out entirely - the page-head buttons already carry these actions on a
   phone. This rule has to live HERE, not in a page's own <style>: app.js injects .topbar-qa into
   every page that has a topbar, and the only copy of this rule used to sit in dashboard.html - the
   one page that ships its own bar and therefore never needed it. Everywhere else the ~194px bar
   stayed on screen, and because .topbar is flex-wrap:nowrap it pushed the bell and the account
   avatar past the right edge: 67px of sideways page scroll on a 390px screen, on 28 of 29 pages. */
@media (max-width:600px){ .topbar-qa{display:none} }
/* Phone header fit: the JS-injected controls (module switcher + theme/admin/bell/account) plus the
   page title overran a 375px bar and pushed the account avatar off-screen (a few px of page scroll on
   most pages). Tighten the gap/padding, collapse the module switcher to its icon+chevron (its
   aria-label still names the module, so it stays operable), and let a long page title truncate. */
@media (max-width:600px){
  .topbar{gap:8px;padding:0 12px}
  /* Let the title block yield exactly as much as the row needs and truncate, instead of a guessed
     max-width that a wide title (e.g. "Monthly Sheet") still overran. The classless title wrapper is
     the only bare div child; flex-shrink + min-width:0 makes it the element that gives. */
  .topbar > div:has(> .page-title){min-width:0;flex:0 1 auto}
  .topbar .page-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mod-switch{margin-left:0}
  .mod-switch .mod-name{display:none}
}
.search{
  display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--line);
  border-radius:999px;padding:7px 14px;color:var(--muted);font-size:13px;
  /* Was a fixed 260px. That fixed width was the one item in the control cluster that could not give,
     so once the sidebar is docked (~961-1400px) the bar was 5-26px too wide for its column and the
     whole page scrolled sideways on every screen. Flex-shrinkable now: it caps at 260px on a wide
     header (the spacer eats the rest of the slack) and shrinks to absorb the deficit on tighter
     desktops, keeping the header one row and the page free of horizontal scroll. Hidden <=960 anyway. */
  flex:0 1 260px;min-width:0;
}
.search input{border:none;background:none;outline:none;font-family:inherit;font-size:13px;width:100%;color:var(--ink)}
.icon-btn{position:relative;width:36px;height:36px;border-radius:9px;border:1px solid var(--line);
  background:var(--surface);color:var(--ink-2);display:grid;place-items:center;cursor:pointer;font-size:16px}
.icon-btn:hover{background:var(--bg)}
.icon-btn .dot{position:absolute;top:6px;right:7px;width:8px;height:8px;background:var(--maroon);border-radius:50%;border:2px solid #fff}
.chip-select{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--line);
  border-radius:999px;padding:5px 12px;font-size:12.5px;font-weight:600;cursor:pointer}

/* ---------- shared list search + filter bar (API.listFilter) ---------- */
.lf-bar{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;margin:0 0 12px}
.lf-bar .lf-search{flex:1 1 220px;min-width:170px;max-width:340px;width:auto}
.lf-item{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;color:var(--muted)}
.lf-item .chip-select{font:inherit;font-size:12.5px;font-weight:600;color:var(--ink);min-height:30px}
.lf-item input.chip-select{cursor:text}
.lf-item input.lf-num{width:92px}
.lf-item input[type=date].chip-select{width:135px}
.lf-count{font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums;margin-left:auto}
.lf-views-wrap .lf-views{min-width:130px}
@media (max-width:600px){ .lf-bar .lf-search{max-width:none} .lf-count{margin-left:0;flex-basis:100%} }

/* ---------- status timeline (A.timeline) ---------- */
.tl{display:flex;align-items:flex-start;flex-wrap:nowrap;overflow-x:auto;gap:0;padding:6px 2px 2px}
.tl-step{display:flex;flex-direction:column;align-items:center;gap:6px;flex:0 0 auto;min-width:64px;text-align:center}
.tl-dot{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:800;
  border:2px solid var(--line);background:var(--surface);color:var(--muted);font-variant-numeric:tabular-nums}
.tl-lbl{font-size:11px;color:var(--muted);line-height:1.2;max-width:80px}
.tl-conn{flex:1 1 auto;height:2px;min-width:16px;background:var(--line);margin-top:12px}
.tl-conn.on{background:var(--success)}
.tl-done .tl-dot{background:var(--success);border-color:var(--success);color:#fff}
.tl-done .tl-lbl{color:var(--ink)}
.tl-current .tl-dot{background:var(--maroon);border-color:var(--maroon);color:#fff;box-shadow:0 0 0 4px var(--maroon-tint,rgba(128,0,32,.12))}
.tl-current .tl-lbl{color:var(--ink);font-weight:700}
.tl-reject .tl-dot{background:var(--danger);border-color:var(--danger);color:#fff}
.tl-reject .tl-lbl{color:var(--danger)}
.tl-skip .tl-dot{opacity:.6}

/* ---------- mini charts (A.chart) ---------- */
.ch-card{display:flex;flex-direction:column;gap:8px}
.ch-title{font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.ch-donut-wrap{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.ch-legend{display:flex;flex-direction:column;gap:5px;font-size:12px}
.ch-leg{display:inline-flex;align-items:center;gap:7px;color:var(--ink)}
.ch-leg i{width:11px;height:11px;border-radius:3px;flex:none;display:inline-block}
.ch-leg b{font-variant-numeric:tabular-nums}
.ch-metric{font-size:24px;font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums}
.ch-metric small{font-size:12px;font-weight:600;color:var(--muted)}

/* ---------- clickable KPI cards ---------- */
.kpi.clickable,a.kpi{cursor:pointer;transition:border-color .15s,box-shadow .15s,transform .05s;text-decoration:none;color:inherit}
.kpi.clickable:hover,a.kpi:hover{border-color:var(--maroon);box-shadow:0 3px 10px rgba(0,0,0,.07)}
.kpi.clickable:active,a.kpi:active{transform:translateY(1px)}
.kpi.clickable:focus-visible,a.kpi:focus-visible{outline:2px solid var(--maroon);outline-offset:2px}

/* ---------- mobile-first data tables: stacked cards below 640px ---------- */
@media (max-width:640px){
  table.tbl.stackable{border:0}
  table.tbl.stackable thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  table.tbl.stackable tbody tr{display:block;border:1px solid var(--line);border-radius:12px;background:var(--surface);
    margin:0 0 10px;padding:8px 12px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
  table.tbl.stackable tbody tr.empty-row{padding:20px;text-align:center}
  table.tbl.stackable tbody td{display:flex;justify-content:space-between;align-items:center;gap:12px;
    border:0;padding:6px 0;text-align:right;white-space:normal}
  table.tbl.stackable tbody td::before{content:attr(data-label);font-size:11.5px;font-weight:700;color:var(--muted);
    text-align:left;flex:0 0 42%;text-transform:none}
  table.tbl.stackable tbody td[data-label=""]::before,table.tbl.stackable tbody td:not([data-label])::before{content:""}
  table.tbl.stackable tbody td.ec-actcell,table.tbl.stackable tbody td.actcell{justify-content:flex-end}
  /* SR# is a desktop/print row-alignment aid; the mobile card IS the row, so drop it here */
  table.tbl.stackable tbody td.sr-cell{display:none}
}
.avatar{width:34px;height:34px;border-radius:50%;background:var(--maroon);color:#fff;display:grid;place-items:center;
  font-weight:700;font-size:13px;font-family:var(--font-head)}

/* ---------- Module switcher (MICO360 Finance topbar) ----------
   Styled through theme tokens so it themes in BOTH modes. It previously carried
   inline var(--card,#fff)/var(--chip,#eef) — tokens that don't exist here — so the
   menu was stuck white and its labels turned invisible on dark. --surface/--ink/--line
   are the real tokens; the selected row uses the brand tint. */
.mod-switch{position:relative;display:inline-flex;margin-left:10px}
.mod-switch .mod-btn{display:inline-flex;align-items:center;gap:7px}
.mod-menu{position:absolute;top:110%;left:0;z-index:60;min-width:200px;
  background:var(--surface);color:var(--ink);border:1px solid var(--line);
  border-radius:10px;box-shadow:var(--shadow-lg);padding:6px}
.mod-menu[hidden]{display:none}
.mod-item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  padding:7px 9px;border:0;background:transparent;color:var(--ink);
  border-radius:7px;cursor:pointer;font:inherit}
.mod-item:hover{background:var(--bg-2)}
.mod-item[aria-selected="true"]{background:var(--maroon-tint);color:var(--maroon-fg);font-weight:600}
.mod-item .mod-emoji{font-size:15px;line-height:1}

/* ---------- Content ---------- */
.content{padding:var(--sp-6) var(--sp-6) var(--sp-10)}
/* The page head is the only level-1 landmark on a screen, so it gets real
   separation from the card wall below it instead of the old flat 18px.
   page-head (20px/800) > card-head (14.5px/800 caps-ish) > body: three clearly
   different sizes rather than two nearly-identical ones. */
.page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--sp-4);
  margin-bottom:var(--sp-5);padding-bottom:var(--sp-4);border-bottom:1px solid var(--line);flex-wrap:wrap}
.page-head h1{font-size:var(--fs-2xl);letter-spacing:-.015em;line-height:1.2}
.page-head .sub{color:var(--muted);font-size:var(--fs-base);margin-top:var(--sp-1)}
/* .hub-tabs / .tabs carry their own bottom rule - don't stack two hairlines */
.page-head:has(+ .hub-tabs),.page-head:has(+ .tabs){border-bottom:none;padding-bottom:0}

/* ---------- Cards ---------- */
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.card-pad{padding:var(--sp-5)}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-5);border-bottom:1px solid var(--line)}
.card-head h2,.card-head h3{font-size:14.5px;letter-spacing:.005em}
.card-head .sub{color:var(--muted);font-size:var(--fs-sm);font-weight:500}
/* standalone "View all" style links need a real target, not an 18px sliver */
a.sub,.card-head a{display:inline-flex;align-items:center;min-height:32px;padding:0 6px;border-radius:6px}
a.sub:hover,.card-head a:hover{background:var(--bg-2);text-decoration:none}
.grid{display:grid;gap:16px}
/* Grid items default to min-width:auto, so a card whose header cannot wrap widens its track past
   the container and the page scrolls sideways by a few px (import.html at 375px). Let them shrink. */
.grid>*{min-width:0}
.g-2{grid-template-columns:repeat(2,1fr)}
.g-3{grid-template-columns:repeat(3,1fr)}
.g-4{grid-template-columns:repeat(4,1fr)}
.g-1-2{grid-template-columns:1fr 2fr}
.g-2-1{grid-template-columns:2fr 1fr}
/* KPI strip that fits as many cards per row as the width allows (5 on a desktop dashboard, fewer on
   tablets). A theme class, not an inline style, so the compact-KPI phone rules below can reach it. */
/* 200px (not 185) so the strip drops to fewer, wider cards one step sooner: on a docked-sidebar
   desktop 4 cramped cards left a long money value a few px too wide, spilling past the grid and
   scrolling the whole page. Wider cards hold a realistic float on one line. */
.grid.kpi-row{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}
/* A KPI card must never be wider than its 1fr track, and its value must not push past the card:
   min-width:0 lets the card shrink to its track; the value is sized so a realistic float fits on one
   line and an extreme number wraps inside the card rather than overflowing the page. */
.grid.kpi-row .kpi{min-width:0}
.grid.kpi-row .kpi .val{min-width:0;font-size:22px;overflow-wrap:anywhere}
/* Exactly four cards: 4-up, or 2x2 below 1100px - never 3+1. auto-fit left the fourth card alone on a
   row (audit BUG-14). Safe now that app.js shrinks a value that would not fit its card (BUG-04), which
   was the reason the minimum above had been widened. */
.grid.kpi-row:has(> :nth-child(4):last-child){grid-template-columns:repeat(4,minmax(0,1fr))}
@media(max-width:1100px){ .grid.kpi-row:has(> :nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr))} }
@media(max-width:1100px){.g-4{grid-template-columns:repeat(2,1fr)}.g-3{grid-template-columns:repeat(2,1fr)}
  .g-1-2,.g-2-1{grid-template-columns:1fr}}
@media(max-width:720px){.g-2,.g-4,.g-3{grid-template-columns:1fr}}

/* ---------- KPI tiles ---------- */
.kpi{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow)}
.kpi .lbl{font-size:12px;color:var(--muted);font-weight:600;display:flex;align-items:center;gap:7px}
.kpi .val{font-family:var(--font-head);font-weight:800;font-size:26px;margin-top:8px;letter-spacing:-.01em}
.kpi .val .cur{font-size:14px;color:var(--muted);font-weight:700}
.kpi .delta{font-size:12px;font-weight:700;margin-top:6px;display:inline-flex;align-items:center;gap:4px}
.kpi .delta.up{color:var(--success)} .kpi .delta.down{color:var(--danger)}
.kpi .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;font-size:20px;flex:none;
  background:var(--maroon-tint);color:var(--maroon-fg)}
/* colour-coded KPI chips, matching the dashboard reference */
.kpi .ic.red{background:var(--maroon-tint);color:var(--maroon-fg)}
.kpi .ic.green{background:var(--success-bg);color:var(--success)}
.kpi .ic.blue{background:var(--info-bg);color:var(--info)}
.kpi .ic.amber{background:var(--warning-bg);color:var(--warning)}
.kpi .ic.violet{background:var(--violet-bg);color:var(--violet)}
/* icon on the LEFT, text block filling the rest */
.kpi.row{display:flex;align-items:center;gap:14px}
.kpi.row>div:not(.ic){flex:1;min-width:0}
.kpi.row .ic{order:-1}

/* ---------- Buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:7px;border:1px solid transparent;border-radius:var(--radius-sm);
  padding:9px 15px;font-family:var(--font-body);font-weight:700;font-size:13px;cursor:pointer;line-height:1;transition:.12s}
.btn:hover{text-decoration:none}
.btn.primary{background:var(--maroon);color:#fff}
.btn.primary:hover{background:var(--maroon-dark)}
.btn.outline{background:var(--surface);color:var(--maroon-fg);border-color:var(--maroon-fg)}
.btn.outline:hover{background:var(--maroon-tint-2)}
.btn.ghost{background:transparent;color:var(--ink-2);border-color:var(--line)}
.btn.ghost:hover{background:var(--bg)}
.btn.success{background:var(--success);color:#fff}
.btn.danger{background:var(--danger);color:#fff}
.btn.warn{background:var(--warning);color:#fff}
/* .outline COMBINED with a semantic colour.
   The three rules above are declared after .btn.outline and carry the same specificity, so they
   win on source order: an outlined Approve kept the solid green background and white label and
   took only the maroon border - the outline did nothing except mismatch the fill it sat on.
   Declared here, after them, and keyed to each variant's OWN colour so the solid and outlined
   halves of a pair read as one control: Approve solid green, Approve & Next green on white. */
.btn.success.outline{background:var(--surface);color:var(--success);border-color:var(--success)}
.btn.danger.outline{background:var(--surface);color:var(--danger);border-color:var(--danger)}
.btn.warn.outline{background:var(--surface);color:var(--warning);border-color:var(--warning)}
.btn.success.outline:hover{background:var(--success);color:#fff}
.btn.danger.outline:hover{background:var(--danger);color:#fff}
.btn.warn.outline:hover{background:var(--warning);color:#fff}
.btn.sm{padding:6px 11px;font-size:12px;min-height:30px}
.btn.lg{padding:12px 20px;font-size:14px}
.btn.block{width:100%;justify-content:center}
.btn[disabled]{opacity:.5;cursor:not-allowed}

/* ---------- Badges / status ----------
   Expense status is the single most-scanned value in the product, so it must
   not be carried by hue alone: every status badge pairs its colour with a
   distinct leading glyph AND a tinted border. That gives three independent
   channels (shape, colour, edge), so the set stays separable for deuteranopic
   /protanopic users and survives greyscale printing.
   It also fixes a real ambiguity: Approved, Settled and Closed all mapped to
   the same green pill and were indistinguishable at a glance. */
.badge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:3px 9px;border-radius:999px;
  border:1px solid transparent;white-space:nowrap;font-variant-numeric:tabular-nums}
.badge .d{width:6px;height:6px;border-radius:50%;background:currentColor}
.badge.draft{background:#f1f1f2;color:#52525b;border-color:#d6d6da}
.badge.submitted{background:var(--info-bg);color:var(--info);border-color:#c3d3fa}
.badge.approved,.badge.settled,.badge.closed{background:var(--success-bg);color:var(--success);border-color:#bce0cd}
.badge.returned,.badge.flag,.badge.pending{background:var(--warning-bg);color:var(--warning);border-color:#efd4ac}
.badge.rejected,.badge.over,.badge.voided{background:var(--danger-bg);color:var(--danger);border-color:#f2c6c6}
.badge.maroon{background:var(--maroon-tint);color:var(--maroon-fg)}
.badge.neutral{background:#f1f1f2;color:#52525b;border-color:#d6d6da}

/* status glyphs - the marker replaces the generic dot where one is present */
.badge.draft::before,.badge.submitted::before,.badge.approved::before,
.badge.settled::before,.badge.closed::before,.badge.returned::before,
.badge.pending::before,.badge.rejected::before,.badge.voided::before,
.badge.over::before,.badge.flag::before{
  font-size:.92em;line-height:1;font-weight:700;flex:none}
.badge.draft::before{content:"✎"}
.badge.submitted::before{content:"↗"}
.badge.approved::before{content:"✔"}
.badge.settled::before{content:"⇄"}
.badge.closed::before{content:"▣"}   /* monochrome on purpose - an emoji lock
                                        renders in colour and breaks the set */
.badge.returned::before{content:"↩"}
.badge.pending::before{content:"◷"}
.badge.rejected::before{content:"✕"}
.badge.voided::before{content:"⊘"}
.badge.over::before{content:"!"}
.badge.flag::before{content:"⚑"}
/* the old generic dot is redundant once a glyph is present */
.badge.draft>.d,.badge.submitted>.d,.badge.approved>.d,.badge.settled>.d,
.badge.closed>.d,.badge.returned>.d,.badge.pending>.d,.badge.rejected>.d,
.badge.voided>.d,.badge.over>.d,.badge.flag>.d{display:none}

/* ---------- Tables ----------
   The register/ledger tables are the product's primary surface, so they get
   the most attention: tabular figures everywhere (so OMR columns align to the
   decimal), tighter line-height, a sticky header for long lists, and a zebra
   that stays subordinate to the hover/selected states. */
.table-wrap{overflow-x:auto}
table.tbl{width:100%;border-collapse:collapse;font-size:13px;line-height:1.35}
table.tbl th{
  text-align:left;font-weight:700;color:var(--muted);font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;
  padding:9px 14px;border-bottom:1px solid var(--line);white-space:nowrap;background:var(--bg-2)}
table.tbl td{padding:9px 14px;border-bottom:1px solid var(--line-2);vertical-align:middle}
/* zebra first, hover second - matched specificity (0,3,3) with hover declared
   later, so hover reliably beats the stripe instead of losing to it */
table.tbl tbody tr:nth-child(even):not(.empty-row){background:var(--bg-2)}
table.tbl tbody tr:hover:not(.empty-row){background:var(--maroon-tint-2)}
/* The "not on this statement yet" rows on the Monthly Sheet are clickable in the same way as
   the statement rows above them, but they are divs, so the table-scoped rules never reach
   them. Same tint, so a reader cannot tell the two lists apart by how they behave. */
#ms-unsent .row-link:hover{background:var(--maroon-tint-2)}
/* Money and dates are compared down a column, never read as prose - lock every
   numeric cell to tabular figures so digits occupy identical advance widths. */
table.tbl td,table.tbl th{font-variant-numeric:tabular-nums}
table.tbl .num{text-align:right}
table.tbl th.num{text-align:right}
/* Trailing action cell: cluster the row's buttons into a tidy, right-aligned group with an even
   gap that wraps onto a second line on narrow screens, instead of inline buttons separated by
   literal spaces that drift and misalign. Zero the buttons' own margins so only the gap spaces them. */
.row-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end;align-items:center}
.row-actions>.btn{margin:0}
/* a .text-right header must beat the default `table.tbl th{text-align:left}` (higher specificity),
   the same way th.num does - otherwise an Actions header sits left while its buttons sit right */
table.tbl th.text-right{text-align:right}
/* SR# serial column - an ordinal LABEL, not a measure. A 1-2 digit index right-aligned in
   a wide column floats away from both its header and the next column and reads as broken.
   Left-align it, shrink the column to its content, and mute it so the real data leads.
   Deliberately NOT given .num (that right-aligns, incl. a mobile !important override). */
table.tbl .sr-col,table.tbl .sr-cell{text-align:left;width:1%;white-space:nowrap;padding-right:18px}
table.tbl th.sr-col,table.tbl td.sr-cell{color:var(--muted)}
table.tbl .rowlink{cursor:pointer}
/* A whole row wired to open its source record (pending claims, ledger movements, drill-downs)
   must READ as clickable, not just react on hover. .row-link marks those rows; the hover tint
   already comes from the tbody rule above, this adds the pointer cursor everywhere it is used. */
tr.row-link{cursor:pointer}
/* Sticky header for long registers - opt in per table by adding .tall to the
   wrapper:  <div class="table-wrap tall">
   NOTE: it only works this way. .table-wrap already carries overflow-x:auto for
   wide tables, and a box cannot be auto on one axis and visible on the other -
   the used overflow-y becomes auto too, which makes .table-wrap (not the page)
   the nearest scroll container for position:sticky. A header stuck to the page
   viewport therefore never pins; it just scrolls away with the wrapper.
   Capping the wrapper's height makes it a real scroll container, and then the
   header pins to the top of the wrapper exactly as intended (verified pinned at
   offset 0 with the wrapper scrolled 400px).
   Left as opt-in rather than applied globally: capping a register's height
   changes how the whole page scrolls, which is a product decision, and no
   current screen has enough rows to need it. */
.table-wrap.tall{max-height:min(68vh,640px);overflow-y:auto}
.table-wrap.tall table.tbl thead th{
  position:sticky;top:0;z-index:5;
  background:var(--bg-2);                /* must stay opaque over the rows */
  box-shadow:inset 0 -1px 0 var(--line);
}
.t-actions{display:flex;gap:6px}
.avatar-xs{width:26px;height:26px;border-radius:50%;background:var(--maroon-tint);color:var(--maroon-fg);
  display:inline-grid;place-items:center;font-weight:700;font-size:11px}
.cell-user{display:flex;align-items:center;gap:8px}

/* ---------- Forms ---------- */
.form-grid{display:grid;gap:var(--sp-4)}
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-size:var(--fs-sm);font-weight:700;color:var(--ink-2);line-height:1.35}
/* Required marker: a bare red asterisk carries no meaning in greyscale and
   nothing at all to a screen reader. Keep the asterisk, attach a visually
   hidden word to it using the same clip technique as .sr-only. */
.field label .req{color:var(--danger);font-weight:800;margin-left:2px;position:relative}
.field label .req::after{content:"required";position:absolute;width:1px;height:1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}
.field .hint{font-size:var(--fs-xs);color:var(--muted);line-height:1.45}
/* hint sits under the control, error replaces the visual slot below it */
.field .hint + .error-msg{margin-top:2px}

/* Long settings pages: group related fields under a labelled fieldset so the
   page reads as sections instead of one continuous ladder of inputs. */
.field-group{border:1px solid var(--line);border-radius:var(--radius-sm);padding:var(--sp-4);margin:0}
.field-group > legend{font-family:var(--font-head);font-weight:800;font-size:var(--fs-sm);
  text-transform:uppercase;letter-spacing:.06em;color:var(--muted);padding:0 var(--sp-2);margin-left:-4px}
.sec-head{font-family:var(--font-head);font-weight:800;font-size:var(--fs-xs);text-transform:uppercase;
  letter-spacing:.07em;color:var(--muted);margin:var(--sp-5) 0 var(--sp-2)}
.sec-head:first-child{margin-top:0}
.input,.select,.textarea{
  font-family:inherit;font-size:13.5px;color:var(--ink);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:10px 12px;width:100%;outline:none;transition:.12s}
.input:focus,.select:focus,.textarea:focus{border-color:var(--maroon);box-shadow:0 0 0 3px var(--maroon-tint)}

/* ---------- Searchable dropdown (app.js enhances every <select>) ----------
   The native <select> stays in the DOM as the value holder and is visually hidden, NOT
   display:none - a display:none control is dropped from form submission and skipped by
   some validation paths. Everything here is var()-driven so it follows the theme, and the
   panel is a real element rather than an OS popup, which is what made the native list
   unreadable in dark mode in the first place. */
/* The wrapper must end up exactly as wide as the button, because the panel is pinned to the
   wrapper (left:0;right:0). In a filter bar `.filters .input{width:auto}` shrinks the button
   to its content while the wrapper stayed 100%, so the list opened at full page width under a
   180px button. The wrapper therefore mirrors whatever sizing the original select had, and the
   button is forced to fill it - `.ss-wrap button.ss-btn` outranks `.filters .input`. */
.ss-wrap{position:relative;width:100%}
.ss-wrap button.ss-btn{width:100%}
.ss-native{position:absolute!important;width:1px;height:1px;opacity:0;pointer-events:none;clip:rect(0,0,0,0)}
.ss-btn{display:flex;align-items:center;justify-content:space-between;gap:8px;text-align:left;cursor:pointer}
.ss-btn:disabled{opacity:.6;cursor:not-allowed}
.ss-val{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ss-val.ss-empty{color:var(--muted)}
.ss-car{flex:0 0 auto;color:var(--muted);font-size:12px;line-height:1}
.ss-wrap.on .ss-btn{border-color:var(--maroon);box-shadow:0 0 0 3px var(--maroon-tint)}
.ss-panel{position:absolute;z-index:60;left:0;right:0;top:calc(100% + 4px);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);
  box-shadow:var(--shadow-lg);overflow:hidden}
.ss-search{width:100%;border:0;border-bottom:1px solid var(--line);background:var(--bg-2);
  color:var(--ink);font:inherit;font-size:13px;padding:9px 11px;outline:none}
/* Short pickers (month/year/status) hide the filter box: it stays in the DOM and focusable so
   keyboard type-ahead still works, but is visually removed (screen-reader-only) so the popup is
   a clean list. Applied by app.js when a select has <=12 options and no data-filterable. */
.ss-search.ss-search-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  border:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.ss-list{max-height:240px;overflow:auto}
.ss-opt{padding:8px 11px;font-size:13.5px;color:var(--ink);cursor:pointer}
.ss-opt:hover,.ss-opt.act{background:var(--maroon-tint-2)}
.ss-opt.on{background:var(--maroon-tint);font-weight:700}
.ss-opt.dis{opacity:.5;cursor:not-allowed}
.ss-none{padding:10px 11px;font-size:13px;color:var(--muted)}
/* the panel is page furniture, never part of a printed document */
@media print{ .ss-panel{display:none!important} .ss-car{display:none!important} }
.textarea{resize:vertical;min-height:84px}
.input-group{display:flex;align-items:stretch}
.input-group .addon{display:grid;place-items:center;padding:0 12px;background:var(--bg);border:1px solid var(--line);
  border-right:none;border-radius:var(--radius-sm) 0 0 var(--radius-sm);color:var(--muted);font-weight:700;font-size:13px}
.input-group .input{border-radius:0 var(--radius-sm) var(--radius-sm) 0}
.checkbox{display:flex;align-items:center;gap:8px;font-size:13px}
/* default 13px boxes are hard to hit, especially in table rows on touch */
input[type=checkbox],input[type=radio]{width:24px;height:24px;accent-color:var(--maroon);cursor:pointer;flex:none}
@media(pointer:coarse){input[type=checkbox],input[type=radio]{width:26px;height:26px}}
.switch{width:38px;height:22px;border-radius:999px;background:var(--line);position:relative;cursor:pointer;transition:.15s;flex:none}
.switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:.15s;box-shadow:var(--shadow)}
.switch.on{background:var(--maroon)} .switch.on::after{left:18px}

/* ---------- Alerts / callouts ---------- */
.alert{display:flex;gap:11px;padding:12px 14px;border-radius:var(--radius-sm);font-size:13px;border:1px solid}
.alert .ai{font-size:16px;line-height:1.3}
.alert strong{font-weight:700}
.alert.warn{background:var(--warning-bg);border-color:#f0d8b5;color:#7a4407}
.alert.info{background:var(--info-bg);border-color:#c7d6fb;color:#1c418f}
.alert.danger{background:var(--danger-bg);border-color:#f2c9c9;color:#8f1f1f}
.alert.success{background:var(--success-bg);border-color:#bfe2cd;color:#155e35}

/* ---------- Tabs ---------- */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:18px;flex-wrap:wrap}
.tabs a,.tabs button{padding:10px 14px;font-weight:700;font-size:13px;color:var(--muted);border:none;background:none;
  cursor:pointer;border-bottom:2px solid transparent;font-family:inherit}
.tabs a:hover,.tabs button:hover{color:var(--ink);text-decoration:none}
.tabs a.active,.tabs button.active{color:var(--maroon-fg);border-bottom-color:var(--maroon-fg)}

/* ---------- Hub section tabs (injected under the page title) ---------- */
.hub-tabs{display:flex;gap:4px;flex-wrap:wrap;margin:-4px 0 18px;border-bottom:1px solid var(--line)}
.hub-tabs a{padding:9px 14px;font-weight:700;font-size:13px;color:var(--muted);
  border-bottom:2px solid transparent;white-space:nowrap}
.hub-tabs a:hover{color:var(--ink);text-decoration:none;background:var(--maroon-tint);border-radius:6px 6px 0 0}
.hub-tabs a.active{color:var(--maroon-fg);border-bottom-color:var(--maroon-fg)}

/* ---------- Filter bar ---------- */
.filters{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:16px}
.filters .select,.filters .input{width:auto;min-width:150px;padding:8px 11px;font-size:13px}
.filters .ss-wrap{width:auto;min-width:150px}   /* mirror the select it replaced */
.pill{display:inline-flex;align-items:center;gap:6px;background:var(--maroon-tint);color:var(--maroon-fg);
  border-radius:999px;padding:5px 11px;font-size:12px;font-weight:700}
.pill .x{cursor:pointer;opacity:.7}

/* ---------- Stepper (workflow) ---------- */
.stepper{display:flex;align-items:center;gap:0;flex-wrap:wrap}
.stepper .step{display:flex;align-items:center;gap:9px;font-size:12.5px;font-weight:700;color:var(--muted)}
.stepper .dot{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:var(--bg);
  border:2px solid var(--line);color:var(--muted);font-size:12px}
.stepper .step.done .dot{background:var(--success);border-color:var(--success);color:#fff}
.stepper .step.active .dot{background:var(--maroon);border-color:var(--maroon);color:#fff}
.stepper .step.done{color:var(--ink)} .stepper .step.active{color:var(--maroon-fg)}
.stepper .bar{width:34px;height:2px;background:var(--line);margin:0 6px}
.stepper .step.done + .bar{background:var(--success)}

/* ---------- Timeline / audit ---------- */
.timeline{list-style:none;position:relative;padding-left:22px}
.timeline::before{content:"";position:absolute;left:7px;top:4px;bottom:4px;width:2px;background:var(--line)}
.timeline li{position:relative;padding:0 0 16px 0}
.timeline li::before{content:"";position:absolute;left:-19px;top:3px;width:10px;height:10px;border-radius:50%;
  background:var(--surface);border:2px solid var(--maroon)}
.timeline .tl-t{font-size:12px;color:var(--muted)}
.timeline .tl-h{font-weight:700;font-size:13px}

/* ---------- Comments ---------- */
.comment{display:flex;gap:10px;padding:12px 0;border-bottom:1px solid var(--line-2)}
.comment .body .meta{font-size:12px;color:var(--muted);margin-bottom:2px}
.comment .body .meta b{color:var(--ink)}

/* ---------- Receipt / upload ---------- */
.dropzone{border:1.5px dashed var(--line);border-radius:var(--radius);padding:22px;text-align:center;color:var(--muted);
  background:var(--bg-2);cursor:pointer}
.dropzone:hover{border-color:var(--maroon-fg);color:var(--maroon-fg);background:var(--maroon-tint-2)}
/* active drag-over feedback (set by capture.js / the dashboard transfer-drop handler) */
.dropzone.drop-hover{border-color:var(--maroon);color:var(--maroon-fg);background:var(--maroon-tint);border-style:solid}
.dropzone > div{pointer-events:none}   /* so a drop always lands on the zone, not an inner label */
.dropzone .btn{pointer-events:auto}

/* ---------- Inline evidence preview (transfer accept modal + incoming request) ---------- */
.evi-preview{border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;background:var(--bg-2);display:flex;flex-direction:column;min-height:0}
.evi-head{display:flex;align-items:center;gap:6px;padding:6px 8px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}
.evi-name{font-size:12px;font-weight:600;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:150px}
.evi-count{font-size:11.5px;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums;padding:0 2px}
.evi-body{flex:1;overflow:auto;display:flex;align-items:center;justify-content:center;background:var(--bg-2);min-height:180px;padding:8px}
.evi-body img{max-width:100%;max-height:100%;object-fit:contain;border-radius:4px;box-shadow:0 1px 8px rgba(0,0,0,.12)}
.evi-body iframe{width:100%;height:100%;min-height:320px;border:0;background:#fff;border-radius:4px}
/* Accept modal: evidence LEFT, details RIGHT; stacks (evidence ABOVE details) on small screens. */
.atr-2col{display:flex;gap:16px;align-items:stretch}
.atr-2col .atr-evi{flex:1.15 1 0;min-width:0;display:flex}
.atr-2col .atr-evi .evi-preview{flex:1;min-height:300px}
.atr-2col .atr-form{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:12px}
@media(max-width:720px){ .atr-2col{flex-direction:column} .atr-2col .atr-evi .evi-preview{min-height:220px} }
/* Incoming-request inline preview: bounded so it never dominates the card. */
.inc-evi .evi-preview{margin-bottom:16px;max-height:300px}
.inc-evi .evi-body{min-height:150px}
.thumb{width:100%;aspect-ratio:1/1;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg);
  display:grid;place-items:center;color:var(--muted);position:relative;overflow:hidden;font-size:26px}
.thumb .tag{position:absolute;bottom:0;left:0;right:0;background:rgba(0,0,0,.6);color:#fff;font-size:11px;padding:3px 6px;text-align:left}

/* ---------- Modal ---------- */
.modal-bg{position:fixed;inset:0;background:rgba(20,20,20,.5);display:none;place-items:center;z-index:50;padding:20px}
.modal-bg.open{display:grid}
/* --surface, not #fff: the footer already used var(--bg-2) and so followed the theme, while
   the shell stayed hard white. In dark mode that produced a half-white dialog whose heading
   was var(--ink) - near-white text on white. --surface is #FFFFFF in light, so this is a
   no-op there. */
.modal{background:var(--surface);border-radius:var(--radius-lg);width:100%;max-width:520px;box-shadow:var(--shadow-lg);overflow:hidden}
.modal .m-head{padding:16px 20px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}
.modal .m-body{padding:20px}
.modal .m-foot{padding:14px 20px;border-top:1px solid var(--line);display:flex;justify-content:flex-end;gap:10px;background:var(--bg-2)}

/* ---------- Bars / progress ---------- */
.bar-track{height:8px;background:var(--line-2);border-radius:999px;overflow:hidden}
.bar-fill{height:100%;background:var(--maroon);border-radius:999px}
.bar-fill.ok{background:var(--success)} .bar-fill.warn{background:var(--warning)} .bar-fill.over{background:var(--danger)}

/* ---------- Ledger amounts ---------- */
.amt-in{color:var(--success);font-weight:700}
.amt-out{color:var(--danger);font-weight:700}

/* ---------- OMR currency symbol (Omani Rial) ----------
   Prefix any amount:  <span class="omr">1,180.000</span>
   Rendered via a mask so it inherits text colour and stays crisp/light. */
:root{ --omr-mask:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20220%20140'%3E%3Cpath%20d='M112%2096%20C84%2068%2090%2028%20128%2014%20C152%205%20180%2015%20180%2045%20C180%2077%20152%2096%20118%20104%20L110%2086%20C140%2079%20160%2066%20160%2046%20C160%2029%20147%2022%20132%2029%20C111%2039%20107%2068%20129%2094%20Z'/%3E%3Cpath%20d='M42%2088%20L212%2088%20L190%20112%20L20%20112%20Z'/%3E%3Cpath%20d='M28%20116%20L202%20116%20L180%20140%20L6%20140%20Z'/%3E%3C/svg%3E"); }
.omr{white-space:nowrap}
.omr::before{content:"";display:inline-block;width:.72em;height:.72em;margin-right:.18em;vertical-align:-0.06em;
  background-color:currentColor;
  -webkit-mask:var(--omr-mask) no-repeat center/contain;
  mask:var(--omr-mask) no-repeat center/contain;}
/* symbol-only marker (e.g. inside an input addon) */
.omr.sym-only::before{margin-right:0}
@media print{ .omr::before{background-color:#000!important} }

/* ---------- Misc utility ---------- */
.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}
.justify-end{justify-content:flex-end}
.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}
.wrap{flex-wrap:wrap}.mt-8{margin-top:8px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}
.mb-8{margin-bottom:8px}.mb-16{margin-bottom:16px}
.text-muted{color:var(--muted)}.text-sm{font-size:12.5px}.text-xs{font-size:11.5px}
.text-right{text-align:right}.fw-700{font-weight:700}.fw-800{font-weight:800}
.divider{height:1px;background:var(--line);margin:16px 0}
/* Mobile-app download badges: big variant on the login page, compact variant in the sidebar footer */
.app-dl{display:flex;flex-direction:column;gap:9px;align-items:center;margin:16px 0}
.app-dl .app-dl-label{font-size:12px;color:var(--muted);font-weight:600}
.app-dl .app-dl-btns{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.app-badge{display:inline-flex;align-items:center;gap:9px;padding:8px 14px;border:1px solid var(--line);border-radius:10px;
  background:var(--bg-2);color:var(--fg);text-decoration:none;transition:border-color .15s,background .15s;min-width:148px}
.app-badge:hover{border-color:var(--maroon-fg);background:var(--maroon-tint-2)}
.app-badge[aria-disabled="true"]{opacity:.55;pointer-events:none}
.app-badge svg{width:22px;height:22px;flex:none}
.app-badge .tx{display:flex;flex-direction:column;line-height:1.15;text-align:left}
.app-badge .tx small{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.app-badge .tx b{font-size:13.5px;font-weight:700}
/* compact row pinned to the bottom of the sidebar (sibling below .nav); inherits sidebar text colour */
.nav-dl{display:flex;gap:8px;padding:10px 12px 6px;border-top:1px solid var(--sidebar-border)}
.nav-dl a{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:7px 8px;border:1px solid rgba(128,128,128,.35);
  border-radius:8px;color:inherit;text-decoration:none;font-size:12px;font-weight:600;opacity:.9}
.nav-dl a:hover{opacity:1;border-color:currentColor}
.nav-dl a[aria-disabled="true"]{opacity:.45;pointer-events:none}
.nav-dl svg{width:15px;height:15px;flex:none}
.big-amt{font-family:var(--font-head);font-weight:800;font-size:20px;font-variant-numeric:tabular-nums}
.hl{background:var(--maroon-tint);border-radius:4px;padding:0 4px}

/* prototype ribbon */

/* mobile sidebar */
@media(max-width:960px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;left:0;top:0;z-index:60;width:var(--sidebar-w);transform:translateX(-100%);transition:.2s}
  .sidebar.open{transform:none}
  .search{width:auto;min-width:0}
}
.menu-toggle{display:none}
@media(max-width:960px){.menu-toggle{display:grid}}

/* ============================================================
   ENHANCEMENTS v1.2 - responsiveness, a11y, print, UI states
   ============================================================ */

/* ---------- Accessibility: focus states ---------- */
:where(a,button,input,select,textarea,.switch,.rowlink,[tabindex],[role="button"]):focus-visible{
  outline:2px solid var(--maroon-fg);outline-offset:2px;border-radius:6px}
.sidebar a:focus-visible{outline:2px solid var(--sidebar-active-fg);outline-offset:2px}
.btn.primary:focus-visible,.btn.success:focus-visible,.btn.danger:focus-visible,.btn.warn:focus-visible{
  outline-color:var(--ink);outline-offset:3px}
:focus:not(:focus-visible){outline:none}
/* skip link */
.skip-link{position:absolute;left:-9999px;top:0;z-index:200;background:var(--maroon);color:#fff;
  padding:10px 16px;border-radius:0 0 8px 0;font-weight:700;font-size:13px}
.skip-link:focus{left:0;text-decoration:none}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
}

/* ---------- Touch targets & responsive typography ---------- */
@media(max-width:640px){
  .btn{min-height:42px;padding:11px 16px}
  .btn.sm{min-height:36px}
  .icon-btn{width:42px;height:42px}
  .input,.select,.textarea{font-size:16px;min-height:44px} /* 16px stops iOS zoom */
  .page-head h1{font-size:19px}
  .content{padding:16px}
  .kpi .val{font-size:22px}
}
@media(max-width:380px){ .content{padding:12px} .page-head h1{font-size:18px} }

/* Inline prose links (help text, tip lists, callouts) were 15px tall - e.g.
   "Bulk import from Excel/CSV" measured 162x15. Vertical padding on a
   non-replaced INLINE element expands the hit-testing box without changing
   the line box, so the target grows to ~44px with zero layout shift.
   Scoped away from anything that is already a sized component. */
@media(max-width:640px),(pointer:coarse){
  .card-pad li a:not(.btn):not(.badge):not(.pill):not(.acct-item):not(.co-item),
  .card-pad p a:not(.btn):not(.badge):not(.pill),
  .alert a:not(.btn), .state p a:not(.btn), .hint a:not(.btn), .foot-note a, .legal a{
    padding-block:15px;   /* 15px line + 2x15 = 45px hit box */
  }
  /* give the rows a little more air so the enlarged targets barely overlap */
  .card-pad ul li{margin-block:2px}
}

/* ---------- Mobile off-canvas nav + scrim ---------- */
.scrim{position:fixed;inset:0;background:rgba(20,20,20,.45);z-index:55;opacity:0;visibility:hidden;transition:.2s}
.scrim.show{opacity:1;visibility:visible}
@media(min-width:961px){.scrim{display:none}}
.sidebar .close-nav{display:none;margin-left:auto;background:none;border:none;color:var(--sidebar-fg);font-size:20px;
  cursor:pointer;width:34px;height:34px;border-radius:8px}
.sidebar .close-nav:hover{background:var(--sidebar-hover-bg);color:var(--sidebar-active-fg)}
@media(max-width:960px){.sidebar .close-nav{display:grid;place-items:center}}
@media(max-width:960px){
  .topbar{padding:0 14px;gap:10px}
  .topbar .search{display:none}
  .topbar .chip-select{display:none}
}
/* The avatar now opens the account menu (My Account / App Settings / Sign out), so it must stay
   reachable on phones - it used to be hidden < 520px, which left mobile users with no sign-out. */
@media(max-width:520px){ .topbar .avatar{display:grid} }

/* ---------- Account menu (topbar avatar dropdown) ---------- */
.acct-wrap{position:relative;display:inline-flex}
.acct-menu{position:absolute;right:0;top:calc(100% + 8px);min-width:212px;max-width:calc(100vw - 24px);
  background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);
  padding:6px;z-index:70}
.acct-menu .acct-head{padding:8px 11px 9px;border-bottom:1px solid var(--line);margin-bottom:5px}
.acct-menu .acct-nm{font-weight:800;font-size:13.5px;color:var(--ink);line-height:1.25;word-break:break-word}
.acct-menu .acct-rl{font-size:11.5px;color:var(--muted);margin-top:1px}
.acct-item{display:flex;width:100%;align-items:center;gap:10px;padding:10px 11px;border-radius:8px;
  background:none;border:none;text-align:left;font-size:13.5px;font-weight:600;color:var(--ink);cursor:pointer}
.acct-item .ai{width:18px;text-align:center;font-size:15px}
.acct-item:hover{background:var(--maroon-tint);color:var(--ink);text-decoration:none}
.acct-item:focus-visible{outline:2px solid var(--maroon-fg);outline-offset:-2px}
.acct-item.acct-out{color:#B23A3A;margin-top:2px}
.acct-item.acct-out:hover{background:rgba(178,58,58,.10);color:#B23A3A}

/* ---------- Responsive tables: stack into cards on mobile ---------- */
.table-wrap{position:relative}
@media(max-width:640px){
  /* The header was hidden with the sr-only clip technique, which keeps it in
     the accessibility tree AND in the tab order. app.js gives every sortable
     th tabindex="0" + role="button", so at 375px a keyboard or switch user
     tabbed through six invisible "buttons" before reaching the first row.
     In the stacked view each td already carries its own label via
     data-label -> td::before, so the header row is redundant here: take it
     out of the tree entirely. @media print restores display:table-header-group. */
  table.tbl:not(.no-stack) thead{display:none}
  table.tbl:not(.no-stack),
  table.tbl:not(.no-stack) tbody{display:block;width:100%}
  table.tbl:not(.no-stack) tr{display:block;border:1px solid var(--line);border-radius:var(--radius);
    margin-bottom:10px;padding:6px 2px;background:var(--surface);box-shadow:var(--shadow)}
  /* zebra is meaningless once rows become discrete cards - and it would
     otherwise out-specify the card background and grey out every 2nd card */
  table.tbl:not(.no-stack) tbody tr:nth-child(even):not(.empty-row),
  table.tbl:not(.no-stack) tbody tr:hover:not(.empty-row){background:var(--surface)}
  table.tbl:not(.no-stack) td{display:flex;justify-content:space-between;align-items:center;gap:14px;
    padding:8px 14px;border-bottom:1px solid var(--line-2);text-align:right;white-space:normal}
  table.tbl:not(.no-stack) tr td:last-child{border-bottom:none}
  table.tbl:not(.no-stack) td::before{content:attr(data-label);font-weight:700;color:var(--muted);
    text-transform:uppercase;font-size:11.5px;letter-spacing:.04em;text-align:left;flex:0 0 auto}
  table.tbl:not(.no-stack) td[data-label=""]::before,
  table.tbl:not(.no-stack) td:not([data-label])::before{content:""}
  table.tbl:not(.no-stack) td.num{justify-content:space-between}
  table.tbl:not(.no-stack) .t-actions{flex-wrap:wrap;justify-content:flex-end}
  table.tbl:not(.no-stack) td.cell-checkbox{justify-content:flex-start}
  /* horizontal-scroll tables keep an affordance */
  table.tbl.no-stack{min-width:640px}
}
/* scroll hint for no-stack tables */
.table-wrap.scrollable::after{content:"›";position:absolute;right:6px;top:50%;transform:translateY(-50%);
  color:var(--muted);font-size:22px;pointer-events:none;animation:nudge 1.4s ease-in-out infinite}
@keyframes nudge{0%,100%{opacity:.2}50%{opacity:.8}}

/* ---------- Filters: full-width on mobile ---------- */
@media(max-width:640px){
  .filters{gap:8px}
  .filters .select,.filters .input{width:100%;min-width:0;flex:1 1 100%}
  .filters .ss-wrap{width:100%;min-width:0;flex:1 1 100%}
  .filters .btn{width:100%;justify-content:center}
}

/* ---------- Modals: bottom-sheet + scrollable on mobile ---------- */
.modal{max-height:90vh;display:flex;flex-direction:column}
.modal .m-body{overflow:auto}
@media(max-width:560px){
  .modal-bg{align-items:flex-end;padding:0}
  .modal{max-width:none;width:100%;border-radius:16px 16px 0 0;max-height:92vh}
  .modal .m-foot{flex-direction:column-reverse}
  .modal .m-foot .btn{width:100%;justify-content:center}
}

/* ---------- Grids collapse cleanly ---------- */
@media(max-width:640px){ .g-2-1,.g-1-2,.g-2,.g-3,.g-4{grid-template-columns:1fr} }
/* ...except KPI strips. Four full-width tiles pushed the actual register ~340px
   down the page on a 375px screen. Stat tiles are short and read fine at half
   width, so keep them 2-up; :has() scopes this to grids that really are tiles
   and leaves content grids stacked. */
@media(max-width:640px){
  .grid.g-4:has(> .kpi),.grid.g-3:has(> .kpi),.grid.g-2:has(> .kpi),.grid.kpi-row{grid-template-columns:repeat(2,1fr);gap:10px}
  .grid:has(> .kpi) .kpi{padding:12px 13px}
  .grid:has(> .kpi) .kpi .val{font-size:19px}
  .grid:has(> .kpi) .kpi .lbl{font-size:11.5px}
  .grid:has(> .kpi) .kpi.row .ic{width:28px;height:28px;font-size:14px}
}
@media(max-width:380px){
  .grid:has(> .kpi) .kpi .val{font-size:17px}
}

/* ---------- Form validation ---------- */
.field.error .input,.field.error .select,.field.error .textarea{
  border-color:var(--danger);box-shadow:0 0 0 3px var(--danger-bg)}
.field.ok .input,.field.ok .select{border-color:var(--success)}
.error-msg{display:none;align-items:center;gap:6px;color:var(--danger);font-size:12px;font-weight:600}
.field.error .error-msg{display:flex}
.error-msg::before{content:"⚠";font-size:12px}
.field .hint[id]{}
input[aria-invalid="true"],select[aria-invalid="true"],textarea[aria-invalid="true"]{border-color:var(--danger)}

/* ---------- Buttons: loading & disabled ---------- */
.btn.loading{position:relative;color:transparent!important;pointer-events:none}
/* When a button wraps its content in .btn-lbl, hide it with VISIBILITY (not just transparent colour,
   which leaves emoji glyphs showing) while spinning — this keeps the button's WIDTH fixed, so a
   spinning "Snap a bill" no longer shrinks/reflows the topbar as its progress text changes. */
.btn.loading .btn-lbl{visibility:hidden}
.btn.loading::after{content:"";position:absolute;left:50%;top:50%;width:15px;height:15px;margin:-8px 0 0 -8px;
  border:2px solid currentColor;border-top-color:transparent;border-radius:50%;color:#fff;animation:spin .7s linear infinite}
.btn.outline.loading::after,.btn.ghost.loading::after{color:var(--maroon)}
@keyframes spin{to{transform:rotate(360deg)}}
.btn[aria-disabled="true"]{opacity:.5;pointer-events:none}

/* ---------- Toasts ---------- */
.toast-wrap{position:fixed;top:16px;right:16px;z-index:100;display:flex;flex-direction:column;gap:10px;
  max-width:min(360px,calc(100vw - 32px))}
.toast{display:flex;align-items:flex-start;gap:10px;background:var(--surface);border:1px solid var(--line);
  border-left:4px solid var(--muted);border-radius:10px;box-shadow:var(--shadow-lg);padding:12px 14px;font-size:13px;
  animation:toastIn .25s ease}
.toast.success{border-left-color:var(--success)} .toast.error{border-left-color:var(--danger)}
.toast.warn{border-left-color:var(--warning)} .toast.info{border-left-color:var(--info)}
.toast .ti{font-size:15px;line-height:1.3}
.toast.success .ti{color:var(--success)} .toast.error .ti{color:var(--danger)}
.toast.warn .ti{color:var(--warning)} .toast.info .ti{color:var(--info)}
.toast .tt{font-weight:700;margin-bottom:1px}
.toast .tx{color:var(--muted)}
.toast .tc{margin-left:auto;background:none;border:none;color:var(--muted);cursor:pointer;font-size:16px;padding:0 2px}
@keyframes toastIn{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:none}}
@media(max-width:560px){.toast-wrap{top:auto;bottom:14px;left:12px;right:12px;max-width:none}}

/* ---------- Skeleton loading ---------- */
.skeleton{background:var(--line-2);border-radius:6px;animation:pulse 1.4s ease-in-out infinite}
.skeleton.line{height:12px;margin:6px 0}
.skeleton.line.w60{width:60%}.skeleton.line.w40{width:40%}.skeleton.line.w80{width:80%}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.45}}

/* ---------- Empty / error states ----------
   One consistent shape for "nothing here yet", "couldn't load" and "no match":
   icon -> title -> one explanatory line -> the action that resolves it. The
   action was previously left to each page, so some empty states dead-ended. */
.state{text-align:center;padding:var(--sp-10) var(--sp-6);color:var(--muted)}
.state .si{width:56px;height:56px;border-radius:14px;background:var(--bg);display:grid;place-items:center;
  font-size:26px;margin:0 auto var(--sp-3);color:var(--muted);border:1px solid var(--line)}
.state.err .si{background:var(--danger-bg);color:var(--danger);border-color:var(--danger)}
.state.warn .si{background:var(--warning-bg);color:var(--warning);border-color:var(--warning)}
.state h4{font-size:var(--fs-md);color:var(--ink);margin-bottom:var(--sp-1);font-family:var(--font-head)}
.state p{font-size:var(--fs-base);max-width:42ch;margin:0 auto var(--sp-4);line-height:1.55}
.state .state-actions{display:flex;gap:var(--sp-2);justify-content:center;flex-wrap:wrap}
/* compact variant for use inside a small card or a table body */
.state.sm{padding:var(--sp-6) var(--sp-4)}
.state.sm .si{width:40px;height:40px;font-size:19px;border-radius:11px}
/* the JS-injected "no rows" cell should read like a state, not like data */
table.tbl tbody tr.empty-row td{color:var(--muted);font-size:var(--fs-base);
  text-align:center;padding:var(--sp-8) var(--sp-4)}

/* ---------- Pagination ----------
   The row count ("Showing 1-25 of 138") is useful on every register, including
   single-page ones, so .pinfo is always legible rather than muted-to-vanishing;
   the button group is what collapses when there is only one page. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line);flex-wrap:wrap;background:var(--bg-2);
  border-radius:0 0 var(--radius) var(--radius)}
.pager .pinfo{font-size:var(--fs-sm);color:var(--ink-2);font-weight:600}
.pager .pbtns{display:flex;gap:var(--sp-1);align-items:center}
.pager .pbtns button{min-width:36px;height:36px;padding:0 9px;border:1px solid var(--line);background:var(--surface);
  border-radius:8px;font-weight:700;font-size:13px;cursor:pointer;color:var(--ink-2);
  font-variant-numeric:tabular-nums}
.pager .pbtns button:hover:not([disabled]){background:var(--maroon-tint-2);border-color:var(--maroon-fg);
  color:var(--maroon-fg)}
.pager .pbtns button.active{background:var(--maroon);color:#fff;border-color:var(--maroon)}
.pager .pbtns button.active:hover{background:var(--maroon-dark);color:#fff}
.pager .pbtns button[disabled]{opacity:.4;cursor:not-allowed}
@media(max-width:560px){
  .pager{justify-content:center;gap:var(--sp-2)}
  .pager .pinfo{flex:1 1 100%;text-align:center}
  .pager .pbtns button{min-width:44px;height:44px}  /* touch target */
}

/* ---------- Utility: responsive column hiding ---------- */
@media(max-width:960px){ .col-hide-md{display:none!important} }
@media(max-width:640px){ .col-hide-sm{display:none!important} }
.no-print{}
.print-only{display:none}

/* ============================================================
   TOUCH TARGET FLOOR (44x44)
   Declared last on purpose: several of these components set an explicit
   width/height further up the file at equal specificity, so source order is
   what makes the floor win. Measured at 375x812 - before this block the nav
   rows were 41px, the icon buttons and .btn 42px, the sidebar footer links
   and the drawer close button 34px.
   ============================================================ */
@media(max-width:640px),(pointer:coarse){
  /* min-width too: icon-only buttons (e.g. the "remove line" ✕) were 34px wide */
  .btn,.btn.sm{min-height:44px;min-width:44px;justify-content:center}
  .icon-btn{width:44px;height:44px}
  .avatar{width:44px;height:44px}
  .nav a,.nav .nav-cta{min-height:44px}
  .sidebar .foot-links a{min-height:44px;padding:0 12px}
  .sidebar .close-nav{width:44px;height:44px}
  .skip-link{min-height:44px;display:inline-flex;align-items:center}
  .tabs a,.tabs button,.hub-tabs a{min-height:44px;display:inline-flex;align-items:center}
  .pager .pbtns button{min-width:44px;height:44px}
  .switch{width:52px;height:30px}
  .switch::after{width:26px;height:26px}
  .switch.on::after{left:24px}
  .peek{min-height:44px}
  /* the 24px box itself stays visually restrained - the LABEL becomes the
     44px target, which is the correct pattern for a checkbox row */
  .checkbox,.sc-row,.feature-list label,.th-pick label{min-height:44px}
  .t-actions{gap:8px}          /* 8px spacing between adjacent touch targets */
}

/* ============================================================
   PRINT STYLES
   ============================================================ */
@media print{
  /* A4 explicitly. Setting only a margin leaves the SIZE to the printer default, which is
     Letter on a US-locale machine - 279mm tall against A4's 297mm, so the last ~18mm of every
     page was being decided by whoever installed the driver rather than by this stylesheet.
     A statement that fits here must fit on the accountant's printer too. */
  @page{size:A4;margin:14mm}
  html,body{background:#fff!important;color:#000!important;font-size:11pt}

  /* (Printing from dark mode is already handled further down: a complete light-token reset for
     html[data-theme="dark"] lives in the print section near the end of this file. Do not add a
     second partial copy here - an earlier, less complete one shadowed nothing and only made it
     unclear which block was authoritative.) */
  .sidebar,.topbar,.scrim,.toast-wrap,.proto-note,.menu-toggle,.icon-btn,
  .no-print,.filters .btn,.page-head .btn,.t-actions,.pager,.dropzone,.skip-link{display:none!important}
  /* Screen navigation and filter controls are noise on a statement or voucher:
     the section tabs and the month/employee <select>s were still printing.
     The document itself already states its period and subject. */
  .hub-tabs,.tabs,.filters,.search,.chip-select,.acct-menu,.search-palette{display:none!important}
  .card-head a,a.sub{display:none!important}
  .app{display:block!important}
  .main{display:block!important}
  .content{padding:0!important;max-width:none!important}
  .print-only{display:block!important}
  a{color:#000!important;text-decoration:none}
  .card,.kpi{box-shadow:none!important;border:1px solid #bbb!important;break-inside:avoid}
  .grid{display:block!important}
  .grid>*{margin-bottom:12px}
  /* A4 fit. .table-wrap is overflow-x:auto for the screen and th is nowrap, so
     a wide register laid out at its natural 1380px inside a ~780px page and
     everything past the right edge was silently CLIPPED off the paper -
     measured on the Monthly Petty Cash Sheet: 603px / roughly four columns
     lost. Let the wrapper overflow visibly and let headers wrap so the columns
     compress to the page instead of running off it. */
  .table-wrap{overflow:visible!important;max-height:none!important}
  .table-wrap.scrollable::after{display:none!important}
  /* Natural column sizing, but every text cell may wrap at word boundaries so
     the table can compress into the page. (table-layout:fixed also fits, but
     equal-width columns shred a date column into one character per line.)
     Dates, amounts and badges stay unbreakable. */
  table.tbl{width:100%!important;table-layout:auto}
  table.tbl th,table.tbl td{white-space:normal!important;word-break:normal;
    padding:5px 6px!important;font-size:8.5pt!important;line-height:1.25!important}
  /* Clear the screen min-width on EVERY print table, not just .no-stack, so a table compresses to
     the page instead of forcing horizontal overflow (measured on the reports register). A genuinely
     wide financial register is still best printed via the built-in Excel/PDF export. */
  table.tbl{min-width:0!important}
  table.tbl .omr,table.tbl .num,table.tbl .mono,table.tbl .badge{white-space:nowrap!important}
  table.tbl th{font-size:7.5pt!important;letter-spacing:.02em}
  table.tbl.no-stack{min-width:0!important}
  table.tbl thead{display:table-header-group!important;position:static!important;width:auto!important;height:auto!important;clip:auto!important}
  /* sticky headers must not stick to a scrolling viewport on paper */
  table.tbl thead th{position:static!important;box-shadow:none!important}
  /* zebra costs toner and fights the row rules in print */
  table.tbl tbody tr:nth-child(even):not(.empty-row){background:transparent!important}
  /* Undo the mobile card-stacking cleanly. The previous set said
     "tr, td, tbody { display:table-row }" - tbody must be table-ROW-GROUP, not
     table-row. Declaring it as a row made the browser wrap the real rows in
     anonymous boxes, so thead and tbody stopped sharing a column model: the
     header row laid out full width (Date column measured 542px) while the data
     columns bunched up at 79px. Headers and data therefore did not line up on
     any printed register. */
  table.tbl{display:table!important}
  table.tbl tbody{display:table-row-group!important}
  table.tbl tfoot{display:table-footer-group!important}
  table.tbl tr{display:table-row!important}
  table.tbl td,table.tbl th{display:table-cell!important}
  table.tbl td{text-align:left!important;border-bottom:1px solid #ddd!important}
  table.tbl td.num,table.tbl th.num{text-align:right!important}
  table.tbl td::before{content:""!important}
  table.tbl th{background:#f2f2f2!important;color:#000!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  tr,.kpi,.comment,.timeline li{break-inside:avoid}
  .badge{border:1px solid #999!important;color:#000!important;background:#fff!important}
  .btn{display:none!important}
  .modal-bg{display:none!important}
  /* print header block (opt-in per page) */
  .print-head{display:flex!important;align-items:center;gap:12px;border-bottom:2px solid #000;
    padding-bottom:10px;margin-bottom:16px}
  /* The logo MUST be pinned small AND width-capped with !important: a global responsive image rule
     (img{max-width:100%}) was stretching it to the full page width and letting the height scale to
     ~320px, so the letterhead alone ate the first page. flex:none stops it growing as a flex item. */
  .print-head img{height:34px!important;width:auto!important;max-width:150px!important;flex:none!important}
  .print-head .ph-title{font-weight:800;font-size:14pt}
  .print-head .ph-meta{margin-left:auto;font-size:9pt;color:#333;text-align:right}
  /* Empty input controls are noise on a printout: the "Write a comment…" box (and any other
     textarea) printed as a blank rectangle. Posted comments still print - only the entry box goes. */
  textarea{display:none!important}
  .bar-fill{-webkit-print-color-adjust:exact;print-color-adjust:exact}
}

/* ============================================================
   THEMES v1.5 - dark mode (html[data-theme="dark"])
   Light is the default (:root). The toggle stamps data-theme;
   a head snippet applies the saved choice before first paint.
   ============================================================ */
html[data-theme="dark"]{
  /* brand */
  --maroon:#9E2B2B;            /* button/nav fills - still deep, white text ≥5.5:1 */
  --maroon-fg:#E39191;         /* brand as text/icons - readable on dark (≈6:1) */
  --maroon-dark:#B23A3A;       /* hover brightens (dark-mode convention) */
  --maroon-tint:rgba(227,145,145,.16);
  --maroon-tint-2:rgba(227,145,145,.07);
  /* Sidebar stays dark in dark mode — override the light token set back to white-on-dark. */
  --sidebar-bg:#101013;
  --sidebar-fg:#c9c9cf;
  --sidebar-muted:#9A9AA3;
  --sidebar-hover-bg:rgba(255,255,255,.06);
  --sidebar-border:rgba(255,255,255,.08);
  --sidebar-active-bg:var(--maroon);
  --sidebar-active-fg:#fff;
  --sidebar-active-badge-bg:rgba(255,255,255,.25);
  --sidebar-brand-fg:#fff;
  --violet:#B79CE8; --violet-bg:rgba(183,156,232,.14);

  /* neutrals */
  --ink:#ECECEF;
  --ink-2:#C9C9D0;
  --muted:#9C9CA6;
  --line:#2C2C32;
  --line-2:#242429;
  --surface:#1B1B1F;
  --bg:#121215;
  --bg-2:#17171B;

  /* semantic (brightened for dark bg).
     --danger was #E36A6A, which gave the Rejected/Missing/Voided pill only
     4.44:1 against its own .14-alpha tint - the one dark pair that missed AA.
     #EC8080 takes it to 5.23 without changing the hue. */
  --success:#4CBF7E; --success-bg:rgba(76,191,126,.14);
  --warning:#E09A3E; --warning-bg:rgba(224,154,62,.14);
  --danger:#EC8080;  --danger-bg:rgba(236,128,128,.14);
  --info:#7AA5F2;    --info-bg:rgba(122,165,242,.14);
  /* Ink for text sitting ON a bright semantic fill (solid buttons). White on
     these light fills is 2.3-3.2:1 - see the .btn rules further down. */
  --on-accent:#141417;

  --chart-grid:#2C2C32;
  --chart-label:#9C9CA6;
  --shadow:0 1px 2px rgba(0,0,0,.35),0 1px 3px rgba(0,0,0,.4);
  --shadow-lg:0 4px 14px rgba(0,0,0,.5),0 2px 4px rgba(0,0,0,.35);
  color-scheme:dark; /* native form controls, scrollbars */
}

/* The dropdown LIST is painted by the platform, not by the page, and its rows inherit the
   select's own colour - which in this palette is near-white --ink. Where the platform keeps a
   light system background for that popup (common on Windows), every option came out white on
   white and the month / period pickers looked empty. color-scheme alone does not settle it,
   so both halves are named here.

   Literal values, not var(): the popup is rendered outside the page's normal cascade on some
   platforms, where custom properties are not guaranteed to resolve. These are exactly the
   --surface / --ink values above. */
html[data-theme="dark"] select option,
html[data-theme="dark"] select optgroup,
html[data-theme="dark"] datalist option{
  background-color:#1B1B1F;
  color:#ECECEF;
}
/* Solid semantic buttons: the dark palette deliberately BRIGHTENS success /
   warning / danger so they read on a dark background, which means white label
   text on those fills drops to 2.3-3.2:1 - measured on the Approve / Return /
   Reject buttons in the approvals queue. Dark ink on the bright fill is the
   correct dark-mode pairing and lands at 7-8:1. */
html[data-theme="dark"] .btn.success,
html[data-theme="dark"] .btn.danger,
html[data-theme="dark"] .btn.warn{color:var(--on-accent)}
/* ...but ONLY the filled variants. The rule above outranks .btn.success.outline (its selector
   carries an extra attribute + element), so the OUTLINE success/danger buttons - whose dark-mode
   background stays the dark surface - inherited the same dark ink and their labels vanished:
   "Approve & Next" / "Reject & Next" on the review queue were green/red rings around invisible
   text. Outlines keep their accent ink on the dark surface; on hover the accent fills in, so the
   ink flips to the same dark-on-bright pairing the filled buttons use. */
html[data-theme="dark"] .btn.success.outline{color:var(--success)}
html[data-theme="dark"] .btn.danger.outline{color:var(--danger)}
html[data-theme="dark"] .btn.success.outline:hover,
html[data-theme="dark"] .btn.danger.outline:hover{color:var(--on-accent)}
html[data-theme="dark"] .btn.loading::after{color:var(--on-accent)}

/* component patches where light values were hardcoded */
html[data-theme="dark"] .badge.draft,
html[data-theme="dark"] .badge.neutral{background:#2A2A31;color:#B9B9C2;border-color:#3A3A44}
/* the badge hairlines are light-theme tints - restate them for dark so a pale
   pink/green edge does not glow around a dark pill */
html[data-theme="dark"] .badge.submitted{border-color:#3F4F6F}
html[data-theme="dark"] .badge.approved,
html[data-theme="dark"] .badge.settled,
html[data-theme="dark"] .badge.closed{border-color:#2E5943}
html[data-theme="dark"] .badge.returned,
html[data-theme="dark"] .badge.flag,
html[data-theme="dark"] .badge.pending{border-color:#664B2B}
html[data-theme="dark"] .badge.rejected,
html[data-theme="dark"] .badge.over,
html[data-theme="dark"] .badge.voided{border-color:#6A4144}
html[data-theme="dark"] .alert.warn{background:var(--warning-bg);border-color:#4a3a20;color:#E8C48C}
html[data-theme="dark"] .alert.info{background:var(--info-bg);border-color:#2b3a5c;color:#AEC6F5}
html[data-theme="dark"] .alert.danger{background:var(--danger-bg);border-color:#542b2b;color:#EEA9A9}
html[data-theme="dark"] .alert.success{background:var(--success-bg);border-color:#234534;color:#9BD9B6}
html[data-theme="dark"] .thumb .tag{background:rgba(0,0,0,.75)}
/* the brand logo needs a light chip to stay legible (dark artwork), but pure #fff
   glares on the near-black dark sidebar - tone it and add a hairline ring so it
   reads as a deliberate badge rather than a raw white block */
html[data-theme="dark"] .sidebar .brand img{background:#ededf0;box-shadow:0 0 0 1px rgba(255,255,255,.10)}
html[data-theme="dark"] .stepper .dot{background:var(--bg-2)}
html[data-theme="dark"] .switch{background:#3A3A42}
html[data-theme="dark"] .switch.on{background:var(--maroon)}
html[data-theme="dark"] .pager .pbtns button.active{background:var(--maroon);border-color:var(--maroon)}
/* inline SVG charts authored with light-theme presentation attributes */
html[data-theme="dark"] svg [stroke="#EEEEF0"]{stroke:var(--chart-grid)}
html[data-theme="dark"] svg [fill="#6B7280"]{fill:var(--chart-label)}
html[data-theme="dark"] svg [stroke="#8B1B1B"]{stroke:var(--maroon-fg)}
html[data-theme="dark"] svg [fill="#8B1B1B"]{fill:var(--maroon-fg)}

/* ---------- Per-company themes ----------
   Each company (tenant) is assigned a theme by the MICO360 admin.
   app.js stamps html[data-co-theme="…"] from the ACTIVE company;
   no attribute = default MICO maroon. Each theme defines its light
   AND dark accent set, so it composes with data-theme. ---------- */
html[data-co-theme="ocean"]{
  --maroon:#1E4E8C;--maroon-dark:#173D6E;--maroon-fg:#1E4E8C;
  --maroon-tint:#E7EDF6;--maroon-tint-2:#F4F7FB}
html[data-theme="dark"][data-co-theme="ocean"]{
  --maroon:#2C64AC;--maroon-dark:#3B77C4;--maroon-fg:#8FB7E8;
  --maroon-tint:rgba(143,183,232,.16);--maroon-tint-2:rgba(143,183,232,.07)}
html[data-co-theme="teal"]{
  --maroon:#0E6E64;--maroon-dark:#0A564E;--maroon-fg:#0E6E64;
  --maroon-tint:#E4F1EF;--maroon-tint-2:#F2F9F8}
html[data-theme="dark"][data-co-theme="teal"]{
  --maroon:#12857A;--maroon-dark:#1B9C8F;--maroon-fg:#6FC9BF;
  --maroon-tint:rgba(111,201,191,.16);--maroon-tint-2:rgba(111,201,191,.07)}
html[data-co-theme="forest"]{
  --maroon:#2F6B2F;--maroon-dark:#245424;--maroon-fg:#2F6B2F;
  --maroon-tint:#E8F1E8;--maroon-tint-2:#F4FAF4}
html[data-theme="dark"][data-co-theme="forest"]{
  --maroon:#3B843B;--maroon-dark:#4A9C4A;--maroon-fg:#8FCB8F;
  --maroon-tint:rgba(143,203,143,.16);--maroon-tint-2:rgba(143,203,143,.07)}
html[data-co-theme="plum"]{
  --maroon:#5E2A6E;--maroon-dark:#4A2157;--maroon-fg:#5E2A6E;
  --maroon-tint:#EFE7F2;--maroon-tint-2:#F8F4FA}
html[data-theme="dark"][data-co-theme="plum"]{
  --maroon:#7A3A8E;--maroon-dark:#8E4AA4;--maroon-fg:#C79AD4;
  --maroon-tint:rgba(199,154,212,.16);--maroon-tint-2:rgba(199,154,212,.07)}
/* Custom company colour: app.js injects the computed --co-custom-* values
   inline on <html> from the tenant's stored themeColor (#RRGGBB). */
html[data-co-theme="custom"]{
  --maroon:var(--co-custom);--maroon-dark:var(--co-custom-dark);--maroon-fg:var(--co-custom-fg);
  --maroon-tint:var(--co-custom-tint);--maroon-tint-2:var(--co-custom-tint-2)}
html[data-theme="dark"][data-co-theme="custom"]{
  --maroon:var(--co-custom);--maroon-dark:var(--co-custom-fg-dark);--maroon-fg:var(--co-custom-fg-dark);
  --maroon-tint:var(--co-custom-tint-dark);--maroon-tint-2:var(--co-custom-tint-2-dark)}
/* SVG accent recolor must follow the company accent too */
html[data-co-theme] svg [stroke="#8B1B1B"]{stroke:var(--maroon-fg)}
html[data-co-theme] svg [fill="#8B1B1B"]{fill:var(--maroon-fg)}
/* theme swatch dot (super-admin picker & tables) */
.th-dot{display:inline-block;width:14px;height:14px;border-radius:50%;border:2px solid var(--line);vertical-align:-2px}
.th-dot.maroon{background:#8B1B1B}.th-dot.ocean{background:#1E4E8C}
.th-dot.teal{background:#0E6E64}.th-dot.forest{background:#2F6B2F}.th-dot.plum{background:#5E2A6E}
.th-dot.custom{background:#8B1B1B}
.th-color-row{display:flex;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.th-color-row input[type="color"]{width:44px;height:36px;padding:2px;border:1px solid var(--line);
  border-radius:var(--radius-sm);background:var(--surface);cursor:pointer}
.th-color-row input[type="text"]{width:120px;text-transform:uppercase}
/* company logo in the sidebar brand keeps the MICO360 logo footprint */
.sidebar .brand img.co-logo{height:30px;max-width:132px;object-fit:contain}
/* ---------- company logo picker (provisioning + settings) ---------- */
.logo-pick{display:flex;gap:14px;align-items:flex-start;flex-wrap:wrap}
.logo-preview{width:104px;height:72px;flex:none;border:1px dashed var(--line);border-radius:10px;
  background:var(--bg-2);display:grid;place-items:center;overflow:hidden;padding:6px}
.logo-preview img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.logo-preview span{font-size:11px;color:var(--muted);font-weight:600}
.logo-preview.has-img{border-style:solid;background:#fff}
.logo-actions{flex:1;min-width:200px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.logo-actions .hint{flex-basis:100%;margin-top:2px}

.th-pick{display:flex;gap:10px;flex-wrap:wrap}
.th-pick label{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:999px;
  padding:7px 13px;cursor:pointer;font-size:12.5px;font-weight:600}
.th-pick input{accent-color:var(--maroon)}
.th-pick label:has(input:checked){border-color:var(--maroon-fg);background:var(--maroon-tint-2)}

/* ---------- Company switcher (top-left, injected by app.js) ----------
   Lives inside the sidebar, which is now THEME-AWARE (light in light mode, dark in dark),
   so every colour here comes from a token instead of a hard-coded white-on-dark value. */
.co-switch{position:relative;padding:10px 10px 0}
.co-btn{width:100%;display:flex;align-items:center;gap:10px;background:var(--sidebar-hover-bg);
  border:1px solid var(--sidebar-border);border-radius:10px;padding:9px 11px;color:var(--sidebar-fg);
  cursor:pointer;font-family:inherit;text-align:left;transition:.12s}
.co-btn:hover{border-color:var(--maroon-fg)}
.co-btn:focus-visible{outline:2px solid var(--sidebar-active-fg);outline-offset:2px}
.co-ic{width:28px;height:28px;border-radius:8px;background:var(--maroon);color:#fff;
  display:grid;place-items:center;font-size:14px;flex:none}
.co-meta{flex:1;min-width:0}
.co-name{display:block;font-weight:700;font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.co-sub{display:block;font-size:11.5px;color:var(--sidebar-muted)}
.co-caret{color:var(--sidebar-muted);font-size:11px;flex:none}
/* the dropdown is a normal popover surface so it adapts: white in light, dark in dark */
.co-menu{position:absolute;left:10px;right:10px;top:calc(100% + 6px);background:var(--surface);
  border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow-lg);
  z-index:70;overflow:hidden}
.co-menu .co-head{font-size:11.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);
  padding:10px 12px 6px;font-weight:700}
.co-item{display:flex;width:100%;align-items:center;gap:9px;padding:9px 12px;background:none;border:none;
  color:var(--ink);font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit;text-align:left}
.co-item:hover{background:var(--bg-2)}
.co-item:focus-visible{outline:2px solid var(--maroon-fg);outline-offset:-2px}
.co-item .chk{margin-left:auto;color:var(--success);visibility:hidden}
.co-item[aria-selected="true"] .chk{visibility:visible}
.co-foot{font-size:11.5px;color:var(--muted);padding:8px 12px;border-top:1px solid var(--line)}

/* Company chips (e.g. Users page "Companies" column) */
.co-chip{display:inline-flex;align-items:center;gap:5px;background:var(--maroon-tint);color:var(--maroon-fg);
  border-radius:999px;padding:2px 9px;font-size:11px;font-weight:700;white-space:nowrap}
.co-chip.more{background:var(--bg);color:var(--muted);border:1px solid var(--line)}

/* ---------- Theme toggle button (injected by app.js) ---------- */
.theme-toggle .tt-moon{display:block}
.theme-toggle .tt-sun{display:none}
html[data-theme="dark"] .theme-toggle .tt-moon{display:none}
html[data-theme="dark"] .theme-toggle .tt-sun{display:block}
.theme-toggle.floating{position:fixed;top:14px;right:14px;z-index:90;box-shadow:var(--shadow-lg)}

/* Printing always uses the light palette, regardless of theme */
@media print{
  html[data-theme="dark"]{
    /* must mirror the :root light tokens exactly - these had drifted to the
       pre-contrast-fix values, so a page printed from dark mode used the old
       failing --muted / --warning / --info */
    --maroon:#8B1B1B;--maroon-fg:#8B1B1B;--maroon-tint:#F5E7E7;--maroon-tint-2:#FBF4F4;
    --ink:#1C1C1C;--ink-2:#3f3f46;--muted:#5F6673;--line:#E4E4E7;--line-2:#EEEEF0;
    --surface:#FFFFFF;--bg:#F4F4F5;--bg-2:#FAFAFB;--sidebar-muted:#5F6673;
    --success:#1E7D46;--success-bg:#E7F3ED;--warning:#9A4A07;--warning-bg:#FBEEDD;
    --danger:#C62828;--danger-bg:#FBE9E9;--info:#1D4FD8;--info-bg:#E7EEFD;
    --violet:#6D4AAE;--violet-bg:#F0EBFB;
    color-scheme:light;
  }
  /* dark-mode button/badge ink overrides must not survive into print */
  html[data-theme="dark"] .btn.success,
  html[data-theme="dark"] .btn.danger,
  html[data-theme="dark"] .btn.warn{color:#000!important}
  .theme-toggle{display:none!important}
}

/* ===== Role-aware Help nudge (one-time card after login) =====
   z-index sits BELOW every overlay (modal-bg 50, scrim 55, mobile sidebar 60): at 95 it floated over
   open modals and covered their bottom-right primary button (Save / Import / Submit), swallowing the
   click. Page content tops out at 20, so 45 still keeps it above the page itself. */
.help-nudge{position:fixed;right:18px;bottom:18px;z-index:45;width:302px;max-width:calc(100vw - 32px);
  background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow-lg);
  padding:14px 15px 15px;display:flex;gap:11px;opacity:0;transform:translateY(10px);transition:opacity .24s ease,transform .24s ease}
.help-nudge.in{opacity:1;transform:none}
.help-nudge .hn-ic{flex:none;width:34px;height:34px;border-radius:10px;background:var(--maroon-tint-2);color:var(--maroon-fg);
  display:flex;align-items:center;justify-content:center;font-size:17px}
.help-nudge .hn-t{font-weight:800;font-size:14px;color:var(--ink);margin-bottom:2px}
.help-nudge .hn-d{font-size:12.5px;color:var(--muted);line-height:1.45;margin-bottom:10px}
.help-nudge .hn-d b{color:var(--maroon-fg)}
.help-nudge .hn-x{position:absolute;top:8px;right:9px;border:0;background:none;color:var(--muted);font-size:18px;line-height:1;
  cursor:pointer;padding:2px 6px;border-radius:6px}
.help-nudge .hn-x:hover{background:var(--bg-2);color:var(--ink)}
.help-nudge .hn-go{text-decoration:none}
@media print{ .help-nudge{display:none!important} }

/* ===== Global search palette (topbar "jump to anything") ===== */
.search-palette{
  position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:70;
  background:var(--surface); border:1px solid var(--line); border-radius:12px;
  box-shadow:var(--shadow-lg); padding:6px; max-height:min(70vh,420px);
  overflow-y:auto; overscroll-behavior:contain;
}
.search-palette .sp-sec{
  font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); padding:8px 10px 4px;
}
.search-palette .sp-item{
  display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:8px;
  color:inherit; text-decoration:none; cursor:pointer;
}
.search-palette .sp-item.on,
.search-palette .sp-item:hover{ background:var(--maroon-tint); }
.search-palette .sp-ic{ flex:none; width:20px; text-align:center; font-size:14px; opacity:.85; }
.search-palette .sp-main{ display:flex; flex-direction:column; min-width:0; line-height:1.25; }
.search-palette .sp-main{ font-size:13px; font-weight:600; }
.search-palette .sp-sub{
  font-weight:400; font-size:11.5px; color:var(--muted);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.search-palette .sp-empty{ padding:14px 12px; font-size:12.5px; color:var(--muted); text-align:center; }
@media (max-width:520px){
  .search-palette{ position:fixed; left:8px; right:8px; top:58px; }
}

/* ===== Manage-company modal: tighter, two-column layout ===== */
#mgModal .modal{max-width:560px}
#mgModal .m-body{display:flex;flex-direction:column;gap:14px}
#mgModal .grid.g-2{gap:14px;margin:0}
@media (max-width:560px){ #mgModal .grid.g-2{grid-template-columns:1fr} }
#mgModal .divider{margin:2px 0}
#mgModal .sec-lbl{font-size:10.5px;font-weight:800;letter-spacing:.06em;color:var(--muted);margin-bottom:-4px}
/* read-only summary as compact stat chips */
#mgModal .sumchips{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
#mgModal .sumchips > div{background:var(--bg-2);border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px}
#mgModal .sumchips .lbl{margin-bottom:2px}
/* feature toggles as a clean bordered list */
#mgModal .feature-list{border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden}
#mgModal .feature-list label{display:flex;align-items:center;gap:10px;padding:11px 12px;cursor:pointer;font-size:13px;margin:0}
#mgModal .feature-list label + label{border-top:1px solid var(--line)}
#mgModal .feature-list input{width:16px;height:16px;accent-color:var(--maroon);flex:none}
/* danger zone for the destructive action */
.danger-zone{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;
  border:1px solid var(--danger);background:var(--danger-tint,rgba(190,30,45,.06));
  border-radius:var(--radius-sm);padding:12px 14px}
.danger-zone .dz-text{flex:1;min-width:170px;font-size:11.5px;color:var(--muted)}

/* ===== Submit chooser (choose which drafts to submit) ===== */
.sc-list{border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;max-height:46vh;overflow-y:auto}
.sc-row{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;margin:0}
.sc-row + .sc-row{border-top:1px solid var(--line)}
.sc-row:hover{background:var(--maroon-tint)}
.sc-row input{width:16px;height:16px;accent-color:var(--maroon);flex:none}
.sc-main{display:flex;flex-direction:column;min-width:0;flex:1;line-height:1.25}
.sc-main b{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sc-sub{font-size:11.5px;color:var(--muted)}
.sc-amt{font-weight:700;font-size:13px;flex:none;white-space:nowrap}

/* ===== Clickable receipt / voucher cell (opens the file in place) =====
   These used to be bare zero-padding buttons and measured 52x20 - far under
   the 44x44 touch guideline in a table that is routinely used on a phone. */
.rcpt-open{cursor:pointer;background:none;border:none;font:inherit;
  display:inline-flex;align-items:center;gap:5px;min-height:30px;padding:2px 6px;
  border-radius:6px}
button.rcpt-open:not(.badge){color:var(--maroon-fg);font-weight:600;white-space:nowrap}
button.rcpt-open:not(.badge):hover{text-decoration:underline;background:var(--maroon-tint-2)}
@media(max-width:640px),(pointer:coarse){
  .rcpt-open{min-height:44px;padding:6px 12px}
}
button.rcpt-open.badge{cursor:pointer}
button.rcpt-open.badge:hover{filter:brightness(.96)}
.modal.file-view{padding:0;overflow:hidden}

/* version stamp pinned under the sidebar footer links */
.mico-appver{padding:6px 16px 12px;font-size:10.5px;letter-spacing:.02em;
             color:var(--muted);opacity:.85}

/* ---------------------------------------------------------------------------
   Print: cross-browser and page-break safety net.
   Appended last on purpose - everything here FILLS A GAP rather than
   overriding the rules above, which were tuned against measured output.

   `break-inside` is the modern property and is what the rules above use.
   Firefox and Safari still honour only the legacy `page-break-inside` in a
   paged context, so every element we refuse to split needs both spellings or
   it splits on those browsers - which is exactly where a voucher signature
   block or a receipt image lands half on one sheet and half on the next.
   --------------------------------------------------------------------------- */
@media print{
  .card,.kpi,.comment,.timeline li,tr,figure,.voucher,.print-block{
    page-break-inside:avoid;
  }
  thead{page-break-after:avoid}
  tfoot{page-break-before:avoid}

  /* Never strand a single line of a paragraph or a lone row on its own sheet. */
  p,li,blockquote{orphans:3;widows:3}

  /* Headings keep their first rows of content with them. */
  h1,h2,h3,h4,.page-head h1,.card-head h2{
    page-break-after:avoid;break-after:avoid-page;
  }

  /* Images - receipts especially - must fit the sheet rather than be clipped at
     the right margin, and must not be split down the middle across a page break. */
  img,svg,canvas{
    max-width:100%!important;height:auto!important;
    page-break-inside:avoid;break-inside:avoid;
  }

  /* A receipt thumbnail blown up to a full-page scan wastes the whole sheet;
     cap it so the document it belongs to stays readable alongside it. */
  .receipt-img,.evidence img,.attachment-preview img{max-height:170mm!important;object-fit:contain}

  /* Anything the page explicitly marks as screen-only. Complements .no-print
     above; both spellings exist across these pages. */
  .screen-only,[data-print="hide"]{display:none!important}

  /* Force a break where a page asks for one (statement then evidence, etc). */
  .page-break{page-break-before:always;break-before:page}
  .page-break-after{page-break-after:always;break-after:page}

  /* Long unbroken strings - a reference number, a stored file name, a URL -
     otherwise widen the table past the sheet and clip the columns after it. */
  td,th,.mono,.break-anywhere{overflow-wrap:anywhere}

  /* Backgrounds and rules are meaningful here (table header shading, badge
     borders, the voucher's ruled lines), so ask for them in every engine that
     will listen rather than relying on the user ticking "background graphics". */
  html{-webkit-print-color-adjust:exact;print-color-adjust:exact;color-adjust:exact}
}

/* ---- show/hide password (portal-wide; auth pages keep their own .peek) ---- */
.pw-wrap{position:relative;display:block}
.pw-wrap .input{width:100%;padding-right:64px}
.pw-peek{position:absolute;right:7px;top:50%;transform:translateY(-50%);background:none;border:none;
  cursor:pointer;font-family:inherit;font-size:11px;font-weight:700;letter-spacing:.5px;
  color:var(--muted);padding:6px 8px;border-radius:6px}
.pw-peek:hover{color:var(--maroon-fg);background:var(--maroon-tint-2)}
.pw-peek:focus-visible{outline:2px solid var(--maroon-fg);outline-offset:1px}

/* ---- company clock in the topbar (injected by app.js) ---- */
.topbar-clock{font-size:12px;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums;
  padding:0 10px;user-select:none}
/* Hidden until the header genuinely has room for it. It used to hide at 1100px, but with the sidebar
   still docked the bar could not fit the clock (286px) alongside the search + switcher + controls until
   ~1400px, so 1101-1400px scrolled the page sideways. The clock is decorative, so hiding it a little
   wider is the right trade over a scrolling header. */
@media (max-width:1400px){ .topbar-clock{display:none} }

/* ---- Company tab: two-column settings layout ----
   Both cards used to carry an inline max-width:640px and stack, leaving everything right of a
   640px strip blank on any desktop. Side by side on wide screens - details left, branding
   right - and stacked again below 1100px, where two columns would just squeeze both.

   The 1320px cap is gone: the settings pages are meant to use the window. It was leaving a wide
   blank margin on anything above ~1500px while the template pickers below were wrapping for want
   of the very width being thrown away. */
[data-panel="company"]{display:grid;grid-template-columns:minmax(0,1fr);gap:16px;align-items:start}
@media (min-width:1100px){ [data-panel="company"]{grid-template-columns:minmax(0,1fr) minmax(0,1fr)} }

/* A card that spans every column of the settings grid - for content that is wider than a column,
   like the document-template pickers and their tile grids. */
[data-panel="company"] .co-span{grid-column:1/-1}

/* The two pickers are STACKED, each spanning the full card, rather than sitting side by side.
   Measured: side by side gives each picker half the width, which at a 250px minimum tile is still
   only two tiles per row - exactly the cramped layout that spanning the card was meant to fix. Full
   width puts all six options on ONE row each, which is both shorter overall and far easier to
   compare, since every choice is visible at once instead of in a 2x3 block. */
.co-tpl-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:26px}

/* Draft delete on the expenses table. It had NO styling at all, so it rendered as a default browser
   button - a grey bordered box - around an emoji the fonts could not draw. Quiet like the view glyph
   it sits beside, and only turns danger-red on hover, since deleting is the one irreversible thing
   on that row. */
.row-del{display:inline-grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:8px;
  background:none;color:var(--muted);cursor:pointer;transition:background .12s,color .12s}
.row-del:hover{background:var(--danger-bg,rgba(163,29,29,.10));color:var(--danger,#a31d1d)}
.row-del:focus-visible{outline:2px solid var(--danger,#a31d1d);outline-offset:1px}

/* "Bills waiting for details": the whole row opens the completion form, so it must look like it.
   The "Needs details" pill beside the reference is a STATUS badge - it was being clicked as though
   it were the control, while the only live button sat at the far right and wrapped out of sight on
   a narrow window. Cursor + hover make the real affordance obvious. */
.aw-row{cursor:pointer;transition:background .12s}
.aw-row:hover{background:var(--maroon-tint-2)}
.aw-row:focus-visible{outline:2px solid var(--maroon-fg);outline-offset:-2px;border-radius:8px}
/* the two real buttons keep their own pointer semantics and must not inherit the row's hover */
.aw-row .btn, .aw-row .aw-thumb{cursor:pointer}

/* Template tiles. The minimum is 212px rather than 250 so that at full width all SIX options land
   on ONE row: at 250 the sixth tile wrapped on its own, which reads as an afterthought rather than
   a sixth equal choice. auto-fill still reflows to 4 / 3 / 2 / 1 as the window narrows. */
.co-tpl-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:10px}

/* ===== Reconciliation workflow stepper + close-checklist gates =====
   The count's stage, shown as the sequence it actually passes through. A badge alone said what
   state the row was in but never what came next or who had signed which step, which is the whole
   question on a control document. Horizontal on desktop, stacked on narrow screens where five
   columns would crush every label. */
.rec-steps{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px;list-style:none;margin:0;padding:0}
.rec-step{position:relative;padding:10px 8px;border-radius:var(--radius-sm);border:1px solid var(--line);background:var(--bg);text-align:center;min-width:0}
.rec-step .rec-dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:800;background:var(--line);color:var(--ink-2);margin-bottom:6px}
.rec-step .rec-step-label{display:block;font-size:12px;font-weight:700;color:var(--ink-2);overflow-wrap:anywhere}
.rec-step .rec-step-by{display:block;font-size:10.5px;line-height:1.35;color:var(--muted);margin-top:4px;overflow-wrap:anywhere}
.rec-step.done{border-color:#bce0cd;background:var(--success-bg)}
.rec-step.done .rec-dot{background:var(--success);color:#fff}
.rec-step.done .rec-step-label{color:var(--success)}
/* the current stage is the one the eye should land on first */
.rec-step.now{border-color:var(--maroon);box-shadow:0 0 0 2px color-mix(in srgb,var(--maroon) 18%,transparent)}
.rec-step.now .rec-dot{background:var(--maroon);color:#fff}
.rec-step.now .rec-step-label{color:var(--maroon)}
.rec-step.todo{opacity:.62}
@media (max-width:820px){ .rec-steps{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:460px){ .rec-steps{grid-template-columns:minmax(0,1fr)} }

/* One gate on the month-close checklist: its own condition, its own verdict, its own detail. */
.gate{display:flex;gap:12px;align-items:flex-start;padding:12px 0;border-top:1px solid var(--line)}
.gate:first-of-type{border-top:none;padding-top:0}
.gate-mark{flex:0 0 22px;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;margin-top:1px}
.gate-done .gate-mark{background:var(--success);color:#fff}
.gate-blocked .gate-mark{background:var(--danger);color:#fff}
.gate-idle .gate-mark{background:var(--line);color:var(--ink-2)}
.gate-main{flex:1 1 auto;min-width:0}
.gate-title{font-weight:700;font-size:13.5px;color:var(--ink)}
.gate-detail{font-size:12.5px;color:var(--muted);margin-top:2px}
.gate-bar{height:6px;border-radius:6px;background:var(--line);overflow:hidden;margin-top:10px}
.gate-fill{height:100%;background:var(--maroon);transition:width .3s}
.gate-fill.full{background:var(--success)}
.gate-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.gate-chips .chip-pending,.gate-chips .chip-done{font-size:11.5px;border-radius:999px;padding:4px 10px;border:1px solid var(--line);max-width:100%;overflow-wrap:anywhere}
.gate-chips .chip-pending{background:var(--bg);color:var(--ink);cursor:pointer}
.gate-chips .chip-pending:hover{border-color:var(--maroon);color:var(--maroon)}
.gate-chips .chip-done{background:var(--success-bg);color:var(--success);border-color:#bce0cd}
/* Its own class rather than the flex utility trio - see renderFlow: updateVariance finds the
   variance row with a form-wide `.flex.items-center.justify-between` query, and this block is
   earlier in the same form. */
.rec-flow-head{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:12px}

/* ===== OCR trust-and-review (§7.4) =====
   A machine-filled box used to be indistinguishable from one somebody typed; the only trace was a
   sentence under the row naming fields, which scrolls away and does not say which box it means.
   These mark the field itself, and the mark clears the moment a human edits it - at which point
   the value is theirs.

   Colour is not the only signal: each marked field also carries a title, so the provenance
   survives for anyone who cannot see the tint. */
.input.ocr-filled, .select.ocr-filled, .textarea.ocr-filled {
  border-color: var(--info, #2563eb);
  background-image: linear-gradient(0deg, color-mix(in srgb, var(--info, #2563eb) 7%, transparent), color-mix(in srgb, var(--info, #2563eb) 7%, transparent));
  box-shadow: inset 3px 0 0 0 var(--info, #2563eb);
}
/* the reader disagreed with what was already typed - the typed value was kept, so this is a
   flag to look at, not an error to fix */
.input.ocr-differs, .select.ocr-differs, .textarea.ocr-differs {
  border-color: var(--warn, #b45309);
  box-shadow: inset 3px 0 0 0 var(--warn, #b45309);
}
.ocr-note .ocr-confirm { margin-top: 6px; }
@media (prefers-reduced-motion: no-preference) {
  .input.ocr-filled, .select.ocr-filled, .textarea.ocr-filled { transition: box-shadow .2s, border-color .2s, background-image .2s }
}

/* ===== Cash reconciliation: count entry, arithmetic, settlement (§6.3) ===== */
/* The sum written out - counted − book = difference - so the headline figure below it can be
   checked rather than trusted. */
.rec-sum{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;font-size:13px}
.rec-sum-part{display:flex;flex-direction:column;gap:2px}
.rec-sum-lbl{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.rec-sum-op{color:var(--muted);font-weight:700;padding-bottom:1px}
/* the counted box while it is being driven by the denomination table: still readable, visibly
   not typed into */
.input.is-derived{background:var(--bg);color:var(--ink-2);cursor:default}
/* the denomination card is inert until its mode is chosen, so it cannot be mistaken for a second
   figure that counts */
#denomCard{opacity:.6;transition:opacity .2s}
#denomCard.is-active{opacity:1}

/* The table row an approver is currently acting on. Approve/Return/Reject all open a modal
   over the top, and until this there was nothing tying the dialog to the claim that opened
   it. Drawn as a tinted row with a bar down the leading edge, so it reads at a glance even
   with the dialog covering the middle of the screen. */
tr.is-acting > td { background: var(--maroon-tint); }
tr.is-acting > td:first-child { box-shadow: inset 3px 0 0 var(--maroon); }

/* ============================================================================
   PREMIUM POLISH LAYER  (#134) — additive global motion + micro-interactions.
   Brand, layout, spacing and content are unchanged. Everything here animates
   transform/opacity only (no layout thrash). Reveal/entrance effects are scoped
   under html.js-motion, so with JS off nothing is ever hidden; a JS safety-net
   also force-reveals stragglers. The reduced-motion block at the end is a full
   killswitch.
   ============================================================================ */

/* Smooth, consistent transitions on the things people actually interact with. */
.card, .kpi{ transition: transform .18s cubic-bezier(.22,1,.36,1), box-shadow .2s ease, border-color .18s ease; }
.btn{ transition: transform .12s ease, background-color .16s ease, border-color .16s ease, box-shadow .16s ease, color .16s ease; }
table.tbl tbody tr:not(.empty-row){ transition: background-color .15s ease; }
.badge, .pill, .chip, .tab, [role="tab"]{ transition: background-color .16s ease, color .16s ease, border-color .16s ease; }
a:not(.btn){ transition: color .15s ease, background-color .15s ease; }

/* Hover LIFT only on genuinely interactive cards — KPI tiles and card-in-a-link.
   Large static content panels stay put (a whole panel jumping on hover reads as
   noise, not polish); they still get the scroll entrance below. */
.kpi:hover,
a .card:hover, a.card:hover, .card.clickable:hover, .card[role="button"]:hover, [data-href].card:hover{
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--line);
}

/* Buttons: a touch of depth on hover, tactile press on active. */
.btn:hover{ box-shadow: var(--shadow); }
.btn:active{ transform: translateY(1px); }

/* Clearer, on-brand keyboard focus ring layered over existing focus styles. */
:where(a,button,.btn,input,select,textarea,[tabindex]):focus-visible{
  outline: 2px solid var(--maroon-fg);
  outline-offset: 2px;
}

/* Page entrance: the main content settles in once per navigation. */
html.js-motion main.content{ animation: pageIn .42s cubic-bezier(.22,1,.36,1) both; }
@keyframes pageIn{ from{ opacity:0; transform: translateY(8px); } to{ opacity:1; transform:none; } }

/* Scroll-reveal: cards/tiles rise + fade as they enter the viewport. Hidden state
   exists ONLY under js-motion, so a no-JS page shows everything immediately. */
html.js-motion .reveal{ opacity:0; transform: translateY(14px); }
html.js-motion .reveal.in{
  opacity:1; transform:none;
  transition: opacity .5s ease, transform .55s cubic-bezier(.22,1,.36,1);
}

/* Full killswitch for users who ask for less motion. */
@media (prefers-reduced-motion: reduce){
  .card, .kpi, .btn, table.tbl tbody tr, .badge, .pill, .chip, .tab, a{ transition: none !important; }
  .kpi:hover, a .card:hover, a.card:hover, .card.clickable:hover{ transform: none !important; }
  .btn:active{ transform: none !important; }
  html.js-motion main.content{ animation: none !important; }
  html.js-motion .reveal{ opacity: 1 !important; transform: none !important; }
  html.js-motion .reveal.in{ transition: none !important; }
}

/* ============================================================================
   PRINT HARDENING (#135) — patches the two gaps the earlier print blocks predate.
   The three @media print blocks above already set A4 @page + margins, hide all
   chrome/buttons/filters/nav/modals, fit tables to the sheet, reset dark->light,
   and control page breaks / orphans / widows / image sizing. This only adds what
   they could not know about.
   ============================================================================ */
@media print{
  /* The scroll-reveal polish (#134) parks .card/.kpi at opacity:0 until the
     IntersectionObserver (or a 1.5s safety net) reveals them, and animates the
     content container in. A print fired before that completes would drop whole
     cards off the paper. Force everything visible, cancel the one-shot entrance,
     and kill transitions so nothing prints blank, faded or mid-shift. */
  html.js-motion .reveal{ opacity:1 !important; transform:none !important; }
  html.js-motion main.content{ animation:none !important; }
  .card,.kpi,.btn,tr,.badge,.chip,.tab,a{ transition:none !important; }

  /* A signature block is a single unit: the ruled line, the printed name and the
     date must land together on one sheet, never split across a page break. */
  .sign-box,.sign-grid,.signature,.sig-block,.approval-sign{
    break-inside:avoid; page-break-inside:avoid;
  }

  /* Belt-and-suspenders for on-screen-only chrome that pages may add later - the
     quick-action bar, a floating action button, and the live topbar clock are all
     noise on paper (the topbar itself is already hidden above). */
  .fab,.quick-actions,.speed-dial,.topbar-clock,.notif-badge,.reveal[hidden]{
    display:none !important;
  }
}

/* ============================================================================
   PUBLIC DOCUMENT PAGES (#136/#137) — one shared premium chrome for the legal
   and Help pages (terms, privacy, help, change-password, reset-password), so
   every public-facing page carries the same corporate header, footer and type.
   Pages used to hand-roll these rules inline and drifted apart; this is the
   single source of truth. Fully responsive; print handled by the @media print
   blocks above (.doc-hero .back / .doc-toc / .theme-toggle are hidden on paper).
   ============================================================================ */
.doc{ max-width:880px; margin:0 auto; padding:0 20px 96px; }

/* Branded header band — logo + title, a back link and the theme toggle. */
.doc-hero{
  display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  padding:22px 0 18px; margin-bottom:22px; border-bottom:1px solid var(--line);
}
.doc-hero .brand{ display:flex; align-items:center; gap:12px; }
.doc-hero img{ height:42px; background:#fff; border-radius:10px; padding:5px; border:1px solid var(--line); }
.doc-hero h1{ font-size:clamp(21px,3.4vw,27px); line-height:1.15; letter-spacing:-.01em; }
.doc-hero .sub{ color:var(--muted); font-size:13px; font-weight:500; margin-top:2px; }
.doc-hero .spacer{ margin-left:auto; }
.doc-hero .actions{ display:flex; align-items:center; gap:8px; margin-left:auto; }
.doc .meta-line{
  color:var(--muted); font-size:12.5px; margin:0 0 20px;
  display:flex; gap:8px 16px; flex-wrap:wrap; align-items:center;
}
.doc .meta-line .pill-soft{
  background:var(--maroon-tint-2); color:var(--maroon-fg); border:1px solid var(--line);
  border-radius:999px; padding:2px 10px; font-weight:600; font-size:11.5px;
}

/* Optional in-page table of contents (auto-filled by app.js on .doc pages). */
.doc-toc{
  background:var(--bg-2); border:1px solid var(--line); border-radius:var(--radius);
  padding:14px 16px; margin:0 0 26px;
}
.doc-toc h2{ font-size:12px!important; text-transform:uppercase; letter-spacing:.06em;
  color:var(--muted); margin:0 0 8px!important; border:0!important; padding:0!important; }
.doc-toc ol{ margin:0; padding:0; list-style:none; columns:2; column-gap:26px; }
.doc-toc li{ margin:0 0 5px; break-inside:avoid; }
.doc-toc a{ color:var(--ink-2); font-size:13px; text-decoration:none; }
.doc-toc a:hover{ color:var(--maroon-fg); text-decoration:underline; }
@media(max-width:620px){ .doc-toc ol{ columns:1; } }

/* Body copy + sections — comfortable measure, clear hierarchy. */
.doc h2{
  font-size:17.5px; margin:30px 0 9px; padding-top:16px; border-top:1px solid var(--line);
  letter-spacing:-.005em; scroll-margin-top:16px;
}
.doc h2:first-of-type{ border-top:0; padding-top:0; }
.doc p, .doc li{ font-size:14px; line-height:1.68; color:var(--ink-2); }
.doc ul, .doc ol{ margin:8px 0 12px 20px; }
.doc li{ margin-bottom:5px; }
.doc a{ color:var(--maroon-fg); }
.doc .lead{ font-size:15.5px; line-height:1.6; color:var(--ink); }
.doc .callout{
  background:var(--bg-2); border:1px solid var(--line); border-left:3px solid var(--maroon);
  border-radius:8px; padding:12px 14px; margin:16px 0; font-size:13.3px; color:var(--ink-2);
}

/* Shared footer band. */
.doc-foot{
  margin-top:40px; padding-top:16px; border-top:1px solid var(--line);
  font-size:12.5px; color:var(--muted); display:flex; gap:6px 14px;
  flex-wrap:wrap; align-items:center;
}
.doc-foot a{ display:inline-flex; align-items:center; min-height:36px; padding:0 8px;
  border-radius:6px; color:var(--ink-2); }
.doc-foot a:hover{ background:var(--bg-2); text-decoration:none; color:var(--maroon-fg); }
.doc-foot .copy{ margin-left:auto; }
@media(max-width:640px){ .doc-foot .copy{ margin-left:0; width:100%; } }

/* The Help link is for signed-in users only; app.js adds html.authed after login,
   which reveals it with its natural display (never shown to a public visitor). */
html:not(.authed) .js-auth-only{ display:none !important; }
@media print{ .doc-hero .back, .doc-toc, .doc-hero .actions{ display:none!important; } .doc h2{ border-top:0; } }

/* Tables inside a document page (e.g. the Privacy data-collection tables) — plain
   <table> markup, not the portal's table.tbl, so give it its own clean styling. */
.doc table{ width:100%; border-collapse:collapse; margin:14px 0 20px; font-size:13px; }
.doc th,.doc td{ border:1px solid var(--line); padding:8px 11px; text-align:left; vertical-align:top; line-height:1.5; }
.doc thead th{ background:var(--bg-2); font-weight:700; color:var(--ink); }
.doc tbody tr:nth-child(even){ background:var(--bg-2); }
@media(max-width:640px){ .doc table{ display:block; overflow-x:auto; white-space:normal; } }

/* ============================================================
   D3 - no horizontal overflow on small viewports (390px audit).
   The page body must never scroll sideways; wide content (tables,
   toolbars, long unbroken strings) wraps or scrolls INSIDE its own
   container instead of pushing the whole shell wider than the screen.
   ============================================================ */
/* Flex/grid children default to min-width:auto and refuse to shrink below
   their content, which is the usual reason a wide table or a long token
   drags the shell past the viewport. Let the column and its cards shrink;
   the contained scrollers below then take over. */
.main,.content,.card,.card-pad,.page-head{min-width:0}
/* Replaced/large elements never exceed their column. */
.content img,.content svg,.content canvas,.content video{max-width:100%;height:auto}
.content pre{max-width:100%;overflow-x:auto}
/* A very long unbroken string (an email, a URL, a doc number) can force width
   on its own; let those break rather than stretch the layout. */
.card-pad,.cell-user,.tbl td,.tbl th{overflow-wrap:anywhere}
@media(max-width:640px){
  /* Any table that slipped through WITHOUT a .table-wrap still scrolls itself
     instead of the page. The child combinator leaves wrapped tables (and their
     sticky .table-wrap.tall headers) untouched - it only rescues bare ones. */
  .card>table.tbl,.card-pad>table.tbl,.content>table.tbl{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch}
  /* Button rows / toolbars / segmented headers wrap rather than force width. */
  .page-head,.card-head,.filters,.btn-row,.actions,.toolbar,.seg,.chips,.tabs{flex-wrap:wrap}
  /* Modal/popover never wider than the viewport gutter. */
  .modal{max-width:calc(100vw - 24px)}
}
/* Last-resort net: clip stray horizontal overflow at the shell edge so no single
   element can make the page scroll sideways. clip (not hidden) keeps the sticky
   topbar working, and the rules above already give real content a scroller so
   nothing legible is hidden. */
@media(max-width:960px){ html{overflow-x:clip} }

/* ============================================================
   Vendor combobox - ONE field that browses the company's managed vendors AND
   accepts free text. Shared by Add Expense (every line) and the expense detail
   "Edit line" modal, driven by assets/vendor-combo.js; it lives here rather than
   in one page's inline <style> so both screens look identical.
   The ▾ caret shows only when there are vendors to offer (the script adds the
   class), and the list is a body-level popup so it escapes the modal's clipping.
   ============================================================ */
.in-vendor.vendor-combo{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%238A8A93' stroke-width='1.6' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center;padding-right:28px}
.vendor-pop{position:absolute;z-index:1200;background:var(--surface,#fff);color:var(--ink,#1c1c1c);
  border:1px solid var(--line,#e4e4e7);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.16);
  max-height:240px;overflow:auto;min-width:160px;padding:4px}
.vendor-pop .vp-item{padding:8px 12px;border-radius:7px;cursor:pointer;font-size:14px;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vendor-pop .vp-item:hover,.vendor-pop .vp-item.active{background:var(--bg-2,#f4f4f5);font-weight:600}
.vendor-pop .vp-empty{padding:8px 12px;color:var(--muted,#6b7280);font-size:12.5px;line-height:1.35}
